From a7fbc6eadb9731e0ba81c0d70b088231ec680641 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Sat, 24 Sep 2022 15:40:21 -0500
Subject: [PATCH 01/30] Updated .gitignore, renamed plugins file
---
.gitignore | 2 +-
.../solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py | 4 ++--
.../solarfm/plugins/{plugins.py => plugins_controller.py} | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
rename src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/{plugins.py => plugins_controller.py} (98%)
diff --git a/.gitignore b/.gitignore
index a7059ec..cc28be0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+docs/
.idea/
*.zip
@@ -140,4 +141,3 @@ dmypy.json
# Cython debug symbols
cython_debug/
-
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
index 6181d1f..fbd5e89 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
@@ -9,7 +9,7 @@ from gi.repository import GLib
# Application imports
from trasher.xdgtrash import XDGTrash
from shellfm.windows.controller import WindowController
-from plugins.plugins import Plugins
+from plugins.plugins_controller import PluginsController
@dataclass(slots=True)
@@ -36,7 +36,7 @@ class Controller_Data:
self.trashman = XDGTrash()
self.fm_controller = WindowController()
- self.plugins = Plugins(_settings)
+ self.plugins = PluginsController(_settings)
self.fm_controller_data = self.fm_controller.get_state_from_file()
self.trashman.regenerate()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
similarity index 98%
rename from src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py
rename to src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
index e787202..1ef2001 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
@@ -17,8 +17,8 @@ class InvalidPluginException(Exception):
...
-class Plugins:
- """Plugins controller"""
+class PluginsController:
+ """PluginsController controller"""
def __init__(self, settings: type):
path = os.path.dirname(os.path.realpath(__file__))
From ded86b81ec4845d769de29d718d5d998bab9f989 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Thu, 29 Sep 2022 17:22:33 -0500
Subject: [PATCH 02/30] Event system logic rework
---
plugins/favorites/plugin.py | 22 ++-----
plugins/file_properties/plugin.py | 7 +--
plugins/movie_tv_info/plugin.py | 16 ++---
plugins/searcher/plugin.py | 7 +--
plugins/template/plugin.py | 4 +-
plugins/vod_thumbnailer/plugin.py | 32 +++++-----
plugins/youtube_download/plugin.py | 17 ++---
.../SolarFM/solarfm/__builtins__.py | 18 +-----
.../SolarFM/solarfm/core/controller.py | 38 +++--------
.../SolarFM/solarfm/core/controller_data.py | 2 +
.../SolarFM/solarfm/plugins/plugin_base.py | 31 ++-------
.../solarfm/plugins/plugins_controller.py | 1 +
.../solarfm/utils/endpoint_registry.py | 22 +++++++
.../SolarFM/solarfm/utils/event_system.py | 63 +++++--------------
.../SolarFM/solarfm/utils/ipc_server.py | 7 ++-
15 files changed, 102 insertions(+), 185 deletions(-)
create mode 100644 src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/endpoint_registry.py
diff --git a/plugins/favorites/plugin.py b/plugins/favorites/plugin.py
index e586de7..aca3f63 100644
--- a/plugins/favorites/plugin.py
+++ b/plugins/favorites/plugin.py
@@ -38,7 +38,6 @@ class Plugin(PluginBase):
self._favorites_dialog = None
self._favorites_store = None
self._favorites = None
- self._state = None
self._selected = None
@@ -48,8 +47,6 @@ class Plugin(PluginBase):
return button
def run(self):
- self._module_event_observer()
-
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
@@ -81,19 +78,15 @@ class Plugin(PluginBase):
@threaded
def _get_state(self, widget=None, eve=None):
- self._event_system.push_gui_event([self.name, "get_current_state", ()])
- self.wait_for_fm_message()
+ self._event_system.post_event("get_current_state", None)
- self._state = self._event_message
- self._event_message = None
@threaded
def _set_current_dir_lbl(self, widget=None, eve=None):
- self.wait_for_state()
- self._current_dir_lbl.set_label(f"Current Directory:\n{self._state.tab.get_current_directory()}")
+ self._current_dir_lbl.set_label(f"Current Directory:\n{self._fm_state.tab.get_current_directory()}")
def _add_to_favorite(self, state):
- current_directory = self._state.tab.get_current_directory()
+ current_directory = self._fm_state.tab.get_current_directory()
self._favorites_store.append([current_directory])
self._favorites.append(current_directory)
self._save_favorites()
@@ -111,11 +104,10 @@ class Plugin(PluginBase):
def _set_selected_path(self, widget=None, eve=None):
path = self._favorites_store.get_value(self._selected, 0)
self._ui_objects[0].set_text(path)
-
-
+ self._set_current_dir_lbl()
def _show_favorites_menu(self, widget=None, eve=None):
- self._state = None
+ self._fm_state = None
self._get_state()
self._set_current_dir_lbl()
self._favorites_dialog.run()
@@ -127,7 +119,3 @@ class Plugin(PluginBase):
selected = user_data.get_selected()[1]
if selected:
self._selected = selected
-
- def wait_for_state(self):
- while not self._state:
- pass
diff --git a/plugins/file_properties/plugin.py b/plugins/file_properties/plugin.py
index 29472ed..5177943 100644
--- a/plugins/file_properties/plugin.py
+++ b/plugins/file_properties/plugin.py
@@ -89,8 +89,6 @@ class Plugin(PluginBase):
return button
def run(self):
- self._module_event_observer()
-
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
@@ -109,10 +107,9 @@ class Plugin(PluginBase):
@threaded
def _show_properties_page(self, widget=None, eve=None):
- self._event_system.push_gui_event([self.name, "get_current_state", ()])
- self.wait_for_fm_message()
+ event_system.post_event("get_current_state", None)
- state = self._event_message
+ state = self._fm_state
self._event_message = None
GLib.idle_add(self._process_changes, (state))
diff --git a/plugins/movie_tv_info/plugin.py b/plugins/movie_tv_info/plugin.py
index 3a4a1c5..64df1c6 100644
--- a/plugins/movie_tv_info/plugin.py
+++ b/plugins/movie_tv_info/plugin.py
@@ -39,7 +39,6 @@ class Plugin(PluginBase):
self._dialog = None
self._thumbnail_preview_img = None
self._tmdb = scraper.get_tmdb_scraper()
- self._state = None
self._overview = None
self._file_name = None
self._file_location = None
@@ -53,8 +52,6 @@ class Plugin(PluginBase):
return button
def run(self):
- self._module_event_observer()
-
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
@@ -80,19 +77,18 @@ class Plugin(PluginBase):
@threaded
def _show_info_page(self, widget=None, eve=None):
- self._event_system.push_gui_event([self.name, "get_current_state", ()])
- self.wait_for_fm_message()
+ self._event_system.post_event("get_current_state", None)
- state = self._event_message
+ state = self._fm_state
self._event_message = None
GLib.idle_add(self._process_changes, (state))
def _process_changes(self, state):
- self._state = None
+ self._fm_state = None
if len(state.selected_files) == 1:
- self._state = state
+ self._fm_state = state
self._set_ui_data()
response = self._thumbnailer_dialog.run()
if response in [Gtk.ResponseType.CLOSE, Gtk.ResponseType.DELETE_EVENT]:
@@ -111,8 +107,8 @@ class Plugin(PluginBase):
print(video_data["videos"]) if not keys in ("", None) and "videos" in keys else ...
def get_video_data(self):
- uri = self._state.selected_files[0]
- path = self._state.tab.get_current_directory()
+ uri = self._fm_state.selected_files[0]
+ path = self._fm_state.tab.get_current_directory()
parts = uri.split("/")
_title = parts[ len(parts) - 1 ]
diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py
index 3ace6ba..9328a90 100644
--- a/plugins/searcher/plugin.py
+++ b/plugins/searcher/plugin.py
@@ -98,8 +98,6 @@ class Plugin(PluginBase):
return button
def run(self):
- self._module_event_observer()
-
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
@@ -127,10 +125,9 @@ class Plugin(PluginBase):
@daemon_threaded
def _show_grep_list_page(self, widget=None, eve=None):
- self._event_system.push_gui_event([self.name, "get_current_state", ()])
- self.wait_for_fm_message()
+ self._event_system.post_event("get_current_state", None)
- state = self._event_message
+ state = self._fm_state
self._event_message = None
GLib.idle_add(self._process_queries, (state))
diff --git a/plugins/template/plugin.py b/plugins/template/plugin.py
index 08c2b72..b99308a 100644
--- a/plugins/template/plugin.py
+++ b/plugins/template/plugin.py
@@ -39,8 +39,8 @@ class Plugin(PluginBase):
return button
def run(self):
- self._module_event_observer()
+ ...
def send_message(self, widget=None, eve=None):
message = "Hello, World!"
- self._event_system.push_gui_event([self.name, "display_message", ("warning", message, None)])
+ event_system.post_event("display_message", ("warning", message, None))
diff --git a/plugins/vod_thumbnailer/plugin.py b/plugins/vod_thumbnailer/plugin.py
index 4a8abad..3d6f164 100644
--- a/plugins/vod_thumbnailer/plugin.py
+++ b/plugins/vod_thumbnailer/plugin.py
@@ -42,7 +42,6 @@ class Plugin(PluginBase):
self._file_name = None
self._file_location = None
self._file_hash = None
- self._state = None
def get_ui_element(self):
@@ -51,8 +50,6 @@ class Plugin(PluginBase):
return button
def run(self):
- self._module_event_observer()
-
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
@@ -78,20 +75,19 @@ class Plugin(PluginBase):
@threaded
def _show_thumbnailer_page(self, widget=None, eve=None):
- self._event_system.push_gui_event([self.name, "get_current_state", ()])
- self.wait_for_fm_message()
+ self._event_system.post_event("get_current_state", None)
- state = self._event_message
+ state = self._fm_state
self._event_message = None
GLib.idle_add(self._process_changes, (state))
def _process_changes(self, state):
- self._state = None
+ self._fm_state = None
if len(state.selected_files) == 1:
if state.selected_files[0].lower().endswith(state.tab.fvideos):
- self._state = state
+ self._fm_state = state
self._set_ui_data()
response = self._thumbnailer_dialog.run()
if response in [Gtk.ResponseType.CLOSE, Gtk.ResponseType.DELETE_EVENT]:
@@ -103,32 +99,32 @@ class Plugin(PluginBase):
file = self._file_name.get_text()
dir = self._file_location.get_text()
file_hash = self._file_hash.get_text()
- hash_img_pth = f"{self._state.tab.ABS_THUMBS_PTH}/{file_hash}.jpg"
+ hash_img_pth = f"{self._fm_state.tab.ABS_THUMBS_PTH}/{file_hash}.jpg"
try:
os.remove(hash_img_pth) if os.path.isfile(hash_img_pth) else ...
- self._state.tab.create_thumbnail(dir, file, f"{scrub_percent}%")
+ self._fm_state.tab.create_thumbnail(dir, file, f"{scrub_percent}%")
preview_pixbuf = GdkPixbuf.Pixbuf.new_from_file(hash_img_pth)
self._thumbnail_preview_img.set_from_pixbuf(preview_pixbuf)
- img_pixbuf = self._state.tab.create_scaled_image(hash_img_pth)
- tree_pth = self._state.icon_grid.get_selected_items()[0]
- itr = self._state.store.get_iter(tree_pth)
- pixbuff = self._state.store.get(itr, 0)[0]
- self._state.store.set(itr, 0, img_pixbuf)
+ img_pixbuf = self._fm_state.tab.create_scaled_image(hash_img_pth)
+ tree_pth = self._fm_state.icon_grid.get_selected_items()[0]
+ itr = self._fm_state.store.get_iter(tree_pth)
+ pixbuff = self._fm_state.store.get(itr, 0)[0]
+ self._fm_state.store.set(itr, 0, img_pixbuf)
except Exception as e:
print(repr(e))
print("Couldn't regenerate thumbnail!")
def _set_ui_data(self):
- uri = self._state.selected_files[0]
- path = self._state.tab.get_current_directory()
+ uri = self._fm_state.selected_files[0]
+ path = self._fm_state.tab.get_current_directory()
parts = uri.split("/")
file_hash = hashlib.sha256(str.encode(uri)).hexdigest()
- hash_img_pth = f"{self._state.tab.ABS_THUMBS_PTH}/{file_hash}.jpg"
+ hash_img_pth = f"{self._fm_state.tab.ABS_THUMBS_PTH}/{file_hash}.jpg"
preview_pixbuf = GdkPixbuf.Pixbuf.new_from_file(hash_img_pth)
self._thumbnail_preview_img.set_from_pixbuf(preview_pixbuf)
diff --git a/plugins/youtube_download/plugin.py b/plugins/youtube_download/plugin.py
index d2d4919..5ebf3b1 100644
--- a/plugins/youtube_download/plugin.py
+++ b/plugins/youtube_download/plugin.py
@@ -40,14 +40,15 @@ class Plugin(PluginBase):
return button
def run(self):
- self._module_event_observer()
+ ...
+ def _do_download(self, widget=None, eve=None):
+ self._event_system.post_event("get_current_state", None)
+
+ dir = self._fm_state.tab.get_current_directory()
+ self._download(dir)
+
@threaded
- def _do_download(self, widget=None, eve=None):
- self._event_system.push_gui_event([self.name, "get_current_state", ()])
- self.wait_for_fm_message()
-
- state = self._event_message
- subprocess.Popen([f'{self.path}/download.sh' , state.tab.get_current_directory()])
- self._event_message = None
+ def _download(self, dir):
+ subprocess.Popen([f'{self.path}/download.sh', dir])
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py
index d9a77d8..428f16f 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py
@@ -5,6 +5,7 @@ import builtins, threading
# Application imports
from utils.event_system import EventSystem
+from utils.endpoint_registry import EndpointRegistry
@@ -24,23 +25,6 @@ def daemon_threaded_wrapper(fn):
-class EndpointRegistry():
- 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
-
-
-
-
# NOTE: Just reminding myself we can add to builtins two different ways...
# __builtins__.update({"event_system": Builtins()})
builtins.app_name = "SolarFM"
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
index e844325..8d99d05 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
@@ -28,52 +28,32 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
if debug:
self.window.set_interactive_debugging(True)
+
if not trace_debug:
- self.gui_event_observer()
+ self._subscribe_to_events()
if unknownargs:
for arg in unknownargs:
if os.path.isdir(arg):
message = f"FILE|{arg}"
- event_system.send_ipc_message(message)
+ event_system.post_event("post_file_to_ipc", message)
if args.new_tab and os.path.isdir(args.new_tab):
message = f"FILE|{args.new_tab}"
- event_system.send_ipc_message(message)
+ event_system.post_event("post_file_to_ipc", message)
+ def _subscribe_to_events(self):
+ event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
+ event_system.subscribe("get_current_state", self.get_current_state)
+ event_system.subscribe("display_message", self.display_message)
+
def tear_down(self, widget=None, eve=None):
self.fm_controller.save_state()
time.sleep(event_sleep_time)
Gtk.main_quit()
- @daemon_threaded
- def gui_event_observer(self):
- while True:
- time.sleep(event_sleep_time)
- event = event_system.consume_gui_event()
- if event:
- try:
- sender_id, method_target, parameters = event
- if sender_id:
- method = getattr(self.__class__, "handle_gui_event_and_return_message")
- GLib.idle_add(method, *(self, sender_id, method_target, parameters))
- else:
- method = getattr(self.__class__, method_target)
- GLib.idle_add(method, *(self, *parameters,))
- except Exception as e:
- print(repr(e))
-
- def handle_gui_event_and_return_message(self, sender, method_target, parameters):
- method = getattr(self.__class__, f"{method_target}")
- data = method(*(self, *parameters))
- event_system.push_module_event([sender, None, data])
-
- def handle_plugin_key_event(self, sender, method_target, parameters=()):
- event_system.push_module_event([sender, method_target, parameters])
-
-
def save_load_session(self, action="save_session"):
wid, tid = self.fm_controller.get_active_wid_and_tid()
tab = self.get_fm_window(wid).get_tab_by_id(tid)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
index fbd5e89..04a6656 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
@@ -154,6 +154,8 @@ class Controller_Data:
# if self.to_cut_files:
# state.to_cut_files = self.format_to_uris(state.store, state.wid, state.tid, self.to_cut_files, True)
+ event_system.post_event("update_state_info_plugins", state)
+
return state
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugin_base.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugin_base.py
index 8e33157..effd377 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugin_base.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugin_base.py
@@ -13,10 +13,9 @@ class PluginBase:
self._builder = None
self._ui_objects = None
-
+ self._fm_state = None
self._event_system = None
- self._event_sleep_time = .5
- self._event_message = None
+
def set_fm_event_system(self, fm_event_system):
self._event_system = fm_event_system
@@ -24,32 +23,14 @@ class PluginBase:
def set_ui_object_collection(self, ui_objects):
self._ui_objects = ui_objects
- def wait_for_fm_message(self):
- while not self._event_message:
- pass
def clear_children(self, widget: type) -> None:
''' Clear children of a gtk widget. '''
for child in widget.get_children():
widget.remove(child)
- @daemon_threaded
- def _module_event_observer(self):
- while True:
- time.sleep(self._event_sleep_time)
- event = self._event_system.read_module_event()
- if event:
- try:
- if event[0] == self.name:
- target_id, method_target, data = self._event_system.consume_module_event()
+ def subscribe_to_events(self):
+ self._event_system.subscribe("update_state_info_plugins", self._update_fm_state_info)
- if not method_target:
- self._event_message = data
- else:
- method = getattr(self.__class__, f"{method_target}")
- if data:
- data = method(*(self, *data))
- else:
- method(*(self,))
- except Exception as e:
- print(repr(e))
+ def _update_fm_state_info(self, state):
+ self._fm_state = state
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
index 1ef2001..11f9dda 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
@@ -93,6 +93,7 @@ class PluginsController:
if "pass_fm_events" in keys:
plugin.reference.set_fm_event_system(event_system)
+ plugin.reference.subscribe_to_events()
if "bind_keys" in keys:
self._keybindings.append_bindings( loading_data["bind_keys"] )
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/endpoint_registry.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/endpoint_registry.py
new file mode 100644
index 0000000..15ffa9e
--- /dev/null
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/endpoint_registry.py
@@ -0,0 +1,22 @@
+# Python imports
+
+# Lib imports
+
+# Application imports
+
+
+
+
+class EndpointRegistry():
+ 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
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py
index f3ea280..53aa64e 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py
@@ -1,4 +1,5 @@
# Python imports
+from collections import defaultdict
# Lib imports
@@ -7,57 +8,23 @@
-class EventSystemPushException(Exception):
- ...
-
-
class EventSystem:
- """ Inheret IPCServerMixin. Create an pub/sub systems. """
+ """ Create event system. """
def __init__(self):
- # NOTE: The format used is list of ['who', target, (data,)] Where:
- # who is the sender or target ID and is used for context and control flow,
- # method_target is the method to call,
- # data is the method parameters OR message data to give
- # Where data may be any kind of data
- self._gui_events = []
- self._module_events = []
+ self.subscribers = defaultdict(list)
- # Makeshift "events" system FIFO
- def _pop_gui_event(self) -> None:
- if len(self._gui_events) > 0:
- return self._gui_events.pop(0)
- return None
+ def subscribe(self, event_type, fn):
+ self.subscribers[event_type].append(fn)
- def _pop_module_event(self) -> None:
- if len(self._module_events) > 0:
- return self._module_events.pop(0)
- return None
-
-
- def push_gui_event(self, event: list) -> None:
- if len(event) == 3:
- self._gui_events.append(event)
- return None
-
- raise EventSystemPushException("Invald event format! Please do: ['sender_id': str, method_target: method, (data,): any]")
-
- def push_module_event(self, event: list) -> None:
- if len(event) == 3:
- self._module_events.append(event)
- return None
-
- raise EventSystemPushException("Invald event format! Please do: ['target_id': str, method_target: method, (data,): any]")
-
- def read_gui_event(self) -> list:
- return self._gui_events[0] if self._gui_events else None
-
- def read_module_event(self) -> list:
- return self._module_events[0] if self._module_events else None
-
- def consume_gui_event(self) -> list:
- return self._pop_gui_event()
-
- def consume_module_event(self) -> list:
- return self._pop_module_event()
+ def post_event(self, event_type, data):
+ if event_type in self.subscribers:
+ for fn in self.subscribers[event_type]:
+ if data:
+ if hasattr(data, '__iter__'):
+ fn(*data)
+ else:
+ fn(data)
+ else:
+ fn()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
index 1ddf584..c73dc0a 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
@@ -29,6 +29,11 @@ class IPCServer:
elif conn_type == "local_network_unsecured":
self._ipc_authkey = None
+ self._subscribe_to_events()
+
+ def _subscribe_to_events(self):
+ event_system.subscribe("post_file_to_ipc", self.send_ipc_message)
+
@daemon_threaded
def create_ipc_listener(self) -> None:
@@ -60,7 +65,7 @@ class IPCServer:
if "FILE|" in msg:
file = msg.split("FILE|")[1].strip()
if file:
- event_system.push_gui_event([None, "handle_file_from_ipc", (file,)])
+ event_system.post_event("handle_file_from_ipc", file)
conn.close()
break
From bdd532060a364d701ed02401fa12cfd2ff42a76c Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Thu, 29 Sep 2022 17:32:35 -0500
Subject: [PATCH 03/30] Dgata arg check
---
.../solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py
index 53aa64e..10b2765 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py
@@ -22,7 +22,7 @@ class EventSystem:
if event_type in self.subscribers:
for fn in self.subscribers[event_type]:
if data:
- if hasattr(data, '__iter__'):
+ if hasattr(data, '__iter__') and not type(data) is str:
fn(*data)
else:
fn(data)
From d3e42b3ae09ff596d97d317dc0dd19e41081d30e Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Fri, 30 Sep 2022 23:30:38 -0500
Subject: [PATCH 04/30] Added PIL support for images like webp; changed emit
name
---
plugins/favorites/plugin.py | 2 +-
plugins/file_properties/plugin.py | 2 +-
plugins/movie_tv_info/plugin.py | 4 +--
plugins/searcher/plugin.py | 2 +-
plugins/template/plugin.py | 2 +-
plugins/vod_thumbnailer/plugin.py | 2 +-
plugins/youtube_download/plugin.py | 2 +-
.../SolarFM/solarfm/core/controller.py | 4 +--
.../SolarFM/solarfm/core/controller_data.py | 2 +-
.../shellfm/windows/tabs/icons/icon.py | 28 +++++++++++++++++--
.../SolarFM/solarfm/utils/event_system.py | 2 +-
.../SolarFM/solarfm/utils/ipc_server.py | 2 +-
12 files changed, 38 insertions(+), 16 deletions(-)
diff --git a/plugins/favorites/plugin.py b/plugins/favorites/plugin.py
index aca3f63..3a68f05 100644
--- a/plugins/favorites/plugin.py
+++ b/plugins/favorites/plugin.py
@@ -78,7 +78,7 @@ class Plugin(PluginBase):
@threaded
def _get_state(self, widget=None, eve=None):
- self._event_system.post_event("get_current_state", None)
+ self._event_system.emit("get_current_state
@threaded
diff --git a/plugins/file_properties/plugin.py b/plugins/file_properties/plugin.py
index 5177943..2cb6262 100644
--- a/plugins/file_properties/plugin.py
+++ b/plugins/file_properties/plugin.py
@@ -107,7 +107,7 @@ class Plugin(PluginBase):
@threaded
def _show_properties_page(self, widget=None, eve=None):
- event_system.post_event("get_current_state", None)
+ event_system.emit("get_current_state
state = self._fm_state
self._event_message = None
diff --git a/plugins/movie_tv_info/plugin.py b/plugins/movie_tv_info/plugin.py
index 64df1c6..f7f9c3c 100644
--- a/plugins/movie_tv_info/plugin.py
+++ b/plugins/movie_tv_info/plugin.py
@@ -77,7 +77,7 @@ class Plugin(PluginBase):
@threaded
def _show_info_page(self, widget=None, eve=None):
- self._event_system.post_event("get_current_state", None)
+ self._event_system.emit("get_current_state")
state = self._fm_state
self._event_message = None
@@ -111,6 +111,7 @@ class Plugin(PluginBase):
path = self._fm_state.tab.get_current_directory()
parts = uri.split("/")
_title = parts[ len(parts) - 1 ]
+ trailer = None
try:
title = _title.split("(")[0].strip()
@@ -136,7 +137,6 @@ class Plugin(PluginBase):
raise Exception("No key found. Defering to none...")
except Exception as e:
print("No trailer found...")
- trailer = None
except Exception as e:
print(repr(e))
diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py
index 9328a90..cc50574 100644
--- a/plugins/searcher/plugin.py
+++ b/plugins/searcher/plugin.py
@@ -125,7 +125,7 @@ class Plugin(PluginBase):
@daemon_threaded
def _show_grep_list_page(self, widget=None, eve=None):
- self._event_system.post_event("get_current_state", None)
+ self._event_system.emit("get_current_state
state = self._fm_state
self._event_message = None
diff --git a/plugins/template/plugin.py b/plugins/template/plugin.py
index b99308a..dff5d40 100644
--- a/plugins/template/plugin.py
+++ b/plugins/template/plugin.py
@@ -43,4 +43,4 @@ class Plugin(PluginBase):
def send_message(self, widget=None, eve=None):
message = "Hello, World!"
- event_system.post_event("display_message", ("warning", message, None))
+ event_system.emit("display_message", ("warning", message, None))
diff --git a/plugins/vod_thumbnailer/plugin.py b/plugins/vod_thumbnailer/plugin.py
index 3d6f164..c74f551 100644
--- a/plugins/vod_thumbnailer/plugin.py
+++ b/plugins/vod_thumbnailer/plugin.py
@@ -75,7 +75,7 @@ class Plugin(PluginBase):
@threaded
def _show_thumbnailer_page(self, widget=None, eve=None):
- self._event_system.post_event("get_current_state", None)
+ self._event_system.emit("get_current_state
state = self._fm_state
self._event_message = None
diff --git a/plugins/youtube_download/plugin.py b/plugins/youtube_download/plugin.py
index 5ebf3b1..da13c75 100644
--- a/plugins/youtube_download/plugin.py
+++ b/plugins/youtube_download/plugin.py
@@ -44,7 +44,7 @@ class Plugin(PluginBase):
def _do_download(self, widget=None, eve=None):
- self._event_system.post_event("get_current_state", None)
+ self._event_system.emit("get_current_state
dir = self._fm_state.tab.get_current_directory()
self._download(dir)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
index 8d99d05..7a9c930 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
@@ -36,11 +36,11 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
for arg in unknownargs:
if os.path.isdir(arg):
message = f"FILE|{arg}"
- event_system.post_event("post_file_to_ipc", message)
+ event_system.emit("post_file_to_ipc", message)
if args.new_tab and os.path.isdir(args.new_tab):
message = f"FILE|{args.new_tab}"
- event_system.post_event("post_file_to_ipc", message)
+ event_system.emit("post_file_to_ipc", message)
def _subscribe_to_events(self):
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
index 04a6656..ea44a63 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
@@ -154,7 +154,7 @@ class Controller_Data:
# if self.to_cut_files:
# state.to_cut_files = self.format_to_uris(state.store, state.wid, state.tid, self.to_cut_files, True)
- event_system.post_event("update_state_info_plugins", state)
+ event_system.emit("update_state_info_plugins", state)
return state
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/icon.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/icon.py
index 3a18857..94a9b2e 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/icon.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/icon.py
@@ -2,10 +2,17 @@
import os, subprocess, threading, hashlib
from os.path import isfile
-# Gtk imports
+# Lib imports
import gi
gi.require_version('GdkPixbuf', '2.0')
-from gi.repository import GdkPixbuf
+from gi.repository import GdkPixbuf, GLib
+
+
+try:
+ from PIL import Image as PImage
+except Exception as e:
+ PImage = None
+
# Application imports
from .mixins.desktopiconmixin import DesktopIconMixin
@@ -67,12 +74,27 @@ class Icon(DesktopIconMixin, VideoIconMixin):
.get_static_image() \
.scale_simple(wxh[0], wxh[1], GdkPixbuf.InterpType.BILINEAR)
else:
- return GdkPixbuf.Pixbuf.new_from_file_at_scale(path, wxh[0], wxh[1], True)
+ if PImage and path.lower().endswith(".webp"):
+ return self.image2pixbuf(path, wxh)
+ else:
+ return GdkPixbuf.Pixbuf.new_from_file_at_scale(path, wxh[0], wxh[1], True)
except Exception as e:
print("Image Scaling Issue:")
print( repr(e) )
return None
+ def image2pixbuf(self, path, wxh):
+ """Convert Pillow image to GdkPixbuf"""
+ im = PImage.open(path)
+ data = im.tobytes()
+ data = GLib.Bytes.new(data)
+ w, h = im.size
+
+ pixbuf = GdkPixbuf.Pixbuf.new_from_bytes(data, GdkPixbuf.Colorspace.RGB,
+ False, 8, w, h, w * 3)
+
+ return pixbuf.scale_simple(wxh[0], wxh[1], 2) # BILINEAR = 2
+
def create_from_file(self, path):
try:
return GdkPixbuf.Pixbuf.new_from_file(path)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py
index 10b2765..25c96fc 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py
@@ -18,7 +18,7 @@ class EventSystem:
def subscribe(self, event_type, fn):
self.subscribers[event_type].append(fn)
- def post_event(self, event_type, data):
+ def emit(self, event_type, data = None):
if event_type in self.subscribers:
for fn in self.subscribers[event_type]:
if data:
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
index c73dc0a..f71691b 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
@@ -65,7 +65,7 @@ class IPCServer:
if "FILE|" in msg:
file = msg.split("FILE|")[1].strip()
if file:
- event_system.post_event("handle_file_from_ipc", file)
+ event_system.emit("handle_file_from_ipc", file)
conn.close()
break
From da63e6e44e75d29abcdcbc8b402272b74a010c7f Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Sat, 1 Oct 2022 16:04:46 -0500
Subject: [PATCH 05/30] Moved trash logic to plugin structure
---
plugins/trasher/__init__.py | 3 +
plugins/trasher/__main__.py | 3 +
plugins/trasher/manifest.json | 12 +
plugins/trasher/plugin.py | 115 +++
.../solarfm => plugins}/trasher/trash.py | 0
plugins/trasher/trasher.glade | 126 ++++
.../solarfm => plugins}/trasher/xdgtrash.py | 0
.../SolarFM/solarfm/core/controller.py | 20 +-
.../SolarFM/solarfm/core/controller_data.py | 6 +-
.../mixins/ui/widget_file_action_mixin.py | 39 -
.../solarfm/core/mixins/ui/window_mixin.py | 7 +-
.../SolarFM/solarfm/trasher/__init__.py | 3 -
.../usr/share/solarfm/Main_Window.glade | 694 +++++++-----------
13 files changed, 556 insertions(+), 472 deletions(-)
create mode 100644 plugins/trasher/__init__.py
create mode 100644 plugins/trasher/__main__.py
create mode 100644 plugins/trasher/manifest.json
create mode 100644 plugins/trasher/plugin.py
rename {src/versions/solarfm-0.0.1/SolarFM/solarfm => plugins}/trasher/trash.py (100%)
create mode 100644 plugins/trasher/trasher.glade
rename {src/versions/solarfm-0.0.1/SolarFM/solarfm => plugins}/trasher/xdgtrash.py (100%)
delete mode 100755 src/versions/solarfm-0.0.1/SolarFM/solarfm/trasher/__init__.py
diff --git a/plugins/trasher/__init__.py b/plugins/trasher/__init__.py
new file mode 100644
index 0000000..d36fa8c
--- /dev/null
+++ b/plugins/trasher/__init__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Module
+"""
diff --git a/plugins/trasher/__main__.py b/plugins/trasher/__main__.py
new file mode 100644
index 0000000..a576329
--- /dev/null
+++ b/plugins/trasher/__main__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Package
+"""
diff --git a/plugins/trasher/manifest.json b/plugins/trasher/manifest.json
new file mode 100644
index 0000000..e6f0c35
--- /dev/null
+++ b/plugins/trasher/manifest.json
@@ -0,0 +1,12 @@
+{
+ "manifest": {
+ "name": "Trasher",
+ "author": "ITDominator",
+ "version": "0.0.1",
+ "support": "",
+ "requests": {
+ "ui_target": "context_menu",
+ "pass_fm_events": "true"
+ }
+ }
+}
diff --git a/plugins/trasher/plugin.py b/plugins/trasher/plugin.py
new file mode 100644
index 0000000..cbfe70d
--- /dev/null
+++ b/plugins/trasher/plugin.py
@@ -0,0 +1,115 @@
+# Python imports
+import os, threading, subprocess, inspect
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, GLib, Gio
+
+# Application imports
+from plugins.plugin_base import PluginBase
+from .xdgtrash import XDGTrash
+
+
+# NOTE: Threads WILL NOT die with parent's destruction.
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
+ return wrapper
+
+# NOTE: Threads WILL die with parent's destruction.
+def daemon_threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
+ return wrapper
+
+
+
+
+class Plugin(PluginBase):
+ def __init__(self):
+ super().__init__()
+ self.path = os.path.dirname(os.path.realpath(__file__))
+ self._GLADE_FILE = f"{self.path}/trasher.glade"
+ self.name = "Trasher" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
+ # where self.name should not be needed for message comms
+ self.trashman = XDGTrash()
+ self.trash_files_path = f"{GLib.get_user_data_dir()}/Trash/files"
+ self.trash_info_path = f"{GLib.get_user_data_dir()}/Trash/info"
+
+ self.trashman.regenerate()
+
+ def get_ui_element(self):
+ self._builder = Gtk.Builder()
+ self._builder.add_from_file(self._GLADE_FILE)
+
+ classes = [self]
+ handlers = {}
+ for c in classes:
+ methods = None
+ try:
+ methods = inspect.getmembers(c, predicate=inspect.ismethod)
+ handlers.update(methods)
+ except Exception as e:
+ print(repr(e))
+
+ self._builder.connect_signals(handlers)
+
+ trasher = self._builder.get_object("trasher")
+ trasher.show_all()
+
+ return trasher
+
+
+ def run(self):
+ self._event_system.subscribe("show_trash_buttons", self._show_trash_buttons)
+ self._event_system.subscribe("hide_trash_buttons", self._hide_trash_buttons)
+
+ def _show_trash_buttons(self):
+ self._builder.get_object("restore_from_trash").show()
+ self._builder.get_object("empty_trash").show()
+
+ def _hide_trash_buttons(self):
+ self._builder.get_object("restore_from_trash").hide()
+ self._builder.get_object("empty_trash").hide()
+
+ def delete_files(self, widget = None, eve = None):
+ self._event_system.emit("get_current_state")
+ state = self._fm_state
+ uris = state.selected_files
+ response = None
+
+ state.warning_alert.format_secondary_text(f"Do you really want to delete the {len(uris)} file(s)?")
+ for uri in uris:
+ file = Gio.File.new_for_path(uri)
+
+ if not response:
+ response = state.warning_alert.run()
+ state.warning_alert.hide()
+ if response == Gtk.ResponseType.YES:
+ type = file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
+
+ if type == Gio.FileType.DIRECTORY:
+ state.tab.delete_file( file.get_path() )
+ else:
+ file.delete(cancellable=None)
+ else:
+ break
+
+ def trash_files(self, widget = None, eve = None, verbocity = False):
+ self._event_system.emit("get_current_state")
+ state = self._fm_state
+ for uri in state.selected_files:
+ self.trashman.trash(uri, verbocity)
+
+ def restore_trash_files(self, widget = None, eve = None, verbocity = False):
+ self._event_system.emit("get_current_state")
+ state = self._fm_state
+ for uri in state.selected_files:
+ self.trashman.restore(filename=uri.split("/")[-1], verbose = verbocity)
+
+ def empty_trash(self, widget = None, eve = None, verbocity = False):
+ self.trashman.empty(verbose = verbocity)
+
+ def go_to_trash(self, widget = None, eve = None, verbocity = False):
+ self._event_system.emit("go_to_path", self.trash_files_path)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/trasher/trash.py b/plugins/trasher/trash.py
similarity index 100%
rename from src/versions/solarfm-0.0.1/SolarFM/solarfm/trasher/trash.py
rename to plugins/trasher/trash.py
diff --git a/plugins/trasher/trasher.glade b/plugins/trasher/trasher.glade
new file mode 100644
index 0000000..54837b9
--- /dev/null
+++ b/plugins/trasher/trasher.glade
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/trasher/xdgtrash.py b/plugins/trasher/xdgtrash.py
similarity index 100%
rename from src/versions/solarfm-0.0.1/SolarFM/solarfm/trasher/xdgtrash.py
rename to plugins/trasher/xdgtrash.py
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
index 7a9c930..c034bd3 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
@@ -47,6 +47,8 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc)
event_system.subscribe("get_current_state", self.get_current_state)
event_system.subscribe("display_message", self.display_message)
+ event_system.subscribe("go_to_path", self.go_to_path)
+ event_system.subscribe("do_hide_context_menu", self.do_hide_context_menu)
def tear_down(self, widget=None, eve=None):
self.fm_controller.save_state()
@@ -100,7 +102,7 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
gc.collect()
- def do_action_from_menu_controls(self, widget, event_button):
+ def do_action_from_menu_controls(self, widget, eve = None):
action = widget.get_name()
self.hide_context_menu()
self.hide_new_file_menu()
@@ -124,16 +126,6 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
self.paste_files()
if action == "archive":
self.show_archiver_dialogue()
- if action == "delete":
- self.delete_files()
- if action == "trash":
- self.trash_files()
- if action == "go_to_trash":
- self.path_entry.set_text(self.trash_files_path)
- if action == "restore_from_trash":
- self.restore_trash_files()
- if action == "empty_trash":
- self.empty_trash()
if action == "create":
self.create_files()
if action in ["save_session", "save_session_as", "load_session"]:
@@ -169,3 +161,9 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
wid, tid = self.fm_controller.get_active_wid_and_tid()
tab = self.get_fm_window(wid).get_tab_by_id(tid)
tab.execute([f"{tab.terminal_app}"], start_dir=tab.get_current_directory())
+
+ def go_to_path(self, path):
+ self.path_entry.set_text(path)
+
+ def do_hide_context_menu(self):
+ self.hide_context_menu()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
index ea44a63..3e69f9f 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
@@ -7,7 +7,6 @@ import gi
from gi.repository import GLib
# Application imports
-from trasher.xdgtrash import XDGTrash
from shellfm.windows.controller import WindowController
from plugins.plugins_controller import PluginsController
@@ -22,6 +21,7 @@ class State:
selected_files: [] = None
to_copy_files: [] = None
to_cut_files: [] = None
+ warning_alert: type = None
class Controller_Data:
@@ -34,11 +34,9 @@ class Controller_Data:
self.logger = self.settings.get_logger()
self.keybindings = self.settings.get_keybindings()
- self.trashman = XDGTrash()
self.fm_controller = WindowController()
self.plugins = PluginsController(_settings)
self.fm_controller_data = self.fm_controller.get_state_from_file()
- self.trashman.regenerate()
self.window = self.settings.get_main_window()
self.window1 = self.builder.get_object("window_1")
@@ -142,6 +140,7 @@ class Controller_Data:
state.tab = self.get_fm_window(state.wid).get_tab_by_id(state.tid)
state.icon_grid = self.builder.get_object(f"{state.wid}|{state.tid}|icon_grid")
state.store = state.icon_grid.get_model()
+ state.warning_alert = self.warning_alert
selected_files = state.icon_grid.get_selected_items()
@@ -155,7 +154,6 @@ class Controller_Data:
# state.to_cut_files = self.format_to_uris(state.store, state.wid, state.tid, self.to_cut_files, True)
event_system.emit("update_state_info_plugins", state)
-
return state
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
index 9072990..0058147 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
@@ -208,45 +208,6 @@ class WidgetFileActionMixin:
elif self.to_cut_files:
self.handle_files(self.to_cut_files, "move", target)
- def delete_files(self):
- state = self.get_current_state()
- uris = self.format_to_uris(state.store, state.wid, state.tid, self.selected_files, True)
- response = None
-
- self.warning_alert.format_secondary_text(f"Do you really want to delete the {len(uris)} file(s)?")
- for uri in uris:
- file = Gio.File.new_for_path(uri)
-
- if not response:
- response = self.warning_alert.run()
- self.warning_alert.hide()
- if response == Gtk.ResponseType.YES:
- type = file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
-
- if type == Gio.FileType.DIRECTORY:
- state.tab.delete_file( file.get_path() )
- else:
- file.delete(cancellable=None)
- else:
- break
-
-
- def trash_files(self):
- state = self.get_current_state()
- uris = self.format_to_uris(state.store, state.wid, state.tid, self.selected_files, True)
- for uri in uris:
- self.trashman.trash(uri, False)
-
- def restore_trash_files(self):
- state = self.get_current_state()
- uris = self.format_to_uris(state.store, state.wid, state.tid, self.selected_files, True)
- for uri in uris:
- self.trashman.restore(filename=uri.split("/")[-1], verbose=False)
-
- def empty_trash(self):
- self.trashman.empty(verbose=False)
-
-
def create_files(self):
fname_field = self.builder.get_object("new_fname_field")
self.show_new_file_menu(fname_field)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
index 1d22380..513182e 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
@@ -88,12 +88,11 @@ class WindowMixin(TabMixin):
formatted_mount_free = self.sizeof_fmt( int(mount_file.get_attribute_as_string("filesystem::free")) )
formatted_mount_size = self.sizeof_fmt( int(mount_file.get_attribute_as_string("filesystem::size")) )
+ # NOTE: Hides empty trash and other desired buttons based on context.
if self.trash_files_path == current_directory:
- self.builder.get_object("restore_from_trash").show()
- self.builder.get_object("empty_trash").show()
+ event_system.emit("show_trash_buttons")
else:
- self.builder.get_object("restore_from_trash").hide()
- self.builder.get_object("empty_trash").hide()
+ event_system.emit("hide_trash_buttons")
# If something selected
self.bottom_size_label.set_label(f"{formatted_mount_free} free / {formatted_mount_size}")
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/trasher/__init__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/trasher/__init__.py
deleted file mode 100755
index 5c16e50..0000000
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/trasher/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""
-Trasher module
-"""
diff --git a/user_config/usr/share/solarfm/Main_Window.glade b/user_config/usr/share/solarfm/Main_Window.glade
index 2bf3bd8..2343d0c 100644
--- a/user_config/usr/share/solarfm/Main_Window.glade
+++ b/user_config/usr/share/solarfm/Main_Window.glade
@@ -668,6 +668,289 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
False
gtk-edit
+
False
dialog
@@ -2321,417 +2604,6 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
-
- True
- False
- user-trash
-
-
- True
- False
- user-trash
-
-
False
False
From 72f0236e58b0e07fd1e1761f3304e10ea7177087 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Sat, 1 Oct 2022 19:08:05 -0500
Subject: [PATCH 06/30] Added submodule search logic
---
.../SolarFM/solarfm/plugins/plugins_controller.py | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
index 11f9dda..63cccfe 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
@@ -72,13 +72,23 @@ class PluginsController:
def load_plugin_module(self, path, folder, target):
os.chdir(path)
- spec = importlib.util.spec_from_file_location(folder, target, submodule_search_locations=path)
+
+ locations = []
+ self.collect_search_locations(path, locations)
+
+ spec = importlib.util.spec_from_file_location(folder, target, submodule_search_locations = locations)
module = importlib.util.module_from_spec(spec)
sys.modules[folder] = module
spec.loader.exec_module(module)
return module
+ def collect_search_locations(self, path, locations):
+ locations.append(path)
+ for file in os.listdir(path):
+ _path = os.path.join(path, file)
+ if os.path.isdir(_path):
+ self.collect_search_locations(_path, locations)
def execute_plugin(self, module: type, plugin: PluginInfo, loading_data: []):
plugin.reference = module.Plugin()
From dc9cae6d3838d6809ae1f5eebf1657b1efe52265 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Sat, 1 Oct 2022 22:12:14 -0500
Subject: [PATCH 07/30] Fixed keyboard events, updated exception types
---
plugins/trasher/manifest.json | 6 ++-
.../solarfm-0.0.1/SolarFM/solarfm/app.py | 10 ++---
.../solarfm/core/mixins/ui/window_mixin.py | 13 +++---
.../core/signals/keyboard_signals_mixin.py | 7 +++-
.../shellfm/windows/tabs/icons/icon.py | 40 +++++++++++--------
.../usr/share/solarfm/key-bindings.json | 3 --
6 files changed, 46 insertions(+), 33 deletions(-)
diff --git a/plugins/trasher/manifest.json b/plugins/trasher/manifest.json
index e6f0c35..0657d65 100644
--- a/plugins/trasher/manifest.json
+++ b/plugins/trasher/manifest.json
@@ -6,7 +6,11 @@
"support": "",
"requests": {
"ui_target": "context_menu",
- "pass_fm_events": "true"
+ "pass_fm_events": "true",
+ "bind_keys": [
+ "Trasher||delete_files:Delete",
+ "Trasher||trash_files:d"
+ ]
}
}
}
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
index cd89d84..a16c779 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
@@ -10,10 +10,10 @@ from utils.settings import Settings
from core.controller import Controller
-class App_Launch_Exception(Exception):
+class AppLaunchException(Exception):
...
-class Controller_Start_Exceptio(Exception):
+class ControllerStartExceptio(Exception):
...
@@ -38,7 +38,7 @@ class Application(IPCServer):
message = f"FILE|{args.new_tab}"
self.send_ipc_message(message)
- raise App_Launch_Exception(f"IPC Server Exists: Will send path(s) to it and close...\nNote: If no fm exists, remove /tmp/{app_name}-ipc.sock")
+ raise AppLaunchException(f"IPC Server Exists: Will send path(s) to it and close...\nNote: If no fm exists, remove /tmp/{app_name}-ipc.sock")
settings = Settings()
@@ -46,7 +46,7 @@ class Application(IPCServer):
controller = Controller(args, unknownargs, settings)
if not controller:
- raise Controller_Start_Exceptio("Controller exited and doesn't exist...")
+ raise ControllerStartExceptio("Controller exited and doesn't exist...")
# Gets the methods from the classes and sets to handler.
# Then, builder connects to any signals it needs.
@@ -57,7 +57,7 @@ class Application(IPCServer):
try:
methods = inspect.getmembers(c, predicate=inspect.ismethod)
handlers.update(methods)
- except Exception as e:
+ except AppLaunchException as e:
print(repr(e))
settings.get_builder().connect_signals(handlers)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
index 513182e..2ab6be1 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
@@ -12,6 +12,10 @@ from gi.repository import Gdk, Gio
from .tab_mixin import TabMixin
+class WindowException(Exception):
+ ...
+
+
class WindowMixin(TabMixin):
"""docstring for WindowMixin"""
@@ -46,7 +50,7 @@ class WindowMixin(TabMixin):
icon_grid.event(Gdk.Event().new(type=Gdk.EventType.BUTTON_RELEASE))
icon_grid.event(Gdk.Event().new(type=Gdk.EventType.BUTTON_RELEASE))
- except Exception as e:
+ except WindowException as e:
print("\n: The saved session might be missing window data! :\nLocation: ~/.config/solarfm/session.json\nFix: Back it up and delete it to reset.\n")
print(repr(e))
else:
@@ -107,7 +111,7 @@ class WindowMixin(TabMixin):
cancellable=None)
file_size = file_info.get_size()
combined_size += file_size
- except Exception as e:
+ except WindowException as e:
if debug:
print(repr(e))
@@ -168,14 +172,13 @@ class WindowMixin(TabMixin):
self.set_path_text(wid, tid)
self.set_window_title()
-
if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 1: # l-click
if self.single_click_open: # FIXME: need to find a way to pass the model index
self.grid_icon_double_click(icons_grid)
elif eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 3: # r-click
self.show_context_menu()
- except Exception as e:
+ except WindowException as e:
print(repr(e))
self.display_message(self.error_color, f"{repr(e)}")
@@ -204,7 +207,7 @@ class WindowMixin(TabMixin):
self.update_tab(tab_label, state.tab, state.store, state.wid, state.tid)
else:
self.open_files()
- except Exception as e:
+ except WindowException as e:
traceback.print_exc()
self.display_message(self.error_color, f"{repr(e)}")
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py
index e5f70e8..e4151f8 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py
@@ -52,8 +52,8 @@ class KeyboardSignalsMixin:
return True
except Exception:
# Must be plugins scope or we forgot to add method to file manager scope
- sender, method_target = mapping.split("||")
- self.handle_plugin_key_event(sender, method_target)
+ sender, eve_type = mapping.split("||")
+ self.handle_plugin_key_event(sender, eve_type)
else:
if debug:
print(f"on_global_key_release_controller > key > {keyname}")
@@ -77,6 +77,9 @@ class KeyboardSignalsMixin:
return True
+ def handle_plugin_key_event(self, sender, eve_type):
+ event_system.emit(eve_type)
+
def keyboard_close_tab(self):
wid, tid = self.fm_controller.get_active_wid_and_tid()
notebook = self.builder.get_object(f"window_{wid}")
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/icon.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/icon.py
index 94a9b2e..0d392ac 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/icon.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/icon.py
@@ -42,11 +42,14 @@ class Icon(DesktopIconMixin, VideoIconMixin):
thumbnl = self.parse_desktop_files(full_path)
return thumbnl
- except Exception as e:
- return None
+ except Exception:
+ ...
+
+ return None
def create_thumbnail(self, dir, file, scrub_percent = "65%"):
full_path = f"{dir}/{file}"
+
try:
file_hash = hashlib.sha256(str.encode(full_path)).hexdigest()
hash_img_pth = f"{self.ABS_THUMBS_PTH}/{file_hash}.jpg"
@@ -61,27 +64,29 @@ class Icon(DesktopIconMixin, VideoIconMixin):
except Exception as e:
print("Thumbnail generation issue:")
print( repr(e) )
- return GdkPixbuf.Pixbuf.new_from_file(f"{self.DEFAULT_ICONS}/video.png")
+
+ return GdkPixbuf.Pixbuf.new_from_file(f"{self.DEFAULT_ICONS}/video.png")
def create_scaled_image(self, path, wxh = None):
if not wxh:
wxh = self.video_icon_wh
- try:
- if path.lower().endswith(".gif"):
- return GdkPixbuf.PixbufAnimation.new_from_file(path) \
- .get_static_image() \
- .scale_simple(wxh[0], wxh[1], GdkPixbuf.InterpType.BILINEAR)
- else:
- if PImage and path.lower().endswith(".webp"):
+ if path:
+ try:
+ if path.lower().endswith(".gif"):
+ return GdkPixbuf.PixbufAnimation.new_from_file(path) \
+ .get_static_image() \
+ .scale_simple(wxh[0], wxh[1], GdkPixbuf.InterpType.BILINEAR)
+ elif path.lower().endswith(".webp") and PImage:
return self.image2pixbuf(path, wxh)
- else:
- return GdkPixbuf.Pixbuf.new_from_file_at_scale(path, wxh[0], wxh[1], True)
- except Exception as e:
- print("Image Scaling Issue:")
- print( repr(e) )
- return None
+
+ return GdkPixbuf.Pixbuf.new_from_file_at_scale(path, wxh[0], wxh[1], True)
+ except Exception as e:
+ print("Image Scaling Issue:")
+ print( repr(e) )
+
+ return None
def image2pixbuf(self, path, wxh):
"""Convert Pillow image to GdkPixbuf"""
@@ -101,7 +106,8 @@ class Icon(DesktopIconMixin, VideoIconMixin):
except Exception as e:
print("Image from file Issue:")
print( repr(e) )
- return None
+
+ return None
def return_generic_icon(self):
return GdkPixbuf.Pixbuf.new_from_file(self.DEFAULT_ICON)
diff --git a/user_config/usr/share/solarfm/key-bindings.json b/user_config/usr/share/solarfm/key-bindings.json
index bd36c53..f3b10a0 100644
--- a/user_config/usr/share/solarfm/key-bindings.json
+++ b/user_config/usr/share/solarfm/key-bindings.json
@@ -6,10 +6,7 @@
"open_terminal" : "F4",
"refresh_tab" : ["F5",
"r"],
- "delete_files" : ["Delete",
- "d"],
"tggl_top_main_menubar" : "h",
- "trash_files" : "t",
"tear_down" : "q",
"go_up" : "Up",
"go_home" : "slash",
From f48d84a0048336ab595461ed0e53893b566e4164 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Mon, 3 Oct 2022 00:52:50 -0500
Subject: [PATCH 08/30] Reworked the search logic
---
plugins/searcher/plugin.py | 189 ++++++++++++++-------------
plugins/searcher/search.py | 103 +++++++++++++++
plugins/searcher/search_dialog.glade | 4 +-
3 files changed, 204 insertions(+), 92 deletions(-)
create mode 100755 plugins/searcher/search.py
diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py
index cc50574..3a2ae7c 100644
--- a/plugins/searcher/plugin.py
+++ b/plugins/searcher/plugin.py
@@ -1,16 +1,17 @@
# Python imports
-import os, multiprocessing, threading, subprocess, inspect, time, json
-from multiprocessing import Manager, Process
+import os, threading, subprocess, inspect, time, json, base64, shlex, select, signal, pickle
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk, GLib, GObject
+from gi.repository import Gtk, GLib
# Application imports
from plugins.plugin_base import PluginBase
+
+
# NOTE: Threads WILL NOT die with parent's destruction.
def threaded(fn):
def wrapper(*args, **kwargs):
@@ -35,19 +36,21 @@ class FilePreviewWidget(Gtk.LinkButton):
class GrepPreviewWidget(Gtk.Box):
- def __init__(self, path, sub_keys, data):
+ def __init__(self, _path, sub_keys, data):
super(GrepPreviewWidget, self).__init__()
self.set_orientation(Gtk.Orientation.VERTICAL)
self.line_color = "#e0cc64"
-
+ path = base64.urlsafe_b64decode(_path.encode('utf-8')).decode('utf-8')
_label = '/'.join( path.split("/")[-3:] )
title = Gtk.LinkButton.new_with_label(uri=f"file://{path}", label=_label)
self.add(title)
for key in sub_keys:
line_num = key
- text = data[key]
+ text = base64.urlsafe_b64decode(data[key].encode('utf-8')).decode('utf-8')
+
+
box = Gtk.Box()
number_label = Gtk.Label()
text_view = Gtk.Label(label=text[:-1])
@@ -69,11 +72,7 @@ class GrepPreviewWidget(Gtk.Box):
self.show_all()
-
-manager = Manager()
-grep_result_set = manager.dict()
-file_result_set = manager.list()
-
+pause_fifo_update = False
class Plugin(PluginBase):
def __init__(self):
@@ -84,6 +83,9 @@ class Plugin(PluginBase):
# where self.name should not be needed for message comms
self._GLADE_FILE = f"{self.path}/search_dialog.glade"
+ self._files_fifo_file = f"/tmp/search_files_fifo"
+ self._grep_fifo_file = f"/tmp/grep_files_fifo"
+
self._search_dialog = None
self._active_path = None
self._file_list = None
@@ -116,113 +118,118 @@ class Plugin(PluginBase):
self._search_dialog = self._builder.get_object("search_dialog")
self._grep_list = self._builder.get_object("grep_list")
self._file_list = self._builder.get_object("file_list")
+ self.fsearch = self._builder.get_object("fsearch")
- GObject.signal_new("update-file-ui-signal", self._search_dialog, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
- self._search_dialog.connect("update-file-ui-signal", self._load_file_ui)
- GObject.signal_new("update-grep-ui-signal", self._search_dialog, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
- self._search_dialog.connect("update-grep-ui-signal", self._load_grep_ui)
+ self._event_system.subscribe("update-file-ui", self._load_file_ui)
+ self._event_system.subscribe("update-grep-ui", self._load_grep_ui)
+
+ if not os.path.exists(self._files_fifo_file):
+ os.mkfifo(self._files_fifo_file, 0o777)
+ if not os.path.exists(self._grep_fifo_file):
+ os.mkfifo(self._grep_fifo_file, 0o777)
+
+ self.run_files_fifo_thread()
+ self.run_grep_fifo_thread()
@daemon_threaded
+ def run_files_fifo_thread(self):
+ with open(self._files_fifo_file) as fifo:
+ while True:
+ select.select([fifo],[],[fifo])
+ data = fifo.read()
+ GLib.idle_add(self._load_file_ui, data)
+
+ @daemon_threaded
+ def run_grep_fifo_thread(self):
+ with open(self._grep_fifo_file) as fifo:
+ while True:
+ select.select([fifo],[],[fifo])
+ data = fifo.read()
+ GLib.idle_add(self._load_grep_ui, data)
+
+
def _show_grep_list_page(self, widget=None, eve=None):
- self._event_system.emit("get_current_state
+ self._event_system.emit("get_current_state")
state = self._fm_state
self._event_message = None
- GLib.idle_add(self._process_queries, (state))
-
- def _process_queries(self, state):
self._active_path = state.tab.get_current_directory()
response = self._search_dialog.run()
self._search_dialog.hide()
def _run_find_file_query(self, widget=None, eve=None):
- if self._list_proc:
- self._list_proc.terminate()
- self._list_proc = None
- time.sleep(.2)
-
- del file_result_set[:]
- self.clear_children(self._file_list)
+ self._stop_find_file_query()
query = widget.get_text()
- if query:
- self._list_proc = multiprocessing.Process(self._do_list_search(self._active_path, query))
- self._list_proc.start()
+ if not query in ("", None):
+ target_dir = shlex.quote( self._fm_state.tab.get_current_directory() )
+ # command = [f"{self.path}/search.sh", "-t", "file_search", "-d", f"{target_dir}", "-q", f"{query}"]
+ command = ["python", f"{self.path}/search.py", "-t", "file_search", "-d", f"{target_dir}", "-q", f"{query}"]
- def _do_list_search(self, path, query):
- self._file_traverse_path(path, query)
- for target, file in file_result_set:
- widget = FilePreviewWidget(target, file)
- self._search_dialog.emit("update-file-ui-signal", (widget))
+ process = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
- def _load_file_ui(self, parent=None, widget=None):
- self._file_list.add(widget)
+ def _stop_find_file_query(self, widget=None, eve=None):
+ pause_fifo_update = True
- def _file_traverse_path(self, path, query):
- try:
- for file in os.listdir(path):
- target = os.path.join(path, file)
- if os.path.isdir(target):
- self._file_traverse_path(target, query)
- else:
- if query.lower() in file.lower():
- file_result_set.append([target, file])
- except Exception as e:
- if debug:
- print("Couldn't traverse to path. Might be permissions related...")
+ if self._list_proc:
+ if self._list_proc.poll():
+ self._list_proc.send_signal(signal.SIGKILL)
+ while self._list_proc.poll():
+ pass
+
+ self._list_proc = None
+ else:
+ self._list_proc = None
+
+ self.clear_children(self._file_list)
+ pause_fifo_update = False
def _run_grep_query(self, widget=None, eve=None):
- if self._grep_proc:
- self._grep_proc.terminate()
- self._grep_proc = None
- time.sleep(.2)
-
- grep_result_set.clear()
- self.clear_children(self._grep_list)
+ self._stop_grep_query()
query = widget.get_text()
- if query:
- self._grep_proc = multiprocessing.Process(self._do_grep_search(self._active_path, query))
- self._grep_proc.start()
+ if not query in ("", None):
+ target_dir = shlex.quote( self._fm_state.tab.get_current_directory() )
+ command = ["python", f"{self.path}/search.py", "-t", "grep_search", "-d", f"{target_dir}", "-q", f"{query}"]
+ process = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
- def _do_grep_search(self, path, query):
- self._grep_traverse_path(path, query)
+ def _stop_grep_query(self, widget=None, eve=None):
+ pause_fifo_update = True
- keys = grep_result_set.keys()
- for key in keys:
- sub_keys = grep_result_set[key].keys()
- widget = GrepPreviewWidget(key, sub_keys, grep_result_set[key])
- self._search_dialog.emit("update-grep-ui-signal", (widget))
+ if self._grep_proc:
+ if self._grep_proc.poll():
+ self._grep_proc.send_signal(signal.SIGKILL)
+ while self._grep_proc.poll():
+ pass
- def _load_grep_ui(self, parent=None, widget=None):
- self._grep_list.add(widget)
+ self._grep_proc = None
+ else:
+ self._grep_proc = None
- def _grep_traverse_path(self, path, query):
- try:
- for file in os.listdir(path):
- target = os.path.join(path, file)
- if os.path.isdir(target):
- self._grep_traverse_path(target, query)
- else:
- self._search_for_string(target, query)
- except Exception as e:
- if debug:
- print("Couldn't traverse to path. Might be permissions related...")
+ self.clear_children(self._grep_list)
+ pause_fifo_update = False
- def _search_for_string(self, file, query):
- try:
- with open(file, 'r') as fp:
- for i, line in enumerate(fp):
- if query in line:
- if f"{file}" in grep_result_set.keys():
- grep_result_set[f"{file}"][f"{i+1}"] = line
- else:
- grep_result_set[f"{file}"] = {}
- grep_result_set[f"{file}"] = {f"{i+1}": line}
- except Exception as e:
- if debug:
- print("Couldn't read file. Might be binary or other cause...")
+
+ def _load_file_ui(self, data):
+ if not data in ("", None) and not pause_fifo_update:
+ jdata = json.loads( data )
+ target = jdata[0]
+ file = jdata[1]
+
+ widget = FilePreviewWidget(target, file)
+ self._file_list.add(widget)
+
+ def _load_grep_ui(self, data):
+ if not data in ("", None) and not pause_fifo_update:
+ jdata = json.loads( data )
+ jkeys = jdata.keys()
+ for key in jkeys:
+ sub_keys = jdata[key].keys()
+ grep_result = jdata[key]
+
+ widget = GrepPreviewWidget(key, sub_keys, grep_result)
+ self._grep_list.add(widget)
diff --git a/plugins/searcher/search.py b/plugins/searcher/search.py
new file mode 100755
index 0000000..5756dfe
--- /dev/null
+++ b/plugins/searcher/search.py
@@ -0,0 +1,103 @@
+#!/usr/bin/python3
+
+
+# Python imports
+import os, traceback, argparse, time, json, base64
+from setproctitle import setproctitle
+
+# Lib imports
+
+# Application imports
+
+
+
+_files_fifo_file = f"/tmp/search_files_fifo"
+_grep_fifo_file = f"/tmp/grep_files_fifo"
+
+filter = (".mkv", ".mp4", ".webm", ".avi", ".mov", ".m4v", ".mpg", ".mpeg", ".wmv", ".flv") + \
+ (".png", ".jpg", ".jpeg", ".gif", ".ico", ".tga", ".webp") + \
+ (".psf", ".mp3", ".ogg", ".flac", ".m4a")
+
+file_result_set = []
+
+
+def file_search(fifo, path, query):
+ try:
+ for file in os.listdir(path):
+ target = os.path.join(path, file)
+ if os.path.isdir(target):
+ file_search(fifo, target, query)
+ else:
+ if query.lower() in file.lower():
+ # file_result_set.append([target, file])
+ data = json.dumps([target, file])
+ fifo.write(data)
+ time.sleep(0.01)
+ except Exception as e:
+ print("Couldn't traverse to path. Might be permissions related...")
+ traceback.print_exc()
+
+def _search_for_string(file, query):
+ try:
+ b64_file = base64.urlsafe_b64encode(file.encode('utf-8')).decode('utf-8')
+ grep_result_set = {}
+
+ with open(file, 'r') as fp:
+ for i, line in enumerate(fp):
+ if query in line:
+ b64_line = base64.urlsafe_b64encode(line.encode('utf-8')).decode('utf-8')
+
+ if f"{b64_file}" in grep_result_set.keys():
+ grep_result_set[f"{b64_file}"][f"{i+1}"] = b64_line
+ else:
+ grep_result_set[f"{b64_file}"] = {}
+ grep_result_set[f"{b64_file}"] = {f"{i+1}": b64_line}
+
+ # NOTE: Push to fifo here after loop
+ with open(_grep_fifo_file, 'w') as fifo:
+ data = json.dumps(grep_result_set)
+ fifo.write(data)
+ time.sleep(0.05)
+ except Exception as e:
+ print("Couldn't read file. Might be binary or other cause...")
+ traceback.print_exc()
+
+
+def grep_search(path, query):
+ try:
+ for file in os.listdir(path):
+ target = os.path.join(path, file)
+ if os.path.isdir(target):
+ grep_search(target, query)
+ else:
+ if not target.lower().endswith(filter):
+ _search_for_string(target, query)
+ except Exception as e:
+ print("Couldn't traverse to path. Might be permissions related...")
+ traceback.print_exc()
+
+def search(args):
+ if args.type == "file_search":
+ with open(_files_fifo_file, 'w') as fifo:
+ file_search(fifo, args.dir, args.query)
+
+ if args.type == "grep_search":
+ grep_search(args.dir, args.query)
+
+
+if __name__ == "__main__":
+ try:
+ setproctitle('SolarFM: File Search - Grepy')
+
+ parser = argparse.ArgumentParser()
+ # Add long and short arguments
+ parser.add_argument("--type", "-t", default=None, help="Type of search to do.")
+ parser.add_argument("--dir", "-d", default=None, help="Directory root for search type.")
+ parser.add_argument("--query", "-q", default=None, help="Query search is working against.")
+
+ # Read arguments (If any...)
+ args = parser.parse_args()
+ search(args)
+
+ except Exception as e:
+ traceback.print_exc()
diff --git a/plugins/searcher/search_dialog.glade b/plugins/searcher/search_dialog.glade
index 47fc1bc..9fbca4b 100644
--- a/plugins/searcher/search_dialog.glade
+++ b/plugins/searcher/search_dialog.glade
@@ -74,7 +74,7 @@
False
vertical
-
+
True
True
Query...
@@ -83,6 +83,7 @@
False
Search for file...
+
False
@@ -149,6 +150,7 @@
False
Query string in file...
+
False
From 867c651a046c0c6cb91b4f30956dc54922d54338 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Mon, 3 Oct 2022 20:50:38 -0500
Subject: [PATCH 09/30] Changed search plugin IPC logic
---
plugins/searcher/ipc_server.py | 104 +++++++++++++++++++++++++++++++++
plugins/searcher/plugin.py | 36 ++----------
plugins/searcher/search.py | 67 +++++++++++----------
3 files changed, 143 insertions(+), 64 deletions(-)
create mode 100644 plugins/searcher/ipc_server.py
diff --git a/plugins/searcher/ipc_server.py b/plugins/searcher/ipc_server.py
new file mode 100644
index 0000000..c654842
--- /dev/null
+++ b/plugins/searcher/ipc_server.py
@@ -0,0 +1,104 @@
+# Python imports
+import os, threading, time, pickle
+from multiprocessing.connection import Listener, Client
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, GLib
+
+# Application imports
+
+
+
+
+class IPCServer:
+ """ Create a listener so that other SolarFM instances send requests back to existing instance. """
+ def __init__(self, ipc_address: str = '127.0.0.1', conn_type: str = "socket"):
+ self.is_ipc_alive = False
+ self._ipc_port = 4848
+ self._ipc_address = ipc_address
+ self._conn_type = conn_type
+ self._ipc_authkey = b'' + bytes(f'solarfm-search_grep-ipc', 'utf-8')
+ self._ipc_timeout = 15.0
+
+ if conn_type == "socket":
+ self._ipc_address = f'/tmp/solarfm-search_grep-ipc.sock'
+ elif conn_type == "full_network":
+ self._ipc_address = '0.0.0.0'
+ elif conn_type == "full_network_unsecured":
+ self._ipc_authkey = None
+ self._ipc_address = '0.0.0.0'
+ elif conn_type == "local_network_unsecured":
+ self._ipc_authkey = None
+
+
+ @daemon_threaded
+ def create_ipc_listener(self) -> None:
+ if self._conn_type == "socket":
+ if os.path.exists(self._ipc_address):
+ os.unlink(self._ipc_address)
+
+ 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)
+ else:
+ listener = Listener((self._ipc_address, self._ipc_port))
+
+
+ self.is_ipc_alive = True
+ while True:
+ conn = listener.accept()
+ start_time = time.perf_counter()
+ self.handle_message(conn, start_time)
+
+ listener.close()
+
+ def handle_message(self, conn, start_time) -> None:
+ while True:
+ msg = conn.recv()
+ data = msg
+
+ if "SEARCH|" in msg:
+ file = msg.split("SEARCH|")[1].strip()
+ if file:
+ GLib.idle_add(self._load_file_ui, file)
+
+ conn.close()
+ break
+
+ if "GREP|" in msg:
+ data = msg.split("GREP|")[1].strip()
+ if data:
+ GLib.idle_add(self._load_grep_ui, data)
+
+ conn.close()
+ break
+
+
+ if msg in ['close connection', 'close server']:
+ conn.close()
+ break
+
+ # NOTE: Not perfect but insures we don't lock up the connection for too long.
+ end_time = time.perf_counter()
+ if (end_time - start_time) > self._ipc_timeout:
+ conn.close()
+ break
+
+
+ def send_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:
+ print("Connection refused...")
+ except Exception as e:
+ print(repr(e))
diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py
index 3a2ae7c..cedbfdf 100644
--- a/plugins/searcher/plugin.py
+++ b/plugins/searcher/plugin.py
@@ -1,5 +1,5 @@
# Python imports
-import os, threading, subprocess, inspect, time, json, base64, shlex, select, signal, pickle
+import os, threading, subprocess, inspect, time, json, base64, shlex, select, signal
# Lib imports
import gi
@@ -8,7 +8,7 @@ from gi.repository import Gtk, GLib
# Application imports
from plugins.plugin_base import PluginBase
-
+from .ipc_server import IPCServer
@@ -74,7 +74,7 @@ class GrepPreviewWidget(Gtk.Box):
pause_fifo_update = False
-class Plugin(PluginBase):
+class Plugin(IPCServer, PluginBase):
def __init__(self):
super().__init__()
@@ -83,9 +83,6 @@ class Plugin(PluginBase):
# where self.name should not be needed for message comms
self._GLADE_FILE = f"{self.path}/search_dialog.glade"
- self._files_fifo_file = f"/tmp/search_files_fifo"
- self._grep_fifo_file = f"/tmp/grep_files_fifo"
-
self._search_dialog = None
self._active_path = None
self._file_list = None
@@ -123,30 +120,7 @@ class Plugin(PluginBase):
self._event_system.subscribe("update-file-ui", self._load_file_ui)
self._event_system.subscribe("update-grep-ui", self._load_grep_ui)
- if not os.path.exists(self._files_fifo_file):
- os.mkfifo(self._files_fifo_file, 0o777)
- if not os.path.exists(self._grep_fifo_file):
- os.mkfifo(self._grep_fifo_file, 0o777)
-
- self.run_files_fifo_thread()
- self.run_grep_fifo_thread()
-
-
- @daemon_threaded
- def run_files_fifo_thread(self):
- with open(self._files_fifo_file) as fifo:
- while True:
- select.select([fifo],[],[fifo])
- data = fifo.read()
- GLib.idle_add(self._load_file_ui, data)
-
- @daemon_threaded
- def run_grep_fifo_thread(self):
- with open(self._grep_fifo_file) as fifo:
- while True:
- select.select([fifo],[],[fifo])
- data = fifo.read()
- GLib.idle_add(self._load_grep_ui, data)
+ self.create_ipc_listener()
def _show_grep_list_page(self, widget=None, eve=None):
@@ -166,9 +140,7 @@ class Plugin(PluginBase):
query = widget.get_text()
if not query in ("", None):
target_dir = shlex.quote( self._fm_state.tab.get_current_directory() )
- # command = [f"{self.path}/search.sh", "-t", "file_search", "-d", f"{target_dir}", "-q", f"{query}"]
command = ["python", f"{self.path}/search.py", "-t", "file_search", "-d", f"{target_dir}", "-q", f"{query}"]
-
process = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
def _stop_find_file_query(self, widget=None, eve=None):
diff --git a/plugins/searcher/search.py b/plugins/searcher/search.py
index 5756dfe..f06f3fa 100755
--- a/plugins/searcher/search.py
+++ b/plugins/searcher/search.py
@@ -2,8 +2,9 @@
# Python imports
-import os, traceback, argparse, time, json, base64
+import os, traceback, argparse, json, base64
from setproctitle import setproctitle
+from multiprocessing.connection import Client
# Lib imports
@@ -11,8 +12,9 @@ from setproctitle import setproctitle
-_files_fifo_file = f"/tmp/search_files_fifo"
-_grep_fifo_file = f"/tmp/grep_files_fifo"
+
+_ipc_address = f'/tmp/solarfm-search_grep-ipc.sock'
+_ipc_authkey = b'' + bytes(f'solarfm-search_grep-ipc', 'utf-8')
filter = (".mkv", ".mp4", ".webm", ".avi", ".mov", ".m4v", ".mpg", ".mpeg", ".wmv", ".flv") + \
(".png", ".jpg", ".jpeg", ".gif", ".ico", ".tga", ".webp") + \
@@ -21,46 +23,49 @@ filter = (".mkv", ".mp4", ".webm", ".avi", ".mov", ".m4v", ".mpg", ".mpeg", ".wm
file_result_set = []
-def file_search(fifo, path, query):
+def send_ipc_message(message) -> None:
+ try:
+ conn = Client(address=_ipc_address, family="AF_UNIX", authkey=_ipc_authkey)
+
+ conn.send(message)
+ conn.close()
+ except ConnectionRefusedError as e:
+ print("Connection refused...")
+ except Exception as e:
+ print(repr(e))
+
+
+def file_search(path, query):
try:
for file in os.listdir(path):
target = os.path.join(path, file)
if os.path.isdir(target):
- file_search(fifo, target, query)
+ file_search(target, query)
else:
if query.lower() in file.lower():
- # file_result_set.append([target, file])
- data = json.dumps([target, file])
- fifo.write(data)
- time.sleep(0.01)
+ data = f"SEARCH|{json.dumps([target, file])}"
+ send_ipc_message(data)
except Exception as e:
print("Couldn't traverse to path. Might be permissions related...")
traceback.print_exc()
def _search_for_string(file, query):
- try:
- b64_file = base64.urlsafe_b64encode(file.encode('utf-8')).decode('utf-8')
- grep_result_set = {}
+ b64_file = base64.urlsafe_b64encode(file.encode('utf-8')).decode('utf-8')
+ grep_result_set = {}
- with open(file, 'r') as fp:
- for i, line in enumerate(fp):
- if query in line:
- b64_line = base64.urlsafe_b64encode(line.encode('utf-8')).decode('utf-8')
+ with open(file, 'r') as fp:
+ for i, line in enumerate(fp):
+ if query in line:
+ b64_line = base64.urlsafe_b64encode(line.encode('utf-8')).decode('utf-8')
- if f"{b64_file}" in grep_result_set.keys():
- grep_result_set[f"{b64_file}"][f"{i+1}"] = b64_line
- else:
- grep_result_set[f"{b64_file}"] = {}
- grep_result_set[f"{b64_file}"] = {f"{i+1}": b64_line}
+ if f"{b64_file}" in grep_result_set.keys():
+ grep_result_set[f"{b64_file}"][f"{i+1}"] = b64_line
+ else:
+ grep_result_set[f"{b64_file}"] = {}
+ grep_result_set[f"{b64_file}"] = {f"{i+1}": b64_line}
- # NOTE: Push to fifo here after loop
- with open(_grep_fifo_file, 'w') as fifo:
- data = json.dumps(grep_result_set)
- fifo.write(data)
- time.sleep(0.05)
- except Exception as e:
- print("Couldn't read file. Might be binary or other cause...")
- traceback.print_exc()
+ data = f"GREP|{json.dumps(grep_result_set)}"
+ send_ipc_message(data)
def grep_search(path, query):
@@ -78,8 +83,7 @@ def grep_search(path, query):
def search(args):
if args.type == "file_search":
- with open(_files_fifo_file, 'w') as fifo:
- file_search(fifo, args.dir, args.query)
+ file_search(args.dir, args.query)
if args.type == "grep_search":
grep_search(args.dir, args.query)
@@ -98,6 +102,5 @@ if __name__ == "__main__":
# Read arguments (If any...)
args = parser.parse_args()
search(args)
-
except Exception as e:
traceback.print_exc()
From 982e5869363858701f35523d7e9057a9c8cbe5cd Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Mon, 3 Oct 2022 22:14:18 -0500
Subject: [PATCH 10/30] Restructured searcher layout
---
plugins/searcher/mixins/__init__.py | 3 +
plugins/searcher/mixins/file_search_mixin.py | 71 +++++++++
plugins/searcher/mixins/grep_search_mixin.py | 73 ++++++++++
plugins/searcher/plugin.py | 136 ++----------------
plugins/searcher/search_dialog.glade | 4 +-
plugins/searcher/{ => utils}/ipc_server.py | 41 +++---
plugins/searcher/{ => utils}/search.py | 0
plugins/searcher/widgets/__init__.py | 3 +
.../searcher/widgets/file_preview_widget.py | 16 +++
.../searcher/widgets/grep_preview_widget.py | 46 ++++++
10 files changed, 249 insertions(+), 144 deletions(-)
create mode 100644 plugins/searcher/mixins/__init__.py
create mode 100644 plugins/searcher/mixins/file_search_mixin.py
create mode 100644 plugins/searcher/mixins/grep_search_mixin.py
rename plugins/searcher/{ => utils}/ipc_server.py (74%)
rename plugins/searcher/{ => utils}/search.py (100%)
create mode 100644 plugins/searcher/widgets/__init__.py
create mode 100644 plugins/searcher/widgets/file_preview_widget.py
create mode 100644 plugins/searcher/widgets/grep_preview_widget.py
diff --git a/plugins/searcher/mixins/__init__.py b/plugins/searcher/mixins/__init__.py
new file mode 100644
index 0000000..0c12f42
--- /dev/null
+++ b/plugins/searcher/mixins/__init__.py
@@ -0,0 +1,3 @@
+"""
+ Mixins Module
+"""
diff --git a/plugins/searcher/mixins/file_search_mixin.py b/plugins/searcher/mixins/file_search_mixin.py
new file mode 100644
index 0000000..4095d65
--- /dev/null
+++ b/plugins/searcher/mixins/file_search_mixin.py
@@ -0,0 +1,71 @@
+# Python imports
+import threading, subprocess, signal, time, json, shlex
+
+# Lib imports
+from gi.repository import GLib
+
+# Application imports
+from ..widgets.file_preview_widget import FilePreviewWidget
+
+
+# NOTE: Threads WILL NOT die with parent's destruction.
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
+ return wrapper
+
+# NOTE: Threads WILL die with parent's destruction.
+def daemon_threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
+ return wrapper
+
+
+class FileSearchMixin:
+ def _run_find_file_query(self, widget=None, eve=None):
+ self._handle_find_file_query(query=widget)
+
+ @daemon_threaded
+ def _handle_find_file_query(self, widget=None, eve=None, query=None):
+ # NOTE: Freeze IPC consumption
+ self.pause_fifo_update = True
+
+ # NOTE: Kill the former process
+ if self._list_proc:
+ if self._list_proc.poll():
+ self._list_proc.send_signal(signal.SIGKILL)
+ while self._list_proc.poll():
+ pass
+
+ self._list_proc = None
+ else:
+ self._list_proc = None
+
+ GLib.idle_add(self.clear_children, self._file_list)
+ while len(self._file_list.get_children()) > 0:
+ ...
+
+ # NOTE: Make sure ui thread redraws
+ time.sleep(0.5)
+ self.pause_fifo_update = False
+
+ # NOTE: If query create new process and do all new loop.
+ if query:
+ GLib.idle_add(self._exec_find_file_query, query)
+
+ def _exec_find_file_query(self, widget=None, eve=None):
+ query = widget.get_text()
+
+ if not query in ("", None):
+ target_dir = shlex.quote( self._fm_state.tab.get_current_directory() )
+ command = ["python", f"{self.path}/utils/search.py", "-t", "file_search", "-d", f"{target_dir}", "-q", f"{query}"]
+ self._list_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
+
+ def _load_file_ui(self, data):
+ if not data in ("", None) and not self.pause_fifo_update:
+ jdata = json.loads( data )
+ target = jdata[0]
+ file = jdata[1]
+
+ widget = FilePreviewWidget(target, file)
+ self._file_list.add(widget)
diff --git a/plugins/searcher/mixins/grep_search_mixin.py b/plugins/searcher/mixins/grep_search_mixin.py
new file mode 100644
index 0000000..860a346
--- /dev/null
+++ b/plugins/searcher/mixins/grep_search_mixin.py
@@ -0,0 +1,73 @@
+# Python imports
+import threading, subprocess, signal, time, json, shlex
+
+# Lib imports
+from gi.repository import GLib
+
+# Application imports
+from ..widgets.grep_preview_widget import GrepPreviewWidget
+
+
+# NOTE: Threads WILL NOT die with parent's destruction.
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
+ return wrapper
+
+# NOTE: Threads WILL die with parent's destruction.
+def daemon_threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
+ return wrapper
+
+
+class GrepSearchMixin:
+ def _run_grep_query(self, widget=None, eve=None):
+ self._handle_grep_query(query=widget)
+
+ @daemon_threaded
+ def _handle_grep_query(self, widget=None, eve=None, query=None):
+ # NOTE: Freeze IPC consumption
+ self.pause_fifo_update = True
+
+ # NOTE: Kill the former process
+ if self._grep_proc:
+ if self._grep_proc.poll():
+ self._grep_proc.send_signal(signal.SIGKILL)
+ while self._grep_proc.poll():
+ pass
+
+ self._grep_proc = None
+ else:
+ self._grep_proc = None
+
+ # NOTE: Clear children from ui
+ GLib.idle_add(self.clear_children, self._grep_list)
+ while len(self._grep_list.get_children()) > 0:
+ ...
+
+ # NOTE: Make sure ui thread redraws
+ time.sleep(0.5)
+ self.pause_fifo_update = False
+
+ # NOTE: If query create new process and do all new loop.
+ if query:
+ GLib.idle_add(self._exec_grep_query, query)
+
+ def _exec_grep_query(self, widget=None, eve=None):
+ query = widget.get_text()
+ if not query in ("", None):
+ target_dir = shlex.quote( self._fm_state.tab.get_current_directory() )
+ command = ["python", f"{self.path}/utils/search.py", "-t", "grep_search", "-d", f"{target_dir}", "-q", f"{query}"]
+ self._grep_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
+
+ def _load_grep_ui(self, data):
+ if not data in ("", None) and not self.pause_fifo_update:
+ jdata = json.loads( data )
+ jkeys = jdata.keys()
+ for key in jkeys:
+ sub_keys = jdata[key].keys()
+ grep_result = jdata[key]
+
+ widget = GrepPreviewWidget(key, sub_keys, grep_result)
+ self._grep_list.add(widget)
diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py
index cedbfdf..61eaeda 100644
--- a/plugins/searcher/plugin.py
+++ b/plugins/searcher/plugin.py
@@ -1,5 +1,5 @@
# Python imports
-import os, threading, subprocess, inspect, time, json, base64, shlex, select, signal
+import os, threading, inspect
# Lib imports
import gi
@@ -8,7 +8,9 @@ from gi.repository import Gtk, GLib
# Application imports
from plugins.plugin_base import PluginBase
-from .ipc_server import IPCServer
+from .mixins.file_search_mixin import FileSearchMixin
+from .mixins.grep_search_mixin import GrepSearchMixin
+from .utils.ipc_server import IPCServer
@@ -27,54 +29,7 @@ def daemon_threaded(fn):
-class FilePreviewWidget(Gtk.LinkButton):
- def __init__(self, path, file):
- super(FilePreviewWidget, self).__init__()
- self.set_label(file)
- self.set_uri(f"file://{path}")
- self.show_all()
-
-
-class GrepPreviewWidget(Gtk.Box):
- def __init__(self, _path, sub_keys, data):
- super(GrepPreviewWidget, self).__init__()
- self.set_orientation(Gtk.Orientation.VERTICAL)
- self.line_color = "#e0cc64"
-
- path = base64.urlsafe_b64decode(_path.encode('utf-8')).decode('utf-8')
- _label = '/'.join( path.split("/")[-3:] )
- title = Gtk.LinkButton.new_with_label(uri=f"file://{path}", label=_label)
-
- self.add(title)
- for key in sub_keys:
- line_num = key
- text = base64.urlsafe_b64decode(data[key].encode('utf-8')).decode('utf-8')
-
-
- box = Gtk.Box()
- number_label = Gtk.Label()
- text_view = Gtk.Label(label=text[:-1])
- label_text = f"{line_num}"
-
- number_label.set_markup(label_text)
- number_label.set_margin_left(15)
- number_label.set_margin_right(5)
- number_label.set_margin_top(5)
- number_label.set_margin_bottom(5)
- text_view.set_margin_top(5)
- text_view.set_margin_bottom(5)
- text_view.set_line_wrap(True)
-
- box.add(number_label)
- box.add(text_view)
- self.add(box)
-
- self.show_all()
-
-
-pause_fifo_update = False
-
-class Plugin(IPCServer, PluginBase):
+class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
def __init__(self):
super().__init__()
@@ -89,11 +44,13 @@ class Plugin(IPCServer, PluginBase):
self._grep_list = None
self._grep_proc = None
self._list_proc = None
+ self.pause_fifo_update = False
+ self.update_list_ui_buffer = ()
def get_ui_element(self):
button = Gtk.Button(label=self.name)
- button.connect("button-release-event", self._show_grep_list_page)
+ button.connect("button-release-event", self._show_page)
return button
def run(self):
@@ -123,7 +80,7 @@ class Plugin(IPCServer, PluginBase):
self.create_ipc_listener()
- def _show_grep_list_page(self, widget=None, eve=None):
+ def _show_page(self, widget=None, eve=None):
self._event_system.emit("get_current_state")
state = self._fm_state
@@ -134,74 +91,7 @@ class Plugin(IPCServer, PluginBase):
self._search_dialog.hide()
- def _run_find_file_query(self, widget=None, eve=None):
- self._stop_find_file_query()
-
- query = widget.get_text()
- if not query in ("", None):
- target_dir = shlex.quote( self._fm_state.tab.get_current_directory() )
- command = ["python", f"{self.path}/search.py", "-t", "file_search", "-d", f"{target_dir}", "-q", f"{query}"]
- process = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
-
- def _stop_find_file_query(self, widget=None, eve=None):
- pause_fifo_update = True
-
- if self._list_proc:
- if self._list_proc.poll():
- self._list_proc.send_signal(signal.SIGKILL)
- while self._list_proc.poll():
- pass
-
- self._list_proc = None
- else:
- self._list_proc = None
-
- self.clear_children(self._file_list)
- pause_fifo_update = False
-
-
- def _run_grep_query(self, widget=None, eve=None):
- self._stop_grep_query()
-
- query = widget.get_text()
- if not query in ("", None):
- target_dir = shlex.quote( self._fm_state.tab.get_current_directory() )
- command = ["python", f"{self.path}/search.py", "-t", "grep_search", "-d", f"{target_dir}", "-q", f"{query}"]
- process = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
-
- def _stop_grep_query(self, widget=None, eve=None):
- pause_fifo_update = True
-
- if self._grep_proc:
- if self._grep_proc.poll():
- self._grep_proc.send_signal(signal.SIGKILL)
- while self._grep_proc.poll():
- pass
-
- self._grep_proc = None
- else:
- self._grep_proc = None
-
- self.clear_children(self._grep_list)
- pause_fifo_update = False
-
-
- def _load_file_ui(self, data):
- if not data in ("", None) and not pause_fifo_update:
- jdata = json.loads( data )
- target = jdata[0]
- file = jdata[1]
-
- widget = FilePreviewWidget(target, file)
- self._file_list.add(widget)
-
- def _load_grep_ui(self, data):
- if not data in ("", None) and not pause_fifo_update:
- jdata = json.loads( data )
- jkeys = jdata.keys()
- for key in jkeys:
- sub_keys = jdata[key].keys()
- grep_result = jdata[key]
-
- widget = GrepPreviewWidget(key, sub_keys, grep_result)
- self._grep_list.add(widget)
+ def clear_children(self, widget: type) -> None:
+ ''' Clear children of a gtk widget. '''
+ for child in widget.get_children():
+ widget.remove(child)
diff --git a/plugins/searcher/search_dialog.glade b/plugins/searcher/search_dialog.glade
index 9fbca4b..aaa3b09 100644
--- a/plugins/searcher/search_dialog.glade
+++ b/plugins/searcher/search_dialog.glade
@@ -83,7 +83,7 @@
False
Search for file...
-
+
False
@@ -150,7 +150,7 @@
False
Query string in file...
-
+
False
diff --git a/plugins/searcher/ipc_server.py b/plugins/searcher/utils/ipc_server.py
similarity index 74%
rename from plugins/searcher/ipc_server.py
rename to plugins/searcher/utils/ipc_server.py
index c654842..937a474 100644
--- a/plugins/searcher/ipc_server.py
+++ b/plugins/searcher/utils/ipc_server.py
@@ -57,32 +57,35 @@ class IPCServer:
def handle_message(self, conn, start_time) -> None:
while True:
msg = conn.recv()
- data = msg
- if "SEARCH|" in msg:
- file = msg.split("SEARCH|")[1].strip()
- if file:
- GLib.idle_add(self._load_file_ui, file)
+ if not self.pause_fifo_update:
+ if "SEARCH|" in msg:
+ file = msg.split("SEARCH|")[1].strip()
+ if file:
+ GLib.idle_add(self._load_file_ui, file)
- conn.close()
- break
+ conn.close()
+ break
- if "GREP|" in msg:
- data = msg.split("GREP|")[1].strip()
- if data:
- GLib.idle_add(self._load_grep_ui, data)
+ if "GREP|" in msg:
+ data = msg.split("GREP|")[1].strip()
+ if data:
+ GLib.idle_add(self._load_grep_ui, data)
- conn.close()
- break
+ conn.close()
+ break
- if msg in ['close connection', 'close server']:
- conn.close()
- break
+ if msg in ['close connection', 'close server']:
+ conn.close()
+ break
- # NOTE: Not perfect but insures we don't lock up the connection for too long.
- end_time = time.perf_counter()
- if (end_time - start_time) > self._ipc_timeout:
+ # NOTE: Not perfect but insures we don't lock up the connection for too long.
+ end_time = time.perf_counter()
+ if (end_time - start_time) > self._ipc_timeout:
+ conn.close()
+ break
+ else:
conn.close()
break
diff --git a/plugins/searcher/search.py b/plugins/searcher/utils/search.py
similarity index 100%
rename from plugins/searcher/search.py
rename to plugins/searcher/utils/search.py
diff --git a/plugins/searcher/widgets/__init__.py b/plugins/searcher/widgets/__init__.py
new file mode 100644
index 0000000..72b072b
--- /dev/null
+++ b/plugins/searcher/widgets/__init__.py
@@ -0,0 +1,3 @@
+"""
+ Widgets Module
+"""
diff --git a/plugins/searcher/widgets/file_preview_widget.py b/plugins/searcher/widgets/file_preview_widget.py
new file mode 100644
index 0000000..227e37a
--- /dev/null
+++ b/plugins/searcher/widgets/file_preview_widget.py
@@ -0,0 +1,16 @@
+# Python imports
+
+# Gtk imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+# Application imports
+
+
+class FilePreviewWidget(Gtk.LinkButton):
+ def __init__(self, path, file):
+ super(FilePreviewWidget, self).__init__()
+ self.set_label(file)
+ self.set_uri(f"file://{path}")
+ self.show_all()
diff --git a/plugins/searcher/widgets/grep_preview_widget.py b/plugins/searcher/widgets/grep_preview_widget.py
new file mode 100644
index 0000000..85aa2f8
--- /dev/null
+++ b/plugins/searcher/widgets/grep_preview_widget.py
@@ -0,0 +1,46 @@
+# Python imports
+import base64
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+# Application imports
+
+
+class GrepPreviewWidget(Gtk.Box):
+ def __init__(self, _path, sub_keys, data):
+ super(GrepPreviewWidget, self).__init__()
+ self.set_orientation(Gtk.Orientation.VERTICAL)
+ self.line_color = "#e0cc64"
+
+ path = base64.urlsafe_b64decode(_path.encode('utf-8')).decode('utf-8')
+ _label = '/'.join( path.split("/")[-3:] )
+ title = Gtk.LinkButton.new_with_label(uri=f"file://{path}", label=_label)
+
+ self.add(title)
+ for key in sub_keys:
+ line_num = key
+ text = base64.urlsafe_b64decode(data[key].encode('utf-8')).decode('utf-8')
+
+
+ box = Gtk.Box()
+ number_label = Gtk.Label()
+ text_view = Gtk.Label(label=text[:-1])
+ label_text = f"{line_num}"
+
+ number_label.set_markup(label_text)
+ number_label.set_margin_left(15)
+ number_label.set_margin_right(5)
+ number_label.set_margin_top(5)
+ number_label.set_margin_bottom(5)
+ text_view.set_margin_top(5)
+ text_view.set_margin_bottom(5)
+ text_view.set_line_wrap(True)
+
+ box.add(number_label)
+ box.add(text_view)
+ self.add(box)
+
+ self.show_all()
From 0dece2cec96254cc2644ff5478bd87dcf1e5e6cb Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Tue, 4 Oct 2022 02:30:46 -0500
Subject: [PATCH 11/30] More searcher plugin updates, added additional file
settings
---
plugins/searcher/mixins/grep_search_mixin.py | 3 +-
plugins/searcher/utils/search.py | 69 ++++++++++++++-----
.../searcher/widgets/grep_preview_widget.py | 52 +++++++-------
3 files changed, 78 insertions(+), 46 deletions(-)
diff --git a/plugins/searcher/mixins/grep_search_mixin.py b/plugins/searcher/mixins/grep_search_mixin.py
index 860a346..cdf1c58 100644
--- a/plugins/searcher/mixins/grep_search_mixin.py
+++ b/plugins/searcher/mixins/grep_search_mixin.py
@@ -70,4 +70,5 @@ class GrepSearchMixin:
grep_result = jdata[key]
widget = GrepPreviewWidget(key, sub_keys, grep_result)
- self._grep_list.add(widget)
+ GLib.idle_add(self._grep_list.add, widget)
+ # self._grep_list.add(widget)
diff --git a/plugins/searcher/utils/search.py b/plugins/searcher/utils/search.py
index f06f3fa..1355f1d 100755
--- a/plugins/searcher/utils/search.py
+++ b/plugins/searcher/utils/search.py
@@ -2,7 +2,7 @@
# Python imports
-import os, traceback, argparse, json, base64
+import os, traceback, argparse, threading, json, base64, time
from setproctitle import setproctitle
from multiprocessing.connection import Client
@@ -16,11 +16,20 @@ from multiprocessing.connection import Client
_ipc_address = f'/tmp/solarfm-search_grep-ipc.sock'
_ipc_authkey = b'' + bytes(f'solarfm-search_grep-ipc', 'utf-8')
-filter = (".mkv", ".mp4", ".webm", ".avi", ".mov", ".m4v", ".mpg", ".mpeg", ".wmv", ".flv") + \
- (".png", ".jpg", ".jpeg", ".gif", ".ico", ".tga", ".webp") + \
- (".psf", ".mp3", ".ogg", ".flac", ".m4a")
+filter = (".cpp", ".css", ".c", ".go", ".html", ".htm", ".java", ".js", ".json", ".lua", ".md", ".py", ".rs", ".toml", ".xml", ".pom") + \
+ (".txt", ".text", ".sh", ".cfg", ".conf", ".log")
-file_result_set = []
+# NOTE: Threads WILL NOT die with parent's destruction.
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
+ return wrapper
+
+# NOTE: Threads WILL die with parent's destruction.
+def daemon_threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
+ return wrapper
def send_ipc_message(message) -> None:
@@ -42,7 +51,7 @@ def file_search(path, query):
if os.path.isdir(target):
file_search(target, query)
else:
- if query.lower() in file.lower():
+ if query in file.lower():
data = f"SEARCH|{json.dumps([target, file])}"
send_ipc_message(data)
except Exception as e:
@@ -52,21 +61,40 @@ def file_search(path, query):
def _search_for_string(file, query):
b64_file = base64.urlsafe_b64encode(file.encode('utf-8')).decode('utf-8')
grep_result_set = {}
-
+ padding = 15
with open(file, 'r') as fp:
- for i, line in enumerate(fp):
- if query in line:
- b64_line = base64.urlsafe_b64encode(line.encode('utf-8')).decode('utf-8')
+ # NOTE: I know there's an issue if there's a very large file with content all on one line will lower and dupe it.
+ # And, yes, it will only return one instance from the file.
+ for i, raw in enumerate(fp):
+ line = None
+ llower = raw.lower()
+ if not query in llower:
+ continue
- if f"{b64_file}" in grep_result_set.keys():
- grep_result_set[f"{b64_file}"][f"{i+1}"] = b64_line
- else:
- grep_result_set[f"{b64_file}"] = {}
- grep_result_set[f"{b64_file}"] = {f"{i+1}": b64_line}
+ if len(raw) > 72:
+ start = 0
+ end = len(raw) - 1
+ index = llower.index(query)
+ sindex = llower.index(query) - 15 if index >= 15 else abs(start - index) - index
+ eindex = sindex + 15 if end > (index + 15) else abs(index - end) + index
+ line = raw[sindex:eindex]
+ else:
+ line = raw
+
+ b64_line = base64.urlsafe_b64encode(line.encode('utf-8')).decode('utf-8')
+ if f"{b64_file}" in grep_result_set.keys():
+ grep_result_set[f"{b64_file}"][f"{i+1}"] = b64_line
+ else:
+ grep_result_set[f"{b64_file}"] = {}
+ grep_result_set[f"{b64_file}"] = {f"{i+1}": b64_line}
data = f"GREP|{json.dumps(grep_result_set)}"
send_ipc_message(data)
+@daemon_threaded
+def _search_for_string_threaded(file, query):
+ _search_for_string(file, query)
+
def grep_search(path, query):
try:
@@ -75,18 +103,23 @@ def grep_search(path, query):
if os.path.isdir(target):
grep_search(target, query)
else:
- if not target.lower().endswith(filter):
+ if target.lower().endswith(filter):
+ size = os.path.getsize(target)
+ if size < 5000:
_search_for_string(target, query)
+ else:
+ _search_for_string_threaded(target, query)
+
except Exception as e:
print("Couldn't traverse to path. Might be permissions related...")
traceback.print_exc()
def search(args):
if args.type == "file_search":
- file_search(args.dir, args.query)
+ file_search(args.dir, args.query.lower())
if args.type == "grep_search":
- grep_search(args.dir, args.query)
+ grep_search(args.dir, args.query.lower())
if __name__ == "__main__":
diff --git a/plugins/searcher/widgets/grep_preview_widget.py b/plugins/searcher/widgets/grep_preview_widget.py
index 85aa2f8..899d309 100644
--- a/plugins/searcher/widgets/grep_preview_widget.py
+++ b/plugins/searcher/widgets/grep_preview_widget.py
@@ -10,37 +10,35 @@ from gi.repository import Gtk
class GrepPreviewWidget(Gtk.Box):
- def __init__(self, _path, sub_keys, data):
+ def __init__(self, _path, sub_keys, _data):
super(GrepPreviewWidget, self).__init__()
self.set_orientation(Gtk.Orientation.VERTICAL)
self.line_color = "#e0cc64"
- path = base64.urlsafe_b64decode(_path.encode('utf-8')).decode('utf-8')
- _label = '/'.join( path.split("/")[-3:] )
- title = Gtk.LinkButton.new_with_label(uri=f"file://{path}", label=_label)
+ path = self.decode_str(_path)
+ _label = '/'.join( path.split("/")[-3:] )
+ title = Gtk.LinkButton.new_with_label(uri=f"file://{path}", label=_label)
+
+ text_view = Gtk.TextView()
+ text_view.set_editable(False)
+ text_view.set_wrap_mode(Gtk.WrapMode.NONE)
+ buffer = text_view.get_buffer()
+
+ for i, key in enumerate(sub_keys):
+ line_num = self.make_utf8_line_num(self.line_color, key)
+ text = f"\t\t{ self.decode_str(_data[key]) }"
+
+ itr = buffer.get_end_iter()
+ buffer.insert_markup(itr, line_num, len(line_num))
+ itr = buffer.get_end_iter()
+ buffer.insert(itr, text, length=len(text))
self.add(title)
- for key in sub_keys:
- line_num = key
- text = base64.urlsafe_b64decode(data[key].encode('utf-8')).decode('utf-8')
-
-
- box = Gtk.Box()
- number_label = Gtk.Label()
- text_view = Gtk.Label(label=text[:-1])
- label_text = f"{line_num}"
-
- number_label.set_markup(label_text)
- number_label.set_margin_left(15)
- number_label.set_margin_right(5)
- number_label.set_margin_top(5)
- number_label.set_margin_bottom(5)
- text_view.set_margin_top(5)
- text_view.set_margin_bottom(5)
- text_view.set_line_wrap(True)
-
- box.add(number_label)
- box.add(text_view)
- self.add(box)
-
+ self.add(text_view)
self.show_all()
+
+ def decode_str(self, target):
+ return base64.urlsafe_b64decode(target.encode('utf-8')).decode('utf-8')
+
+ def make_utf8_line_num(self, color, target):
+ return bytes(f"\n{target}", "utf-8").decode("utf-8")
From e929e9b742ffcd7bbbcf7a464946c2d589f6975b Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Tue, 4 Oct 2022 22:58:27 -0500
Subject: [PATCH 12/30] searcher - updated timings
---
plugins/searcher/mixins/file_search_mixin.py | 15 +++---
plugins/searcher/mixins/grep_search_mixin.py | 22 +++++----
plugins/searcher/plugin.py | 5 +-
plugins/searcher/utils/ipc_server.py | 49 +++++++------------
plugins/searcher/utils/search.py | 10 ++--
.../searcher/widgets/grep_preview_widget.py | 43 ++++++++++------
user_config/usr/share/solarfm/settings.json | 2 +-
7 files changed, 75 insertions(+), 71 deletions(-)
diff --git a/plugins/searcher/mixins/file_search_mixin.py b/plugins/searcher/mixins/file_search_mixin.py
index 4095d65..51cddce 100644
--- a/plugins/searcher/mixins/file_search_mixin.py
+++ b/plugins/searcher/mixins/file_search_mixin.py
@@ -29,6 +29,7 @@ class FileSearchMixin:
def _handle_find_file_query(self, widget=None, eve=None, query=None):
# NOTE: Freeze IPC consumption
self.pause_fifo_update = True
+ self.search_query = ""
# NOTE: Kill the former process
if self._list_proc:
@@ -41,15 +42,13 @@ class FileSearchMixin:
else:
self._list_proc = None
+ # NOTE: Clear children from ui and make sure ui thread redraws
GLib.idle_add(self.clear_children, self._file_list)
while len(self._file_list.get_children()) > 0:
- ...
-
- # NOTE: Make sure ui thread redraws
- time.sleep(0.5)
- self.pause_fifo_update = False
+ time.sleep(0.2)
# NOTE: If query create new process and do all new loop.
+ self.pause_fifo_update = False
if query:
GLib.idle_add(self._exec_find_file_query, query)
@@ -57,12 +56,16 @@ class FileSearchMixin:
query = widget.get_text()
if not query in ("", None):
+ self.search_query = query
target_dir = shlex.quote( self._fm_state.tab.get_current_directory() )
command = ["python", f"{self.path}/utils/search.py", "-t", "file_search", "-d", f"{target_dir}", "-q", f"{query}"]
self._list_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
def _load_file_ui(self, data):
- if not data in ("", None) and not self.pause_fifo_update:
+ if self.pause_fifo_update:
+ return
+
+ if not data in ("", None):
jdata = json.loads( data )
target = jdata[0]
file = jdata[1]
diff --git a/plugins/searcher/mixins/grep_search_mixin.py b/plugins/searcher/mixins/grep_search_mixin.py
index cdf1c58..a83a24c 100644
--- a/plugins/searcher/mixins/grep_search_mixin.py
+++ b/plugins/searcher/mixins/grep_search_mixin.py
@@ -29,6 +29,7 @@ class GrepSearchMixin:
def _handle_grep_query(self, widget=None, eve=None, query=None):
# NOTE: Freeze IPC consumption
self.pause_fifo_update = True
+ self.grep_query = ""
# NOTE: Kill the former process
if self._grep_proc:
@@ -41,34 +42,35 @@ class GrepSearchMixin:
else:
self._grep_proc = None
- # NOTE: Clear children from ui
+ # NOTE: Clear children from ui and make sure ui thread redraws
GLib.idle_add(self.clear_children, self._grep_list)
while len(self._grep_list.get_children()) > 0:
- ...
-
- # NOTE: Make sure ui thread redraws
- time.sleep(0.5)
- self.pause_fifo_update = False
+ time.sleep(0.2)
# NOTE: If query create new process and do all new loop.
+ self.pause_fifo_update = False
if query:
GLib.idle_add(self._exec_grep_query, query)
def _exec_grep_query(self, widget=None, eve=None):
query = widget.get_text()
if not query in ("", None):
+ self.grep_query = query
+
target_dir = shlex.quote( self._fm_state.tab.get_current_directory() )
command = ["python", f"{self.path}/utils/search.py", "-t", "grep_search", "-d", f"{target_dir}", "-q", f"{query}"]
self._grep_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
def _load_grep_ui(self, data):
- if not data in ("", None) and not self.pause_fifo_update:
+ if self.pause_fifo_update:
+ return
+
+ if not data in ("", None):
jdata = json.loads( data )
jkeys = jdata.keys()
for key in jkeys:
sub_keys = jdata[key].keys()
grep_result = jdata[key]
- widget = GrepPreviewWidget(key, sub_keys, grep_result)
- GLib.idle_add(self._grep_list.add, widget)
- # self._grep_list.add(widget)
+ widget = GrepPreviewWidget(key, sub_keys, grep_result, self.grep_query)
+ self._grep_list.add(widget)
diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py
index 61eaeda..d506e21 100644
--- a/plugins/searcher/plugin.py
+++ b/plugins/searcher/plugin.py
@@ -1,5 +1,5 @@
# Python imports
-import os, threading, inspect
+import os, threading, inspect, time
# Lib imports
import gi
@@ -46,6 +46,8 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self._list_proc = None
self.pause_fifo_update = False
self.update_list_ui_buffer = ()
+ self.grep_query = ""
+ self.search_query = ""
def get_ui_element(self):
@@ -95,3 +97,4 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
''' Clear children of a gtk widget. '''
for child in widget.get_children():
widget.remove(child)
+ time.sleep(0.01)
diff --git a/plugins/searcher/utils/ipc_server.py b/plugins/searcher/utils/ipc_server.py
index 937a474..96969cf 100644
--- a/plugins/searcher/utils/ipc_server.py
+++ b/plugins/searcher/utils/ipc_server.py
@@ -48,47 +48,32 @@ class IPCServer:
self.is_ipc_alive = True
while True:
- conn = listener.accept()
- start_time = time.perf_counter()
- self.handle_message(conn, start_time)
+ conn = listener.accept()
+
+ if not self.pause_fifo_update:
+ self.handle_message(conn)
+ else:
+ conn.close()
listener.close()
- def handle_message(self, conn, start_time) -> None:
+ def handle_message(self, conn) -> None:
while True:
msg = conn.recv()
- if not self.pause_fifo_update:
- if "SEARCH|" in msg:
- file = msg.split("SEARCH|")[1].strip()
- if file:
- GLib.idle_add(self._load_file_ui, file)
+ if "SEARCH|" in msg:
+ file = msg.split("SEARCH|")[1].strip()
+ if file:
+ GLib.idle_add(self._load_file_ui, file)
- conn.close()
- break
-
- if "GREP|" in msg:
- data = msg.split("GREP|")[1].strip()
- if data:
- GLib.idle_add(self._load_grep_ui, data)
-
- conn.close()
- break
+ if "GREP|" in msg:
+ data = msg.split("GREP|")[1].strip()
+ if data:
+ GLib.idle_add(self._load_grep_ui, data)
- if msg in ['close connection', 'close server']:
- conn.close()
- break
-
- # NOTE: Not perfect but insures we don't lock up the connection for too long.
- end_time = time.perf_counter()
- if (end_time - start_time) > self._ipc_timeout:
- conn.close()
- break
- else:
- conn.close()
- break
-
+ conn.close()
+ break
def send_ipc_message(self, message: str = "Empty Data...") -> None:
try:
diff --git a/plugins/searcher/utils/search.py b/plugins/searcher/utils/search.py
index 1355f1d..31bdcc6 100755
--- a/plugins/searcher/utils/search.py
+++ b/plugins/searcher/utils/search.py
@@ -33,15 +33,11 @@ def daemon_threaded(fn):
def send_ipc_message(message) -> None:
- try:
- conn = Client(address=_ipc_address, family="AF_UNIX", authkey=_ipc_authkey)
-
+ with Client(address=_ipc_address, family="AF_UNIX", authkey=_ipc_authkey) as conn:
conn.send(message)
conn.close()
- except ConnectionRefusedError as e:
- print("Connection refused...")
- except Exception as e:
- print(repr(e))
+
+ time.sleep(0.05)
def file_search(path, query):
diff --git a/plugins/searcher/widgets/grep_preview_widget.py b/plugins/searcher/widgets/grep_preview_widget.py
index 899d309..4e6b799 100644
--- a/plugins/searcher/widgets/grep_preview_widget.py
+++ b/plugins/searcher/widgets/grep_preview_widget.py
@@ -1,5 +1,5 @@
# Python imports
-import base64
+import base64, re
# Lib imports
import gi
@@ -10,28 +10,31 @@ from gi.repository import Gtk
class GrepPreviewWidget(Gtk.Box):
- def __init__(self, _path, sub_keys, _data):
+ def __init__(self, _path, sub_keys, _data, _grep_query):
super(GrepPreviewWidget, self).__init__()
- self.set_orientation(Gtk.Orientation.VERTICAL)
- self.line_color = "#e0cc64"
- path = self.decode_str(_path)
- _label = '/'.join( path.split("/")[-3:] )
- title = Gtk.LinkButton.new_with_label(uri=f"file://{path}", label=_label)
+ self.set_orientation(Gtk.Orientation.VERTICAL)
+ line_color = "#e0cc64"
+ highlight_color = "#FBF719"
+ grep_query = _grep_query
+
+ path = self.decode_str(_path)
+ lbl = '/'.join( path.split("/")[-3:] )
+ title = Gtk.LinkButton.new_with_label(uri=f"file://{path}", label=lbl)
text_view = Gtk.TextView()
- text_view.set_editable(False)
- text_view.set_wrap_mode(Gtk.WrapMode.NONE)
buffer = text_view.get_buffer()
+ text_view.set_editable(False)
+ text_view.set_monospace(True)
+ text_view.set_wrap_mode(Gtk.WrapMode.NONE)
for i, key in enumerate(sub_keys):
- line_num = self.make_utf8_line_num(self.line_color, key)
- text = f"\t\t{ self.decode_str(_data[key]) }"
-
+ line_num = self.make_utf8_line_num(line_color, key)
itr = buffer.get_end_iter()
buffer.insert_markup(itr, line_num, len(line_num))
- itr = buffer.get_end_iter()
- buffer.insert(itr, text, length=len(text))
+
+ decoded = f"\t{self.decode_str(_data[key])}"
+ self.make_utf8_line_highlight(buffer, itr, i, highlight_color, decoded, grep_query)
self.add(title)
self.add(text_view)
@@ -42,3 +45,15 @@ class GrepPreviewWidget(Gtk.Box):
def make_utf8_line_num(self, color, target):
return bytes(f"\n{target}", "utf-8").decode("utf-8")
+
+ def make_utf8_line_highlight(self, buffer, itr, i, color, target, query):
+ parts = re.split(r"(" + query + ")(?i)", target.replace("\n", ""))
+ for part in parts:
+ itr = buffer.get_end_iter()
+
+ if not query.lower() == part.lower() and not query.lower() in part.lower():
+ buffer.insert(itr, part, length=len(part))
+ else:
+ new_s = f"{part}"
+ _part = bytes(new_s, "utf-8").decode("utf-8")
+ buffer.insert_markup(itr, _part, len(_part))
diff --git a/user_config/usr/share/solarfm/settings.json b/user_config/usr/share/solarfm/settings.json
index 9b6850b..d9d1789 100644
--- a/user_config/usr/share/solarfm/settings.json
+++ b/user_config/usr/share/solarfm/settings.json
@@ -23,7 +23,7 @@
"remux_folder_max_disk_usage": "8589934592"
},
"filters": {
- "code": [".cpp", ".css", ".c", ".go", ".html", ".htm", ".java", ".js", ".json", ".lua", ".md", ".py", ".rs"],
+ "code": [".cpp", ".css", ".c", ".go", ".html", ".htm", ".java", ".js", ".json", ".lua", ".md", ".py", ".rs", ".toml", ".xml", ".pom"],
"videos": [".mkv", ".mp4", ".webm", ".avi", ".mov", ".m4v", ".mpg", ".mpeg", ".wmv", ".flv"],
"office": [".doc", ".docx", ".xls", ".xlsx", ".xlt", ".xltx", ".xlm", ".ppt", ".pptx", ".pps", ".ppsx", ".odt", ".rtf"],
"images": [".png", ".jpg", ".jpeg", ".gif", ".ico", ".tga", ".webp"],
From 206f67f2f01328277194f83010c350c119b5b8a7 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Thu, 6 Oct 2022 20:48:44 -0500
Subject: [PATCH 13/30] Finally resolved UI thread overloads
---
plugins/searcher/mixins/file_search_mixin.py | 22 ++---
plugins/searcher/mixins/grep_search_mixin.py | 22 ++---
plugins/searcher/plugin.py | 37 ++++++--
plugins/searcher/search_dialog.glade | 92 +++++++++++++++-----
plugins/searcher/utils/search.py | 28 +++---
5 files changed, 132 insertions(+), 69 deletions(-)
diff --git a/plugins/searcher/mixins/file_search_mixin.py b/plugins/searcher/mixins/file_search_mixin.py
index 51cddce..ffcc32a 100644
--- a/plugins/searcher/mixins/file_search_mixin.py
+++ b/plugins/searcher/mixins/file_search_mixin.py
@@ -12,12 +12,14 @@ from ..widgets.file_preview_widget import FilePreviewWidget
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
+
return wrapper
# NOTE: Threads WILL die with parent's destruction.
def daemon_threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
+
return wrapper
@@ -25,6 +27,7 @@ class FileSearchMixin:
def _run_find_file_query(self, widget=None, eve=None):
self._handle_find_file_query(query=widget)
+ # TODO: Merge this logic with nearly the exact same thing in grep_search_mixin
@daemon_threaded
def _handle_find_file_query(self, widget=None, eve=None, query=None):
# NOTE: Freeze IPC consumption
@@ -33,19 +36,15 @@ class FileSearchMixin:
# NOTE: Kill the former process
if self._list_proc:
- if self._list_proc.poll():
- self._list_proc.send_signal(signal.SIGKILL)
- while self._list_proc.poll():
- pass
+ if self._list_proc.poll() == None:
+ self._list_proc.terminate()
+ while self._list_proc.poll() == None:
+ ...
- self._list_proc = None
- else:
- self._list_proc = None
+ self._list_proc = None
# NOTE: Clear children from ui and make sure ui thread redraws
- GLib.idle_add(self.clear_children, self._file_list)
- while len(self._file_list.get_children()) > 0:
- time.sleep(0.2)
+ GLib.idle_add(self.reset_file_list_box)
# NOTE: If query create new process and do all new loop.
self.pause_fifo_update = False
@@ -62,9 +61,6 @@ class FileSearchMixin:
self._list_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
def _load_file_ui(self, data):
- if self.pause_fifo_update:
- return
-
if not data in ("", None):
jdata = json.loads( data )
target = jdata[0]
diff --git a/plugins/searcher/mixins/grep_search_mixin.py b/plugins/searcher/mixins/grep_search_mixin.py
index a83a24c..dc8372d 100644
--- a/plugins/searcher/mixins/grep_search_mixin.py
+++ b/plugins/searcher/mixins/grep_search_mixin.py
@@ -12,12 +12,14 @@ from ..widgets.grep_preview_widget import GrepPreviewWidget
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
+
return wrapper
# NOTE: Threads WILL die with parent's destruction.
def daemon_threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
+
return wrapper
@@ -25,6 +27,7 @@ class GrepSearchMixin:
def _run_grep_query(self, widget=None, eve=None):
self._handle_grep_query(query=widget)
+ # TODO: Merge this logic with nearly the exact same thing in file_search_mixin
@daemon_threaded
def _handle_grep_query(self, widget=None, eve=None, query=None):
# NOTE: Freeze IPC consumption
@@ -33,19 +36,15 @@ class GrepSearchMixin:
# NOTE: Kill the former process
if self._grep_proc:
- if self._grep_proc.poll():
- self._grep_proc.send_signal(signal.SIGKILL)
- while self._grep_proc.poll():
- pass
+ if self._grep_proc.poll() == None:
+ self._grep_proc.terminate()
+ while self._grep_proc.poll() == None:
+ ...
- self._grep_proc = None
- else:
- self._grep_proc = None
+ self._grep_proc = None
# NOTE: Clear children from ui and make sure ui thread redraws
- GLib.idle_add(self.clear_children, self._grep_list)
- while len(self._grep_list.get_children()) > 0:
- time.sleep(0.2)
+ GLib.idle_add(self.reset_grep_box)
# NOTE: If query create new process and do all new loop.
self.pause_fifo_update = False
@@ -62,9 +61,6 @@ class GrepSearchMixin:
self._grep_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
def _load_grep_ui(self, data):
- if self.pause_fifo_update:
- return
-
if not data in ("", None):
jdata = json.loads( data )
jkeys = jdata.keys()
diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py
index d506e21..4b66a63 100644
--- a/plugins/searcher/plugin.py
+++ b/plugins/searcher/plugin.py
@@ -40,6 +40,8 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self._search_dialog = None
self._active_path = None
+ self.file_list_parent = None
+ self.grep_list_parent = None
self._file_list = None
self._grep_list = None
self._grep_proc = None
@@ -72,10 +74,11 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self._builder.connect_signals(handlers)
self._search_dialog = self._builder.get_object("search_dialog")
- self._grep_list = self._builder.get_object("grep_list")
- self._file_list = self._builder.get_object("file_list")
self.fsearch = self._builder.get_object("fsearch")
+ self.grep_list_parent = self._builder.get_object("grep_list_parent")
+ self.file_list_parent = self._builder.get_object("file_list_parent")
+
self._event_system.subscribe("update-file-ui", self._load_file_ui)
self._event_system.subscribe("update-grep-ui", self._load_grep_ui)
@@ -92,9 +95,29 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
response = self._search_dialog.run()
self._search_dialog.hide()
+ # TODO: Merge the below methods into some unified logic
+ def reset_grep_box(self) -> None:
+ try:
+ child = self.grep_list_parent.get_children()[0]
+ self._grep_list = None
+ self.grep_list_parent.remove(child)
+ except Exception:
+ ...
- def clear_children(self, widget: type) -> None:
- ''' Clear children of a gtk widget. '''
- for child in widget.get_children():
- widget.remove(child)
- time.sleep(0.01)
+ self._grep_list = Gtk.Box()
+ self._grep_list.set_orientation(Gtk.Orientation.VERTICAL)
+ self.grep_list_parent.add(self._grep_list)
+ self.grep_list_parent.show_all()
+
+ def reset_file_list_box(self) -> None:
+ try:
+ child = self.file_list_parent.get_children()[0]
+ self._file_list = None
+ self.file_list_parent.remove(child)
+ except Exception:
+ ...
+
+ self._file_list = Gtk.Box()
+ self._file_list.set_orientation(Gtk.Orientation.VERTICAL)
+ self.file_list_parent.add(self._file_list)
+ self.file_list_parent.show_all()
diff --git a/plugins/searcher/search_dialog.glade b/plugins/searcher/search_dialog.glade
index aaa3b09..8578e51 100644
--- a/plugins/searcher/search_dialog.glade
+++ b/plugins/searcher/search_dialog.glade
@@ -74,16 +74,41 @@
False
vertical
-
+
True
- True
- Query...
- edit-find-symbolic
- False
- False
- Search for file...
-
-
+ False
+
+
+ True
+ True
+ Query...
+ edit-find-symbolic
+ False
+ False
+ Search for file...
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ gtk-stop
+ True
+ True
+ True
+ True
+
+
+
+ False
+ True
+ 1
+
+
False
@@ -103,7 +128,7 @@
True
False
-
+
True
False
vertical
@@ -141,16 +166,41 @@
False
vertical
-
+
True
- True
- Query...
- edit-find-symbolic
- False
- False
- Query string in file...
-
-
+ False
+
+
+ True
+ True
+ Query...
+ edit-find-symbolic
+ False
+ False
+ Query string in file...
+
+
+
+ True
+ True
+ 0
+
+
+
+
+ gtk-stop
+ True
+ True
+ True
+ True
+
+
+
+ False
+ True
+ 1
+
+
False
@@ -170,12 +220,10 @@
True
False
-
+
True
False
vertical
- 5
- top
diff --git a/plugins/searcher/utils/search.py b/plugins/searcher/utils/search.py
index 31bdcc6..5c1efca 100755
--- a/plugins/searcher/utils/search.py
+++ b/plugins/searcher/utils/search.py
@@ -2,7 +2,7 @@
# Python imports
-import os, traceback, argparse, threading, json, base64, time
+import os, traceback, argparse, threading, json, base64, time, pickle
from setproctitle import setproctitle
from multiprocessing.connection import Client
@@ -37,19 +37,18 @@ def send_ipc_message(message) -> None:
conn.send(message)
conn.close()
- time.sleep(0.05)
+ # NOTE: Kinda important as this prevents overloading the UI thread
+ time.sleep(0.04)
def file_search(path, query):
try:
- for file in os.listdir(path):
- target = os.path.join(path, file)
- if os.path.isdir(target):
- file_search(target, query)
- else:
- if query in file.lower():
- data = f"SEARCH|{json.dumps([target, file])}"
- send_ipc_message(data)
+ for _path, _dir, _files in os.walk(path, topdown = True):
+ for file in _files:
+ if query in file.lower():
+ target = os.path.join(_path, file)
+ data = f"SEARCH|{json.dumps([target, file])}"
+ send_ipc_message(data)
except Exception as e:
print("Couldn't traverse to path. Might be permissions related...")
traceback.print_exc()
@@ -59,8 +58,9 @@ def _search_for_string(file, query):
grep_result_set = {}
padding = 15
with open(file, 'r') as fp:
- # NOTE: I know there's an issue if there's a very large file with content all on one line will lower and dupe it.
- # And, yes, it will only return one instance from the file.
+ # NOTE: I know there's an issue if there's a very large file with content
+ # all on one line will lower and dupe it. And, yes, it will only
+ # return one instance from the file.
for i, raw in enumerate(fp):
line = None
llower = raw.lower()
@@ -87,11 +87,11 @@ def _search_for_string(file, query):
data = f"GREP|{json.dumps(grep_result_set)}"
send_ipc_message(data)
+
@daemon_threaded
def _search_for_string_threaded(file, query):
_search_for_string(file, query)
-
def grep_search(path, query):
try:
for file in os.listdir(path):
@@ -101,7 +101,7 @@ def grep_search(path, query):
else:
if target.lower().endswith(filter):
size = os.path.getsize(target)
- if size < 5000:
+ if not size > 5000:
_search_for_string(target, query)
else:
_search_for_string_threaded(target, query)
From e96d9e682db34ff0c8b1f416cf9cc4355c93e89d Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Sun, 9 Oct 2022 20:59:44 -0500
Subject: [PATCH 14/30] Rewired settings, improved plugin structural coupling,
cleanedup session file generation n load
---
plugins/README.md | 24 +------
plugins/favorites/plugin.py | 12 ++--
plugins/file_properties/plugin.py | 11 ++-
plugins/movie_tv_info/plugin.py | 11 ++-
plugins/movie_tv_info/tmdbscraper/__init__.py | 3 +
plugins/searcher/mixins/file_search_mixin.py | 8 ++-
plugins/searcher/mixins/grep_search_mixin.py | 10 ++-
plugins/searcher/plugin.py | 14 ++--
plugins/searcher/utils/ipc_server.py | 10 ++-
plugins/searcher/utils/search.py | 68 +++++++++++--------
plugins/template/plugin.py | 2 +-
plugins/trasher/plugin.py | 18 +++--
plugins/vod_thumbnailer/plugin.py | 12 ++--
plugins/youtube_download/plugin.py | 5 +-
.../SolarFM/solarfm/__builtins__.py | 8 +--
.../solarfm-0.0.1/SolarFM/solarfm/__main__.py | 4 ++
.../solarfm-0.0.1/SolarFM/solarfm/app.py | 13 ++--
.../SolarFM/solarfm/core/controller.py | 22 +++---
.../SolarFM/solarfm/core/controller_data.py | 20 +++---
.../solarfm/core/mixins/ui/grid_mixin.py | 2 +-
.../solarfm/core/mixins/ui/pane_mixin.py | 3 +-
.../solarfm/core/mixins/ui/tab_mixin.py | 15 ++--
.../mixins/ui/widget_file_action_mixin.py | 18 ++---
.../solarfm/core/mixins/ui/window_mixin.py | 6 +-
.../core/signals/keyboard_signals_mixin.py | 5 +-
.../SolarFM/solarfm/plugins/plugin_base.py | 30 +++++++-
.../solarfm/plugins/plugins_controller.py | 11 ++-
.../solarfm/shellfm/windows/controller.py | 18 +++--
.../tabs/icons/mixins/xdg/IconTheme.py | 6 +-
.../SolarFM/solarfm/utils/ipc_server.py | 2 +-
.../SolarFM/solarfm/utils/settings.py | 13 ++++
31 files changed, 232 insertions(+), 172 deletions(-)
create mode 100644 plugins/movie_tv_info/tmdbscraper/__init__.py
diff --git a/plugins/README.md b/plugins/README.md
index 45cabc0..1f18e50 100644
--- a/plugins/README.md
+++ b/plugins/README.md
@@ -25,7 +25,7 @@ requests: {} = {
'pass_fm_events': "true", # If empty or not present will be ignored.
"pass_ui_objects": [""], # Request reference to a UI component. Will be passed back as array to plugin.
'bind_keys': [f"{name}||send_message:f"],
- f"{name}||do_save:s"] # Bind keys with method and key pare using list. Must pass "name" like shown with delimiter to its right.
+ f"{name}||do_save:s"] # Bind keys with method and key pare using list. Must pass "name" like shown with delimiter to its right.
}
```
@@ -40,25 +40,3 @@ UI Targets:
context_menu
other
-
-### Methods
-```
-# Must define and return a widget if "ui_target" is defined.
-def get_ui_element(self):
- button = Gtk.Button(label=self.name)
- button.connect("button-release-event", self._do_download)
- return button
-
-# Must define in plugin if "pass_fm_events" is set to "true" string.
-def set_fm_event_system(self, fm_event_system):
- self._fm_event_system = fm_event_system
-
-# Must define regardless if needed. Can just pass if plugin does stuff in its __init__
-def run(self):
- self._module_event_observer()
-
-# Must define in plugin if "pass_ui_objects" is set and an array of valid glade UI IDs.
-def set_ui_object_collection(self, ui_objects):
- self._ui_objects = ui_objects
-
-```
diff --git a/plugins/favorites/plugin.py b/plugins/favorites/plugin.py
index 3a68f05..e55fe8a 100644
--- a/plugins/favorites/plugin.py
+++ b/plugins/favorites/plugin.py
@@ -41,11 +41,6 @@ class Plugin(PluginBase):
self._selected = None
- def get_ui_element(self):
- button = Gtk.Button(label=self.name)
- button.connect("button-release-event", self._show_favorites_menu)
- return button
-
def run(self):
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
@@ -76,9 +71,14 @@ class Plugin(PluginBase):
f.write('[]')
+ def generate_reference_ui_element(self):
+ button = Gtk.Button(label=self.name)
+ button.connect("button-release-event", self._show_favorites_menu)
+ return button
+
@threaded
def _get_state(self, widget=None, eve=None):
- self._event_system.emit("get_current_state
+ self._event_system.emit("get_current_state")
@threaded
diff --git a/plugins/file_properties/plugin.py b/plugins/file_properties/plugin.py
index 2cb6262..ec41032 100644
--- a/plugins/file_properties/plugin.py
+++ b/plugins/file_properties/plugin.py
@@ -83,11 +83,6 @@ class Plugin(PluginBase):
}
- def get_ui_element(self):
- button = Gtk.Button(label=self.name)
- button.connect("button-release-event", self._show_properties_page)
- return button
-
def run(self):
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
@@ -103,11 +98,15 @@ class Plugin(PluginBase):
self._file_owner = self._builder.get_object("file_owner")
self._file_group = self._builder.get_object("file_group")
+ def generate_reference_ui_element(self):
+ button = Gtk.Button(label=self.name)
+ button.connect("button-release-event", self._show_properties_page)
+ return button
@threaded
def _show_properties_page(self, widget=None, eve=None):
- event_system.emit("get_current_state
+ event_system.emit("get_current_state")
state = self._fm_state
self._event_message = None
diff --git a/plugins/movie_tv_info/plugin.py b/plugins/movie_tv_info/plugin.py
index f7f9c3c..3f8459e 100644
--- a/plugins/movie_tv_info/plugin.py
+++ b/plugins/movie_tv_info/plugin.py
@@ -45,12 +45,6 @@ class Plugin(PluginBase):
self._trailer_link = None
-
- def get_ui_element(self):
- button = Gtk.Button(label=self.name)
- button.connect("button-release-event", self._show_info_page)
- return button
-
def run(self):
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
@@ -75,6 +69,11 @@ class Plugin(PluginBase):
self._file_hash = self._builder.get_object("file_hash")
self._trailer_link = self._builder.get_object("trailer_link")
+ def generate_reference_ui_element(self):
+ button = Gtk.Button(label=self.name)
+ button.connect("button-release-event", self._show_info_page)
+ return button
+
@threaded
def _show_info_page(self, widget=None, eve=None):
self._event_system.emit("get_current_state")
diff --git a/plugins/movie_tv_info/tmdbscraper/__init__.py b/plugins/movie_tv_info/tmdbscraper/__init__.py
new file mode 100644
index 0000000..d36fa8c
--- /dev/null
+++ b/plugins/movie_tv_info/tmdbscraper/__init__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Module
+"""
diff --git a/plugins/searcher/mixins/file_search_mixin.py b/plugins/searcher/mixins/file_search_mixin.py
index ffcc32a..749cf9d 100644
--- a/plugins/searcher/mixins/file_search_mixin.py
+++ b/plugins/searcher/mixins/file_search_mixin.py
@@ -1,8 +1,10 @@
# Python imports
-import threading, subprocess, signal, time, json, shlex
+import threading, subprocess, signal, json, shlex
# Lib imports
-from gi.repository import GLib
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, GLib
# Application imports
from ..widgets.file_preview_widget import FilePreviewWidget
@@ -61,6 +63,8 @@ class FileSearchMixin:
self._list_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
def _load_file_ui(self, data):
+ Gtk.main_iteration()
+
if not data in ("", None):
jdata = json.loads( data )
target = jdata[0]
diff --git a/plugins/searcher/mixins/grep_search_mixin.py b/plugins/searcher/mixins/grep_search_mixin.py
index dc8372d..ad5c9da 100644
--- a/plugins/searcher/mixins/grep_search_mixin.py
+++ b/plugins/searcher/mixins/grep_search_mixin.py
@@ -1,8 +1,10 @@
# Python imports
-import threading, subprocess, signal, time, json, shlex
+import threading, subprocess, signal, json, shlex
# Lib imports
-from gi.repository import GLib
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, GLib
# Application imports
from ..widgets.grep_preview_widget import GrepPreviewWidget
@@ -53,7 +55,7 @@ class GrepSearchMixin:
def _exec_grep_query(self, widget=None, eve=None):
query = widget.get_text()
- if not query in ("", None):
+ if not query.strip() in ("", None):
self.grep_query = query
target_dir = shlex.quote( self._fm_state.tab.get_current_directory() )
@@ -61,6 +63,8 @@ class GrepSearchMixin:
self._grep_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None)
def _load_grep_ui(self, data):
+ Gtk.main_iteration()
+
if not data in ("", None):
jdata = json.loads( data )
jkeys = jdata.keys()
diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py
index 4b66a63..f941dc9 100644
--- a/plugins/searcher/plugin.py
+++ b/plugins/searcher/plugin.py
@@ -52,11 +52,6 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self.search_query = ""
- def get_ui_element(self):
- button = Gtk.Button(label=self.name)
- button.connect("button-release-event", self._show_page)
- return button
-
def run(self):
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
@@ -84,6 +79,11 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self.create_ipc_listener()
+ def generate_reference_ui_element(self):
+ button = Gtk.Button(label=self.name)
+ button.connect("button-release-event", self._show_page)
+ return button
+
def _show_page(self, widget=None, eve=None):
self._event_system.emit("get_current_state")
@@ -109,6 +109,8 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self.grep_list_parent.add(self._grep_list)
self.grep_list_parent.show_all()
+ Gtk.main_iteration()
+
def reset_file_list_box(self) -> None:
try:
child = self.file_list_parent.get_children()[0]
@@ -121,3 +123,5 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self._file_list.set_orientation(Gtk.Orientation.VERTICAL)
self.file_list_parent.add(self._file_list)
self.file_list_parent.show_all()
+
+ Gtk.main_iteration()
diff --git a/plugins/searcher/utils/ipc_server.py b/plugins/searcher/utils/ipc_server.py
index 96969cf..e4ca2aa 100644
--- a/plugins/searcher/utils/ipc_server.py
+++ b/plugins/searcher/utils/ipc_server.py
@@ -1,11 +1,9 @@
# Python imports
-import os, threading, time, pickle
+import os, threading, pickle
from multiprocessing.connection import Listener, Client
# Lib imports
-import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk, GLib
+from gi.repository import GLib
# Application imports
@@ -64,12 +62,12 @@ class IPCServer:
if "SEARCH|" in msg:
file = msg.split("SEARCH|")[1].strip()
if file:
- GLib.idle_add(self._load_file_ui, file)
+ GLib.idle_add(self._load_file_ui, file, priority=GLib.PRIORITY_LOW)
if "GREP|" in msg:
data = msg.split("GREP|")[1].strip()
if data:
- GLib.idle_add(self._load_grep_ui, data)
+ GLib.idle_add(self._load_grep_ui, data, priority=GLib.PRIORITY_LOW)
conn.close()
diff --git a/plugins/searcher/utils/search.py b/plugins/searcher/utils/search.py
index 5c1efca..692fbc2 100755
--- a/plugins/searcher/utils/search.py
+++ b/plugins/searcher/utils/search.py
@@ -16,8 +16,9 @@ from multiprocessing.connection import Client
_ipc_address = f'/tmp/solarfm-search_grep-ipc.sock'
_ipc_authkey = b'' + bytes(f'solarfm-search_grep-ipc', 'utf-8')
-filter = (".cpp", ".css", ".c", ".go", ".html", ".htm", ".java", ".js", ".json", ".lua", ".md", ".py", ".rs", ".toml", ".xml", ".pom") + \
- (".txt", ".text", ".sh", ".cfg", ".conf", ".log")
+filter = (".mkv", ".mp4", ".webm", ".avi", ".mov", ".m4v", ".mpg", ".mpeg", ".wmv", ".flv") + \
+ (".png", ".jpg", ".jpeg", ".gif", ".ico", ".tga", ".webp") + \
+ (".psf", ".mp3", ".ogg", ".flac", ".m4a")
# NOTE: Threads WILL NOT die with parent's destruction.
def threaded(fn):
@@ -33,12 +34,12 @@ def daemon_threaded(fn):
def send_ipc_message(message) -> None:
- with Client(address=_ipc_address, family="AF_UNIX", authkey=_ipc_authkey) as conn:
- conn.send(message)
- conn.close()
+ conn = Client(address=_ipc_address, family="AF_UNIX", authkey=_ipc_authkey)
+ conn.send(message)
+ conn.close()
# NOTE: Kinda important as this prevents overloading the UI thread
- time.sleep(0.04)
+ time.sleep(0.05)
def file_search(path, query):
@@ -57,35 +58,44 @@ def _search_for_string(file, query):
b64_file = base64.urlsafe_b64encode(file.encode('utf-8')).decode('utf-8')
grep_result_set = {}
padding = 15
+
with open(file, 'r') as fp:
# NOTE: I know there's an issue if there's a very large file with content
# all on one line will lower and dupe it. And, yes, it will only
# return one instance from the file.
- for i, raw in enumerate(fp):
- line = None
- llower = raw.lower()
- if not query in llower:
- continue
+ try:
+ for i, raw in enumerate(fp):
+ line = None
+ llower = raw.lower()
+ if not query in llower:
+ continue
- if len(raw) > 72:
- start = 0
- end = len(raw) - 1
- index = llower.index(query)
- sindex = llower.index(query) - 15 if index >= 15 else abs(start - index) - index
- eindex = sindex + 15 if end > (index + 15) else abs(index - end) + index
- line = raw[sindex:eindex]
- else:
- line = raw
+ if len(raw) > 72:
+ start = 0
+ end = len(raw) - 1
+ index = llower.index(query)
+ sindex = llower.index(query) - 15 if index >= 15 else abs(start - index) - index
+ eindex = sindex + 15 if end > (index + 15) else abs(index - end) + index
+ line = raw[sindex:eindex]
+ else:
+ line = raw
- b64_line = base64.urlsafe_b64encode(line.encode('utf-8')).decode('utf-8')
- if f"{b64_file}" in grep_result_set.keys():
- grep_result_set[f"{b64_file}"][f"{i+1}"] = b64_line
- else:
- grep_result_set[f"{b64_file}"] = {}
- grep_result_set[f"{b64_file}"] = {f"{i+1}": b64_line}
+ b64_line = base64.urlsafe_b64encode(line.encode('utf-8')).decode('utf-8')
+ if f"{b64_file}" in grep_result_set.keys():
+ grep_result_set[f"{b64_file}"][f"{i+1}"] = b64_line
+ else:
+ grep_result_set[f"{b64_file}"] = {}
+ grep_result_set[f"{b64_file}"] = {f"{i+1}": b64_line}
+
+ except Exception as e:
+ ...
+
+ try:
+ data = f"GREP|{json.dumps(grep_result_set)}"
+ send_ipc_message(data)
+ except Exception as e:
+ ...
- data = f"GREP|{json.dumps(grep_result_set)}"
- send_ipc_message(data)
@daemon_threaded
@@ -99,7 +109,7 @@ def grep_search(path, query):
if os.path.isdir(target):
grep_search(target, query)
else:
- if target.lower().endswith(filter):
+ if not target.lower().endswith(filter):
size = os.path.getsize(target)
if not size > 5000:
_search_for_string(target, query)
diff --git a/plugins/template/plugin.py b/plugins/template/plugin.py
index dff5d40..5535f4d 100644
--- a/plugins/template/plugin.py
+++ b/plugins/template/plugin.py
@@ -33,7 +33,7 @@ class Plugin(PluginBase):
# where self.name should not be needed for message comms
- def get_ui_element(self):
+ def generate_reference_ui_element(self):
button = Gtk.Button(label=self.name)
button.connect("button-release-event", self.send_message)
return button
diff --git a/plugins/trasher/plugin.py b/plugins/trasher/plugin.py
index cbfe70d..78cc759 100644
--- a/plugins/trasher/plugin.py
+++ b/plugins/trasher/plugin.py
@@ -39,7 +39,14 @@ class Plugin(PluginBase):
self.trashman.regenerate()
- def get_ui_element(self):
+
+ def run(self):
+ self._event_system.subscribe("show_trash_buttons", self._show_trash_buttons)
+ self._event_system.subscribe("hide_trash_buttons", self._hide_trash_buttons)
+ self._event_system.subscribe("delete_files", self.delete_files)
+ self._event_system.subscribe("trash_files", self.trash_files)
+
+ def generate_reference_ui_element(self):
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
@@ -61,10 +68,6 @@ class Plugin(PluginBase):
return trasher
- def run(self):
- self._event_system.subscribe("show_trash_buttons", self._show_trash_buttons)
- self._event_system.subscribe("hide_trash_buttons", self._hide_trash_buttons)
-
def _show_trash_buttons(self):
self._builder.get_object("restore_from_trash").show()
self._builder.get_object("empty_trash").show()
@@ -74,6 +77,7 @@ class Plugin(PluginBase):
self._builder.get_object("empty_trash").hide()
def delete_files(self, widget = None, eve = None):
+ self._event_system.emit("do_hide_context_menu")
self._event_system.emit("get_current_state")
state = self._fm_state
uris = state.selected_files
@@ -97,19 +101,23 @@ class Plugin(PluginBase):
break
def trash_files(self, widget = None, eve = None, verbocity = False):
+ self._event_system.emit("do_hide_context_menu")
self._event_system.emit("get_current_state")
state = self._fm_state
for uri in state.selected_files:
self.trashman.trash(uri, verbocity)
def restore_trash_files(self, widget = None, eve = None, verbocity = False):
+ self._event_system.emit("do_hide_context_menu")
self._event_system.emit("get_current_state")
state = self._fm_state
for uri in state.selected_files:
self.trashman.restore(filename=uri.split("/")[-1], verbose = verbocity)
def empty_trash(self, widget = None, eve = None, verbocity = False):
+ self._event_system.emit("do_hide_context_menu")
self.trashman.empty(verbose = verbocity)
def go_to_trash(self, widget = None, eve = None, verbocity = False):
+ self._event_system.emit("do_hide_context_menu")
self._event_system.emit("go_to_path", self.trash_files_path)
diff --git a/plugins/vod_thumbnailer/plugin.py b/plugins/vod_thumbnailer/plugin.py
index c74f551..e730797 100644
--- a/plugins/vod_thumbnailer/plugin.py
+++ b/plugins/vod_thumbnailer/plugin.py
@@ -44,11 +44,6 @@ class Plugin(PluginBase):
self._file_hash = None
- def get_ui_element(self):
- button = Gtk.Button(label=self.name)
- button.connect("button-release-event", self._show_thumbnailer_page)
- return button
-
def run(self):
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
@@ -72,10 +67,15 @@ class Plugin(PluginBase):
self._thumbnail_preview_img = self._builder.get_object("thumbnail_preview_img")
self._file_hash = self._builder.get_object("file_hash")
+ def generate_reference_ui_element(self):
+ button = Gtk.Button(label=self.name)
+ button.connect("button-release-event", self._show_thumbnailer_page)
+ return button
+
@threaded
def _show_thumbnailer_page(self, widget=None, eve=None):
- self._event_system.emit("get_current_state
+ self._event_system.emit("get_current_state")
state = self._fm_state
self._event_message = None
diff --git a/plugins/youtube_download/plugin.py b/plugins/youtube_download/plugin.py
index da13c75..1b4ce50 100644
--- a/plugins/youtube_download/plugin.py
+++ b/plugins/youtube_download/plugin.py
@@ -33,8 +33,7 @@ class Plugin(PluginBase):
self.name = "Youtube Download" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
# where self.name should not be needed for message comms
-
- def get_ui_element(self):
+ def generate_reference_ui_element(self):
button = Gtk.Button(label=self.name)
button.connect("button-release-event", self._do_download)
return button
@@ -44,7 +43,7 @@ class Plugin(PluginBase):
def _do_download(self, widget=None, eve=None):
- self._event_system.emit("get_current_state
+ self._event_system.emit("get_current_state")
dir = self._fm_state.tab.get_current_directory()
self._download(dir)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py
index 428f16f..9ed03c8 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py
@@ -6,7 +6,7 @@ import builtins, threading
# Application imports
from utils.event_system import EventSystem
from utils.endpoint_registry import EndpointRegistry
-
+from utils.settings import Settings
@@ -28,11 +28,11 @@ def daemon_threaded_wrapper(fn):
# NOTE: Just reminding myself we can add to builtins two different ways...
# __builtins__.update({"event_system": Builtins()})
builtins.app_name = "SolarFM"
+builtins.settings = Settings()
+builtins.logger = settings.get_logger()
builtins.event_system = EventSystem()
builtins.endpoint_registry = EndpointRegistry()
+
builtins.threaded = threaded_wrapper
builtins.daemon_threaded = daemon_threaded_wrapper
builtins.event_sleep_time = 0.05
-builtins.trace_debug = False
-builtins.debug = False
-builtins.app_settings = None
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
index 2dfcfe2..9bfffcb 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
@@ -15,6 +15,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
+from __builtins__ import *
from app import Application
@@ -27,6 +28,9 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser()
# Add long and short arguments
+ parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.")
+ parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.")
+
parser.add_argument("--new-tab", "-t", default="", help="Open a file into new tab.")
parser.add_argument("--new-window", "-w", default="", help="Open a file into a new window.")
# Read arguments (If any...)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
index a16c779..7df637a 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
@@ -4,7 +4,7 @@ import os, inspect, time
# Lib imports
# Application imports
-from __builtins__ import *
+
from utils.ipc_server import IPCServer
from utils.settings import Settings
from core.controller import Controller
@@ -22,8 +22,14 @@ class Application(IPCServer):
def __init__(self, args, unknownargs):
super(Application, self).__init__()
+ if args.debug == "true":
+ settings.set_debug(True)
- if not trace_debug:
+ if args.trace_debug == "true":
+ settings.set_trace_debug(True)
+
+ # NOTE: Instance found, sending files to it...
+ if not settings.is_trace_debug():
self.create_ipc_listener()
time.sleep(0.05)
@@ -41,10 +47,9 @@ class Application(IPCServer):
raise AppLaunchException(f"IPC Server Exists: Will send path(s) to it and close...\nNote: If no fm exists, remove /tmp/{app_name}-ipc.sock")
- settings = Settings()
settings.create_window()
- controller = Controller(args, unknownargs, settings)
+ controller = Controller(args, unknownargs)
if not controller:
raise ControllerStartExceptio("Controller exited and doesn't exist...")
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
index c034bd3..428edb4 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
@@ -18,18 +18,18 @@ from .controller_data import Controller_Data
class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMixin, Controller_Data):
""" Controller coordinates the mixins and is somewhat the root hub of it all. """
- def __init__(self, args, unknownargs, _settings):
- self.setup_controller_data(_settings)
+ def __init__(self, args, unknownargs):
+ self.setup_controller_data()
self.window.show()
self.generate_windows(self.fm_controller_data)
self.plugins.launch_plugins()
- if debug:
+ if settings.is_debug():
self.window.set_interactive_debugging(True)
-
- if not trace_debug:
+ # NOTE: Open files if passed in from cli and not trace debugging...
+ if not settings.is_trace_debug():
self._subscribe_to_events()
if unknownargs:
@@ -51,7 +51,9 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
event_system.subscribe("do_hide_context_menu", self.do_hide_context_menu)
def tear_down(self, widget=None, eve=None):
- self.fm_controller.save_state()
+ if not settings.is_trace_debug():
+ self.fm_controller.save_state()
+
time.sleep(event_sleep_time)
Gtk.main_quit()
@@ -62,7 +64,9 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
save_load_dialog = self.builder.get_object("save_load_dialog")
if action == "save_session":
- self.fm_controller.save_state()
+ if not settings.is_trace_debug():
+ self.fm_controller.save_state()
+
return
elif action == "save_session_as":
save_load_dialog.set_action(Gtk.FileChooserAction.SAVE)
@@ -88,8 +92,8 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
save_load_dialog.hide()
def load_session(self, session_json):
- if debug:
- self.logger.debug(f"Session Data: {session_json}")
+ if settings.is_debug():
+ logger.debug(f"Session Data: {session_json}")
self.ctrl_down = False
self.shift_down = False
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
index 3e69f9f..14524e0 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
@@ -28,17 +28,15 @@ class Controller_Data:
""" Controller_Data contains most of the state of the app at ay given time. It also has some support methods. """
__slots__ = "settings", "builder", "logger", "keybindings", "trashman", "fm_controller", "window", "window1", "window2", "window3", "window4"
- def setup_controller_data(self, _settings: type) -> None:
- self.settings = _settings
- self.builder = self.settings.get_builder()
- self.logger = self.settings.get_logger()
- self.keybindings = self.settings.get_keybindings()
+ def setup_controller_data(self) -> None:
+ self.builder = settings.get_builder()
+ self.keybindings = settings.get_keybindings()
self.fm_controller = WindowController()
- self.plugins = PluginsController(_settings)
+ self.plugins = PluginsController()
self.fm_controller_data = self.fm_controller.get_state_from_file()
- self.window = self.settings.get_main_window()
+ self.window = settings.get_main_window()
self.window1 = self.builder.get_object("window_1")
self.window2 = self.builder.get_object("window_2")
self.window3 = self.builder.get_object("window_3")
@@ -64,7 +62,7 @@ class Controller_Data:
self.trash_files_path = f"{GLib.get_user_data_dir()}/Trash/files"
self.trash_info_path = f"{GLib.get_user_data_dir()}/Trash/info"
- self.icon_theme = self.settings.get_icon_theme()
+ self.icon_theme = settings.get_icon_theme()
# In compress commands:
# %n: First selected filename/dir to archive
@@ -116,9 +114,9 @@ class Controller_Data:
self.shift_down = False
self.alt_down = False
- self.success_color = self.settings.get_success_color()
- self.warning_color = self.settings.get_warning_color()
- self.error_color = self.settings.get_error_color()
+ self.success_color = settings.get_success_color()
+ self.warning_color = settings.get_warning_color()
+ self.error_color = settings.get_error_color()
# sys.excepthook = self.custom_except_hook
self.window.connect("delete-event", self.tear_down)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/grid_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/grid_mixin.py
index 2d3afeb..c93a3ca 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/grid_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/grid_mixin.py
@@ -61,7 +61,7 @@ class GridMixin:
self.create_icon(i, tab, store, dir, file[0])
# NOTE: Not likely called often from here but it could be useful
- if save_state:
+ if save_state and not trace_debug:
self.fm_controller.save_state()
@threaded
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/pane_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/pane_mixin.py
index 3d2c888..380efa7 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/pane_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/pane_mixin.py
@@ -56,4 +56,5 @@ class PaneMixin:
def _save_state(self, state, pane_index):
window = self.fm_controller.get_window_by_index(pane_index - 1)
window.set_is_hidden(state)
- self.fm_controller.save_state()
+ if not settings.is_trace_debug():
+ self.fm_controller.save_state()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/tab_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/tab_mixin.py
index 539f840..683e6b4 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/tab_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/tab_mixin.py
@@ -22,7 +22,7 @@ class TabMixin(GridMixin):
notebook = self.builder.get_object(f"window_{wid}")
path_entry = self.builder.get_object(f"path_entry")
tab = self.fm_controller.add_tab_for_window_by_nickname(f"window_{wid}")
- tab.logger = self.logger
+ tab.logger = logger
tab.set_wid(wid)
if not path:
@@ -63,7 +63,8 @@ class TabMixin(GridMixin):
watcher.cancel()
self.get_fm_window(wid).delete_tab_by_id(tid)
notebook.remove_page(page)
- self.fm_controller.save_state()
+ if not settings.is_trace_debug():
+ self.fm_controller.save_state()
self.set_window_title()
def on_tab_reorder(self, child, page_num, new_index):
@@ -80,7 +81,8 @@ class TabMixin(GridMixin):
tab = window.get_tab_by_id(tid)
self.set_file_watcher(tab)
- self.fm_controller.save_state()
+ if not settings.is_trace_debug():
+ self.fm_controller.save_state()
def on_tab_switch_update(self, notebook, content=None, index=None):
self.selected_files.clear()
@@ -115,7 +117,8 @@ class TabMixin(GridMixin):
tab_label.set_label(tab.get_end_of_path())
self.set_window_title()
self.set_file_watcher(tab)
- self.fm_controller.save_state()
+ if not settings.is_trace_debug():
+ self.fm_controller.save_state()
def do_action_from_bar_controls(self, widget, eve=None):
action = widget.get_name()
@@ -127,7 +130,9 @@ class TabMixin(GridMixin):
if action == "create_tab":
dir = tab.get_current_directory()
self.create_tab(wid, None, dir)
- self.fm_controller.save_state()
+ if not settings.is_trace_debug():
+ self.fm_controller.save_state()
+
return
if action == "go_up":
tab.pop_from_path()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
index 0058147..e680801 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
@@ -40,8 +40,8 @@ class WidgetFileActionMixin:
if tab.get_dir_watcher():
watcher = tab.get_dir_watcher()
watcher.cancel()
- if debug:
- self.logger.debug(f"Watcher Is Cancelled: {watcher.is_cancelled()}")
+ if settings.is_debug():
+ logger.debug(f"Watcher Is Cancelled: {watcher.is_cancelled()}")
cur_dir = tab.get_current_directory()
@@ -59,8 +59,8 @@ class WidgetFileActionMixin:
if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED,
Gio.FileMonitorEvent.RENAMED, Gio.FileMonitorEvent.MOVED_IN,
Gio.FileMonitorEvent.MOVED_OUT]:
- if debug:
- self.logger.debug(eve_type)
+ if settings.is_debug():
+ logger.debug(eve_type)
if eve_type in [Gio.FileMonitorEvent.MOVED_IN, Gio.FileMonitorEvent.MOVED_OUT]:
self.update_on_soft_lock_end(data[0])
@@ -389,11 +389,11 @@ class WidgetFileActionMixin:
target = Gio.File.new_for_path(full_path)
start = "-copy"
- if debug:
- self.logger.debug(f"Path: {full_path}")
- self.logger.debug(f"Base Path: {base_path}")
- self.logger.debug(f'Name: {file_name}')
- self.logger.debug(f"Extension: {extension}")
+ if settings.is_debug():
+ logger.debug(f"Path: {full_path}")
+ logger.debug(f"Base Path: {base_path}")
+ logger.debug(f'Name: {file_name}')
+ logger.debug(f"Extension: {extension}")
i = 2
while target.query_exists():
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
index 2ab6be1..fddb9d3 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
@@ -24,8 +24,8 @@ class WindowMixin(TabMixin):
for j, value in enumerate(session_json):
i = j + 1
notebook_tggl_button = self.builder.get_object(f"tggl_notebook_{i}")
- is_hidden = True if value[0]["window"]["isHidden"] == "True" else False
- tabs = value[0]["window"]["tabs"]
+ is_hidden = True if value["window"]["isHidden"] == "True" else False
+ tabs = value["window"]["tabs"]
self.fm_controller.create_window()
notebook_tggl_button.set_active(True)
@@ -112,7 +112,7 @@ class WindowMixin(TabMixin):
file_size = file_info.get_size()
combined_size += file_size
except WindowException as e:
- if debug:
+ if settings.is_debug():
print(repr(e))
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py
index e4151f8..bde0689 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py
@@ -55,7 +55,7 @@ class KeyboardSignalsMixin:
sender, eve_type = mapping.split("||")
self.handle_plugin_key_event(sender, eve_type)
else:
- if debug:
+ if settings.is_debug():
print(f"on_global_key_release_controller > key > {keyname}")
if self.ctrl_down:
@@ -91,5 +91,6 @@ class KeyboardSignalsMixin:
self.get_fm_window(wid).delete_tab_by_id(tid)
notebook.remove_page(page)
- self.fm_controller.save_state()
+ if not trace_debug:
+ self.fm_controller.save_state()
self.set_window_title()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugin_base.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugin_base.py
index effd377..44aec62 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugin_base.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugin_base.py
@@ -6,6 +6,10 @@ import os, time
# Application imports
+class PluginBaseException(Exception):
+ ...
+
+
class PluginBase:
def __init__(self):
self.name = "Example Plugin" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
@@ -18,14 +22,23 @@ class PluginBase:
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 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. '''
+ """ Clear children of a gtk widget. """
for child in widget.get_children():
widget.remove(child)
@@ -34,3 +47,18 @@ class PluginBase:
def _update_fm_state_info(self, state):
self._fm_state = state
+
+ def generate_reference_ui_element(self):
+ """
+ Requests Key: 'ui_target': "plugin_control_list",
+ Must define regardless if needed and can 'pass' if plugin doesn't use it.
+ Must return a widget if "ui_target" is set.
+ """
+ raise PluginBaseException("Method hasn't been overriden...")
+
+ def run(self):
+ """
+ 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.
+ """
+ raise PluginBaseException("Method hasn't been overriden...")
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
index 63cccfe..41adcef 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins_controller.py
@@ -20,14 +20,13 @@ class InvalidPluginException(Exception):
class PluginsController:
"""PluginsController controller"""
- def __init__(self, settings: type):
+ def __init__(self):
path = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, path) # NOTE: I think I'm not using this correctly...
- self._settings = settings
- self._builder = self._settings.get_builder()
- self._plugins_path = self._settings.get_plugins_path()
- self._keybindings = self._settings.get_keybindings()
+ self._builder = settings.get_builder()
+ self._plugins_path = settings.get_plugins_path()
+ self._keybindings = settings.get_keybindings()
self._plugins_dir_watcher = None
self._plugin_collection = []
@@ -95,7 +94,7 @@ class PluginsController:
keys = loading_data.keys()
if "ui_target" in keys:
- loading_data["ui_target"].add( plugin.reference.get_ui_element() )
+ loading_data["ui_target"].add( plugin.reference.generate_reference_ui_element() )
loading_data["ui_target"].show_all()
if "pass_ui_objects" in keys:
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/controller.py
index abec786..f2b0e16 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/controller.py
@@ -158,17 +158,15 @@ class WindowController:
tabs.append(tab.get_current_directory())
windows.append(
- [
- {
- 'window':{
- "ID": window.get_id(),
- "Name": window.get_name(),
- "Nickname": window.get_nickname(),
- "isHidden": f"{window.is_hidden()}",
- 'tabs': tabs
- }
+ {
+ 'window':{
+ "ID": window.get_id(),
+ "Name": window.get_name(),
+ "Nickname": window.get_nickname(),
+ "isHidden": f"{window.is_hidden()}",
+ 'tabs': tabs
}
- ]
+ }
)
with open(session_file, 'w') as outfile:
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/xdg/IconTheme.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/xdg/IconTheme.py
index 2ff3c05..61ee2c3 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/xdg/IconTheme.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/xdg/IconTheme.py
@@ -322,10 +322,8 @@ def getIconPath(iconname, size = None, theme = None, extensions = ["png", "svg",
icon_cache[tmp] = [time.time(), icon]
return icon
except UnicodeDecodeError as e:
- if debug:
- raise e
- else:
- pass
+ ...
+
# we haven't found anything? "hicolor" is our fallback
if theme != "hicolor":
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
index f71691b..5aa0ebd 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
@@ -59,7 +59,7 @@ class IPCServer:
def handle_message(self, conn, start_time) -> None:
while True:
msg = conn.recv()
- if debug:
+ if settings.is_debug():
print(msg)
if "FILE|" in msg:
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
index 3826e3d..0967a25 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
@@ -63,6 +63,9 @@ class Settings:
self._builder = Gtk.Builder()
self._builder.add_from_file(self._GLADE_FILE)
+ self._trace_debug = False
+ self._debug = False
+
def create_window(self) -> None:
# Get window and connect signals
@@ -111,3 +114,13 @@ class Settings:
def get_success_color(self) -> str: return self._success_color
def get_warning_color(self) -> str: return self._warning_color
def get_error_color(self) -> str: return self._error_color
+
+ def is_trace_debug(self) -> str: return self._trace_debug
+ def is_debug(self) -> str: return self._debug
+
+
+ def set_trace_debug(self, trace_debug):
+ self._trace_debug = trace_debug
+
+ def set_debug(self, debug):
+ self._debug = debug
From 49ed89201a43e52621d33f107e55dfa99a32de83 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Sun, 9 Oct 2022 21:56:12 -0500
Subject: [PATCH 15/30] Externalized archiver to plugin
---
plugins/archiver/__init__.py | 3 +
plugins/archiver/__main__.py | 3 +
plugins/archiver/archiver.glade | 164 +++++++++++++++++
plugins/archiver/manifest.json | 12 ++
plugins/archiver/plugin.py | 122 +++++++++++++
.../SolarFM/solarfm/core/controller.py | 2 -
.../SolarFM/solarfm/core/controller_data.py | 26 ---
.../core/mixins/exception_hook_mixin.py | 5 -
.../solarfm/core/mixins/show_hide_mixin.py | 20 ---
.../mixins/ui/widget_file_action_mixin.py | 13 --
.../usr/share/solarfm/Main_Window.glade | 165 ------------------
11 files changed, 304 insertions(+), 231 deletions(-)
create mode 100644 plugins/archiver/__init__.py
create mode 100644 plugins/archiver/__main__.py
create mode 100644 plugins/archiver/archiver.glade
create mode 100644 plugins/archiver/manifest.json
create mode 100644 plugins/archiver/plugin.py
diff --git a/plugins/archiver/__init__.py b/plugins/archiver/__init__.py
new file mode 100644
index 0000000..d36fa8c
--- /dev/null
+++ b/plugins/archiver/__init__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Module
+"""
diff --git a/plugins/archiver/__main__.py b/plugins/archiver/__main__.py
new file mode 100644
index 0000000..a576329
--- /dev/null
+++ b/plugins/archiver/__main__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Package
+"""
diff --git a/plugins/archiver/archiver.glade b/plugins/archiver/archiver.glade
new file mode 100644
index 0000000..1879b61
--- /dev/null
+++ b/plugins/archiver/archiver.glade
@@ -0,0 +1,164 @@
+
+
+
+
+
+ $(which 7za || echo 7zr) a %o %N
+
+
+ False
+ True
+ center
+ dialog
+ center
+ True
+ True
+
+
+ False
+ vertical
+ 2
+
+
+ False
+ end
+
+
+ gtk-cancel
+ True
+ True
+ True
+ True
+
+
+ True
+ True
+ 0
+
+
+
+
+ gtk-ok
+ True
+ True
+ True
+ True
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ False
+ 0
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ True
+
+
+ True
+ False
+ Compress Commands:
+ 0.20000000298023224
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+
+
+
+ True
+ False
+ Archive Format:
+ 1
+
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+ True
+ False
+ 0
+ 0
+
+ - 7Zip (*.7z)
+ - Zip (*.zip *.ZIP)
+ - RAR (*.rar *.RAR)
+ - Tar (*.tar)
+ - Tar bzip2 (*.tar.bz2)
+ - Tar Gzip (*.tar.gz *.tgz)
+ - Tar xz (*.tar.xz *.txz)
+ - Gzip (*.gz)
+ - XZ (*.xz)
+
+
+
+
+ False
+ True
+ 3
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ 72
+ True
+ True
+ arc_command_buffer
+
+
+ True
+ True
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+ button21
+ button22
+
+
+
diff --git a/plugins/archiver/manifest.json b/plugins/archiver/manifest.json
new file mode 100644
index 0000000..761a2eb
--- /dev/null
+++ b/plugins/archiver/manifest.json
@@ -0,0 +1,12 @@
+{
+ "manifest": {
+ "name": "Archiver",
+ "author": "ITDominator",
+ "version": "0.0.1",
+ "support": "",
+ "requests": {
+ "ui_target": "context_menu",
+ "pass_fm_events": "true"
+ }
+ }
+}
diff --git a/plugins/archiver/plugin.py b/plugins/archiver/plugin.py
new file mode 100644
index 0000000..1c02225
--- /dev/null
+++ b/plugins/archiver/plugin.py
@@ -0,0 +1,122 @@
+# Python imports
+import os, threading, subprocess, inspect, shlex
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+# Application imports
+from plugins.plugin_base import PluginBase
+
+
+# NOTE: Threads WILL NOT die with parent's destruction.
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
+ return wrapper
+
+# NOTE: Threads WILL die with parent's destruction.
+def daemon_threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
+ return wrapper
+
+
+
+
+class Plugin(PluginBase):
+ def __init__(self):
+ super().__init__()
+ self.path = os.path.dirname(os.path.realpath(__file__))
+ self._GLADE_FILE = f"{self.path}/archiver.glade"
+ self.name = "Archiver" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
+ # where self.name should not be needed for message comms
+ self._archiver_dialogue = None
+ self._arc_command_buffer = None
+
+ # In compress commands:
+ # %n: First selected filename/dir to archive
+ # %N: All selected filenames/dirs to archive, or (with %O) a single filename
+ # %o: Resulting single archive file
+ # %O: Resulting archive per source file/directory (use changes %N meaning)
+ #
+ # In extract commands:
+ # %x: Archive file to extract
+ # %g: Unique extraction target filename with optional subfolder
+ # %G: Unique extraction target filename, never with subfolder
+ #
+ # In list commands:
+ # %x: Archive to list
+ #
+ # Plus standard bash variables are accepted.
+ self.arc_commands = [ '$(which 7za || echo 7zr) a %o %N',
+ 'zip -r %o %N',
+ 'rar a -r %o %N',
+ 'tar -cvf %o %N',
+ 'tar -cvjf %o %N',
+ 'tar -cvzf %o %N',
+ 'tar -cvJf %o %N',
+ 'gzip -c %N > %O',
+ 'xz -cz %N > %O'
+ ]
+
+
+ def generate_reference_ui_element(self):
+ self._builder = Gtk.Builder()
+ self._builder.add_from_file(self._GLADE_FILE)
+
+ classes = [self]
+ handlers = {}
+ for c in classes:
+ methods = None
+ try:
+ methods = inspect.getmembers(c, predicate=inspect.ismethod)
+ handlers.update(methods)
+ except Exception as e:
+ print(repr(e))
+
+ self._builder.connect_signals(handlers)
+
+ self._archiver_dialogue = self._builder.get_object("archiver_dialogue")
+ self._arc_command_buffer = self._builder.get_object("arc_command_buffer")
+
+ button = Gtk.Button(label=self.name)
+ button.connect("button-release-event", self.show_archiver_dialogue)
+ return button
+
+ def run(self):
+ ...
+
+ def show_archiver_dialogue(self, widget=None, eve=None):
+ self._event_system.emit("get_current_state")
+ state = self._fm_state
+
+ self._archiver_dialogue.set_action(Gtk.FileChooserAction.SAVE)
+ self._archiver_dialogue.set_current_folder(state.tab.get_current_directory())
+ self._archiver_dialogue.set_current_name("arc.7z")
+
+ response = self._archiver_dialogue.run()
+ if response == Gtk.ResponseType.OK:
+ save_target = self._archiver_dialogue.get_filename()
+ self.archive_files(save_target, state)
+ if (response == Gtk.ResponseType.CANCEL) or (response == Gtk.ResponseType.DELETE_EVENT):
+ pass
+
+ self._archiver_dialogue.hide()
+
+ def archive_files(self, save_target, state):
+ paths = [shlex.quote(p) for p in state.selected_files]
+
+ sItr, eItr = self._arc_command_buffer.get_bounds()
+ pre_command = self._arc_command_buffer.get_text(sItr, eItr, False)
+ pre_command = pre_command.replace("%o", shlex.quote(save_target))
+ pre_command = pre_command.replace("%N", ' '.join(paths))
+ command = f"{state.tab.terminal_app} -e {shlex.quote(pre_command)}"
+ current_dir = state.tab.get_current_directory()
+
+ state.tab.execute(shlex.split(command), start_dir=shlex.quote(current_dir))
+
+ def set_arc_buffer_text(self, widget=None, eve=None):
+ sid = widget.get_active_id()
+ self.arc_command_buffer.set_text(self.arc_commands[int(sid)])
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
index 428edb4..22ab397 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
@@ -128,8 +128,6 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
self.copy_files()
if action == "paste":
self.paste_files()
- if action == "archive":
- self.show_archiver_dialogue()
if action == "create":
self.create_files()
if action in ["save_session", "save_session_as", "load_session"]:
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
index 14524e0..112140b 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
@@ -64,32 +64,6 @@ class Controller_Data:
self.trash_info_path = f"{GLib.get_user_data_dir()}/Trash/info"
self.icon_theme = settings.get_icon_theme()
- # In compress commands:
- # %n: First selected filename/dir to archive
- # %N: All selected filenames/dirs to archive, or (with %O) a single filename
- # %o: Resulting single archive file
- # %O: Resulting archive per source file/directory (use changes %N meaning)
- #
- # In extract commands:
- # %x: Archive file to extract
- # %g: Unique extraction target filename with optional subfolder
- # %G: Unique extraction target filename, never with subfolder
- #
- # In list commands:
- # %x: Archive to list
- #
- # Plus standard bash variables are accepted.
- self.arc_commands = [ '$(which 7za || echo 7zr) a %o %N',
- 'zip -r %o %N',
- 'rar a -r %o %N',
- 'tar -cvf %o %N',
- 'tar -cvjf %o %N',
- 'tar -cvzf %o %N',
- 'tar -cvJf %o %N',
- 'gzip -c %N > %O',
- 'xz -cz %N > %O'
- ]
-
self.notebooks = [self.window1, self.window2, self.window3, self.window4]
self.selected_files = []
self.to_copy_files = []
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/exception_hook_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/exception_hook_mixin.py
index 660b9b6..4ec0b0a 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/exception_hook_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/exception_hook_mixin.py
@@ -51,8 +51,3 @@ class ExceptionHookMixin:
f.write(text)
save_location_prompt.destroy()
-
-
- def set_arc_buffer_text(self, widget=None, eve=None):
- sid = widget.get_active_id()
- self.arc_command_buffer.set_text(self.arc_commands[int(sid)])
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/show_hide_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/show_hide_mixin.py
index 331c6ef..8377158 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/show_hide_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/show_hide_mixin.py
@@ -55,26 +55,6 @@ class ShowHideMixin:
self.builder.get_object("about_page").hide()
- def show_archiver_dialogue(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)
- archiver_dialogue = self.builder.get_object("archiver_dialogue")
- archiver_dialogue.set_action(Gtk.FileChooserAction.SAVE)
- archiver_dialogue.set_current_folder(tab.get_current_directory())
- archiver_dialogue.set_current_name("arc.7z")
-
- response = archiver_dialogue.run()
- if response == Gtk.ResponseType.OK:
- self.archive_files(archiver_dialogue)
- if (response == Gtk.ResponseType.CANCEL) or (response == Gtk.ResponseType.DELETE_EVENT):
- pass
-
- archiver_dialogue.hide()
-
- def hide_archiver_dialogue(self, widget=None, eve=None):
- self.builder.get_object("archiver_dialogue").hide()
-
-
def show_appchooser_menu(self, widget=None, eve=None):
appchooser_menu = self.builder.get_object("appchooser_menu")
appchooser_widget = self.builder.get_object("appchooser_widget")
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
index e680801..04f8400 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
@@ -143,19 +143,6 @@ class WidgetFileActionMixin:
command = f"{shlex.quote(path)}" if not in_terminal else f"{state.tab.terminal_app} -e {shlex.quote(path)}"
state.tab.execute(shlex.split(command), start_dir=state.tab.get_current_directory())
- def archive_files(self, archiver_dialogue):
- state = self.get_current_state()
- paths = [shlex.quote(p) for p in self.format_to_uris(state.store, state.wid, state.tid, self.selected_files, True)]
-
- save_target = archiver_dialogue.get_filename();
- sItr, eItr = self.arc_command_buffer.get_bounds()
- pre_command = self.arc_command_buffer.get_text(sItr, eItr, False)
- pre_command = pre_command.replace("%o", shlex.quote(save_target))
- pre_command = pre_command.replace("%N", ' '.join(paths))
- command = f"{state.tab.terminal_app} -e {shlex.quote(pre_command)}"
-
- state.tab.execute(shlex.split(command), start_dir=shlex.quote(state.tab.get_current_directory()))
-
def rename_files(self):
rename_label = self.builder.get_object("file_to_rename_label")
rename_input = self.builder.get_object("new_rename_fname")
diff --git a/user_config/usr/share/solarfm/Main_Window.glade b/user_config/usr/share/solarfm/Main_Window.glade
index 2343d0c..d102e67 100644
--- a/user_config/usr/share/solarfm/Main_Window.glade
+++ b/user_config/usr/share/solarfm/Main_Window.glade
@@ -452,170 +452,6 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
appchooser_select_btn
-
- $(which 7za || echo 7zr) a %o %N
-
-
- False
- True
- center
- dialog
- center
- True
- True
-
-
- False
- vertical
- 2
-
-
- False
- end
-
-
- gtk-cancel
- True
- True
- True
- True
-
-
- True
- True
- 0
-
-
-
-
- gtk-ok
- True
- True
- True
- True
-
-
- True
- True
- 1
-
-
-
-
- False
- False
- 0
-
-
-
-
- True
- False
- vertical
-
-
- True
- False
- True
-
-
- True
- False
- Compress Commands:
- 0.20000000298023224
-
-
-
-
-
- False
- True
- 0
-
-
-
-
-
-
-
- True
- False
- Archive Format:
- 1
-
-
-
-
-
- False
- True
- 2
-
-
-
-
- True
- False
- 0
- 0
-
- - 7Zip (*.7z)
- - Zip (*.zip *.ZIP)
- - RAR (*.rar *.RAR)
- - Tar (*.tar)
- - Tar bzip2 (*.tar.bz2)
- - Tar Gzip (*.tar.gz *.tgz)
- - Tar xz (*.tar.xz *.txz)
- - Gzip (*.gz)
- - XZ (*.xz)
-
-
-
-
- False
- True
- 3
-
-
-
-
- False
- True
- 0
-
-
-
-
- 72
- True
- True
- arc_command_buffer
-
-
- True
- True
- 1
-
-
-
-
- False
- True
- 2
-
-
-
-
-
- button21
- button22
-
-
-
- True
- False
- gtk-save-as
-
True
False
@@ -912,7 +748,6 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
True
True
Archive...
- archive_img
True
From 75da08d0819fada0ab51e5fece11036527c2dd36 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Thu, 20 Oct 2022 22:23:14 -0500
Subject: [PATCH 16/30] Added dirty start check, added images to plugin
buttons, cleanup
---
plugins/archiver/plugin.py | 4 ++
plugins/file_properties/plugin.py | 4 ++
plugins/movie_tv_info/plugin.py | 4 ++
plugins/searcher/mixins/file_search_mixin.py | 2 +-
plugins/searcher/mixins/grep_search_mixin.py | 5 ++-
plugins/searcher/plugin.py | 4 ++
plugins/searcher/utils/search.py | 14 +++----
plugins/trasher/trasher.glade | 2 +-
plugins/vod_thumbnailer/plugin.py | 5 +++
.../solarfm-0.0.1/SolarFM/solarfm/__main__.py | 1 -
.../SolarFM/solarfm/core/controller.py | 1 +
.../tabs/icons/mixins/desktopiconmixin.py | 7 +---
.../shellfm/windows/tabs/utils/launcher.py | 2 +-
.../shellfm/windows/tabs/utils/settings.py | 2 +-
.../SolarFM/solarfm/utils/ipc_server.py | 4 +-
.../SolarFM/solarfm/utils/settings.py | 42 +++++++++++++++++++
.../usr/share/solarfm/Main_Window.glade | 28 -------------
17 files changed, 81 insertions(+), 50 deletions(-)
diff --git a/plugins/archiver/plugin.py b/plugins/archiver/plugin.py
index 1c02225..ccfd732 100644
--- a/plugins/archiver/plugin.py
+++ b/plugins/archiver/plugin.py
@@ -81,8 +81,12 @@ class Plugin(PluginBase):
self._archiver_dialogue = self._builder.get_object("archiver_dialogue")
self._arc_command_buffer = self._builder.get_object("arc_command_buffer")
+ icon = Gtk.Image(stock=Gtk.STOCK_FLOPPY)
button = Gtk.Button(label=self.name)
+
+ button.set_image(icon)
button.connect("button-release-event", self.show_archiver_dialogue)
+
return button
def run(self):
diff --git a/plugins/file_properties/plugin.py b/plugins/file_properties/plugin.py
index ec41032..c20bc1a 100644
--- a/plugins/file_properties/plugin.py
+++ b/plugins/file_properties/plugin.py
@@ -99,8 +99,12 @@ class Plugin(PluginBase):
self._file_group = self._builder.get_object("file_group")
def generate_reference_ui_element(self):
+ icon = Gtk.Image(stock=Gtk.STOCK_PROPERTIES )
button = Gtk.Button(label=self.name)
+
button.connect("button-release-event", self._show_properties_page)
+ button.set_image(icon)
+
return button
diff --git a/plugins/movie_tv_info/plugin.py b/plugins/movie_tv_info/plugin.py
index 3f8459e..2bae410 100644
--- a/plugins/movie_tv_info/plugin.py
+++ b/plugins/movie_tv_info/plugin.py
@@ -70,8 +70,12 @@ class Plugin(PluginBase):
self._trailer_link = self._builder.get_object("trailer_link")
def generate_reference_ui_element(self):
+ icon = Gtk.Image(stock=Gtk.STOCK_FIND)
button = Gtk.Button(label=self.name)
+
button.connect("button-release-event", self._show_info_page)
+ button.set_image(icon)
+
return button
@threaded
diff --git a/plugins/searcher/mixins/file_search_mixin.py b/plugins/searcher/mixins/file_search_mixin.py
index 749cf9d..6f17f08 100644
--- a/plugins/searcher/mixins/file_search_mixin.py
+++ b/plugins/searcher/mixins/file_search_mixin.py
@@ -49,8 +49,8 @@ class FileSearchMixin:
GLib.idle_add(self.reset_file_list_box)
# NOTE: If query create new process and do all new loop.
- self.pause_fifo_update = False
if query:
+ self.pause_fifo_update = False
GLib.idle_add(self._exec_find_file_query, query)
def _exec_find_file_query(self, widget=None, eve=None):
diff --git a/plugins/searcher/mixins/grep_search_mixin.py b/plugins/searcher/mixins/grep_search_mixin.py
index ad5c9da..a7590c1 100644
--- a/plugins/searcher/mixins/grep_search_mixin.py
+++ b/plugins/searcher/mixins/grep_search_mixin.py
@@ -1,5 +1,6 @@
# Python imports
-import threading, subprocess, signal, json, shlex
+import ctypes, threading, subprocess, signal, json, shlex
+libgcc_s = ctypes.CDLL('libgcc_s.so.1')
# Lib imports
import gi
@@ -49,8 +50,8 @@ class GrepSearchMixin:
GLib.idle_add(self.reset_grep_box)
# NOTE: If query create new process and do all new loop.
- self.pause_fifo_update = False
if query:
+ self.pause_fifo_update = False
GLib.idle_add(self._exec_grep_query, query)
def _exec_grep_query(self, widget=None, eve=None):
diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py
index f941dc9..ab63097 100644
--- a/plugins/searcher/plugin.py
+++ b/plugins/searcher/plugin.py
@@ -80,8 +80,12 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self.create_ipc_listener()
def generate_reference_ui_element(self):
+ icon = Gtk.Image(stock=Gtk.STOCK_FIND)
button = Gtk.Button(label=self.name)
+
button.connect("button-release-event", self._show_page)
+ button.set_image(icon)
+
return button
diff --git a/plugins/searcher/utils/search.py b/plugins/searcher/utils/search.py
index 692fbc2..be0b31c 100755
--- a/plugins/searcher/utils/search.py
+++ b/plugins/searcher/utils/search.py
@@ -16,9 +16,9 @@ from multiprocessing.connection import Client
_ipc_address = f'/tmp/solarfm-search_grep-ipc.sock'
_ipc_authkey = b'' + bytes(f'solarfm-search_grep-ipc', 'utf-8')
-filter = (".mkv", ".mp4", ".webm", ".avi", ".mov", ".m4v", ".mpg", ".mpeg", ".wmv", ".flv") + \
- (".png", ".jpg", ".jpeg", ".gif", ".ico", ".tga", ".webp") + \
- (".psf", ".mp3", ".ogg", ".flac", ".m4a")
+filter = (".cpp", ".css", ".c", ".go", ".html", ".htm", ".java", ".js", ".json", ".lua", ".md", ".py", ".rs", ".toml", ".xml", ".pom") + \
+ (".txt", ".text", ".sh", ".cfg", ".conf", ".log")
+
# NOTE: Threads WILL NOT die with parent's destruction.
def threaded(fn):
@@ -59,7 +59,7 @@ def _search_for_string(file, query):
grep_result_set = {}
padding = 15
- with open(file, 'r') as fp:
+ with open(file, 'rb') as fp:
# NOTE: I know there's an issue if there's a very large file with content
# all on one line will lower and dupe it. And, yes, it will only
# return one instance from the file.
@@ -80,7 +80,7 @@ def _search_for_string(file, query):
else:
line = raw
- b64_line = base64.urlsafe_b64encode(line.encode('utf-8')).decode('utf-8')
+ b64_line = base64.urlsafe_b64encode(line).decode('utf-8')
if f"{b64_file}" in grep_result_set.keys():
grep_result_set[f"{b64_file}"][f"{i+1}"] = b64_line
else:
@@ -109,7 +109,7 @@ def grep_search(path, query):
if os.path.isdir(target):
grep_search(target, query)
else:
- if not target.lower().endswith(filter):
+ if target.lower().endswith(filter):
size = os.path.getsize(target)
if not size > 5000:
_search_for_string(target, query)
@@ -125,7 +125,7 @@ def search(args):
file_search(args.dir, args.query.lower())
if args.type == "grep_search":
- grep_search(args.dir, args.query.lower())
+ grep_search(args.dir, args.query.lower().encode("utf-8"))
if __name__ == "__main__":
diff --git a/plugins/trasher/trasher.glade b/plugins/trasher/trasher.glade
index 54837b9..2867476 100644
--- a/plugins/trasher/trasher.glade
+++ b/plugins/trasher/trasher.glade
@@ -47,6 +47,7 @@
True
True
Empty Trash...
+ 20
@@ -63,7 +64,6 @@
True
True
Move to Trash...
- 20
trash_img
True
diff --git a/plugins/vod_thumbnailer/plugin.py b/plugins/vod_thumbnailer/plugin.py
index e730797..694972f 100644
--- a/plugins/vod_thumbnailer/plugin.py
+++ b/plugins/vod_thumbnailer/plugin.py
@@ -68,8 +68,13 @@ class Plugin(PluginBase):
self._file_hash = self._builder.get_object("file_hash")
def generate_reference_ui_element(self):
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(f"{self.path}/../../icons/video.png", 16, 16, True)
+ icon = Gtk.Image.new_from_pixbuf(pixbuf)
button = Gtk.Button(label=self.name)
+
+ button.set_image(icon)
button.connect("button-release-event", self._show_thumbnailer_page)
+
return button
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
index 9bfffcb..daee641 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
@@ -1,6 +1,5 @@
#!/usr/bin/python3
-
# Python imports
import argparse, faulthandler, traceback
from setproctitle import setproctitle
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
index 22ab397..2174dda 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
@@ -54,6 +54,7 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
if not settings.is_trace_debug():
self.fm_controller.save_state()
+ settings.clear_pid()
time.sleep(event_sleep_time)
Gtk.main_quit()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/desktopiconmixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/desktopiconmixin.py
index 3f69ea3..3ee2a50 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/desktopiconmixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/desktopiconmixin.py
@@ -50,13 +50,8 @@ class DesktopIconMixin:
return None
def traverse_icons_folder(self, path, icon):
- alt_icon_path = ""
-
for (dirpath, dirnames, filenames) in os.walk(path):
for file in filenames:
appNM = "application-x-" + icon
if icon in file or appNM in file:
- alt_icon_path = dirpath + "/" + file
- break
-
- return alt_icon_path
+ return f"{dirpath}/{file}"
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/utils/launcher.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/utils/launcher.py
index eab19cd..87f7da6 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/utils/launcher.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/utils/launcher.py
@@ -1,5 +1,5 @@
# System import
-import os, threading, subprocess, shlex
+import os, threading, subprocess
# Lib imports
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/utils/settings.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/utils/settings.py
index 928bf6a..b1e5dec 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/utils/settings.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/utils/settings.py
@@ -25,7 +25,7 @@ class Settings:
FFMPG_THUMBNLR = f"{CONFIG_PATH}/ffmpegthumbnailer" # Thumbnail generator binary
REMUX_FOLDER = f"{USER_HOME}/.remuxs" # Remuxed files folder
- ICON_DIRS = ["/usr/share/pixmaps", "/usr/share/icons", f"{USER_HOME}/.icons" ,]
+ ICON_DIRS = ["/usr/share/icons", f"{USER_HOME}/.icons" "/usr/share/pixmaps"]
BASE_THUMBS_PTH = f"{USER_HOME}/.thumbnails" # Used for thumbnail generation
ABS_THUMBS_PTH = f"{BASE_THUMBS_PTH}/normal" # Used for thumbnail generation
STEAM_ICONS_PTH = f"{BASE_THUMBS_PTH}/steam_icons"
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
index 5aa0ebd..f8c4fd9 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
@@ -38,8 +38,8 @@ class IPCServer:
@daemon_threaded
def create_ipc_listener(self) -> None:
if self._conn_type == "socket":
- if os.path.exists(self._ipc_address):
- return
+ 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)
elif "unsecured" not in self._conn_type:
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
index 0967a25..392b672 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
@@ -31,6 +31,7 @@ class Settings:
self._KEY_BINDINGS = f"{self._CONFIG_PATH}/key-bindings.json"
self._DEFAULT_ICONS = f"{self._CONFIG_PATH}/icons"
self._WINDOW_ICON = f"{self._DEFAULT_ICONS}/{app_name.lower()}.png"
+ self._PID_FILE = f"{self._CONFIG_PATH}/solarfm.pid"
self._ICON_THEME = Gtk.IconTheme.get_default()
if not os.path.exists(self._CONFIG_PATH):
@@ -65,6 +66,44 @@ class Settings:
self._trace_debug = False
self._debug = False
+ self._dirty_start = False
+
+ self._check_for_dirty_state()
+
+
+ def _check_for_dirty_state(self):
+ if not os.path.exists(self._PID_FILE):
+ self._write_new_pid()
+ else:
+ with open(self._PID_FILE, "r") as _pid:
+ pid = _pid.readline().strip()
+ if pid not in ("", None):
+ self._check_alive_status(int(pid))
+ else:
+ self._write_new_pid()
+
+ """ Check For the existence of a unix pid. """
+ def _check_alive_status(self, pid):
+ print(f"PID Found: {pid}")
+ try:
+ os.kill(pid, 0)
+ except OSError:
+ print("SolarFM Is starting dirty...")
+ self._dirty_start = True
+ self._write_new_pid()
+
+ print("PID is alive... Let downstream errors handle app closure.")
+
+ def _write_new_pid(self):
+ pid = os.getpid()
+ self._write_pid(pid)
+
+ def _clean_pid(self):
+ os.unlink(self._PID_FILE)
+
+ def _write_pid(self, pid):
+ with open(self._PID_FILE, "w") as _pid:
+ _pid.write(f"{pid}")
def create_window(self) -> None:
@@ -104,6 +143,7 @@ class Settings:
return monitors
+
def get_main_window(self) -> Gtk.ApplicationWindow: return self._main_window
def get_builder(self) -> Gtk.Builder: return self._builder
def get_logger(self) -> Logger: return self._logger
@@ -117,6 +157,8 @@ class Settings:
def is_trace_debug(self) -> str: return self._trace_debug
def is_debug(self) -> str: return self._debug
+ def is_dirty_start(self) -> bool: return self._dirty_start
+ def clear_pid(self): self._clean_pid()
def set_trace_debug(self, trace_debug):
diff --git a/user_config/usr/share/solarfm/Main_Window.glade b/user_config/usr/share/solarfm/Main_Window.glade
index d102e67..fa62070 100644
--- a/user_config/usr/share/solarfm/Main_Window.glade
+++ b/user_config/usr/share/solarfm/Main_Window.glade
@@ -740,23 +740,6 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
4
-
-
- Archive
- archive
- True
- True
- True
- Archive...
- True
-
-
-
- False
- True
- 5
-
-
@@ -1079,17 +1062,6 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
-
-
-
From efa42a301c278f1d3db3218707ca43413baf52af Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Sat, 22 Oct 2022 23:53:33 -0500
Subject: [PATCH 17/30] small refactoring
---
.../solarfm-0.0.1/SolarFM/solarfm/__main__.py | 7 ++++
.../solarfm-0.0.1/SolarFM/solarfm/app.py | 41 ++++++++-----------
.../SolarFM/solarfm/core/controller.py | 24 +++--------
.../SolarFM/solarfm/core/controller_data.py | 4 ++
.../SolarFM/solarfm/utils/ipc_server.py | 11 +++--
.../SolarFM/solarfm/utils/settings.py | 11 +++--
6 files changed, 45 insertions(+), 53 deletions(-)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
index daee641..b61b260 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
@@ -35,6 +35,13 @@ if __name__ == "__main__":
# Read arguments (If any...)
args, unknownargs = parser.parse_known_args()
+ if args.debug == "true":
+ settings.set_debug(True)
+
+ if args.trace_debug == "true":
+ settings.set_trace_debug(True)
+
+ settings.do_dirty_start_check()
Application(args, unknownargs)
Gtk.main()
except Exception as e:
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
index 7df637a..82436d4 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py
@@ -1,57 +1,50 @@
# Python imports
-import os, inspect, time
+import os, inspect
# Lib imports
# Application imports
from utils.ipc_server import IPCServer
-from utils.settings import Settings
from core.controller import Controller
class AppLaunchException(Exception):
...
-class ControllerStartExceptio(Exception):
+class ControllerStartException(Exception):
...
class Application(IPCServer):
- """ Create Settings and Controller classes. Bind signal to Builder. Inherit from Builtins to bind global methods and classes. """
+ """ Inherit from IPCServer; create Controller classe; bind any signal(s) to Builder. """
def __init__(self, args, unknownargs):
super(Application, self).__init__()
- if args.debug == "true":
- settings.set_debug(True)
+ self.args, self.unknownargs = args, unknownargs
- if args.trace_debug == "true":
- settings.set_trace_debug(True)
-
- # NOTE: Instance found, sending files to it...
if not settings.is_trace_debug():
- self.create_ipc_listener()
- time.sleep(0.05)
+ try:
+ self.create_ipc_listener()
+ except Exception:
+ ...
if not self.is_ipc_alive:
- if unknownargs:
- for arg in unknownargs:
- if os.path.isdir(arg):
- message = f"FILE|{arg}"
- self.send_ipc_message(message)
+ for arg in unknownargs + [args.new_tab,]:
+ if os.path.isdir(arg):
+ message = f"FILE|{arg}"
+ self.send_ipc_message(message)
- if args.new_tab and os.path.isdir(args.new_tab):
- message = f"FILE|{args.new_tab}"
- self.send_ipc_message(message)
-
- raise AppLaunchException(f"IPC Server Exists: Will send path(s) to it and close...\nNote: If no fm exists, remove /tmp/{app_name}-ipc.sock")
+ raise AppLaunchException(f"{app_name} IPC Server Exists: Will send path(s) to it and close...")
settings.create_window()
+ self._load_controller_and_builder()
- controller = Controller(args, unknownargs)
+ def _load_controller_and_builder(self):
+ controller = Controller(self.args, self.unknownargs)
if not controller:
- raise ControllerStartExceptio("Controller exited and doesn't exist...")
+ raise ControllerStartException("Controller exited and doesn't exist...")
# Gets the methods from the classes and sets to handler.
# Then, builder connects to any signals it needs.
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
index 2174dda..abb3b2d 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
@@ -19,27 +19,14 @@ from .controller_data import Controller_Data
class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMixin, Controller_Data):
""" Controller coordinates the mixins and is somewhat the root hub of it all. """
def __init__(self, args, unknownargs):
+ self._subscribe_to_events()
self.setup_controller_data()
- self.window.show()
-
self.generate_windows(self.fm_controller_data)
self.plugins.launch_plugins()
- if settings.is_debug():
- self.window.set_interactive_debugging(True)
-
- # NOTE: Open files if passed in from cli and not trace debugging...
- if not settings.is_trace_debug():
- self._subscribe_to_events()
-
- if unknownargs:
- for arg in unknownargs:
- if os.path.isdir(arg):
- message = f"FILE|{arg}"
- event_system.emit("post_file_to_ipc", message)
-
- if args.new_tab and os.path.isdir(args.new_tab):
- message = f"FILE|{args.new_tab}"
+ for arg in unknownargs + [args.new_tab,]:
+ if os.path.isdir(arg):
+ message = f"FILE|{arg}"
event_system.emit("post_file_to_ipc", message)
@@ -88,7 +75,7 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
session_json = self.fm_controller.get_state_from_file(path)
self.load_session(session_json)
if (response == Gtk.ResponseType.CANCEL) or (response == Gtk.ResponseType.DELETE_EVENT):
- pass
+ ...
save_load_dialog.hide()
@@ -137,7 +124,6 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
-
@endpoint_registry.register(rule="go_home")
def go_home(self, widget=None, eve=None):
self.builder.get_object("go_home").released()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
index 112140b..904a8ad 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
@@ -96,6 +96,10 @@ class Controller_Data:
self.window.connect("delete-event", self.tear_down)
GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.tear_down)
+ self.window.show()
+ if settings.is_debug():
+ self.window.set_interactive_debugging(True)
+
def get_current_state(self) -> State:
'''
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
index f8c4fd9..f1dbd4f 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py
@@ -31,11 +31,10 @@ class IPCServer:
self._subscribe_to_events()
+
def _subscribe_to_events(self):
event_system.subscribe("post_file_to_ipc", self.send_ipc_message)
-
- @daemon_threaded
def create_ipc_listener(self) -> None:
if self._conn_type == "socket":
if os.path.exists(self._ipc_address) and settings.is_dirty_start():
@@ -49,14 +48,18 @@ class IPCServer:
self.is_ipc_alive = True
+ self._run_ipc_loop(listener)
+
+ @daemon_threaded
+ def _run_ipc_loop(self, listener) -> None:
while True:
conn = listener.accept()
start_time = time.perf_counter()
- self.handle_message(conn, start_time)
+ self._handle_ipc_message(conn, start_time)
listener.close()
- def handle_message(self, conn, start_time) -> None:
+ def _handle_ipc_message(self, conn, start_time) -> None:
while True:
msg = conn.recv()
if settings.is_debug():
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
index 392b672..58eb0f4 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
@@ -31,7 +31,7 @@ class Settings:
self._KEY_BINDINGS = f"{self._CONFIG_PATH}/key-bindings.json"
self._DEFAULT_ICONS = f"{self._CONFIG_PATH}/icons"
self._WINDOW_ICON = f"{self._DEFAULT_ICONS}/{app_name.lower()}.png"
- self._PID_FILE = f"{self._CONFIG_PATH}/solarfm.pid"
+ self._PID_FILE = f"{self._CONFIG_PATH}/{app_name.lower()}.pid"
self._ICON_THEME = Gtk.IconTheme.get_default()
if not os.path.exists(self._CONFIG_PATH):
@@ -68,10 +68,8 @@ class Settings:
self._debug = False
self._dirty_start = False
- self._check_for_dirty_state()
-
- def _check_for_dirty_state(self):
+ def do_dirty_start_check(self):
if not os.path.exists(self._PID_FILE):
self._write_new_pid()
else:
@@ -88,11 +86,12 @@ class Settings:
try:
os.kill(pid, 0)
except OSError:
- print("SolarFM Is starting dirty...")
+ print(f"{app_name} is starting dirty...")
self._dirty_start = True
self._write_new_pid()
+ return
- print("PID is alive... Let downstream errors handle app closure.")
+ print("PID is alive... Let downstream errors (sans debug args) handle app closure propigation.")
def _write_new_pid(self):
pid = os.getpid()
From 9697e8ca169c6e945687a40c86501222569d09e2 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Tue, 25 Oct 2022 23:27:21 -0500
Subject: [PATCH 18/30] Moving to use ContextMenu
---
plugins/archiver/manifest.json | 2 +-
plugins/archiver/plugin.py | 11 +-
plugins/file_properties/plugin.py | 12 +-
plugins/movie_tv_info/manifest.json | 2 +-
plugins/movie_tv_info/plugin.py | 12 +-
plugins/searcher/plugin.py | 14 +-
plugins/trasher/plugin.py | 53 ++--
plugins/trasher/trasher.glade | 126 --------
plugins/vod_thumbnailer/manifest.json | 2 +-
plugins/vod_thumbnailer/plugin.py | 11 +-
.../SolarFM/solarfm/__builtins__.py | 1 -
.../solarfm-0.0.1/SolarFM/solarfm/__main__.py | 2 +-
.../SolarFM/solarfm/core/controller_data.py | 85 ++++++
.../solarfm/core/mixins/show_hide_mixin.py | 4 +-
.../SolarFM/solarfm/plugins/manifest.py | 3 +-
.../usr/share/solarfm/Main_Window.glade | 280 ------------------
16 files changed, 154 insertions(+), 466 deletions(-)
delete mode 100644 plugins/trasher/trasher.glade
diff --git a/plugins/archiver/manifest.json b/plugins/archiver/manifest.json
index 761a2eb..5d9fc1b 100644
--- a/plugins/archiver/manifest.json
+++ b/plugins/archiver/manifest.json
@@ -5,7 +5,7 @@
"version": "0.0.1",
"support": "",
"requests": {
- "ui_target": "context_menu",
+ "ui_target": "context_menu_plugins",
"pass_fm_events": "true"
}
}
diff --git a/plugins/archiver/plugin.py b/plugins/archiver/plugin.py
index ccfd732..5426094 100644
--- a/plugins/archiver/plugin.py
+++ b/plugins/archiver/plugin.py
@@ -81,13 +81,12 @@ class Plugin(PluginBase):
self._archiver_dialogue = self._builder.get_object("archiver_dialogue")
self._arc_command_buffer = self._builder.get_object("arc_command_buffer")
- icon = Gtk.Image(stock=Gtk.STOCK_FLOPPY)
- button = Gtk.Button(label=self.name)
+ item = Gtk.ImageMenuItem(self.name)
+ item.set_image( Gtk.Image(stock=Gtk.STOCK_FLOPPY) )
+ item.connect("activate", self.show_archiver_dialogue)
+ item.set_always_show_image(True)
+ return item
- button.set_image(icon)
- button.connect("button-release-event", self.show_archiver_dialogue)
-
- return button
def run(self):
...
diff --git a/plugins/file_properties/plugin.py b/plugins/file_properties/plugin.py
index c20bc1a..e28d1e3 100644
--- a/plugins/file_properties/plugin.py
+++ b/plugins/file_properties/plugin.py
@@ -99,13 +99,11 @@ class Plugin(PluginBase):
self._file_group = self._builder.get_object("file_group")
def generate_reference_ui_element(self):
- icon = Gtk.Image(stock=Gtk.STOCK_PROPERTIES )
- button = Gtk.Button(label=self.name)
-
- button.connect("button-release-event", self._show_properties_page)
- button.set_image(icon)
-
- return button
+ item = Gtk.ImageMenuItem(self.name)
+ item.set_image( Gtk.Image(stock=Gtk.STOCK_PROPERTIES) )
+ item.connect("activate", self._show_properties_page)
+ item.set_always_show_image(True)
+ return item
@threaded
diff --git a/plugins/movie_tv_info/manifest.json b/plugins/movie_tv_info/manifest.json
index 9290b70..2bff10f 100644
--- a/plugins/movie_tv_info/manifest.json
+++ b/plugins/movie_tv_info/manifest.json
@@ -5,7 +5,7 @@
"version": "0.0.1",
"support": "",
"requests": {
- "ui_target": "context_menu",
+ "ui_target": "context_menu_plugins",
"pass_fm_events": "true"
}
}
diff --git a/plugins/movie_tv_info/plugin.py b/plugins/movie_tv_info/plugin.py
index 2bae410..f16f282 100644
--- a/plugins/movie_tv_info/plugin.py
+++ b/plugins/movie_tv_info/plugin.py
@@ -70,13 +70,11 @@ class Plugin(PluginBase):
self._trailer_link = self._builder.get_object("trailer_link")
def generate_reference_ui_element(self):
- icon = Gtk.Image(stock=Gtk.STOCK_FIND)
- button = Gtk.Button(label=self.name)
-
- button.connect("button-release-event", self._show_info_page)
- button.set_image(icon)
-
- return button
+ item = Gtk.ImageMenuItem(self.name)
+ item.set_image( Gtk.Image(stock=Gtk.STOCK_FIND) )
+ item.connect("activate", self._show_info_page)
+ item.set_always_show_image(True)
+ return item
@threaded
def _show_info_page(self, widget=None, eve=None):
diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py
index ab63097..17eaa63 100644
--- a/plugins/searcher/plugin.py
+++ b/plugins/searcher/plugin.py
@@ -80,13 +80,11 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self.create_ipc_listener()
def generate_reference_ui_element(self):
- icon = Gtk.Image(stock=Gtk.STOCK_FIND)
- button = Gtk.Button(label=self.name)
-
- button.connect("button-release-event", self._show_page)
- button.set_image(icon)
-
- return button
+ item = Gtk.ImageMenuItem(self.name)
+ item.set_image( Gtk.Image(stock=Gtk.STOCK_FIND) )
+ item.connect("activate", self._show_page)
+ item.set_always_show_image(True)
+ return item
def _show_page(self, widget=None, eve=None):
@@ -113,6 +111,7 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self.grep_list_parent.add(self._grep_list)
self.grep_list_parent.show_all()
+ time.sleep(0.05)
Gtk.main_iteration()
def reset_file_list_box(self) -> None:
@@ -128,4 +127,5 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self.file_list_parent.add(self._file_list)
self.file_list_parent.show_all()
+ time.sleep(0.05)
Gtk.main_iteration()
diff --git a/plugins/trasher/plugin.py b/plugins/trasher/plugin.py
index 78cc759..69618d9 100644
--- a/plugins/trasher/plugin.py
+++ b/plugins/trasher/plugin.py
@@ -47,34 +47,49 @@ class Plugin(PluginBase):
self._event_system.subscribe("trash_files", self.trash_files)
def generate_reference_ui_element(self):
- self._builder = Gtk.Builder()
- self._builder.add_from_file(self._GLADE_FILE)
+ trash_a = Gtk.MenuItem("Trash Actions")
+ trash_menu = Gtk.Menu()
- classes = [self]
- handlers = {}
- for c in classes:
- methods = None
- try:
- methods = inspect.getmembers(c, predicate=inspect.ismethod)
- handlers.update(methods)
- except Exception as e:
- print(repr(e))
+ self.restore = Gtk.MenuItem("Restore From Trash")
+ self.restore.connect("activate", self.restore_trash_files)
- self._builder.connect_signals(handlers)
+ self.empty = Gtk.MenuItem("Empty Trash")
+ self.empty.connect("activate", self.empty_trash)
- trasher = self._builder.get_object("trasher")
- trasher.show_all()
+ trash = Gtk.ImageMenuItem("Trash")
+ trash.set_image( Gtk.Image.new_from_icon_name("user-trash", 16) )
+ trash.connect("activate", self.trash_files)
+ trash.set_always_show_image(True)
- return trasher
+ go_to = Gtk.ImageMenuItem("Go To Trash")
+ go_to.set_image( Gtk.Image.new_from_icon_name("user-trash", 16) )
+ go_to.connect("activate", self.go_to_trash)
+ go_to.set_always_show_image(True)
+
+ delete = Gtk.ImageMenuItem("Delete")
+ delete.set_image( Gtk.Image(stock=Gtk.STOCK_DELETE) )
+ delete.connect("activate", self.delete_files)
+ delete.set_always_show_image(True)
+
+ trash_a.set_submenu(trash_menu)
+ trash_a.show_all()
+ self._appen_menu_items(trash_menu, [self.restore, self.empty, trash, go_to, delete])
+
+ return trash_a
+
+
+ def _appen_menu_items(self, menu, items):
+ for item in items:
+ menu.append(item)
def _show_trash_buttons(self):
- self._builder.get_object("restore_from_trash").show()
- self._builder.get_object("empty_trash").show()
+ self.restore.show()
+ self.empty.show()
def _hide_trash_buttons(self):
- self._builder.get_object("restore_from_trash").hide()
- self._builder.get_object("empty_trash").hide()
+ self.restore.hide()
+ self.empty.hide()
def delete_files(self, widget = None, eve = None):
self._event_system.emit("do_hide_context_menu")
diff --git a/plugins/trasher/trasher.glade b/plugins/trasher/trasher.glade
deleted file mode 100644
index 2867476..0000000
--- a/plugins/trasher/trasher.glade
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-
-
-
- True
- False
- user-trash
-
-
- True
- False
- user-trash
-
-
- True
- True
- 5
- 10
- True
-
-
- True
- False
- vertical
-
-
- Restore From Trash
- restore_from_trash
- True
- True
- True
- Restore From Trash...
-
-
-
- False
- True
- 0
-
-
-
-
- Empty Trash
- empty_trash
- True
- True
- True
- Empty Trash...
- 20
-
-
-
- False
- True
- 1
-
-
-
-
- Trash
- trash
- True
- True
- True
- Move to Trash...
- trash_img
- True
-
-
-
- False
- True
- 2
-
-
-
-
- Go To Trash
- go_to_trash
- True
- True
- True
- Go To Trash...
- trash_img2
- True
-
-
-
- False
- True
- 3
-
-
-
-
- gtk-delete
- delete
- True
- True
- True
- Delete...
- 20
- True
- True
-
-
-
- False
- True
- 4
-
-
-
-
-
-
- True
- False
- False
- Trash
- center
-
-
-
-
diff --git a/plugins/vod_thumbnailer/manifest.json b/plugins/vod_thumbnailer/manifest.json
index 154bd40..21523f0 100644
--- a/plugins/vod_thumbnailer/manifest.json
+++ b/plugins/vod_thumbnailer/manifest.json
@@ -5,7 +5,7 @@
"version": "0.0.1",
"support": "",
"requests": {
- "ui_target": "context_menu",
+ "ui_target": "context_menu_plugins",
"pass_fm_events": "true"
}
}
diff --git a/plugins/vod_thumbnailer/plugin.py b/plugins/vod_thumbnailer/plugin.py
index 694972f..81a97a6 100644
--- a/plugins/vod_thumbnailer/plugin.py
+++ b/plugins/vod_thumbnailer/plugin.py
@@ -70,12 +70,11 @@ class Plugin(PluginBase):
def generate_reference_ui_element(self):
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(f"{self.path}/../../icons/video.png", 16, 16, True)
icon = Gtk.Image.new_from_pixbuf(pixbuf)
- button = Gtk.Button(label=self.name)
-
- button.set_image(icon)
- button.connect("button-release-event", self._show_thumbnailer_page)
-
- return button
+ item = Gtk.ImageMenuItem("Delete")
+ item.set_image( icon )
+ item.connect("activate", self._show_thumbnailer_page)
+ item.set_always_show_image(True)
+ return item
@threaded
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py
index 9ed03c8..a96c67f 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py
@@ -24,7 +24,6 @@ def daemon_threaded_wrapper(fn):
-
# NOTE: Just reminding myself we can add to builtins two different ways...
# __builtins__.update({"event_system": Builtins()})
builtins.app_name = "SolarFM"
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
index b61b260..e1fa0e9 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
@@ -22,7 +22,7 @@ if __name__ == "__main__":
""" Set process title, get arguments, and create GTK main thread. """
try:
- setproctitle('SolarFM')
+ setproctitle(f"{app_name}")
faulthandler.enable() # For better debug info
parser = argparse.ArgumentParser()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
index 904a8ad..dabae41 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
@@ -4,6 +4,8 @@ from dataclasses import dataclass
# Lib imports
import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
from gi.repository import GLib
# Application imports
@@ -100,6 +102,89 @@ class Controller_Data:
if settings.is_debug():
self.window.set_interactive_debugging(True)
+ self.build_context_menu()
+
+
+ def build_context_menu(self) -> None:
+ main_menu = Gtk.Menu()
+ open_menu = Gtk.Menu()
+ filea_menu = Gtk.Menu()
+ plugins_menu = Gtk.Menu()
+ open_a = Gtk.MenuItem("Open Actions")
+ file_a = Gtk.MenuItem("File Actions")
+ plugins = Gtk.MenuItem("Plugins")
+
+ self._appen_menu_items(main_menu, [open_a, file_a, plugins])
+
+ open = Gtk.ImageMenuItem("Open")
+ open.set_name("open")
+ open.set_image( Gtk.Image(stock=Gtk.STOCK_OPEN) )
+ open.set_always_show_image(True)
+ open.connect("activate", self.do_action_from_menu_controls)
+
+ open_with = Gtk.ImageMenuItem("Open With")
+ open_with.set_name("open_with")
+ open_with.set_image( Gtk.Image(stock=Gtk.STOCK_OPEN) )
+ open_with.set_always_show_image(True)
+ open_with.connect("activate", self.do_action_from_menu_controls)
+
+ execute = Gtk.ImageMenuItem("Execute")
+ execute.set_name("execute")
+ execute.set_image( Gtk.Image(stock=Gtk.STOCK_EXECUTE) )
+ execute.set_always_show_image(True)
+ execute.connect("activate", self.do_action_from_menu_controls)
+
+ execute_term = Gtk.ImageMenuItem("Execute in Terminal")
+ execute_term.set_name("execute_in_terminal")
+ execute_term.set_image( Gtk.Image(stock=Gtk.STOCK_EXECUTE) )
+ execute_term.set_always_show_image(True)
+ execute_term.connect("activate", self.do_action_from_menu_controls)
+
+ self._appen_menu_items(open_menu, [open, open_with, execute, execute_term])
+
+ new = Gtk.ImageMenuItem("New")
+ new.set_name("create")
+ new.set_image( Gtk.Image(stock=Gtk.STOCK_ADD) )
+ new.set_always_show_image(True)
+ new.connect("activate", self.do_action_from_menu_controls)
+
+ rename = Gtk.ImageMenuItem("Rename")
+ rename.set_name("rename")
+ rename.set_image( Gtk.Image(stock=Gtk.STOCK_EDIT) )
+ rename.set_always_show_image(True)
+ rename.connect("activate", self.do_action_from_menu_controls)
+
+ cut = Gtk.ImageMenuItem("Cut")
+ cut.set_name("cut")
+ cut.set_image( Gtk.Image(stock=Gtk.STOCK_CUT) )
+ cut.set_always_show_image(True)
+ cut.connect("activate", self.do_action_from_menu_controls)
+
+ copy = Gtk.ImageMenuItem("Copy")
+ copy.set_name("copy")
+ copy.set_image( Gtk.Image(stock=Gtk.STOCK_COPY) )
+ copy.set_always_show_image(True)
+ copy.connect("activate", self.do_action_from_menu_controls)
+
+ paste = Gtk.ImageMenuItem("Paste")
+ paste.set_name("paste")
+ paste.set_image( Gtk.Image(stock=Gtk.STOCK_PASTE) )
+ paste.set_always_show_image(True)
+ paste.connect("activate", self.do_action_from_menu_controls)
+
+ self._appen_menu_items(filea_menu, [new, rename, cut, copy, paste])
+ open_a.set_submenu(open_menu)
+ file_a.set_submenu(filea_menu)
+ plugins.set_submenu(plugins_menu)
+
+ main_menu.attach_to_widget(self.window, None)
+ main_menu.show_all()
+ self.builder.expose_object("context_menu", main_menu)
+ self.builder.expose_object("context_menu_plugins", plugins_menu)
+
+ def _appen_menu_items(self, menu, items):
+ for item in items:
+ menu.append(item)
def get_current_state(self) -> State:
'''
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/show_hide_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/show_hide_mixin.py
index 8377158..239b155 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/show_hide_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/show_hide_mixin.py
@@ -83,10 +83,10 @@ class ShowHideMixin:
self.builder.get_object("plugin_controls").hide()
def show_context_menu(self, widget=None, eve=None):
- self.builder.get_object("context_menu_popup").run()
+ self.builder.get_object("context_menu").popup_at_pointer(None)
def hide_context_menu(self, widget=None, eve=None):
- self.builder.get_object("context_menu_popup").hide()
+ self.builder.get_object("context_menu").popdown()
def show_new_file_menu(self, widget=None, eve=None):
if widget:
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py
index e53c0c5..3d23ecf 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py
@@ -56,7 +56,8 @@ class ManifestProcessor:
if requests["ui_target"] in [
"none", "other", "main_Window", "main_menu_bar",
"main_menu_bttn_box_bar", "path_menu_bar", "plugin_control_list",
- "context_menu", "window_1", "window_2", "window_3", "window_4"
+ "context_menu", "context_menu_plugins", "window_1",
+ "window_2", "window_3", "window_4"
]:
if requests["ui_target"] == "other":
if "ui_target_id" in keys:
diff --git a/user_config/usr/share/solarfm/Main_Window.glade b/user_config/usr/share/solarfm/Main_Window.glade
index fa62070..0176307 100644
--- a/user_config/usr/share/solarfm/Main_Window.glade
+++ b/user_config/usr/share/solarfm/Main_Window.glade
@@ -457,11 +457,6 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
False
gtk-new
-
- True
- False
- gtk-execute
-
True
False
@@ -488,287 +483,12 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
gtk-execute
-
- True
- False
- gtk-open
-
True
False
gtk-edit
3
-
- True
- False
- gtk-edit
-
-
False
dialog
From d0612a2b3757e3fa545fb651c89b6b6b82d0e213 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Wed, 26 Oct 2022 19:47:54 -0500
Subject: [PATCH 19/30] Building context menu from config file
---
plugins/vod_thumbnailer/plugin.py | 3 +-
.../SolarFM/solarfm/core/context_menu.py | 66 +++++++++++++++
.../SolarFM/solarfm/core/controller.py | 13 ++-
.../SolarFM/solarfm/core/controller_data.py | 83 -------------------
.../SolarFM/solarfm/utils/settings.py | 7 ++
.../usr/share/solarfm/contexct_menu.json | 16 ++++
6 files changed, 102 insertions(+), 86 deletions(-)
create mode 100644 src/versions/solarfm-0.0.1/SolarFM/solarfm/core/context_menu.py
create mode 100644 user_config/usr/share/solarfm/contexct_menu.json
diff --git a/plugins/vod_thumbnailer/plugin.py b/plugins/vod_thumbnailer/plugin.py
index 81a97a6..45e08e2 100644
--- a/plugins/vod_thumbnailer/plugin.py
+++ b/plugins/vod_thumbnailer/plugin.py
@@ -70,7 +70,8 @@ class Plugin(PluginBase):
def generate_reference_ui_element(self):
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(f"{self.path}/../../icons/video.png", 16, 16, True)
icon = Gtk.Image.new_from_pixbuf(pixbuf)
- item = Gtk.ImageMenuItem("Delete")
+ item = Gtk.ImageMenuItem(self.name)
+
item.set_image( icon )
item.connect("activate", self._show_thumbnailer_page)
item.set_always_show_image(True)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/context_menu.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/context_menu.py
new file mode 100644
index 0000000..f6f6cec
--- /dev/null
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/context_menu.py
@@ -0,0 +1,66 @@
+# Python imports
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+from gi.repository import GLib
+
+# Application imports
+
+
+class ContextMenu(Gtk.Menu):
+ def __init__(self):
+ super(ContextMenu, self).__init__()
+ self._builder = settings.get_builder()
+ self._context_menu_data = settings.get_context_menu_data()
+ self._window = settings.get_main_window()
+
+
+ def make_submenu(self, name, data, keys):
+ menu = Gtk.Menu()
+ menu_item = Gtk.MenuItem(name)
+
+ for key in keys:
+ if isinstance(data, dict):
+ entry = self.make_menu_item(key, data[key])
+ elif isinstance(data, list):
+ entry = self.make_menu_item(key, data)
+ else:
+ continue
+
+ menu.append(entry)
+
+ menu_item.set_submenu(menu)
+ return menu_item
+
+ def make_menu_item(self, name, data) -> Gtk.MenuItem:
+ if isinstance(data, dict):
+ return self.make_submenu(name, data, data.keys())
+ elif isinstance(data, list):
+ entry = Gtk.ImageMenuItem(name)
+ icon = getattr(Gtk, f"{data[0]}")
+ entry.set_image( Gtk.Image(stock=icon) )
+ entry.set_always_show_image(True)
+ entry.connect("activate", self._emit, (data[1]))
+ return entry
+
+ def build_context_menu(self) -> None:
+ data = self._context_menu_data
+ dkeys = data.keys()
+ plugins_entry = None
+
+ for dkey in dkeys:
+ entry = self.make_menu_item(dkey, data[dkey])
+ self.append(entry)
+ if dkey == "Plugins":
+ plugins_entry = entry
+
+ self.attach_to_widget(self._window, None)
+ self.show_all()
+ self._builder.expose_object("context_menu", self)
+ if plugins_entry:
+ self._builder.expose_object("context_menu_plugins", plugins_entry.get_submenu())
+
+ def _emit(self, menu_item, type):
+ event_system.emit("do_action_from_menu_controls", type)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
index abb3b2d..c301a38 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
@@ -12,7 +12,7 @@ from .mixins.ui_mixin import UIMixin
from .signals.ipc_signals_mixin import IPCSignalsMixin
from .signals.keyboard_signals_mixin import KeyboardSignalsMixin
from .controller_data import Controller_Data
-
+from .context_menu import ContextMenu
@@ -22,6 +22,10 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
self._subscribe_to_events()
self.setup_controller_data()
self.generate_windows(self.fm_controller_data)
+
+ cm = ContextMenu()
+ cm.build_context_menu()
+
self.plugins.launch_plugins()
for arg in unknownargs + [args.new_tab,]:
@@ -36,6 +40,7 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
event_system.subscribe("display_message", self.display_message)
event_system.subscribe("go_to_path", self.go_to_path)
event_system.subscribe("do_hide_context_menu", self.do_hide_context_menu)
+ event_system.subscribe("do_action_from_menu_controls", self.do_action_from_menu_controls)
def tear_down(self, widget=None, eve=None):
if not settings.is_trace_debug():
@@ -95,7 +100,11 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
def do_action_from_menu_controls(self, widget, eve = None):
- action = widget.get_name()
+ if not isinstance(widget, str):
+ action = widget.get_name()
+ else:
+ action = widget
+
self.hide_context_menu()
self.hide_new_file_menu()
self.hide_edit_file_menu()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
index dabae41..9f9943c 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
@@ -102,89 +102,6 @@ class Controller_Data:
if settings.is_debug():
self.window.set_interactive_debugging(True)
- self.build_context_menu()
-
-
- def build_context_menu(self) -> None:
- main_menu = Gtk.Menu()
- open_menu = Gtk.Menu()
- filea_menu = Gtk.Menu()
- plugins_menu = Gtk.Menu()
- open_a = Gtk.MenuItem("Open Actions")
- file_a = Gtk.MenuItem("File Actions")
- plugins = Gtk.MenuItem("Plugins")
-
- self._appen_menu_items(main_menu, [open_a, file_a, plugins])
-
- open = Gtk.ImageMenuItem("Open")
- open.set_name("open")
- open.set_image( Gtk.Image(stock=Gtk.STOCK_OPEN) )
- open.set_always_show_image(True)
- open.connect("activate", self.do_action_from_menu_controls)
-
- open_with = Gtk.ImageMenuItem("Open With")
- open_with.set_name("open_with")
- open_with.set_image( Gtk.Image(stock=Gtk.STOCK_OPEN) )
- open_with.set_always_show_image(True)
- open_with.connect("activate", self.do_action_from_menu_controls)
-
- execute = Gtk.ImageMenuItem("Execute")
- execute.set_name("execute")
- execute.set_image( Gtk.Image(stock=Gtk.STOCK_EXECUTE) )
- execute.set_always_show_image(True)
- execute.connect("activate", self.do_action_from_menu_controls)
-
- execute_term = Gtk.ImageMenuItem("Execute in Terminal")
- execute_term.set_name("execute_in_terminal")
- execute_term.set_image( Gtk.Image(stock=Gtk.STOCK_EXECUTE) )
- execute_term.set_always_show_image(True)
- execute_term.connect("activate", self.do_action_from_menu_controls)
-
- self._appen_menu_items(open_menu, [open, open_with, execute, execute_term])
-
- new = Gtk.ImageMenuItem("New")
- new.set_name("create")
- new.set_image( Gtk.Image(stock=Gtk.STOCK_ADD) )
- new.set_always_show_image(True)
- new.connect("activate", self.do_action_from_menu_controls)
-
- rename = Gtk.ImageMenuItem("Rename")
- rename.set_name("rename")
- rename.set_image( Gtk.Image(stock=Gtk.STOCK_EDIT) )
- rename.set_always_show_image(True)
- rename.connect("activate", self.do_action_from_menu_controls)
-
- cut = Gtk.ImageMenuItem("Cut")
- cut.set_name("cut")
- cut.set_image( Gtk.Image(stock=Gtk.STOCK_CUT) )
- cut.set_always_show_image(True)
- cut.connect("activate", self.do_action_from_menu_controls)
-
- copy = Gtk.ImageMenuItem("Copy")
- copy.set_name("copy")
- copy.set_image( Gtk.Image(stock=Gtk.STOCK_COPY) )
- copy.set_always_show_image(True)
- copy.connect("activate", self.do_action_from_menu_controls)
-
- paste = Gtk.ImageMenuItem("Paste")
- paste.set_name("paste")
- paste.set_image( Gtk.Image(stock=Gtk.STOCK_PASTE) )
- paste.set_always_show_image(True)
- paste.connect("activate", self.do_action_from_menu_controls)
-
- self._appen_menu_items(filea_menu, [new, rename, cut, copy, paste])
- open_a.set_submenu(open_menu)
- file_a.set_submenu(filea_menu)
- plugins.set_submenu(plugins_menu)
-
- main_menu.attach_to_widget(self.window, None)
- main_menu.show_all()
- self.builder.expose_object("context_menu", main_menu)
- self.builder.expose_object("context_menu_plugins", plugins_menu)
-
- def _appen_menu_items(self, menu, items):
- for item in items:
- menu.append(item)
def get_current_state(self) -> State:
'''
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
index 58eb0f4..80b8dac 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/settings.py
@@ -31,6 +31,7 @@ class Settings:
self._KEY_BINDINGS = f"{self._CONFIG_PATH}/key-bindings.json"
self._DEFAULT_ICONS = f"{self._CONFIG_PATH}/icons"
self._WINDOW_ICON = f"{self._DEFAULT_ICONS}/{app_name.lower()}.png"
+ self._CONTEXT_MENU = f"{self._CONFIG_PATH}/contexct_menu.json"
self._PID_FILE = f"{self._CONFIG_PATH}/{app_name.lower()}.pid"
self._ICON_THEME = Gtk.IconTheme.get_default()
@@ -41,6 +42,8 @@ class Settings:
if not os.path.exists(self._GLADE_FILE):
self._GLADE_FILE = f"{self._USR_SOLARFM}/Main_Window.glade"
+ if not os.path.exists(self._CONTEXT_MENU):
+ self._CONTEXT_MENU = f"{self._USR_SOLARFM}/contexct_menu.json"
if not os.path.exists(self._KEY_BINDINGS):
self._KEY_BINDINGS = f"{self._USR_SOLARFM}/key-bindings.json"
if not os.path.exists(self._CSS_FILE):
@@ -59,6 +62,9 @@ class Settings:
keybindings = json.load(file)["keybindings"]
self._keybindings.configure(keybindings)
+ with open(self._CONTEXT_MENU) as file:
+ self._context_menu_data = json.load(file)
+
self._main_window = None
self._logger = Logger(self._CONFIG_PATH, _fh_log_lvl=20).get_logger()
self._builder = Gtk.Builder()
@@ -143,6 +149,7 @@ class Settings:
return monitors
+ def get_context_menu_data(self) -> Gtk.Builder: return self._context_menu_data
def get_main_window(self) -> Gtk.ApplicationWindow: return self._main_window
def get_builder(self) -> Gtk.Builder: return self._builder
def get_logger(self) -> Logger: return self._logger
diff --git a/user_config/usr/share/solarfm/contexct_menu.json b/user_config/usr/share/solarfm/contexct_menu.json
new file mode 100644
index 0000000..fde59f3
--- /dev/null
+++ b/user_config/usr/share/solarfm/contexct_menu.json
@@ -0,0 +1,16 @@
+{
+ "Open Actions": {
+ "Open": ["STOCK_OPEN", "open"],
+ "Open With": ["STOCK_OPEN", "open_with"],
+ "Execute": ["STOCK_EXECUTE", "execute"],
+ "Execute in Terminal": ["STOCK_EXECUTE", "execute_in_terminal"]
+ },
+ "File Actions": {
+ "New": ["STOCK_ADD", "create"],
+ "Rename": ["STOCK_EDIT", "rename"],
+ "Cut": ["STOCK_CUT", "cut"],
+ "Copy": ["STOCK_COPY", "copy"],
+ "Paste": ["STOCK_PASTE", "paste"]
+ },
+ "Plugins": {}
+}
From eeef0a43300ce05dcd6874659d61916b4ab00aed Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Thu, 27 Oct 2022 17:23:27 -0500
Subject: [PATCH 20/30] Search changes, plugin changes
---
plugins/favorites/manifest.json | 2 +-
plugins/favorites/plugin.py | 4 +-
plugins/searcher/manifest.json | 2 +-
plugins/searcher/plugin.py | 2 +
.../SolarFM/solarfm/core/controller_data.py | 3 -
.../mixins/ui/widget_file_action_mixin.py | 35 +++--
.../solarfm/core/mixins/ui/window_mixin.py | 3 -
.../SolarFM/solarfm/core/mixins/ui_mixin.py | 2 +-
.../core/signals/keyboard_signals_mixin.py | 16 --
.../usr/share/solarfm/Main_Window.glade | 146 +++++++-----------
10 files changed, 87 insertions(+), 128 deletions(-)
diff --git a/plugins/favorites/manifest.json b/plugins/favorites/manifest.json
index b6f006b..ce8a2f3 100644
--- a/plugins/favorites/manifest.json
+++ b/plugins/favorites/manifest.json
@@ -8,7 +8,7 @@
"ui_target": "main_menu_bttn_box_bar",
"pass_fm_events": "true",
"pass_ui_objects": ["path_entry"],
- "bind_keys": []
+ "bind_keys": ["Favorites||show_favorites_menu:f"]
}
}
}
diff --git a/plugins/favorites/plugin.py b/plugins/favorites/plugin.py
index e55fe8a..9dc40ed 100644
--- a/plugins/favorites/plugin.py
+++ b/plugins/favorites/plugin.py
@@ -30,7 +30,7 @@ class Plugin(PluginBase):
super().__init__()
self.name = "Favorites" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
- # where self.name should not be needed for message comms
+ # where self.name should not be needed for message comms
self.path = os.path.dirname(os.path.realpath(__file__))
self._GLADE_FILE = f"{self.path}/favorites.glade"
self._FAVORITES_FILE = f"{self.path}/favorites.json"
@@ -70,6 +70,8 @@ class Plugin(PluginBase):
with open(self._FAVORITES_FILE, 'a') as f:
f.write('[]')
+ self._event_system.subscribe("show_favorites_menu", self._show_favorites_menu)
+
def generate_reference_ui_element(self):
button = Gtk.Button(label=self.name)
diff --git a/plugins/searcher/manifest.json b/plugins/searcher/manifest.json
index 845a31f..6b09f7d 100644
--- a/plugins/searcher/manifest.json
+++ b/plugins/searcher/manifest.json
@@ -7,7 +7,7 @@
"requests": {
"ui_target": "context_menu",
"pass_fm_events": "true",
- "bind_keys": ["Search||_show_grep_list_page:f"]
+ "bind_keys": ["Search||show_search_page:s"]
}
}
}
diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py
index 17eaa63..a154805 100644
--- a/plugins/searcher/plugin.py
+++ b/plugins/searcher/plugin.py
@@ -76,6 +76,8 @@ class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase):
self._event_system.subscribe("update-file-ui", self._load_file_ui)
self._event_system.subscribe("update-grep-ui", self._load_grep_ui)
+ self._event_system.subscribe("show_search_page", self._show_page)
+
self.create_ipc_listener()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
index 9f9943c..f842051 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
@@ -79,9 +79,6 @@ class Controller_Data:
self.is_pane4_hidden = False
self.override_drop_dest = None
- self.is_searching = False
- self.search_icon_grid = None
- self.search_tab = None
self.cancel_creation = False
self.skip_edit = False
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
index 04f8400..007008a 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py
@@ -102,24 +102,27 @@ class WidgetFileActionMixin:
self.set_bottom_labels(tab)
- def popup_search_files(self, wid, keyname):
- entry = self.builder.get_object(f"win{wid}_search_field")
- self.builder.get_object(f"win{wid}_search").popup()
- entry.set_text(keyname)
- entry.grab_focus_without_selecting()
- entry.set_position(-1)
-
def do_file_search(self, widget, eve=None):
- query = widget.get_text().lower()
- self.search_icon_grid.unselect_all()
- for i, file in enumerate(self.search_tab.get_files()):
- if query and query in file[0].lower():
- path = Gtk.TreePath().new_from_indices([i])
- self.search_icon_grid.select_path(path)
+ if not self.ctrl_down and not self.shift_down and not self.alt_down:
+ target = widget.get_name()
+ notebook = self.builder.get_object(target)
+ page = notebook.get_current_page()
+ nth_page = notebook.get_nth_page(page)
+ icon_grid = nth_page.get_children()[0]
- items = self.search_icon_grid.get_selected_items()
- if len(items) > 0:
- self.search_icon_grid.scroll_to_path(items[-1], True, 0.5, 0.5)
+ wid, tid = icon_grid.get_name().split("|")
+ tab = self.get_fm_window(wid).get_tab_by_id(tid)
+ query = widget.get_text().lower()
+
+ icon_grid.unselect_all()
+ for i, file in enumerate(tab.get_files()):
+ if query and query in file[0].lower():
+ path = Gtk.TreePath().new_from_indices([i])
+ icon_grid.select_path(path)
+
+ items = icon_grid.get_selected_items()
+ if len(items) == 1:
+ icon_grid.scroll_to_path(items[-1], True, 0.5, 0.5)
def open_files(self):
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
index fddb9d3..5b3d9b1 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/window_mixin.py
@@ -161,9 +161,6 @@ class WindowMixin(TabMixin):
def grid_set_selected_items(self, icons_grid):
self.selected_files = icons_grid.get_selected_items()
- def grid_cursor_toggled(self, icons_grid):
- print("wat...")
-
def grid_icon_single_click(self, icons_grid, eve):
try:
self.path_menu.popdown()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui_mixin.py
index d127b28..6d61e3e 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui_mixin.py
@@ -11,4 +11,4 @@ from .show_hide_mixin import ShowHideMixin
class UIMixin(WidgetFileActionMixin, PaneMixin, WindowMixin, ShowHideMixin):
- pass
+ ...
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py
index bde0689..00062a6 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/signals/keyboard_signals_mixin.py
@@ -21,7 +21,6 @@ class KeyboardSignalsMixin:
self.ctrl_down = False
self.shift_down = False
self.alt_down = False
- self.is_searching = False
def on_global_key_press_controller(self, eve, user_data):
keyname = Gdk.keyval_name(user_data.keyval).lower()
@@ -62,21 +61,6 @@ class KeyboardSignalsMixin:
if keyname in ["1", "kp_1", "2", "kp_2", "3", "kp_3", "4", "kp_4"]:
self.builder.get_object(f"tggl_notebook_{keyname.strip('kp_')}").released()
- if re.fullmatch(valid_keyvalue_pat, keyname):
- if not self.is_searching and not self.ctrl_down \
- and not self.shift_down and not self.alt_down:
- focused_obj = self.window.get_focus()
- if isinstance(focused_obj, Gtk.IconView):
- self.is_searching = True
- state = self.get_current_state()
- self.search_tab = state.tab
- self.search_icon_grid = state.icon_grid
-
- self.unset_keys_and_data()
- self.popup_search_files(state.wid, keyname)
- return True
-
-
def handle_plugin_key_event(self, sender, eve_type):
event_system.emit(eve_type)
diff --git a/user_config/usr/share/solarfm/Main_Window.glade b/user_config/usr/share/solarfm/Main_Window.glade
index 0176307..c5d79b1 100644
--- a/user_config/usr/share/solarfm/Main_Window.glade
+++ b/user_config/usr/share/solarfm/Main_Window.glade
@@ -1073,6 +1073,21 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
+
+
+ window_1
+ True
+ True
+ edit-find-symbolic
+ False
+ False
+ Search...
+
+
+
+ False
+
+
False
@@ -1111,6 +1126,21 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
+
+
+ window_2
+ True
+ True
+ edit-find-symbolic
+ False
+ False
+ Search...
+
+
+
+ False
+
+
False
@@ -1163,6 +1193,21 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
+
+
+ window_3
+ True
+ True
+ edit-find-symbolic
+ False
+ False
+ Search...
+
+
+
+ False
+
+
False
@@ -1200,6 +1245,21 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
+
+
+ window_4
+ True
+ True
+ edit-find-symbolic
+ False
+ False
+ Search...
+
+
+
+ False
+
+
False
@@ -2045,92 +2105,6 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
-
- False
- 5
- 5
- 5
- 5
- window_1
- bottom
- none
-
-
-
- 52
- True
- True
-
-
-
-
-
-
- False
- 5
- 5
- 5
- 5
- window_2
- bottom
- none
-
-
-
- 96
- True
- True
-
-
-
-
-
-
- False
- 5
- 5
- 5
- 5
- window_3
- none
-
-
-
- 96
- True
- True
-
-
-
-
-
-
- False
- 5
- 5
- 5
- 5
- window_4
- none
-
-
-
- 96
- True
- True
-
-
-
-
-
False
False
From ee123c4916cb95cfcdcc6ab30bab3975785c48ed Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Thu, 27 Oct 2022 21:26:58 -0500
Subject: [PATCH 21/30] Added better type hinting
---
.../SolarFM/solarfm/core/controller_data.py | 1 -
.../solarfm/shellfm/windows/controller.py | 60 +++++++-------
.../solarfm/shellfm/windows/tabs/path.py | 18 ++---
.../solarfm/shellfm/windows/tabs/tab.py | 80 +++++++++----------
.../SolarFM/solarfm/shellfm/windows/window.py | 42 +++++-----
5 files changed, 103 insertions(+), 98 deletions(-)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
index f842051..6011cf7 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py
@@ -117,7 +117,6 @@ class Controller_Data:
state.store = state.icon_grid.get_model()
state.warning_alert = self.warning_alert
-
selected_files = state.icon_grid.get_selected_items()
if selected_files:
state.selected_files = self.format_to_uris(state.store, state.wid, state.tid, selected_files, True)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/controller.py
index f2b0e16..b4aa623 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/controller.py
@@ -16,100 +16,100 @@ def threaded(fn):
class WindowController:
def __init__(self):
- USER_HOME = path.expanduser('~')
- CONFIG_PATH = USER_HOME + "/.config/solarfm"
- self._session_file = CONFIG_PATH + "/session.json"
+ USER_HOME: str = path.expanduser('~')
+ CONFIG_PATH: str = f"{USER_HOME}/.config/solarfm"
+ self._session_file: srr = f"{CONFIG_PATH}/session.json"
- self._event_sleep_time = 1
- self._active_window_id = ""
- self._active_tab_id = ""
- self._windows = []
+ self._event_sleep_time: int = 1
+ self._active_window_id: str = ""
+ self._active_tab_id: str = ""
+ self._windows: list = []
- def set_wid_and_tid(self, wid, tid):
+ def set_wid_and_tid(self, wid: int, tid: int) -> None:
self._active_window_id = str(wid)
self._active_tab_id = str(tid)
- def get_active_wid_and_tid(self):
+ def get_active_wid_and_tid(self) -> list:
return self._active_window_id, self._active_tab_id
- def create_window(self):
+ def create_window(self) -> Window:
window = Window()
window.set_nickname(f"window_{str(len(self._windows) + 1)}")
self._windows.append(window)
return window
- def add_tab_for_window(self, win_id):
+ def add_tab_for_window(self, win_id: str) -> None:
for window in self._windows:
if window.get_id() == win_id:
return window.create_tab()
- def add_tab_for_window_by_name(self, name):
+ def add_tab_for_window_by_name(self, name: str) -> None:
for window in self._windows:
if window.get_name() == name:
return window.create_tab()
- def add_tab_for_window_by_nickname(self, nickname):
+ def add_tab_for_window_by_nickname(self, nickname: str) -> None:
for window in self._windows:
if window.get_nickname() == nickname:
return window.create_tab()
- def pop_window(self):
+ def pop_window(self) -> None:
self._windows.pop()
- def delete_window_by_id(self, win_id):
+ def delete_window_by_id(self, win_id: str) -> None:
for window in self._windows:
if window.get_id() == win_id:
self._windows.remove(window)
break
- def delete_window_by_name(self, name):
+ def delete_window_by_name(self, name: str) -> str:
for window in self._windows:
if window.get_name() == name:
self._windows.remove(window)
break
- def delete_window_by_nickname(self, nickname):
+ def delete_window_by_nickname(self, nickname: str) -> str:
for window in self._windows:
if window.get_nickname() == nickname:
self._windows.remove(window)
break
- def get_window_by_id(self, win_id):
+ def get_window_by_id(self, win_id: str) -> Window:
for window in self._windows:
if window.get_id() == win_id:
return window
raise(f"No Window by ID {win_id} found!")
- def get_window_by_name(self, name):
+ def get_window_by_name(self, name: str) -> Window:
for window in self._windows:
if window.get_name() == name:
return window
raise(f"No Window by Name {name} found!")
- def get_window_by_nickname(self, nickname):
+ def get_window_by_nickname(self, nickname: str) -> Window:
for window in self._windows:
if window.get_nickname() == nickname:
return window
raise(f"No Window by Nickname {nickname} found!")
- def get_window_by_index(self, index):
+ def get_window_by_index(self, index: int) -> Window:
return self._windows[index]
- def get_all_windows(self):
+ def get_all_windows(self) -> list:
return self._windows
- def set_window_nickname(self, win_id = None, nickname = ""):
+ def set_window_nickname(self, win_id: str = None, nickname: str = "") -> None:
for window in self._windows:
if window.get_id() == win_id:
window.set_nickname(nickname)
- def list_windows(self):
+ def list_windows(self) -> None:
print("\n[ ---- Windows ---- ]\n")
for window in self._windows:
print(f"\nID: {window.get_id()}")
@@ -121,18 +121,18 @@ class WindowController:
- def list_files_from_tabs_of_window(self, win_id):
+ def list_files_from_tabs_of_window(self, win_id: str) -> None:
for window in self._windows:
if window.get_id() == win_id:
window.list_files_from_tabs()
break
- def get_tabs_count(self, win_id):
+ def get_tabs_count(self, win_id: str) -> int:
for window in self._windows:
if window.get_id() == win_id:
return window.get_tabs_count()
- def get_tabs_from_window(self, win_id):
+ def get_tabs_from_window(self, win_id: str) -> list:
for window in self._windows:
if window.get_id() == win_id:
return window.get_all_tabs()
@@ -140,13 +140,13 @@ class WindowController:
- def unload_tabs_and_windows(self):
+ def unload_tabs_and_windows(self) -> None:
for window in self._windows:
window.get_all_tabs().clear()
self._windows.clear()
- def save_state(self, session_file = None):
+ def save_state(self, session_file: str = None) -> None:
if not session_file:
session_file = self._session_file
@@ -174,7 +174,7 @@ class WindowController:
else:
raise Exception("Window data corrupted! Can not save session!")
- def get_state_from_file(self, session_file = None):
+ def get_state_from_file(self, session_file: str = None) -> dict:
if not session_file:
session_file = self._session_file
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/path.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/path.py
index ecce282..6b3c415 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/path.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/path.py
@@ -7,20 +7,20 @@ import os
class Path:
- def get_home(self):
+ def get_home(self) -> str:
return os.path.expanduser("~") + self.subpath
- def get_path(self):
+ def get_path(self) -> str:
return f"/{'/'.join(self.path)}" if self.path else f"/{''.join(self.path)}"
- def get_path_list(self):
+ def get_path_list(self) -> list:
return self.path
- def push_to_path(self, dir):
+ def push_to_path(self, dir: str):
self.path.append(dir)
self.load_directory()
- def pop_from_path(self):
+ def pop_from_path(self) -> None:
try:
self.path.pop()
@@ -32,9 +32,9 @@ class Path:
except Exception as e:
pass
- def set_path(self, path):
+ def set_path(self, path: str) -> bool:
if path == self.get_path():
- return
+ return False
if os.path.isdir(path):
self.path = list( filter(None, path.replace("\\", "/").split('/')) )
@@ -43,7 +43,7 @@ class Path:
return False
- def set_path_with_sub_path(self, sub_path):
+ def set_path_with_sub_path(self, sub_path: str) -> bool:
path = os.path.join(self.get_home(), sub_path)
if path == self.get_path():
return False
@@ -55,7 +55,7 @@ class Path:
return False
- def set_to_home(self):
+ def set_to_home(self) -> None:
home = os.path.expanduser("~") + self.subpath
path = list( filter(None, home.replace("\\", "/").split('/')) )
self.path = path
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/tab.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/tab.py
index 70acaed..de483ae 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/tab.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/tab.py
@@ -20,25 +20,25 @@ from .path import Path
class Tab(Settings, FileHandler, Launcher, Icon, Path):
def __init__(self):
- self.logger = None
- self._id_length = 10
+ self.logger = None
+ self._id_length: int = 10
- self._id = ""
- self._wid = None
- self._dir_watcher = None
- self._hide_hidden = self.HIDE_HIDDEN_FILES
- self._files = []
- self._dirs = []
- self._vids = []
- self._images = []
- self._desktop = []
- self._ungrouped = []
- self._hidden = []
+ self._id: str = ""
+ self._wid: str = None
+ self._dir_watcher = None
+ self._hide_hidden: bool = self.HIDE_HIDDEN_FILES
+ self._files: list = []
+ self._dirs: list = []
+ self._vids: list = []
+ self._images: list = []
+ self._desktop: list = []
+ self._ungrouped: list = []
+ self._hidden: list = []
self._generate_id()
self.set_to_home()
- def load_directory(self):
+ def load_directory(self) -> None:
path = self.get_path()
self._dirs = []
self._vids = []
@@ -97,7 +97,7 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path):
return False
- def get_not_hidden_count(self):
+ def get_not_hidden_count(self) -> int:
return len(self._files) + \
len(self._dirs) + \
len(self._vids) + \
@@ -105,13 +105,13 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path):
len(self._desktop) + \
len(self._ungrouped)
- def get_hidden_count(self):
+ def get_hidden_count(self) -> int:
return len(self._hidden)
- def get_files_count(self):
+ def get_files_count(self) -> int:
return len(self._files)
- def get_path_part_from_hash(self, hash):
+ def get_path_part_from_hash(self, hash: str) -> str:
files = self.get_files()
file = None
@@ -122,7 +122,7 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path):
return file
- def get_files_formatted(self):
+ def get_files_formatted(self) -> dict:
files = self._hash_set(self._files),
dirs = self._hash_set(self._dirs),
videos = self.get_videos(),
@@ -154,7 +154,7 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path):
return data
- def get_gtk_icon_str_combo(self):
+ def get_gtk_icon_str_combo(self) -> list:
data = []
dir = self.get_current_directory()
for file in self._files:
@@ -163,57 +163,57 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path):
return data
- def get_current_directory(self):
+ def get_current_directory(self) -> str:
return self.get_path()
- def get_current_sub_path(self):
+ def get_current_sub_path(self) -> str:
path = self.get_path()
home = f"{self.get_home()}/"
return path.replace(home, "")
- def get_end_of_path(self):
+ def get_end_of_path(self) -> str:
parts = self.get_current_directory().split("/")
size = len(parts)
return parts[size - 1]
- def set_hiding_hidden(self, state):
+ def set_hiding_hidden(self, state: bool) -> None:
self._hide_hidden = state
- def is_hiding_hidden(self):
+ def is_hiding_hidden(self) -> bool:
return self._hide_hidden
- def get_dot_dots(self):
+ def get_dot_dots(self) -> list:
return self._hash_set(['.', '..'])
- def get_files(self):
+ def get_files(self) -> list:
return self._hash_set(self._files)
- def get_dirs(self):
+ def get_dirs(self) -> list:
return self._hash_set(self._dirs)
- def get_videos(self):
+ def get_videos(self) -> list:
return self._hash_set(self._vids)
- def get_images(self):
+ def get_images(self) -> list:
return self._hash_set(self._images)
- def get_desktops(self):
+ def get_desktops(self) -> list:
return self._hash_set(self._desktop)
- def get_ungrouped(self):
+ def get_ungrouped(self) -> list:
return self._hash_set(self._ungrouped)
- def get_hidden(self):
+ def get_hidden(self) -> list:
return self._hash_set(self._hidden)
- def get_id(self):
+ def get_id(self) -> str:
return self._id
- def set_wid(self, _wid):
+ def set_wid(self, _wid: str) -> None:
self._wid = _wid
- def get_wid(self):
+ def get_wid(self) -> str:
return self._wid
def set_dir_watcher(self, watcher):
@@ -228,19 +228,19 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path):
def _natural_keys(self, text):
return [ self._atoi(c) for c in re.split('(\d+)',text) ]
- def _hash_text(self, text):
+ def _hash_text(self, text) -> str:
return hashlib.sha256(str.encode(text)).hexdigest()[:18]
- def _hash_set(self, arry):
+ def _hash_set(self, arry: list) -> list:
data = []
for arr in arry:
data.append([arr, self._hash_text(arr)])
return data
- def _random_with_N_digits(self, n):
+ def _random_with_N_digits(self, n: int) -> int:
range_start = 10**(n-1)
range_end = (10**n)-1
return randint(range_start, range_end)
- def _generate_id(self):
+ def _generate_id(self) -> str:
self._id = str(self._random_with_N_digits(self._id_length))
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/window.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/window.py
index ec61cd6..8105091 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/window.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/window.py
@@ -11,62 +11,68 @@ from .tabs.tab import Tab
class Window:
def __init__(self):
- self._id_length = 10
- self._id = ""
- self._name = ""
- self._nickname = ""
- self._isHidden = False
- self._tabs = []
+ self._id_length: int = 10
+ self._id: str = ""
+ self._name: str = ""
+ self._nickname:str = ""
+ self._isHidden: bool = False
+ self._active_tab: int = 0
+ self._tabs: list = []
self._generate_id()
self._set_name()
- def create_tab(self):
+ def create_tab(self) -> Tab:
tab = Tab()
self._tabs.append(tab)
return tab
- def pop_tab(self):
+ def pop_tab(self) -> None:
self._tabs.pop()
- def delete_tab_by_id(self, tid):
+ def delete_tab_by_id(self, tid: str):
for tab in self._tabs:
if tab.get_id() == tid:
self._tabs.remove(tab)
break
- def get_tab_by_id(self, tid):
+ def get_tab_by_id(self, tid: str) -> Tab:
for tab in self._tabs:
if tab.get_id() == tid:
return tab
- def get_tab_by_index(self, index):
+ def get_tab_by_index(self, index) -> Tab:
return self._tabs[index]
- def get_tabs_count(self):
+ def get_tabs_count(self) -> int:
return len(self._tabs)
- def get_all_tabs(self):
+ def get_all_tabs(self) -> list:
return self._tabs
- def get_id(self):
+ def get_id(self) -> str:
return self._id
- def get_name(self):
+ def get_name(self) -> str:
return self._name
- def get_nickname(self):
+ def get_nickname(self) -> str:
return self._nickname
- def is_hidden(self):
+ def is_hidden(self) -> bool:
return self._isHidden
- def list_files_from_tabs(self):
+ def list_files_from_tabs(self) -> None:
for tab in self._tabs:
print(tab.get_files())
+ def set_active_tab(self, index: int):
+ self._active_tab = index
+
+ def get_active_tab(self) -> Tab:
+ return self._tabs[self._active_tab]
def set_nickname(self, nickname):
self._nickname = f"{nickname}"
From 9cde8345cfed2fe5c2c1c8b15bbf327ea12a2650 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Wed, 2 Nov 2022 22:06:30 -0500
Subject: [PATCH 22/30] Tab dnd between windows (state not preserved, yet),
plugin fix
---
plugins/archiver/plugin.py | 2 +-
src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py | 9 +++++++--
.../SolarFM/solarfm/core/mixins/ui/tab_mixin.py | 5 +++++
user_config/usr/share/solarfm/Main_Window.glade | 8 ++++++++
4 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/plugins/archiver/plugin.py b/plugins/archiver/plugin.py
index 5426094..393de1f 100644
--- a/plugins/archiver/plugin.py
+++ b/plugins/archiver/plugin.py
@@ -122,4 +122,4 @@ class Plugin(PluginBase):
def set_arc_buffer_text(self, widget=None, eve=None):
sid = widget.get_active_id()
- self.arc_command_buffer.set_text(self.arc_commands[int(sid)])
+ self._arc_command_buffer.set_text(self.arc_commands[int(sid)])
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
index e1fa0e9..e256a29 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
@@ -18,9 +18,9 @@ from __builtins__ import *
from app import Application
-if __name__ == "__main__":
- """ Set process title, get arguments, and create GTK main thread. """
+
+def run():
try:
setproctitle(f"{app_name}")
faulthandler.enable() # For better debug info
@@ -47,3 +47,8 @@ if __name__ == "__main__":
except Exception as e:
traceback.print_exc()
quit()
+
+
+if __name__ == "__main__":
+ """ Set process title, get arguments, and create GTK main thread. """
+ run()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/tab_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/tab_mixin.py
index 683e6b4..2460417 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/tab_mixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/tab_mixin.py
@@ -35,6 +35,7 @@ class TabMixin(GridMixin):
tab_widget = self.create_tab_widget(tab)
scroll, store = self.create_scroll_and_store(tab, wid)
index = notebook.append_page(scroll, tab_widget)
+ notebook.set_tab_detachable(scroll, True)
self.fm_controller.set_wid_and_tid(wid, tab.get_id())
path_entry.set_text(tab.get_current_directory())
@@ -67,6 +68,10 @@ class TabMixin(GridMixin):
self.fm_controller.save_state()
self.set_window_title()
+ # NOTE: Not actually getting called even tho set in the glade file...
+ def on_tab_dnded(self, notebook, page, x, y):
+ ...
+
def on_tab_reorder(self, child, page_num, new_index):
wid, tid = page_num.get_name().split("|")
window = self.get_fm_window(wid)
diff --git a/user_config/usr/share/solarfm/Main_Window.glade b/user_config/usr/share/solarfm/Main_Window.glade
index c5d79b1..de3b02a 100644
--- a/user_config/usr/share/solarfm/Main_Window.glade
+++ b/user_config/usr/share/solarfm/Main_Window.glade
@@ -1053,6 +1053,8 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
5
False
True
+ sfm_windows
+
@@ -1106,6 +1108,8 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
5
False
True
+ sfm_windows
+
@@ -1173,6 +1177,8 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
5
False
True
+ sfm_windows
+
@@ -1225,6 +1231,8 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
5
False
True
+ sfm_windows
+
From 4f9fe37613b21b2db9fe130004b6fd6a84991ef6 Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Fri, 25 Nov 2022 00:34:16 -0600
Subject: [PATCH 23/30] added disable plugin load arg, added disk usage plugin,
updated icon gen
---
plugins/disk_usage/__init__.py | 3 +
plugins/disk_usage/__main__.py | 3 +
plugins/disk_usage/du_usage.glade | 129 ++++++++++++++++++
plugins/disk_usage/manifest.json | 12 ++
plugins/disk_usage/plugin.py | 104 ++++++++++++++
.../solarfm-0.0.1/SolarFM/solarfm/__main__.py | 2 +
.../SolarFM/solarfm/core/controller.py | 3 +-
.../tabs/icons/mixins/desktopiconmixin.py | 12 +-
.../usr/share/solarfm/fileicons/3g2.png | Bin 0 -> 3780 bytes
.../usr/share/solarfm/fileicons/3gp.png | Bin 0 -> 3771 bytes
.../usr/share/solarfm/fileicons/ai.png | Bin 0 -> 3547 bytes
.../usr/share/solarfm/fileicons/air.png | Bin 0 -> 3170 bytes
.../usr/share/solarfm/fileicons/asf.png | Bin 0 -> 3126 bytes
.../usr/share/solarfm/fileicons/avi.png | Bin 0 -> 3208 bytes
.../usr/share/solarfm/fileicons/bib.png | Bin 0 -> 3531 bytes
.../usr/share/solarfm/fileicons/cls.png | Bin 0 -> 1763 bytes
.../usr/share/solarfm/fileicons/csv.png | Bin 0 -> 4519 bytes
.../usr/share/solarfm/fileicons/deb.png | Bin 0 -> 2915 bytes
.../usr/share/solarfm/fileicons/djvu.png | Bin 0 -> 4973 bytes
.../usr/share/solarfm/fileicons/dmg.png | Bin 0 -> 2523 bytes
.../usr/share/solarfm/fileicons/doc.png | Bin 0 -> 1536 bytes
.../usr/share/solarfm/fileicons/docx.png | Bin 0 -> 4144 bytes
.../usr/share/solarfm/fileicons/dwf.png | Bin 0 -> 4319 bytes
.../usr/share/solarfm/fileicons/dwg.png | Bin 0 -> 3107 bytes
.../usr/share/solarfm/fileicons/eps.png | Bin 0 -> 2299 bytes
.../usr/share/solarfm/fileicons/epub.png | Bin 0 -> 2772 bytes
.../usr/share/solarfm/fileicons/exe.png | Bin 0 -> 2651 bytes
user_config/usr/share/solarfm/fileicons/f.png | Bin 0 -> 2050 bytes
.../usr/share/solarfm/fileicons/f77.png | Bin 0 -> 2187 bytes
.../usr/share/solarfm/fileicons/f90.png | Bin 0 -> 2212 bytes
.../usr/share/solarfm/fileicons/flac.png | Bin 0 -> 1121 bytes
.../usr/share/solarfm/fileicons/flv.png | Bin 0 -> 3181 bytes
.../usr/share/solarfm/fileicons/gif.png | Bin 0 -> 2802 bytes
.../usr/share/solarfm/fileicons/gz.png | Bin 0 -> 1480 bytes
.../usr/share/solarfm/fileicons/ico.png | Bin 0 -> 3130 bytes
.../usr/share/solarfm/fileicons/indd.png | Bin 0 -> 4031 bytes
.../usr/share/solarfm/fileicons/iso.png | Bin 0 -> 2704 bytes
.../usr/share/solarfm/fileicons/jpeg.png | Bin 0 -> 3319 bytes
.../usr/share/solarfm/fileicons/jpg.png | Bin 0 -> 3319 bytes
.../usr/share/solarfm/fileicons/log.png | Bin 0 -> 2486 bytes
.../usr/share/solarfm/fileicons/m4a.png | Bin 0 -> 3806 bytes
.../usr/share/solarfm/fileicons/m4v.png | Bin 0 -> 3178 bytes
.../usr/share/solarfm/fileicons/midi.png | Bin 0 -> 3511 bytes
.../usr/share/solarfm/fileicons/mkv.png | Bin 0 -> 1915 bytes
.../usr/share/solarfm/fileicons/mov.png | Bin 0 -> 3197 bytes
.../usr/share/solarfm/fileicons/mp3.png | Bin 0 -> 4064 bytes
.../usr/share/solarfm/fileicons/mp4.png | Bin 0 -> 3156 bytes
.../usr/share/solarfm/fileicons/mpeg.png | Bin 0 -> 3195 bytes
.../usr/share/solarfm/fileicons/mpg.png | Bin 0 -> 3195 bytes
.../usr/share/solarfm/fileicons/msi.png | Bin 0 -> 2655 bytes
.../usr/share/solarfm/fileicons/odp.png | Bin 0 -> 2895 bytes
.../usr/share/solarfm/fileicons/ods.png | Bin 0 -> 2435 bytes
.../usr/share/solarfm/fileicons/odt.png | Bin 0 -> 3701 bytes
.../usr/share/solarfm/fileicons/oga.png | Bin 0 -> 3163 bytes
.../usr/share/solarfm/fileicons/ogg.png | Bin 0 -> 5100 bytes
.../usr/share/solarfm/fileicons/ogv.png | Bin 0 -> 5921 bytes
.../usr/share/solarfm/fileicons/pdf.png | Bin 0 -> 3408 bytes
.../usr/share/solarfm/fileicons/png.png | Bin 0 -> 3014 bytes
.../usr/share/solarfm/fileicons/pps.png | Bin 0 -> 3178 bytes
.../usr/share/solarfm/fileicons/ppsx.png | Bin 0 -> 3306 bytes
.../usr/share/solarfm/fileicons/ppt.png | Bin 0 -> 3390 bytes
.../usr/share/solarfm/fileicons/pptx.png | Bin 0 -> 3942 bytes
.../usr/share/solarfm/fileicons/psd.png | Bin 0 -> 3898 bytes
.../usr/share/solarfm/fileicons/pub.png | Bin 0 -> 3822 bytes
.../usr/share/solarfm/fileicons/py.png | Bin 0 -> 2431 bytes
.../usr/share/solarfm/fileicons/qt.png | Bin 0 -> 3731 bytes
.../usr/share/solarfm/fileicons/ra.png | Bin 0 -> 3141 bytes
.../usr/share/solarfm/fileicons/ram.png | Bin 0 -> 3269 bytes
.../usr/share/solarfm/fileicons/rar.png | Bin 0 -> 3168 bytes
.../usr/share/solarfm/fileicons/rm.png | Bin 0 -> 3185 bytes
.../usr/share/solarfm/fileicons/rpm.png | Bin 0 -> 3278 bytes
.../usr/share/solarfm/fileicons/rtf.png | Bin 0 -> 2400 bytes
.../usr/share/solarfm/fileicons/rv.png | Bin 0 -> 3136 bytes
.../usr/share/solarfm/fileicons/skp.png | Bin 0 -> 3208 bytes
.../usr/share/solarfm/fileicons/spx.png | Bin 0 -> 1283 bytes
.../usr/share/solarfm/fileicons/sql.png | Bin 0 -> 1883 bytes
.../usr/share/solarfm/fileicons/sty.png | Bin 0 -> 1301 bytes
.../usr/share/solarfm/fileicons/tar.png | Bin 0 -> 2781 bytes
.../usr/share/solarfm/fileicons/tex.png | Bin 0 -> 2936 bytes
.../usr/share/solarfm/fileicons/tgz.png | Bin 0 -> 1757 bytes
.../usr/share/solarfm/fileicons/tiff.png | Bin 0 -> 3127 bytes
.../usr/share/solarfm/fileicons/ttf.png | Bin 0 -> 2699 bytes
.../usr/share/solarfm/fileicons/txt.png | Bin 0 -> 1734 bytes
.../usr/share/solarfm/fileicons/vob.png | Bin 0 -> 3184 bytes
.../usr/share/solarfm/fileicons/wav.png | Bin 0 -> 3169 bytes
.../usr/share/solarfm/fileicons/wmv.png | Bin 0 -> 3205 bytes
.../usr/share/solarfm/fileicons/xls.png | Bin 0 -> 4439 bytes
.../usr/share/solarfm/fileicons/xlsx.png | Bin 0 -> 4560 bytes
.../usr/share/solarfm/fileicons/xml.png | Bin 0 -> 1596 bytes
.../usr/share/solarfm/fileicons/xpi.png | Bin 0 -> 4283 bytes
.../usr/share/solarfm/fileicons/zip.png | Bin 0 -> 2099 bytes
91 files changed, 266 insertions(+), 2 deletions(-)
create mode 100644 plugins/disk_usage/__init__.py
create mode 100644 plugins/disk_usage/__main__.py
create mode 100644 plugins/disk_usage/du_usage.glade
create mode 100644 plugins/disk_usage/manifest.json
create mode 100644 plugins/disk_usage/plugin.py
create mode 100644 user_config/usr/share/solarfm/fileicons/3g2.png
create mode 100644 user_config/usr/share/solarfm/fileicons/3gp.png
create mode 100644 user_config/usr/share/solarfm/fileicons/ai.png
create mode 100644 user_config/usr/share/solarfm/fileicons/air.png
create mode 100644 user_config/usr/share/solarfm/fileicons/asf.png
create mode 100644 user_config/usr/share/solarfm/fileicons/avi.png
create mode 100644 user_config/usr/share/solarfm/fileicons/bib.png
create mode 100644 user_config/usr/share/solarfm/fileicons/cls.png
create mode 100644 user_config/usr/share/solarfm/fileicons/csv.png
create mode 100644 user_config/usr/share/solarfm/fileicons/deb.png
create mode 100644 user_config/usr/share/solarfm/fileicons/djvu.png
create mode 100644 user_config/usr/share/solarfm/fileicons/dmg.png
create mode 100644 user_config/usr/share/solarfm/fileicons/doc.png
create mode 100644 user_config/usr/share/solarfm/fileicons/docx.png
create mode 100644 user_config/usr/share/solarfm/fileicons/dwf.png
create mode 100644 user_config/usr/share/solarfm/fileicons/dwg.png
create mode 100644 user_config/usr/share/solarfm/fileicons/eps.png
create mode 100644 user_config/usr/share/solarfm/fileicons/epub.png
create mode 100644 user_config/usr/share/solarfm/fileicons/exe.png
create mode 100644 user_config/usr/share/solarfm/fileicons/f.png
create mode 100644 user_config/usr/share/solarfm/fileicons/f77.png
create mode 100644 user_config/usr/share/solarfm/fileicons/f90.png
create mode 100644 user_config/usr/share/solarfm/fileicons/flac.png
create mode 100644 user_config/usr/share/solarfm/fileicons/flv.png
create mode 100644 user_config/usr/share/solarfm/fileicons/gif.png
create mode 100644 user_config/usr/share/solarfm/fileicons/gz.png
create mode 100644 user_config/usr/share/solarfm/fileicons/ico.png
create mode 100644 user_config/usr/share/solarfm/fileicons/indd.png
create mode 100644 user_config/usr/share/solarfm/fileicons/iso.png
create mode 100644 user_config/usr/share/solarfm/fileicons/jpeg.png
create mode 100644 user_config/usr/share/solarfm/fileicons/jpg.png
create mode 100644 user_config/usr/share/solarfm/fileicons/log.png
create mode 100644 user_config/usr/share/solarfm/fileicons/m4a.png
create mode 100644 user_config/usr/share/solarfm/fileicons/m4v.png
create mode 100644 user_config/usr/share/solarfm/fileicons/midi.png
create mode 100644 user_config/usr/share/solarfm/fileicons/mkv.png
create mode 100644 user_config/usr/share/solarfm/fileicons/mov.png
create mode 100644 user_config/usr/share/solarfm/fileicons/mp3.png
create mode 100644 user_config/usr/share/solarfm/fileicons/mp4.png
create mode 100644 user_config/usr/share/solarfm/fileicons/mpeg.png
create mode 100644 user_config/usr/share/solarfm/fileicons/mpg.png
create mode 100644 user_config/usr/share/solarfm/fileicons/msi.png
create mode 100644 user_config/usr/share/solarfm/fileicons/odp.png
create mode 100644 user_config/usr/share/solarfm/fileicons/ods.png
create mode 100644 user_config/usr/share/solarfm/fileicons/odt.png
create mode 100644 user_config/usr/share/solarfm/fileicons/oga.png
create mode 100644 user_config/usr/share/solarfm/fileicons/ogg.png
create mode 100644 user_config/usr/share/solarfm/fileicons/ogv.png
create mode 100644 user_config/usr/share/solarfm/fileicons/pdf.png
create mode 100644 user_config/usr/share/solarfm/fileicons/png.png
create mode 100644 user_config/usr/share/solarfm/fileicons/pps.png
create mode 100644 user_config/usr/share/solarfm/fileicons/ppsx.png
create mode 100644 user_config/usr/share/solarfm/fileicons/ppt.png
create mode 100644 user_config/usr/share/solarfm/fileicons/pptx.png
create mode 100644 user_config/usr/share/solarfm/fileicons/psd.png
create mode 100644 user_config/usr/share/solarfm/fileicons/pub.png
create mode 100644 user_config/usr/share/solarfm/fileicons/py.png
create mode 100644 user_config/usr/share/solarfm/fileicons/qt.png
create mode 100644 user_config/usr/share/solarfm/fileicons/ra.png
create mode 100644 user_config/usr/share/solarfm/fileicons/ram.png
create mode 100644 user_config/usr/share/solarfm/fileicons/rar.png
create mode 100644 user_config/usr/share/solarfm/fileicons/rm.png
create mode 100644 user_config/usr/share/solarfm/fileicons/rpm.png
create mode 100644 user_config/usr/share/solarfm/fileicons/rtf.png
create mode 100644 user_config/usr/share/solarfm/fileicons/rv.png
create mode 100644 user_config/usr/share/solarfm/fileicons/skp.png
create mode 100644 user_config/usr/share/solarfm/fileicons/spx.png
create mode 100644 user_config/usr/share/solarfm/fileicons/sql.png
create mode 100644 user_config/usr/share/solarfm/fileicons/sty.png
create mode 100644 user_config/usr/share/solarfm/fileicons/tar.png
create mode 100644 user_config/usr/share/solarfm/fileicons/tex.png
create mode 100644 user_config/usr/share/solarfm/fileicons/tgz.png
create mode 100644 user_config/usr/share/solarfm/fileicons/tiff.png
create mode 100644 user_config/usr/share/solarfm/fileicons/ttf.png
create mode 100644 user_config/usr/share/solarfm/fileicons/txt.png
create mode 100644 user_config/usr/share/solarfm/fileicons/vob.png
create mode 100644 user_config/usr/share/solarfm/fileicons/wav.png
create mode 100644 user_config/usr/share/solarfm/fileicons/wmv.png
create mode 100644 user_config/usr/share/solarfm/fileicons/xls.png
create mode 100644 user_config/usr/share/solarfm/fileicons/xlsx.png
create mode 100644 user_config/usr/share/solarfm/fileicons/xml.png
create mode 100644 user_config/usr/share/solarfm/fileicons/xpi.png
create mode 100644 user_config/usr/share/solarfm/fileicons/zip.png
diff --git a/plugins/disk_usage/__init__.py b/plugins/disk_usage/__init__.py
new file mode 100644
index 0000000..d36fa8c
--- /dev/null
+++ b/plugins/disk_usage/__init__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Module
+"""
diff --git a/plugins/disk_usage/__main__.py b/plugins/disk_usage/__main__.py
new file mode 100644
index 0000000..a576329
--- /dev/null
+++ b/plugins/disk_usage/__main__.py
@@ -0,0 +1,3 @@
+"""
+ Pligin Package
+"""
diff --git a/plugins/disk_usage/du_usage.glade b/plugins/disk_usage/du_usage.glade
new file mode 100644
index 0000000..1b609dc
--- /dev/null
+++ b/plugins/disk_usage/du_usage.glade
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 420
+ 450
+ False
+ True
+ center
+ True
+ dialog
+ True
+ True
+ False
+ False
+ center
+
+
+ False
+ vertical
+ 2
+
+
+ False
+ end
+
+
+ gtk-close
+ True
+ True
+ True
+ True
+
+
+
+ True
+ True
+ 2
+
+
+
+
+ False
+ False
+ 0
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ 5
+ 5
+ 5
+ 5
+ Current Directory:
+ center
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ in
+
+
+ True
+ True
+ du_store
+ False
+
+
+
+
+
+ Disk Usage
+
+
+
+ 0
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+
+
diff --git a/plugins/disk_usage/manifest.json b/plugins/disk_usage/manifest.json
new file mode 100644
index 0000000..8bcb387
--- /dev/null
+++ b/plugins/disk_usage/manifest.json
@@ -0,0 +1,12 @@
+{
+ "manifest": {
+ "name": "Disk Usage",
+ "author": "ITDominator",
+ "version": "0.0.1",
+ "support": "",
+ "requests": {
+ "ui_target": "context_menu_plugins",
+ "pass_fm_events": "true"
+ }
+ }
+}
diff --git a/plugins/disk_usage/plugin.py b/plugins/disk_usage/plugin.py
new file mode 100644
index 0000000..790caa2
--- /dev/null
+++ b/plugins/disk_usage/plugin.py
@@ -0,0 +1,104 @@
+# Python imports
+import os, threading, subprocess, time, inspect
+
+# Lib imports
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+# Application imports
+from plugins.plugin_base import PluginBase
+
+
+# NOTE: Threads WILL NOT die with parent's destruction.
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
+ return wrapper
+
+# NOTE: Threads WILL die with parent's destruction.
+def daemon_threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
+ return wrapper
+
+
+
+
+class Plugin(PluginBase):
+ def __init__(self):
+ super().__init__()
+
+ self.name = "Disk Usage" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus
+ # where self.name should not be needed for message comms
+
+ self.path = os.path.dirname(os.path.realpath(__file__))
+ self._GLADE_FILE = f"{self.path}/du_usage.glade"
+ self._du_dialog = None
+ self._du_store = None
+
+
+ def run(self):
+ self._builder = Gtk.Builder()
+ self._builder.add_from_file(self._GLADE_FILE)
+
+ classes = [self]
+ handlers = {}
+ for c in classes:
+ methods = None
+ try:
+ methods = inspect.getmembers(c, predicate=inspect.ismethod)
+ handlers.update(methods)
+ except Exception as e:
+ print(repr(e))
+
+ self._builder.connect_signals(handlers)
+
+ self._du_dialog = self._builder.get_object("du_dialog")
+ self._du_store = self._builder.get_object("du_store")
+ self._current_dir_lbl = self._builder.get_object("current_dir_lbl")
+
+ self._event_system.subscribe("show_du_menu", self._show_du_menu)
+
+ def generate_reference_ui_element(self):
+ item = Gtk.ImageMenuItem(self.name)
+ item.set_image( Gtk.Image(stock=Gtk.STOCK_HARDDISK) )
+ item.connect("activate", self._show_du_menu)
+ item.set_always_show_image(True)
+ return item
+
+ def _get_state(self, widget=None, eve=None):
+ self._event_system.emit("get_current_state")
+
+ def _set_current_dir_lbl(self, widget=None, eve=None):
+ self._current_dir_lbl.set_label(f"Current Directory:\n{self._fm_state.tab.get_current_directory()}")
+
+ def _show_du_menu(self, widget=None, eve=None):
+ self._fm_state = None
+ self._get_state()
+ self._set_current_dir_lbl()
+ self.load_du_data()
+ self._du_dialog.run()
+
+ def load_du_data(self):
+ self._du_store.clear()
+
+ path = self._fm_state.tab.get_current_directory()
+ # NOTE: -h = human readable, -d = depth asigned to 1
+ command = ["du", "-h", "-d", "1", path]
+ proc = subprocess.Popen(command, stdout=subprocess.PIPE)
+ raw_data = proc.communicate()[0]
+ data = raw_data.decode("utf-8").strip() # NOTE: Will return data AFTER completion (if any)
+ parts = data.split("\n")
+
+ # NOTE: Last entry is curret dir. Move to top of list and pop off...
+ size, file = parts[-1].split("\t")
+ self._du_store.append([size, file.split("/")[-1]])
+ parts.pop()
+
+ for part in parts:
+ size, file = part.split("\t")
+ self._du_store.append([size, file.split("/")[-1]])
+
+ def _hide_du_menu(self, widget=None, eve=None):
+ self._du_dialog.hide()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
index e256a29..91e1fd0 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
@@ -29,9 +29,11 @@ def run():
# Add long and short arguments
parser.add_argument("--debug", "-d", default="false", help="Do extra console messaging.")
parser.add_argument("--trace-debug", "-td", default="false", help="Disable saves, ignore IPC lock, do extra console messaging.")
+ parser.add_argument("--no-plugins", "-np", default="false", help="Do not load plugins.")
parser.add_argument("--new-tab", "-t", default="", help="Open a file into new tab.")
parser.add_argument("--new-window", "-w", default="", help="Open a file into a new window.")
+
# Read arguments (If any...)
args, unknownargs = parser.parse_known_args()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
index c301a38..661f72e 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py
@@ -26,7 +26,8 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
cm = ContextMenu()
cm.build_context_menu()
- self.plugins.launch_plugins()
+ if args.no_plugins == "false":
+ self.plugins.launch_plugins()
for arg in unknownargs + [args.new_tab,]:
if os.path.isdir(arg):
diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/desktopiconmixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/desktopiconmixin.py
index 3ee2a50..93105fb 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/desktopiconmixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/shellfm/windows/tabs/icons/mixins/desktopiconmixin.py
@@ -3,6 +3,11 @@ import os, subprocess, hashlib
from os.path import isfile
# Gtk imports
+import gi
+gi.require_version('Gtk', '3.0')
+
+from gi.repository import Gtk
+from gi.repository import Gio
# Application imports
from .xdg.DesktopEntry import DesktopEntry
@@ -36,8 +41,13 @@ class DesktopIconMixin:
elif os.path.exists(icon):
return self.create_scaled_image(icon, self.sys_icon_wh)
else:
- alt_icon_path = ""
+ gio_icon = Gio.Icon.new_for_string(icon)
+ gicon = Gtk.Image.new_from_gicon(gio_icon, 32)
+ pixbuf = gicon.get_pixbuf()
+ if pixbuf:
+ return pixbuf
+ alt_icon_path = ""
for dir in self.ICON_DIRS:
alt_icon_path = self.traverse_icons_folder(dir, icon)
if alt_icon_path != "":
diff --git a/user_config/usr/share/solarfm/fileicons/3g2.png b/user_config/usr/share/solarfm/fileicons/3g2.png
new file mode 100644
index 0000000000000000000000000000000000000000..cccf50a8fa973db06465c39ba1dbbcc99447af26
GIT binary patch
literal 3780
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
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/ai.png b/user_config/usr/share/solarfm/fileicons/ai.png
new file mode 100644
index 0000000000000000000000000000000000000000..ddb172fe56041dd50f9ba44be8fa53adcebdeaae
GIT binary patch
literal 3547
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
VFXbTjN|XQq002ovPDHLkV1mFFyNCb)
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/air.png b/user_config/usr/share/solarfm/fileicons/air.png
new file mode 100644
index 0000000000000000000000000000000000000000..076f08e7d7ad9bff52bdcc623586ea0aa5387b37
GIT binary patch
literal 3170
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
IM6N<$f)KSF&Hw-a
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/asf.png b/user_config/usr/share/solarfm/fileicons/asf.png
new file mode 100644
index 0000000000000000000000000000000000000000..b700cf40b964016385688aece63ef41bd4ab9666
GIT binary patch
literal 3126
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
QJOBUy07*qoM6N<$f>?~xy#N3J
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/avi.png b/user_config/usr/share/solarfm/fileicons/avi.png
new file mode 100644
index 0000000000000000000000000000000000000000..f4436f748617621c45659611fa536693577e874f
GIT binary patch
literal 3208
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
FV1m)ijs^e#
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/cls.png b/user_config/usr/share/solarfm/fileicons/cls.png
new file mode 100644
index 0000000000000000000000000000000000000000..4759ad6f01d3aad46c74f210a7fa461cd01610d9
GIT binary patch
literal 1763
zcmaKr2{hDu6vxNVixDS1A%%q3qD>`95oeTj456Wt^q!L4NTWiAu`gj%V~uKL?3HCO
zqd|~9mYNy#{0=RowxPwIsf~=|KIQa?)`l~=l+9{h)Y5+Nf-nI5z^Ixn}GN6
zjfbBXcUEST?Jv}|3uZW0EJR21qFqLh5s%{rBchv%07Jf0NUl|=7O}%xV*f)
zqN1X*vJw=js;b(oQ&Uq@TU%RKS65$O-_X#|*x1QuWR0A47K0pCD02+aTfk8n*p`oE+VPSw$
zL_|bnWMouSRCIK7OiTT6
zDskY@cT-c-(KSREA&rgQyl|laa|9UI9|lkYTz@k%`O_yb`5Qtv^n5pPC9ZF(ZY>1D
zyGIwUX{u8lZf5Ab+f4NQasy_nBf@Z^C>?vyOe*$*BFO}fdt%w9?88G#4booDT;W+2
z;6m-;8Rn!X$Y?E%=+pTmxDsxA0x>W6WD+^>W5*C-wsPrRR-SDmd9)Wj6TctQSi4&4
zhu1by%Q94X(DY05k$A3dc`f=cyJ6sPSjJYNO$rX9`;PLATq;vx4PKOwM-AkoaW>muc2b<8S#FC)q8bP>8LvIUNd{L
zM^=B*!!<2>N`}ByZKIJwb;1_N5`#;%uz1)<<#?ELDAKO!ly>I1>U)k;p7B%yI?~7M
zUW--PvR%^tQ;o$$BFb3IIkoxp3}b!Zw)u)6D(uuzJ?m`Y3pFdA7$bJ2)8WI5Q}5c|
z=zF+JzR)DX?ow=LD^8x*5<%A>q`b=yV_yVRNKvJjnk@R8Ej+3{us!#J4TOC}y|naR
zhK(r@Om-yW`8n3ue%EuYEQ7pQ_=$O1GA2zmOaRNjB*Ax9UO`QYRyR=AiM;`L7B^G0
zD1R|_fIA|CP(riGTt-~t!D^T_3Cm$GRHIZc&g66{1j@l|J5+voz1p6=OR&7&ZJx_B
zC?@0(PKaq>vZdFO?sKbX1@f~g`W5D?b&*#UZm(O_Fo#K!#ACrimCcyzmUkM4o7k#M
z{B7j~YzM9J4Ie7O?5nORz9l`1r7TA?+IML`{?$^lCf`11k*+IrV;S$==|?41G7H~l
nNbCtA+uj`A`P&hu1s_X;B7Q5&@=|&6#!srNjeuusS!4eMD2>Cb
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/csv.png b/user_config/usr/share/solarfm/fileicons/csv.png
new file mode 100644
index 0000000000000000000000000000000000000000..869e3541b739b1c4380b52eb52e7f7307535d51b
GIT binary patch
literal 4519
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
FV1ml(uT%g4
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/deb.png b/user_config/usr/share/solarfm/fileicons/deb.png
new file mode 100644
index 0000000000000000000000000000000000000000..e5581ad1f784062671ced5047881499c3bc6c461
GIT binary patch
literal 2915
zcmV-p3!LcMPX^bY(7z;HL
ziWnh3Fa;rz1!72`Tr3jff)SFU&_s#_q_9Mmg)F$c?Ci|+_mSKAZD5t4-4_gH
zYenuW`q{S4XfjzX3Mdgc#Ljfmtc!N8n2VlTe5jb2Pr8r$UQ<((R*M2^@O~UfrF--(
z;Ps_n=iLv$+SHDCp&yiqR{$G56qR9v02~r>_#jlLj{~Nup)tAek#AtcI#6FM0!nJBVUhID?naHxg30a!NEOw4)Tw`0?r%niFYZ5p^&Y?r7ei);Yzme6lYtE#1gxn6
znP~v_<^WHeMd$5f0B3sui#9^hhWrbEhT`USNI!cw8efvRpO%U?k8;f`q
zPf*)s`KPyI@t3cKJx59h_4e7Qf9Dp)hS!HYN^s4{O3uCxEc|E-bB~-B58SRE%ssG<
z!O!mn<14c(AjE%bQht2WAOS{Vo(+uk?zX;kb(*Q;eA!w)}$c3Om3s{{u
z88WXv427q+gSD~UgIC^y+;?9FW!wnRXHJMtC{R$1M@w8IML$LM4F0b;-S<9({4aJQ
z+K#uNM-4&av87-U{F+?=Jvq^?-#UY3_B_sv6*rcsg!BpvU?|g#5cMz(mR@roQ>T5R
z^n!Eb6cm5ISEePwrCzlHZEbC_?e?38!QOdLh+ur}1~6az1}H=7;?_`wM~Q0_?S=;C
zqf2Q(Z-Vu?Q$#F5;y{u2tG7Tdms0{)UxJXKaq}X1572J@G@8%c2`r^u5-LJq9y(}|
zx~IB!rrO*(L2Ei{`t7JL^OP=J_4ME#%nBVgwRNdM+R
zVC0#Ys(AkJVjhs?5S&t~hU%-KtAm&vCu)rH#1kPpuh3
z0mf-SuboUB2N}5yKkNo&LX!uVnCGQ23F|~y%43n`AXfzC>Jgw_H?bl!t91=1A#&zX
zXHc3NK!Q$?orO6(aUDF_jgkNupGwdeT6t2ri{+k+Xf`OL8Zr7p5<^xlLFOdW`&War
zvRPw8FRRahd$d!)A*l~`_W_*mD-F!OmbK%b3bE__l}jGiQ=}$0%A^1V
zK)i32W
z_dx1PE#CT)yp_+bLS87T%%feR+MB&OWI-xZ4
zD3u>W*9s9hl~PV3asebT`i{(*Bp8WuR8k>YNnSS*52Gk7bN@|@6Ex0a3R~U^RjH$%
z<5Hd~(kH}uqQQ@=;TM`%O>Q(U4b*3hht#q;US)zPGf(WJRcAT4C7nqrXtX*X*xxrm
z^$M7#smOF%LIP%ga2qIB4MqFtX((*{JH}Kl5-Sl=XvIB~sWu9dN{1U%CaYASv9VDN
z(P`=63$X6_dl_ew{ZG8Y$gL(qC6O;BKq&Ct0c=E(n|P6nF007`*=$xzm_;!|#)4+d
z{^SmDyFSK?-&}`0O_ZO<&2)63%T^61`Cc7>&%Eo39U62U8^fgb@(@z_wo;@vQ=Tn)
zlv%L=vX9-y9HIlg%h!T)-rlbH0?k$FMwkhDX7T;K^-!#
zI2F2ARY-q*9yDx!n3r4N;`cW~vF#9Gu5j4^B^T@v%EJ4w;WID#q|>HQ!f~9M6i^fD
zn{qPmwUg!bmWj~#mmj0K^m^?7;qTz%ML$D=RxIx0))oKjc^iIm*jOS!a^ZPG&)0!v
zS^28D5Vt_w=3J<76)k2NI?H_P^I+aJ53PMiSbo#rKq1bfO&ZO#PmKkl({eOL&(nxq
zDNlHTdx3c|F0Df$JijL4p{J+kY%L0?rR2Lxz7n)?%3v;>E>85z=3=w2l+c&U?*3k&
zc+5q495`^KrX8BNO}FxgF_uopW$s|=Sf2Kv4}(Athw#n?cU?a7
z3Iwo`$Li?lINaOYTc{-;sDO~XM5HhzF2$vNiTlBeM(ubA+e*Hkqs2eh}hAKtZVS6fR<
z%lVqB(|&oq0v__lTeRjAg;ThXw`M)z3G=pve9C6#SF86t@AnVv9y0*+{+tc>F1j2YALfAQ}&{{^bX&waX9Fy;UN
N002ovPDHLkV1mTxdba=o
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/djvu.png b/user_config/usr/share/solarfm/fileicons/djvu.png
new file mode 100644
index 0000000000000000000000000000000000000000..f3ed05deba51cc39dea3fb3c8ae7284c946c3bfc
GIT binary patch
literal 4973
zcmV-z6O!zSP)fE4^APlsxUJRF6NRT9y4>Te^(n)Nhv2DsklORZ^`gV7jo9dcd
z)o~ON$;PG5FnjT_aN!@|!i6i{C=q-ihL_x=1&qpK9Ub6~B6
z)ccYOUFi4>)GCux(;zgEJ{biOh;u;N#QOh0dM4)UK-!EuLm+MetK7guAnpMJZnao~
zP66~*5;Zdkp3`-=InN(JxAG?r0~;W9R)r%?wiZgo;R8TFSg%TIcm(JL>w4MSHwyFs
z&&y`tM9kJkFB%<>Id#O>`LQOi^Jnshnw}zvvd)r!IDdF#Nk38CUjQ;-IkU9`4zLDN
zdrw!#kH&22)77DTlOLJ7GXeYsscV5QjK#C1?l6T$;o+n3kvQbY(no-yMi~zejYkfJ
zkHX*)GW*?1O(DUO953;CiNWJ<>L?@*N66+`=xsSj`>FlLer#X1ui39JX6ild%z`=Y
zxr-OxQ9xl@zRhfNrr6)5#vmF^1;t!zr!X;TV!E<_G1GQ(7cab1=kp&ETdG8-K4wsa
z%wq7EN1+K(3?{n$==K!)?wMQb+Pxl&o5vn@-#xSJu4>({vqfgs^sNTl?T(0oIzuwiX?PU2mb|L0jv|!0JnIbDLW?^G$2Z
zx(|{1CjuC%1*eQxsYY_MZEu%@L7O9Y1XS7X7DUs4gH(kvB^H09e-qqfh`q#hw
z)pYZ~jD6%Q_qProN_lVmB=Wq3Kp;R!gb+6{>3g2n3_>iu2Ld4|-M91}<$IiS(m8h-
zI1D(y76N^@tix#Cp3BYtTT7;8T{4xj0*`{N4}SL8YmJ}z(mjbSJJ)1icy7#|+cnxWw9>dAx)~NDnC_Qi_TPs+M!EWESfz)*7s_
zCDJi5Z4_E7oU=&jA*Db{SsQ_ezuv>={(7aqc5^0Slt4#d6c`PjNU^rrJ4-7`#?7kvhMbTk7uTwWlYU4bJkh&u-!JsWk5#KFf+7leG00F~;KT
za*M&+^3FR~`m7AzUPHtp5HWD!Aw-QH$j?X2Pb$^e6-5KPv-l~EbB0^CW@+dQFa}!E
zJ!JG=UVLH*zw;6g?%&1ng9Xl>9b(O0(=>L37*p{;HQ?*KQ1Vo@SC)tYap;uHtA&6)
zeIL*K@4KnF?8N5>wl%G2X-_gaqUqjxiS7*%^B3z0^B(C`hJ`81#Ow(M@9oAf_VN15
z$LQSvy=&71rz1(&w4hPC7eR3=1J16GGUUJdiTI^tEhD
zzy0h&(~D1?B;9v~{_O#;K7Ej1C(bc$5R94qgW3rD=dQBzlWojgXyDkJ
zqYUiH(lJm#Yehq{9XY*;pMU>tTKcnm>f!r&>z9*^UA(}i2Ll>124ifTw99~BL&PnG
zT3oHHb!e@y*3_BV`O-Y(3{|aPY*JF+;ZaN8P@G+q@@Ae)F_)*{)YRvJx@pD1Vfhw;__*9Re6o|
zQ>L|xwzlqJrJ~22v$kdeK#TADUreW)I)sqr9coN!r>j>H`afnN
zn2gVXlCWC}Bcm0O3ehF2oj|Dy*(HT&R9WguBrEDa%qve$Fmmn^+aGMEG11Mlj~`{#
zR?BVoSoUuEK7VrWzw^+*iwwW<1T)P$i5j;bLOWi7ApWgTq{^8ELQn3?tm6qisYTy|c>o2O7wNK~?$u0ruHs51E23mR=A
z>XR8#xlKIty%SiOrR?lTX80YV77|DRnSGY
z^b1-Vrzomy1$e0;(psT(gfbCINBB1rLPAQcF*qvnwyal5+P*o}%jEbR&pv*PPyGH?
z_UvE7%TEq5JC)<^Pkfx&_6KQdXra@er*7sTL~~$5@KPWXAbtAUEE{?jIB{l4We=c%(eVhXxk;?Wq~kuDujO8t39JO{oyA71G`Wfu$;BOoTD|
z2H;(l0CRNIZDi=sy99;f?E2UMcRt)g-AI}{a{q!?h)DWiq(BM~!h($uB8vZ*gu&H0
zlF1oV5aJB+rs1&0pj8wXk6Jsjc$-Q|T#7@ZD~+}`8lzRE`8En?Eg643=U-3pv+o{3
zF;C;*3UqxZ)_Fw6M;VE>0&OM635*q3C&0n_-U5B?vjjRH3q0pyXIYC<5h_w>rEbOp
zv3N4pV6?))@9;oHJz8tXB-gSqoZ_edagq(&)2!Lr#hSzwBIDspizgkv=kPs{(h+IF
z)rAx%#+p&u5!oC*sY-KY4N6Dx8B}WsEzNACkl0eysqRx$s9n$
zRmhaSfMwHy<-Mydd!nK$g_C3wtC^VCLl7M!7b;#I4tVoI11HDYxHQ#6ROlsPJ4w+@
z+9tutD)1E#Xcgz7l(sek#(D_p;hc|F4i#0G(i@l4N*Gyi@esgR%h=UU9)04=jLhVD
z=gKiW-AjX5L6gWJWujbZtTWevr<4P+2TV-BeH#y0ha!zD-5~%UM+!$O=}7OkM}{Bj
zIX(VJvbFgtp$fq{sA>z{veyP@;;T`P^OyTr$kk)5As-p6@iJtq77CX
zoHiI`F_Fb6gRt-qKX`=6**ow{C%Ll*tic%8bjM%D_914leXHb!Gm7^_Q_s?y`$%!+#g9hYiXo=UhFcrXLe
zd`w0cXsi?5v$dV156``IneQFE%;r9i<6{vEMS~RZ-><{#=N&(Ky9Cz8;bg49SXn;s
zO!)*ajna*|Y4B8i*OK=tQew#i;Vgvnpz~lAHp=0QM%W0W6`Ohurzb4Fbac0Xw&0vV
zTd)?46R|un0%b&rl&g9c@WCPD-X|C0GQHGd8YH4Bgo@L#J`ZPcQI>43z;kcBhfhdP
zb3Dpe*A{Yl!O@h0js}>^!}wer^Tx%%WAKuwlmixv<%1G6)?2=ppuVmFDZTiti6vag
zT%0?ml0$wagccx3#oFA3bfyEG2{}1D$=CKZb9^+1bc)WjW8dvCxC+*FK|>18U4C-3Qst|C|B6uE{2u#SvPR)P)*-E6z6Be_4_k{)STW|2C<8Sv<&i!xvUL5bv2?7P7
z5(JT?5PB3s5935>ci&&_D)Nl-G!wZxc6s-+cJ&T|FdzsD6pJ~E#XPy(d|@#+ePH79
z@V9cgS>AGghaZQK79x8digm=|TO+K(w-Go^(nb8urvz=yDM)xo&jaa!@My9Qqa8|t
zr@#*d$)cdX;AjgK>2xh6(os(*O|0fh^ai6>-=Vu{GeM9i42wijKoH~#h2s2y>Dl)l
z&1AaN4~PB$0DnegLL85iojH7cfC})WQ=&fgZg#~(;Y@R95QSK4!1rUbE`D5NEf}-R
zgf$FLCYhW|AU@!b5(wJ4gK2k+D|15>3pv85Ko|xT3X6rs+{}TAE5qMxY{=*(@Bn_t
zuGu1^{3Dase4_;3gq|{DH}-^N(g)deybyV*<79VZ7r`BJdFSH|2qw
zS@m>MFFVK=vV}r0e_(oU{F@E+t@=jbmjb2cwbaZDYN;$YzZ{N~*GQvWQOFnO4@}LD
zK3bP-_?_Ty!UL3!eaFgG8wkT<)x$Y^g9j8<^Zly!vhrLwZauFZ&48sY)mXsz2VCSm
zno2aP9}j=&eR0)FKm)Hi=dQN4bXdJKfhbQSmj9bTwQMMl$V*o+SSt|15KEy-UI>Xy
zCKJ!3lId?v=g+HOT>RPx0;`ok0r(4Rj4(PZfmawEPbjp#2DH{#tICtKc(xz6iq7e{
z@n7-^Htw&1WHLej+Ab3Xf&0a|Kj;6&`aeqJVM*9Avm*ci03~!qSaf4@Wnpw>Eo5PI
zWdJfTF)}SMGA%MRR53L=GB-LfI4dwPIxsLw)aii$001R)MObugZ)9m^c`amNbY%cC
rFflSMFfuJNG*mG#Ix#sqGBztPFgh?WoqGl}00000NkvXXu0mjf=<;r+
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/dmg.png b/user_config/usr/share/solarfm/fileicons/dmg.png
new file mode 100644
index 0000000000000000000000000000000000000000..b52c92cda4e20ea86d849748b2010cfa3cdd950f
GIT binary patch
literal 2523
zcmV<12_*K3P)%|1976&iCJu48x#WnY9L~
z$x-I$=5jf5I-N8$H1r#6>rS3Ld4RLdg79;nv|
z%aWB>WpF*w=Xba$*?B&)BfR^3K5_riqepb=)Tx7q4-C115yn_p3C-!fB*hN0|NtjWa3v>u3Tx1
zg_>OhWA@Rk3UZ!ev6xn`
z1B)6jB!X-uCt$#nauhYqD8o#D0d6J3BxF$+PFO6g`57)Uc|6vNR$}89z6+}5$)$#h
znBl{FMHW~sJEfCxI9a4bDo*pW
zabdjA=ckW9{u`dL(cs_!Aft+?FLZ#%XjluG>8;?{W~bj()4tUM#mn|j_w^~#p@lNTj-59wt)AO^!V{(TEB5UuG=8$CaI&NLuext
zM%B`zVF#+$0wqC)A}bWh<#Ip*#OaGK?$8GxoL}_)kN!M_cO*J==m_=o^$W}=;yD@{
z8>gPG9xzTp7|t+cuBh%AO%ZboGhi5{>*;hFP|@iJd)}e9-yVelfB-PA8>IydGY-pz
z`T2QpeU8?yZKH?d4@uW_a=JVuV@w{y9J-pWtLLuUf$GlrypG{jnhJv@pRd8Jq#cqI+>~?Fpff3~k`SLt+g~gWJ
zI9F5y&vsp~OiN2p2r;qJvO1W5N`@<-MFAt2gTgdqK4-`6R48g-F^+0!EH{chpD)y`
zwWi5wPPy5QOKQ?8F#hSMH}Ss>M#5%9mlr6S0%-9cV5KO~Hi*37cpMeLi}BCrvc*m+
z-ue8-O;Bi|JddrSR(a0l)PQ9rOUmlh7K;kqzduTeL;|aSi9G-%G6ibHOv+qumDuSL
z^oooqLq&U6ai62BQNZh?$FUeC)7f%u)m}R^>_Bx|c{!L!#L0ow|L~pf3neMDNJFPr
z(TKRP6<+IF00(wPFN$|Kx||6`mqc-AGFjTRc{6?QyWar`==eV+3cCh`q=p@M=EyUL
zR7Im(w{KA(5EKRpr6QH+Haam*iTNa@vKg=rj7712cH_FJ6^mqu>>iBejPiX+@LGr0_|=>js`4-DMT=d;w^(?edrmxljy4;aSc#p9#yH+Le+
zX~A4v+$yre-C4yy1tl-^R3PB5n(Mj1OZ8?zUswaYv92);jn=oXr`fqU8W%hQv#6R1
zxL6?*rYH%yuybcOZQs6)QY`jRs9ccT?6_}m<;UYG+OS~*sVI4*6yLaspk|B|RW4XM@1dBPB%Smp#M|4;c3RQvb$qVTa
z4EWH6ErQJyIK&WI4sF)0MxQN8NMJS?4wC_1pPZaPHuALP_17snIZcV=9L>!o02N-}
zYT}(Dn8<-f=de4;j=B0}Cn5YL&}h)D$SpkEpkI
z7X^bJNQr6SK8mOYY17v`sjJHcgOCZeAd1xE^ij5up*wf}O-bmU*pnxsxVT=XXOn`7
zT3bVvV5zFQ>}Bh+)o7JlqJTr8Af!Zy)~smYIHu1VF99RcDj4_9)0-XB`DD~
zC`dcWXjV4aj%6aUGV?k8{jYx^54x>vlK~$GDPSVMKSX`s`ZgfuBbU7t-R)i7fRept
z)|*-bCN5PN@(Fa%j&Hn4H_)IqZrm&~;c(gEfD>?NrxY65y?gh`hhnv1)y1-T7VCm`
zIQzAZUJ6HUAq(v^8Ff=U@rb^;yQ}#%u-L}%h{dAN_iF{yOiqr0T@vK?g~2|I|9N!b
zETFLtk++Kv08DspmzP{%tI>xeG(CBXEcSIEgie~BnZ&(aukQDOg;476e+vcqFHm5f
z-hKB+#L|bAC#y>mze<(Xw0*mKMAxo#aE_|Zx+e+R{4r_(luB9J+_9DV`upqp*0kab
z@C07q`u+ER-RvJQ>-tb;d6|s(f8XSDHVS3ANER($n3
z5Q#)q-GP_cu#|T)pz{`voA_gKxv0M?EE(nC^3R!LOWC=U11wH%V*lqVCqXGYR2K^w
l<;brV?MUT)(fR);zyRw|0z75LrJ?`;002ovPDHLkV1h<+y`caA
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/doc.png b/user_config/usr/share/solarfm/fileicons/doc.png
new file mode 100644
index 0000000000000000000000000000000000000000..8f615d16fb5abf9dd468f56c691403c97ed3e2de
GIT binary patch
literal 1536
zcmW;LdpOez7zgl;IhovY6i3Cna5@r+BE}+S3Kc51O75jR$+}D`%}(V$k)95+sU5dm
z#!$HoD|5fMxy>zRZe!aIqc-Q~e4h7xzt8*k`@FH|T^y7Yv=kr^h>|15&K1m~TP-gO
zPTvuD1q1?RpLcS%2iKE6Z&ASor*G37BI=wTwOxoAycj!qDeTSV1fECIoM-Z)Cy^ig
zmLD1#dJhyeG^DXJlk#lJl~PTi+Gd
zkw_#mr7$Pw{rjS73aGr9Qq@nXoz5+-$(Xctw2T4pLcfT~W>XHXYV-7Tu=5!Qlw0eYX1-Y>4NuW#rV
zHhc#f8yguc(N_>l&;%L~HV*pwxie*M}STPcx9q*CeT=B8nvdmnSMk#AQ)Ip;Fy9OQWF6O)>ax7f=Uh@Yv^?Y~k!w}W)ha$V{lDc0uRfIAw
zm(<;#7G{`DL2YmBwlWXh*Dn*R$zULSKAYawLFDcKB4wlGB@)97|xGMM*19^cufJFlB^=
z1cWWGR^n`D5i0XnIGK|sJLHbVqnDyChnV>X=zqcLMaZMHW@Fs_jI`qka;$-{s_Xg`
z)V^We)P_kL{m&Zl8<|{cPfrh(s*5IDPTd`hP=?1k$Rb57rG%dLz5~<7bgpT=W3F2)
zhseHh!mmBZ!QKeA{qLw}8n}|IHQ`l<%m?PM^c|Yj9YH28Z#kVR{>%*++zm0c_MkF4
zOM*pet>1rSsjgNlqf^YYRJC!dw73~&jt&v3^+wnrNmJ}CAa@V(F
z;L|C{rm1qzPkMJa-*T+Uary4jdfW9pSl;g42#l+B#z}&=?tsx$CN!gmcJ$E;!TPcG
zR}sAdkz-#=iuumY6gNi;GTQt%jQaVj%)EmAI}rz+ALOwgDHy*i`Tb16Mzwo$pb6kL
zzhW_OC@+(dt+E||)Y9s4@crv|BZFFIewqh#&6EL{MD%-QB7#0fhZ;ZG92(LxP+=W8
zi;Fy{I`Yw*=rOS^wZ-(Lj=ZKu(8o*jYK)^U=oDR59t`4I=k4ih9sC$>MpL0%npj&~XGd7Pz48ejLWW0U
zbspv)AwA!$b4X8^$zdzZc)p`s`5U)WcU;0Y{-&6H&SO{0S`u`dS_o7u-99d7C~F`S
zA@+Ku9(i_#5LhHNijUV$&Sf3POYr^`v!zZ>9tPPkoM11b<0PZV+l(^&JXa37rvSSQ
zq}d@S%~fi&aprsi(*V&ISL`WNFNuoO&7<^zByBc1fH&Ipe*7O4OO{H;J-s0?Op7O
IZGG|o0m92xYybcN
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/docx.png b/user_config/usr/share/solarfm/fileicons/docx.png
new file mode 100644
index 0000000000000000000000000000000000000000..377ecc729ed4659b00b5635b7d3b2041496f0086
GIT binary patch
literal 4144
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
uUnHL)01JBKLp!*bvAd$Oy&&0000D
literal 0
HcmV?d00001
diff --git a/user_config/usr/share/solarfm/fileicons/dwf.png b/user_config/usr/share/solarfm/fileicons/dwf.png
new file mode 100644
index 0000000000000000000000000000000000000000..349610c2718c00cc023ea2f02b30b941d7cbac9a
GIT binary patch
literal 4319
zcmV<55Fqb~P)$mdpU^7(t=|#Y2$YjpK+ygaJ`tU>p_YD6)vb9TE;kgdh$AMkj0r
zvJjHc=_K7vr@O1Wy1KfSx86H{RFgo0lEm=G+;iTkdavr;-~Ha-@B7={6G|!mKM!Ni
z3sa^{(at~r{F`js)=Q;QSqLExra+|>N=8(?K&jAA7K-J*&d$ysIF5sqk}wQuX=&lg
zE3aVAoH@9z%jxsy)8E&}<11IP=Z5bEU~q8Ix$(vuuUAS@sZ_VL<59iHb=C>E)yt!2DW04W*G=l<~6?;l&dzjM9QV&8LC
z$6_%o)5NkYG7XLN^z{KSoX!4pKB_>HPCPJqft{Ks9hcV_EkYQ-3$ep({fCX>&P$u$KQc
zX7TL$A%3_v$H)KJ%W;qNaKlSEa=~sThoK~w8+n`BfBl%ls0nhh$&3oN-OFnepefPi6-QE4(0eEl}(l>1;Hv9yfI**07-%ZU}D_^R-eSGE0
z{+C{Q>ELrQOe-DTsS{0vRxF
zn3zIv!Bmaqy<_}K&S80*$A;I|kaq*(jzfJajTN^D0>y{UI;+)o9ChX8my3f6V9l@2
zL4Dx2U`}F(7Ux^*zY9~K{r$syXWIuM0N#L}^vCI*b{FN&Cc+X}*#esMsG#xDMvd=x
zP34+YmU~X?Bv%9#hJ;~=@B8F)c^+M{g5!=qp2Zhl*uUiKUvE1o02V*o#RKaXa#j0l
z{BryhzBKu6UcdEQT(<5h4!h(T&I{lKBhRVfGL9L%mX4>JAqku2J |