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
- Add simpleish preview plugin for various file types.
- Add simpleish bulk-renamer.
-- Add a basic favorites manager plugin.
# 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 @@
+
+
+
+
+
+
+
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
-