develop #13
|
@ -7,12 +7,12 @@ import sys
|
|||
|
||||
# Application imports
|
||||
from utils.event_system import EventSystem
|
||||
from utils.endpoint_registry import EndpointRegistry
|
||||
from utils.keybindings import Keybindings
|
||||
from utils.logger import Logger
|
||||
from utils.settings_manager.manager import SettingsManager
|
||||
|
||||
|
||||
|
||||
# NOTE: Threads WILL NOT die with parent's destruction.
|
||||
def threaded_wrapper(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
|
@ -39,7 +39,6 @@ def sizeof_fmt_def(num, suffix="B"):
|
|||
builtins.app_name = "SolarFM"
|
||||
builtins.keybindings = Keybindings()
|
||||
builtins.event_system = EventSystem()
|
||||
builtins.endpoint_registry = EndpointRegistry()
|
||||
builtins.settings_manager = SettingsManager()
|
||||
|
||||
settings_manager.load_settings()
|
||||
|
@ -52,7 +51,7 @@ builtins.logger = Logger(settings_manager.get_home_config_path(), \
|
|||
builtins.threaded = threaded_wrapper
|
||||
builtins.daemon_threaded = daemon_threaded_wrapper
|
||||
builtins.sizeof_fmt = sizeof_fmt_def
|
||||
builtins.event_sleep_time = 0.05
|
||||
|
||||
|
||||
|
||||
def custom_except_hook(exc_type, exc_value, exc_traceback):
|
||||
|
@ -62,5 +61,4 @@ def custom_except_hook(exc_type, exc_value, exc_traceback):
|
|||
|
||||
logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
|
||||
|
||||
|
||||
sys.excepthook = custom_except_hook
|
||||
|
|
|
@ -25,7 +25,7 @@ class Application(IPCServer):
|
|||
try:
|
||||
self.create_ipc_listener()
|
||||
except Exception:
|
||||
...
|
||||
self.socket_realization_check()
|
||||
|
||||
if not self.is_ipc_alive:
|
||||
for arg in unknownargs + [args.new_tab,]:
|
||||
|
@ -35,6 +35,16 @@ class Application(IPCServer):
|
|||
|
||||
raise AppLaunchException(f"{app_name} IPC Server Exists: Will send path(s) to it and close...")
|
||||
|
||||
self.setup_debug_hook()
|
||||
|
||||
Window(args, unknownargs)
|
||||
|
||||
|
||||
def socket_realization_check(self):
|
||||
self.send_test_ipc_message()
|
||||
self.create_ipc_listener()
|
||||
|
||||
def setup_debug_hook(self):
|
||||
try:
|
||||
# kill -SIGUSR2 <pid> from Linux/Unix or SIGBREAK signal from Windows
|
||||
signal.signal(
|
||||
|
@ -44,5 +54,3 @@ class Application(IPCServer):
|
|||
except ValueError:
|
||||
# Typically: ValueError: signal only works in main thread
|
||||
...
|
||||
|
||||
Window(args, unknownargs)
|
||||
|
|
|
@ -65,9 +65,11 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
|||
FileSystemActions()
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe("shutting_down", self._shutting_down)
|
||||
event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
|
||||
event_system.subscribe("generate_file_views", self._generate_file_views)
|
||||
event_system.subscribe("clear_notebooks", self.clear_notebooks)
|
||||
|
||||
event_system.subscribe("set_window_title", self._set_window_title)
|
||||
event_system.subscribe("unset_selected_files_views", self._unset_selected_files_views)
|
||||
event_system.subscribe("get_current_state", self.get_current_state)
|
||||
|
@ -107,6 +109,10 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
|||
FileExistsWidget()
|
||||
SaveLoadWidget()
|
||||
|
||||
def _shutting_down(self):
|
||||
if not settings_manager.is_trace_debug():
|
||||
self.fm_controller.save_state()
|
||||
|
||||
def reload_plugins(self, widget=None, eve=None):
|
||||
self.plugins.reload_plugins()
|
||||
|
||||
|
@ -162,28 +168,22 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
|||
event_system.emit("tear_down")
|
||||
|
||||
|
||||
@endpoint_registry.register(rule="go_home")
|
||||
def go_home(self, widget=None, eve=None):
|
||||
self.builder.get_object("go_home").released()
|
||||
|
||||
@endpoint_registry.register(rule="refresh_tab")
|
||||
def refresh_tab(self, widget=None, eve=None):
|
||||
self.builder.get_object("refresh_tab").released()
|
||||
|
||||
@endpoint_registry.register(rule="go_up")
|
||||
def go_up(self, widget=None, eve=None):
|
||||
self.builder.get_object("go_up").released()
|
||||
|
||||
@endpoint_registry.register(rule="grab_focus_path_entry")
|
||||
def grab_focus_path_entry(self, widget=None, eve=None):
|
||||
self.builder.get_object("path_entry").grab_focus()
|
||||
|
||||
@endpoint_registry.register(rule="tggl_top_main_menubar")
|
||||
def tggl_top_main_menubar(self, widget=None, eve=None):
|
||||
top_main_menubar = self.builder.get_object("top_main_menubar")
|
||||
top_main_menubar.hide() if top_main_menubar.is_visible() else top_main_menubar.show()
|
||||
|
||||
@endpoint_registry.register(rule="open_terminal")
|
||||
def open_terminal(self, widget=None, eve=None):
|
||||
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# Python imports
|
||||
import os
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
@ -9,55 +8,13 @@ gi.require_version('Gtk', '3.0')
|
|||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from .sfm_builder import SFMBuilder
|
||||
from .widgets.dialogs.message_widget import MessageWidget
|
||||
from .widgets.dialogs.user_pass_widget import UserPassWidget
|
||||
|
||||
from shellfm.windows.controller import WindowController
|
||||
from utils.types.state import State
|
||||
from plugins.plugins_controller import PluginsController
|
||||
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class State:
|
||||
fm_controller: any = None
|
||||
notebooks: any = None
|
||||
wid: int = None
|
||||
tid: int = None
|
||||
tab: type = None
|
||||
icon_grid: gi.overrides.Gtk.IconView = None
|
||||
store: gi.overrides.Gtk.ListStore = None
|
||||
uris: [] = None
|
||||
uris_raw: [] = None
|
||||
selected_files: [] = None
|
||||
to_copy_files: [] = None
|
||||
to_cut_files: [] = None
|
||||
message_dialog: type = None
|
||||
user_pass_dialog: type = None
|
||||
|
||||
|
||||
|
||||
class SFMBuilder(Gtk.Builder):
|
||||
"""docstring for SFMBuilder."""
|
||||
|
||||
def __init__(self):
|
||||
super(SFMBuilder, self).__init__()
|
||||
|
||||
self.objects = {}
|
||||
|
||||
def get_object(self, id: str, use_gtk: bool = True) -> any:
|
||||
if not use_gtk:
|
||||
return self.objects[id]
|
||||
|
||||
return super(SFMBuilder, self).get_object(id)
|
||||
|
||||
def expose_object(self, id: str, object: any, use_gtk: bool = True) -> None:
|
||||
if not use_gtk:
|
||||
self.objects[id] = object
|
||||
else:
|
||||
super(SFMBuilder, self).expose_object(id, object)
|
||||
|
||||
def dereference_object(self, id: str) -> None:
|
||||
del self.objects[id]
|
||||
from shellfm.windows.controller import WindowController
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ class WindowMixin(TabMixin):
|
|||
self.set_window_title()
|
||||
|
||||
if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 1: # l-click
|
||||
if self.ctrl_down:
|
||||
if eve.state & Gdk.ModifierType.CONTROL_MASK:
|
||||
self.dnd_left_primed = 0
|
||||
|
||||
if self.single_click_open: # FIXME: need to find a way to pass the model index
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class SFMBuilder(Gtk.Builder):
|
||||
"""docstring for SFMBuilder."""
|
||||
|
||||
def __init__(self):
|
||||
super(SFMBuilder, self).__init__()
|
||||
|
||||
self.objects = {}
|
||||
|
||||
def get_object(self, id: str, use_gtk: bool = True) -> any:
|
||||
if not use_gtk:
|
||||
return self.objects[id]
|
||||
|
||||
return super(SFMBuilder, self).get_object(id)
|
||||
|
||||
def expose_object(self, id: str, object: any, use_gtk: bool = True) -> None:
|
||||
if not use_gtk:
|
||||
self.objects[id] = object
|
||||
else:
|
||||
super(SFMBuilder, self).expose_object(id, object)
|
||||
|
||||
def dereference_object(self, id: str) -> None:
|
||||
del self.objects[id]
|
|
@ -77,7 +77,7 @@ class UIMixin(PaneMixin, WindowMixin):
|
|||
|
||||
scroll_win = notebook.get_children()[-1]
|
||||
icon_grid = scroll_win.get_children()[0]
|
||||
self._focus_last_visible_notebook(icon_grid)
|
||||
# self._focus_last_visible_notebook(icon_grid)
|
||||
except UIMixinException as e:
|
||||
logger.info("\n: The saved session might be missing window data! :\nLocation: ~/.config/solarfm/session.json\nFix: Back it up and delete it to reset.\n")
|
||||
logger.debug(repr(e))
|
||||
|
|
|
@ -80,7 +80,7 @@ class WindowMixin(TabMixin):
|
|||
self.set_window_title()
|
||||
|
||||
if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 1: # l-click
|
||||
if self.ctrl_down:
|
||||
if eve.state & Gdk.ModifierType.CONTROL_MASK:
|
||||
self.dnd_left_primed = 0
|
||||
|
||||
if self.single_click_open: # FIXME: need to find a way to pass the model index
|
||||
|
|
|
@ -20,8 +20,6 @@ class IconGridWidget(Gtk.IconView):
|
|||
def __init__(self):
|
||||
super(IconGridWidget, self).__init__()
|
||||
|
||||
# self._store = None
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._set_up_dnd()
|
||||
|
|
|
@ -88,11 +88,7 @@ class Window(Gtk.ApplicationWindow):
|
|||
cr.set_operator(cairo.OPERATOR_OVER)
|
||||
|
||||
|
||||
def _tear_down(self, widget=None, eve=None):
|
||||
if not settings_manager.is_trace_debug():
|
||||
self._controller.fm_controller.save_state()
|
||||
|
||||
def _tear_down(self, widget = None, eve = None):
|
||||
event_system.emit("shutting_down")
|
||||
settings_manager.clear_pid()
|
||||
time.sleep(event_sleep_time)
|
||||
|
||||
Gtk.main_quit()
|
||||
|
|
|
@ -24,32 +24,12 @@ class PluginBase:
|
|||
self._event_system = None
|
||||
|
||||
|
||||
def set_fm_event_system(self, fm_event_system):
|
||||
def run(self):
|
||||
"""
|
||||
Requests Key: 'pass_fm_events': "true"
|
||||
Must define in plugin if "pass_fm_events" is set to "true" string.
|
||||
Must define regardless if needed and can 'pass' if plugin doesn't need it.
|
||||
Is intended to be used to setup internal signals or custom Gtk Builders/UI logic.
|
||||
"""
|
||||
self._event_system = fm_event_system
|
||||
|
||||
def set_ui_object_collection(self, ui_objects):
|
||||
"""
|
||||
Requests Key: "pass_ui_objects": [""]
|
||||
Request reference to a UI component. Will be passed back as array to plugin.
|
||||
Must define in plugin if set and an array of valid glade UI IDs is given.
|
||||
"""
|
||||
self._ui_objects = ui_objects
|
||||
|
||||
|
||||
def clear_children(self, widget: type) -> None:
|
||||
""" Clear children of a gtk widget. """
|
||||
for child in widget.get_children():
|
||||
widget.remove(child)
|
||||
|
||||
def subscribe_to_events(self):
|
||||
self._event_system.subscribe("update_state_info_plugins", self._update_fm_state_info)
|
||||
|
||||
def _update_fm_state_info(self, state):
|
||||
self._fm_state = state
|
||||
raise PluginBaseException("Method hasn't been overriden...")
|
||||
|
||||
def generate_reference_ui_element(self):
|
||||
"""
|
||||
|
@ -59,12 +39,26 @@ class PluginBase:
|
|||
"""
|
||||
raise PluginBaseException("Method hasn't been overriden...")
|
||||
|
||||
def run(self):
|
||||
def set_ui_object_collection(self, ui_objects):
|
||||
"""
|
||||
Must define regardless if needed and can 'pass' if plugin doesn't need it.
|
||||
Is intended to be used to setup internal signals or custom Gtk Builders/UI logic.
|
||||
Requests Key: "pass_ui_objects": [""]
|
||||
Request reference to a UI component. Will be passed back as array to plugin.
|
||||
Must define in plugin if set and an array of valid glade UI IDs is given.
|
||||
"""
|
||||
raise PluginBaseException("Method hasn't been overriden...")
|
||||
self._ui_objects = ui_objects
|
||||
|
||||
def set_fm_event_system(self, fm_event_system):
|
||||
"""
|
||||
Requests Key: 'pass_fm_events': "true"
|
||||
Must define in plugin if "pass_fm_events" is set to "true" string.
|
||||
"""
|
||||
self._event_system = fm_event_system
|
||||
|
||||
def subscribe_to_events(self):
|
||||
self._event_system.subscribe("update_state_info_plugins", self._update_fm_state_info)
|
||||
|
||||
def _update_fm_state_info(self, state):
|
||||
self._fm_state = state
|
||||
|
||||
def _connect_builder_signals(self, caller_class, builder):
|
||||
classes = [caller_class]
|
||||
|
@ -94,3 +88,9 @@ class PluginBase:
|
|||
reload_package_recursive(path, module_dict[path.stem].__dict__)
|
||||
|
||||
reload_package_recursive(Path(plugin_path).parent, module_dict_main["module_dict_main"])
|
||||
|
||||
|
||||
def clear_children(self, widget: type) -> None:
|
||||
""" Clear children of a gtk widget. """
|
||||
for child in widget.get_children():
|
||||
widget.remove(child)
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from .singleton import Singleton
|
||||
|
||||
|
||||
|
||||
class EndpointRegistry(Singleton):
|
||||
def __init__(self):
|
||||
self._endpoints = {}
|
||||
|
||||
def register(self, rule, **options):
|
||||
def decorator(f):
|
||||
self._endpoints[rule] = f
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def get_endpoints(self):
|
||||
return self._endpoints
|
|
@ -43,13 +43,13 @@ class IPCServer(Singleton):
|
|||
if os.path.exists(self._ipc_address) and settings.is_dirty_start():
|
||||
os.unlink(self._ipc_address)
|
||||
|
||||
listener = Listener(address=self._ipc_address, family="AF_UNIX", authkey=self._ipc_authkey)
|
||||
listener = Listener(address = self._ipc_address, family = "AF_UNIX", authkey = self._ipc_authkey)
|
||||
|
||||
elif "unsecured" not in self._conn_type:
|
||||
listener = Listener((self._ipc_address, self._ipc_port), authkey=self._ipc_authkey)
|
||||
listener = Listener((self._ipc_address, self._ipc_port), authkey = self._ipc_authkey)
|
||||
else:
|
||||
listener = Listener((self._ipc_address, self._ipc_port))
|
||||
|
||||
|
||||
self.is_ipc_alive = True
|
||||
self._run_ipc_loop(listener)
|
||||
|
||||
|
@ -61,15 +61,14 @@ class IPCServer(Singleton):
|
|||
start_time = time.perf_counter()
|
||||
GLib.idle_add(self._handle_ipc_message, *(conn, start_time,))
|
||||
except Exception as e:
|
||||
...
|
||||
logger.debug( repr(e) )
|
||||
|
||||
listener.close()
|
||||
|
||||
def _handle_ipc_message(self, conn, start_time) -> None:
|
||||
while True:
|
||||
msg = conn.recv()
|
||||
if settings_manager.is_debug():
|
||||
print(msg)
|
||||
logger.debug(msg)
|
||||
|
||||
if "FILE|" in msg:
|
||||
file = msg.split("FILE|")[1].strip()
|
||||
|
@ -103,6 +102,25 @@ class IPCServer(Singleton):
|
|||
conn.send(message)
|
||||
conn.close()
|
||||
except ConnectionRefusedError as e:
|
||||
print("Connection refused...")
|
||||
logger.error("Connection refused...")
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
logger.error( repr(e) )
|
||||
|
||||
|
||||
def send_test_ipc_message(self, message: str = "Empty Data...") -> None:
|
||||
try:
|
||||
if self._conn_type == "socket":
|
||||
conn = Client(address=self._ipc_address, family="AF_UNIX", authkey=self._ipc_authkey)
|
||||
elif "unsecured" not in self._conn_type:
|
||||
conn = Client((self._ipc_address, self._ipc_port), authkey=self._ipc_authkey)
|
||||
else:
|
||||
conn = Client((self._ipc_address, self._ipc_port))
|
||||
|
||||
conn.send(message)
|
||||
conn.close()
|
||||
except ConnectionRefusedError as e:
|
||||
if self._conn_type == "socket":
|
||||
logger.error("IPC Socket no longer valid.... Removing.")
|
||||
os.unlink(self._ipc_address)
|
||||
except Exception as e:
|
||||
logger.error( repr(e) )
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
Utils module
|
||||
"""
|
|
@ -0,0 +1,27 @@
|
|||
# Python imports
|
||||
from dataclasses import dataclass
|
||||
|
||||
# Lib imports
|
||||
from gi.overrides.Gtk import IconView
|
||||
from gi.overrides.Gtk import ListStore
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class State:
|
||||
fm_controller: any = None
|
||||
notebooks: any = None
|
||||
wid: int = None
|
||||
tid: int = None
|
||||
tab: type = None
|
||||
icon_grid: IconView = None
|
||||
store: ListStore = None
|
||||
uris: list = None
|
||||
uris_raw: list = None
|
||||
selected_files: list = None
|
||||
to_copy_files: list = None
|
||||
to_cut_files: list = None
|
||||
message_dialog: type = None
|
||||
user_pass_dialog: type = None
|
Loading…
Reference in New Issue