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/__main__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
index 22b79dd..2dfcfe2 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__main__.py
@@ -22,16 +22,13 @@ if __name__ == "__main__":
""" Set process title, get arguments, and create GTK main thread. """
try:
- # import web_pdb
- # web_pdb.set_trace()
-
setproctitle('SolarFM')
faulthandler.enable() # For better debug info
+
parser = argparse.ArgumentParser()
# Add long and short arguments
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 d476cbf..e844325 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
@@ -155,7 +155,7 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
if action == "empty_trash":
self.empty_trash()
if action == "create":
- self.show_new_file_menu()
+ self.create_files()
if action in ["save_session", "save_session_as", "load_session"]:
self.save_load_session(action)
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 4a5e5f8..6b1a578 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
@@ -52,6 +52,7 @@ class Controller_Data:
self.exists_file_rename_bttn = self.builder.get_object("exists_file_rename_bttn")
self.warning_alert = self.builder.get_object("warning_alert")
+ self.new_file_menu = self.builder.get_object("new_file_menu")
self.edit_file_menu = self.builder.get_object("edit_file_menu")
self.file_exists_dialog = self.builder.get_object("file_exists_dialog")
self.exists_file_label = self.builder.get_object("exists_file_label")
@@ -110,6 +111,7 @@ class Controller_Data:
self.search_icon_grid = None
self.search_tab = None
+ self.cancel_creation = False
self.skip_edit = False
self.cancel_edit = False
self.ctrl_down = False
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 cf77ba0..331c6ef 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
@@ -108,22 +108,23 @@ class ShowHideMixin:
def hide_context_menu(self, widget=None, eve=None):
self.builder.get_object("context_menu_popup").hide()
-
def show_new_file_menu(self, widget=None, eve=None):
- context_menu_fname = self.builder.get_object("context_menu_fname")
- context_menu_fname.set_text("")
- context_menu_fname.grab_focus()
+ if widget:
+ widget.set_text("")
+ widget.grab_focus()
- new_file_menu = self.builder.get_object("new_file_menu")
- response = new_file_menu.run()
- if response == Gtk.ResponseType.APPLY:
- self.create_files()
+ response = self.new_file_menu.run()
if response == Gtk.ResponseType.CANCEL:
- self.hide_new_file_menu()
+ self.cancel_creation = True
def hide_new_file_menu(self, widget=None, eve=None):
self.builder.get_object("new_file_menu").hide()
+ def hide_new_file_menu_enter_key(self, widget=None, eve=None):
+ keyname = Gdk.keyval_name(eve.keyval).lower()
+ if keyname in ["return", "enter"]:
+ self.builder.get_object("new_file_menu").hide()
+
def show_edit_file_menu(self, widget=None, eve=None):
if widget:
widget.grab_focus()
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 71a3cc3..9072990 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
@@ -248,7 +248,13 @@ class WidgetFileActionMixin:
def create_files(self):
- fname_field = self.builder.get_object("context_menu_fname")
+ fname_field = self.builder.get_object("new_fname_field")
+ self.show_new_file_menu(fname_field)
+
+ if self.cancel_creation:
+ self.cancel_creation = False
+ return
+
file_name = fname_field.get_text().strip()
type = self.builder.get_object("context_menu_type_toggle").get_state()
@@ -264,11 +270,14 @@ class WidgetFileActionMixin:
else: # Create Folder
self.handle_files([path], "create_dir")
+ self.cancel_creation = False
self.hide_new_file_menu()
+
def move_files(self, files, target):
self.handle_files(files, "move", target)
+
# NOTE: Gtk recommends using fail flow than pre check which is more
# race condition proof. They're right; but, they can't even delete
# directories properly. So... f**k them. I'll do it my way.
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
-