diff --git a/README.md b/README.md index f405c7f..dd95b27 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ sudo apt-get install python3.8 wget python3-setproctitle python3-gi ffmpegthumbn # Images diff --git a/plugins/README.md b/plugins/README.md index 2602c94..45cabc0 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -6,7 +6,6 @@ Plugins must have a run method defined; though, you do not need to necessarily d ### Manifest Example (All are required even if empty.) ``` class Manifest: - path: str = os.path.dirname(os.path.realpath(__file__)) name: str = "Example Plugin" author: str = "John Doe" version: str = "0.0.1" @@ -14,7 +13,6 @@ class Manifest: requests: {} = { 'ui_target': "plugin_control_list", 'pass_fm_events': "true" - } ``` @@ -23,8 +21,9 @@ class Manifest: ``` requests: {} = { 'ui_target': "plugin_control_list", - 'ui_target_id': "" # Only needed if using "other" in "ui_target". See below for predefined "ui_target" options... - 'pass_fm_events': "true" # If empty or not present will be ignored. + 'ui_target_id': "", # Only needed if using "other" in "ui_target". See below for predefined "ui_target" options... + '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. @@ -58,4 +57,8 @@ def set_fm_event_system(self, fm_event_system): 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/__init__.py b/plugins/favorites/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/favorites/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/favorites/__main__.py b/plugins/favorites/__main__.py new file mode 100644 index 0000000..a576329 --- /dev/null +++ b/plugins/favorites/__main__.py @@ -0,0 +1,3 @@ +""" + Pligin Package +""" diff --git a/plugins/favorites/favorites.glade b/plugins/favorites/favorites.glade new file mode 100644 index 0000000..6c213db --- /dev/null +++ b/plugins/favorites/favorites.glade @@ -0,0 +1,156 @@ + + + + + + + + + + + + 320 + 450 + False + True + center + True + dialog + True + True + False + False + center + + + False + vertical + 2 + + + False + end + + + gtk-delete + True + True + True + True + True + + + + False + True + 0 + + + + + gtk-add + True + True + True + True + True + + + + False + True + 1 + + + + + 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 + favorites_store + False + + + + + + + + + Favorites + + + + 0 + + + + + + + + + True + True + 1 + + + + + True + True + 1 + + + + + + diff --git a/plugins/favorites/manifest.json b/plugins/favorites/manifest.json new file mode 100644 index 0000000..98fbb31 --- /dev/null +++ b/plugins/favorites/manifest.json @@ -0,0 +1,14 @@ +{ + "manifest": { + "name": "Favorites Plugin", + "author": "ITDominator", + "version": "0.0.1", + "support": "", + "requests": { + "ui_target": "plugin_control_list", + "pass_fm_events": "true", + "pass_ui_objects": ["path_entry"], + "bind_keys": [] + } + } +} diff --git a/plugins/favorites/plugin.py b/plugins/favorites/plugin.py new file mode 100644 index 0000000..c2d8f18 --- /dev/null +++ b/plugins/favorites/plugin.py @@ -0,0 +1,167 @@ +# Python imports +import os, threading, subprocess, time, inspect, json + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +# Application imports + + +# 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: + def __init__(self): + self.name = "Favorites Plugin" # 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}/favorites.glade" + self._FAVORITES_FILE = f"{self.path}/favorites.json" + + self._builder = None + self._event_system = None + self._event_sleep_time = .5 + self._event_message = None + + self._favorites_dialog = None + self._favorites_store = None + self._ui_objects = None + self._favorites = None + self._state = None + 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 set_fm_event_system(self, fm_event_system): + self._event_system = fm_event_system + + def set_ui_object_collection(self, ui_objects): + self._ui_objects = ui_objects + + def run(self): + self._module_event_observer() + + 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._favorites_dialog = self._builder.get_object("favorites_dialog") + self._favorites_store = self._builder.get_object("favorites_store") + self._current_dir_lbl = self._builder.get_object("current_dir_lbl") + + if os.path.exists(self._FAVORITES_FILE): + with open(self._FAVORITES_FILE) as f: + self._favorites = json.load(f) + for favorite in self._favorites: + self._favorites_store.append([favorite]) + else: + with open(self._FAVORITES_FILE, 'a') as f: + f.write('[]') + + + @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._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()}") + + def _add_to_favorite(self, state): + current_directory = self._state.tab.get_current_directory() + self._favorites_store.append([current_directory]) + self._favorites.append(current_directory) + self._save_favorites() + + def _remove_from_favorite(self, state): + path = self._favorites_store.get_value(self._selected, 0) + self._favorites_store.remove(self._selected) + self._favorites.remove(path) + self._save_favorites() + + def _save_favorites(self): + with open(self._FAVORITES_FILE, 'w') as outfile: + json.dump(self._favorites, outfile, separators=(',', ':'), indent=4) + + 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) + + + + def _show_favorites_menu(self, widget=None, eve=None): + self._state = None + self._get_state() + self._set_current_dir_lbl() + self._favorites_dialog.run() + + def _hide_favorites_menu(self, widget=None, eve=None): + self._favorites_dialog.hide() + + def _set_selected(self, user_data): + selected = user_data.get_selected()[1] + if selected: + self._selected = selected + + def wait_for_fm_message(self): + while not self._event_message: + pass + + def wait_for_state(self): + while not self._state: + pass + + @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() + + 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)) 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 6aa3f0a..4f96b2d 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 @@ -69,6 +69,15 @@ class ManifestProcessor: if requests["pass_fm_events"] in ["true"]: loading_data["pass_fm_events"] = True + if "pass_ui_objects" in keys: + if len(requests["pass_ui_objects"]) > 0: + loading_data["pass_ui_objects"] = [] + for ui_id in requests["pass_ui_objects"]: + try: + loading_data["pass_ui_objects"].append( self._builder.get_object(ui_id) ) + except Exception as e: + print(repr(e)) + if "bind_keys" in keys: if isinstance(requests["bind_keys"], list): loading_data["bind_keys"] = requests["bind_keys"] 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.py index b14a1a9..d793f72 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py @@ -58,7 +58,7 @@ class Plugins: self.execute_plugin(module, plugin, loading_data) except Exception as e: print(f"Malformed Plugin: Not loading -->: '{folder}' !") - traceback.print_exc() + traceback.print_exc() os.chdir(parent_path) @@ -78,14 +78,17 @@ class Plugins: 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.get_ui_element() ) loading_data["ui_target"].show_all() + if "pass_ui_objects" in keys: + plugin.reference.set_ui_object_collection( loading_data["pass_ui_objects"] ) + if "pass_fm_events" in keys: plugin.reference.set_fm_event_system(event_system) if "bind_keys" in keys: - self._keybindings.append_bindings(loading_data["bind_keys"]) + self._keybindings.append_bindings( loading_data["bind_keys"] ) plugin.reference.run() self._plugin_collection.append(plugin) diff --git a/user_config/usr/share/solarfm/Main_Window.glade b/user_config/usr/share/solarfm/Main_Window.glade index 56fdb37..c7d8965 100644 --- a/user_config/usr/share/solarfm/Main_Window.glade +++ b/user_config/usr/share/solarfm/Main_Window.glade @@ -757,7 +757,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe gtk-apply 3 - + 800 600 False @@ -775,7 +775,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe vertical top - + True False @@ -1122,7 +1122,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe - + True False @@ -1470,13 +1470,13 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe True center-always True - normal + dialog True True False False center - Main_Window + main_window False @@ -1628,14 +1628,14 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe True center-always True - normal + dialog True True True False False center - Main_Window + main_window False @@ -1958,7 +1958,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe 320 False True - app_menu_bar + main_menu_bar bottom @@ -2015,14 +2015,13 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe True center-always True - normal + dialog True True False False center - Main_Window - + main_window False @@ -2081,7 +2080,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe False vertical - + 500 26 True @@ -2095,6 +2094,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe False False New File/Dir Name... + False @@ -2219,11 +2219,13 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe - + False plugins_buttoin + + - + True False vertical @@ -2329,7 +2331,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe False user-trash - + False False mouse @@ -2367,7 +2369,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe False vertical - + True False vertical diff --git a/user_config/usr/share/solarfm/key-bindings.json b/user_config/usr/share/solarfm/key-bindings.json index a8c6506..9c3c264 100644 --- a/user_config/usr/share/solarfm/key-bindings.json +++ b/user_config/usr/share/solarfm/key-bindings.json @@ -21,6 +21,6 @@ "copy_files" : "c", "cut_files" : "x", "paste_files" : "v", - "show_new_file_menu" : "n" + "create_files" : "n" } } diff --git a/user_config/usr/share/solarfm/settings.json b/user_config/usr/share/solarfm/settings.json index c9f246f..9b6850b 100644 --- a/user_config/usr/share/solarfm/settings.json +++ b/user_config/usr/share/solarfm/settings.json @@ -1,5 +1,5 @@ { - "settings": { + "config": { "base_of_home": "", "hide_hidden_files": "true", "thumbnailer_path": "ffmpegthumbnailer", @@ -7,14 +7,29 @@ "lock_folder": "false", "locked_folders": "venv::::flasks", "mplayer_options": "-quiet -really-quiet -xy 1600 -geometry 50%:50%", - "music_app": "/opt/deadbeef/bin/deadbeef", + "music_app": "deadbeef", "media_app": "mpv", - "image_app": "mirage", + "image_app": "mirage2", "office_app": "libreoffice", "pdf_app": "evince", - "text_app": "leafpad", - "file_manager_app": "solarfm", + "code_app": "atom", + "text_app": "mousepad", "terminal_app": "terminator", + "container_icon_wh": [128, 128], + "video_icon_wh": [128, 64], + "sys_icon_wh": [56, 56], + "file_manager_app": "solarfm", + "steam_cdn_url": "https://steamcdn-a.akamaihd.net/steam/apps/", "remux_folder_max_disk_usage": "8589934592" - } + }, + "filters": { + "code": [".cpp", ".css", ".c", ".go", ".html", ".htm", ".java", ".js", ".json", ".lua", ".md", ".py", ".rs"], + "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"], + "text": [".txt", ".text", ".sh", ".cfg", ".conf", ".log"], + "music": [".psf", ".mp3", ".ogg", ".flac", ".m4a"], + "pdf": [".pdf"] + + } }