develop #11
| @@ -9,7 +9,7 @@ Copy the share/solarfm folder to your user .config/ directory too. | ||||
|  | ||||
| <h6>Install Setup</h6> | ||||
| ``` | ||||
| sudo apt-get install python3.8 wget python3-setproctitle python3-gi ffmpegthumbnailer steamcmd | ||||
| sudo apt-get install python3.8 python3-setproctitle python3-gi wget ffmpegthumbnailer steamcmd | ||||
| ``` | ||||
|  | ||||
| # Known Issues | ||||
|   | ||||
| @@ -31,7 +31,7 @@ class Plugin(PluginBase): | ||||
|                                         #       where self.name should not be needed for message comms | ||||
|         self._GLADE_FILE = f"{self.path}/translate.glade" | ||||
|  | ||||
|         self._link       = "https://duckduckgo.com/translation.js?vqd=4-79469202070473384659389009732578528471&query=translate&to=en" | ||||
|         self._link       = "https://duckduckgo.com/translation.js?" | ||||
|         self._headers    = { | ||||
|             'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:106.0) Gecko/20100101 Firefox/106.0', | ||||
|             'Accept': '*/*', | ||||
| @@ -50,8 +50,19 @@ class Plugin(PluginBase): | ||||
|             'Cache-Control': 'no-cache' | ||||
|         } | ||||
|  | ||||
|         self.vqd_link    = "https://duckduckgo.com/" | ||||
|         self.vqd_data    = {"q": "translate", "ia":"web"} | ||||
|         self.vqd_headers = { | ||||
|             'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:106.0) Gecko/20100101 Firefox/106.0', | ||||
|             "Referer": "https://duckduckgo.com/" | ||||
|         } | ||||
|  | ||||
|         self._queue_translate = False | ||||
|         self._watcher_running = False | ||||
|         self._vqd_attrib      = None | ||||
|         self.from_trans       = "jp" | ||||
|         self.to_trans         = "en" | ||||
|         self.translate_tries  = 0 | ||||
|  | ||||
|  | ||||
|     def generate_reference_ui_element(self): | ||||
| @@ -71,6 +82,9 @@ class Plugin(PluginBase): | ||||
|         self._translate_to_buffer   = self._builder.get_object("translate_to_buffer") | ||||
|         self._detected_language_lbl = self._builder.get_object("detected_language_lbl") | ||||
|  | ||||
|         self._detected_language_lbl.set_label(f"Selected Language: {self.from_trans}") | ||||
|         self.get_vqd() | ||||
|  | ||||
|  | ||||
|     @threaded | ||||
|     def _show_translate_page(self, widget=None, eve=None): | ||||
| @@ -116,20 +130,43 @@ class Plugin(PluginBase): | ||||
|             break | ||||
|  | ||||
|     def _translate(self): | ||||
|         start_itr, end_itr =  self._translate_from_buffer.get_bounds() | ||||
|         from_translate     = self._translate_from_buffer.get_text(start_itr, end_itr, True).encode('utf-8') | ||||
|         start_itr, end_itr   =  self._translate_from_buffer.get_bounds() | ||||
|         from_translate       = self._translate_from_buffer.get_text(start_itr, end_itr, True).encode('utf-8') | ||||
|  | ||||
|         if from_translate in ("", None) or self._queue_translate: | ||||
|             return | ||||
|  | ||||
|         response = requests.post(self._link, headers=self._headers, data=from_translate) | ||||
|         self.translate_tries += 1 | ||||
|         tlink    = f"https://duckduckgo.com/translation.js?vqd={self._vqd_attrib}&query=translate&from={self.from_trans}&to={self.to_trans}" | ||||
|         response = requests.post(self.tlink, headers=self._headers, data=from_translate) | ||||
|  | ||||
|         if response.status_code == 200: | ||||
|             data = response.json() | ||||
|             self._translate_to_buffer.set_text(data["translated"]) | ||||
|  | ||||
|             self.translate_tries = 0 | ||||
|             if "detected_language" in data.keys(): | ||||
|                 self._detected_language_lbl.set_label(f"Detected Language: {data['detected_language']}") | ||||
|             else: | ||||
|                 self._detected_language_lbl.set_label(f"Selected Language: {self.from_trans}") | ||||
|         elif response.status_code >= 400 or response.status_code < 500: | ||||
|             self.get_vqd() | ||||
|             if not self.translate_tries > 4: | ||||
|                 self._translate() | ||||
|         else: | ||||
|             msg = f"Could not translate... Response Code: {response.status_code}" | ||||
|             self._translate_to_buffer.set_text(msg) | ||||
|             self._detected_language_lbl.set_label(f"Detected Language:") | ||||
|  | ||||
|  | ||||
|     def get_vqd(self): | ||||
|         response = requests.post(self.vqd_link, headers=self.vqd_headers, data=self.vqd_data, timeout=10) | ||||
|         if response.status_code == 200: | ||||
|             data             = response.content | ||||
|             vqd_start_index  = data.index(b"vqd='") + 5 | ||||
|             vqd_end_index    = data.index(b"'", vqd_start_index) | ||||
|             self._vqd_attrib = data[vqd_start_index:vqd_end_index].decode("utf-8") | ||||
|  | ||||
|             print(f"Translation VQD: {self._vqd_attrib}") | ||||
|         else: | ||||
|             msg = f"Could not get VQS attribute... Response Code: {response.status_code}" | ||||
|             self._translate_to_buffer.set_text(msg) | ||||
|   | ||||
| @@ -58,7 +58,7 @@ 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) | ||||
|         pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(f"/usr/share/solarfm/icons/video.png", 16, 16, True) | ||||
|         icon   = Gtk.Image.new_from_pixbuf(pixbuf) | ||||
|         item   = Gtk.ImageMenuItem(self.name) | ||||
|  | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| @@ -1,181 +0,0 @@ | ||||
| # Python imports | ||||
| import os | ||||
| import json | ||||
| from os import path | ||||
|  | ||||
| # Gtk imports | ||||
| import gi, cairo | ||||
| gi.require_version('Gtk', '3.0') | ||||
| gi.require_version('Gdk', '3.0') | ||||
| from gi.repository import Gtk | ||||
| from gi.repository import GLib | ||||
| from gi.repository import Gdk | ||||
|  | ||||
| # Application imports | ||||
| from .logger import Logger | ||||
| from .keybindings import Keybindings | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class Settings: | ||||
|     def __init__(self): | ||||
|         self._SCRIPT_PTH      = os.path.dirname(os.path.realpath(__file__)) | ||||
|         self._USER_HOME       = path.expanduser('~') | ||||
|         self._CONFIG_PATH     = f"{self._USER_HOME}/.config/{app_name.lower()}" | ||||
|         self._UI_WIDEGTS_PATH = f"{self._CONFIG_PATH}/ui_widgets" | ||||
|         self._PLUGINS_PATH    = f"{self._CONFIG_PATH}/plugins" | ||||
|         self._USR_SOLARFM     = f"/usr/share/{app_name.lower()}" | ||||
|  | ||||
|         self._CSS_FILE        = f"{self._CONFIG_PATH}/stylesheet.css" | ||||
|         self._GLADE_FILE      = f"{self._CONFIG_PATH}/Main_Window.glade" | ||||
|         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() | ||||
|         self._TRASH_FILES_PATH = f"{GLib.get_user_data_dir()}/Trash/files" | ||||
|         self._TRASH_INFO_PATH  = f"{GLib.get_user_data_dir()}/Trash/info" | ||||
|  | ||||
|         if not os.path.exists(self._CONFIG_PATH): | ||||
|             os.mkdir(self._CONFIG_PATH) | ||||
|         if not os.path.exists(self._PLUGINS_PATH): | ||||
|             os.mkdir(self._PLUGINS_PATH) | ||||
|  | ||||
|         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): | ||||
|             self._CSS_FILE      = f"{self._USR_SOLARFM}/stylesheet.css" | ||||
|         if not os.path.exists(self._WINDOW_ICON): | ||||
|             self._WINDOW_ICON   = f"{self._USR_SOLARFM}/icons/{app_name.lower()}.png" | ||||
|         if not os.path.exists(self._DEFAULT_ICONS): | ||||
|             self._DEFAULT_ICONS = f"{self._USR_SOLARFM}/icons" | ||||
|  | ||||
|         self._success_color = "#88cc27" | ||||
|         self._warning_color = "#ffa800" | ||||
|         self._error_color   = "#ff0000" | ||||
|  | ||||
|         self._keybindings = Keybindings() | ||||
|         with open(self._KEY_BINDINGS) as file: | ||||
|             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() | ||||
|         self._builder.add_from_file(self._GLADE_FILE) | ||||
|  | ||||
|         self._trace_debug   = False | ||||
|         self._debug         = False | ||||
|         self._dirty_start   = False | ||||
|  | ||||
|  | ||||
|     def do_dirty_start_check(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(f"{app_name} is starting dirty...") | ||||
|             self._dirty_start = True | ||||
|             self._write_new_pid() | ||||
|             return | ||||
|  | ||||
|         print("PID is alive... Let downstream errors (sans debug args) handle app closure propigation.") | ||||
|  | ||||
|     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: | ||||
|         # Get window and connect signals | ||||
|         self._main_window = self._builder.get_object("main_window") | ||||
|         self._set_window_data() | ||||
|  | ||||
|     def _set_window_data(self) -> None: | ||||
|         self._main_window.set_icon_from_file(self._WINDOW_ICON) | ||||
|         screen = self._main_window.get_screen() | ||||
|         visual = screen.get_rgba_visual() | ||||
|  | ||||
|         if visual != None and screen.is_composited(): | ||||
|             self._main_window.set_visual(visual) | ||||
|             self._main_window.set_app_paintable(True) | ||||
|             self._main_window.connect("draw", self._area_draw) | ||||
|  | ||||
|         # bind css file | ||||
|         cssProvider  = Gtk.CssProvider() | ||||
|         cssProvider.load_from_path(self._CSS_FILE) | ||||
|         screen       = Gdk.Screen.get_default() | ||||
|         styleContext = Gtk.StyleContext() | ||||
|         styleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER) | ||||
|  | ||||
|     def _area_draw(self, widget: Gtk.ApplicationWindow, cr: cairo.Context) -> None: | ||||
|         cr.set_source_rgba(0, 0, 0, 0.54) | ||||
|         cr.set_operator(cairo.OPERATOR_SOURCE) | ||||
|         cr.paint() | ||||
|         cr.set_operator(cairo.OPERATOR_OVER) | ||||
|  | ||||
|     def get_monitor_data(self) -> list: | ||||
|         screen = self._builder.get_object("main_window").get_screen() | ||||
|         monitors = [] | ||||
|         for m in range(screen.get_n_monitors()): | ||||
|             monitors.append(screen.get_monitor_geometry(m)) | ||||
|             print("{}x{}+{}+{}".format(monitor.width, monitor.height, monitor.x, monitor.y)) | ||||
|  | ||||
|         return monitors | ||||
|  | ||||
|  | ||||
|     def get_main_window(self)       -> Gtk.ApplicationWindow: return self._main_window | ||||
|     def get_builder(self)           -> Gtk.Builder:           return self._builder | ||||
|     def get_context_menu_data(self) -> str:                   return self._context_menu_data | ||||
|     def get_ui_widgets_path(self)   -> str:                   return self._UI_WIDEGTS_PATH | ||||
|     def get_trash_files_path(self)  -> str:                   return self._TRASH_FILES_PATH | ||||
|     def get_trash_info_path(self)   -> str:                   return self._TRASH_INFO_PATH | ||||
|     def get_plugins_path(self)      -> str:                   return self._PLUGINS_PATH | ||||
|  | ||||
|     def get_logger(self)        -> Logger:      return self._logger | ||||
|     def get_keybindings(self)   -> Keybindings: return self._keybindings | ||||
|     def get_icon_theme(self)    -> str:         return self._ICON_THEME | ||||
|  | ||||
|     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)    -> bool:        return self._trace_debug | ||||
|     def is_debug(self)          -> bool:        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: bool): | ||||
|         self._trace_debug = trace_debug | ||||
|  | ||||
|     def set_debug(self, debug: bool): | ||||
|         self._debug = debug | ||||
| @@ -1,10 +0,0 @@ | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| using namespace std; | ||||
|  | ||||
| int main() { | ||||
|     chdir("/opt/"); | ||||
|     system("python solarfm.zip"); | ||||
| return 0; | ||||
| } | ||||
| @@ -1,6 +0,0 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| function main() { | ||||
|     gcc -no-pie -s SolarFM_exec_bin.cpp -o solarfm | ||||
| } | ||||
| main; | ||||
| @@ -1,12 +1,15 @@ | ||||
| # Python imports | ||||
| import builtins | ||||
| import threading | ||||
| import sys | ||||
| 
 | ||||
| # Lib imports | ||||
| 
 | ||||
| # Application imports | ||||
| from utils.event_system import EventSystem | ||||
| from utils.endpoint_registry import EndpointRegistry | ||||
| from utils.keybindings import Keybindings | ||||
| from utils.logger import Logger | ||||
| from utils.settings import Settings | ||||
| 
 | ||||
| 
 | ||||
| @@ -35,12 +38,26 @@ def sizeof_fmt_def(num, suffix="B"): | ||||
| # 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.keybindings       = Keybindings() | ||||
| builtins.event_system      = EventSystem() | ||||
| builtins.endpoint_registry = EndpointRegistry() | ||||
| builtins.settings          = Settings() | ||||
| builtins.logger            = Logger(settings.get_home_config_path(), \ | ||||
|                                     _ch_log_lvl=settings.get_ch_log_lvl(), \ | ||||
|                                     _fh_log_lvl=settings.get_fh_log_lvl()).get_logger() | ||||
| 
 | ||||
| builtins.threaded          = threaded_wrapper | ||||
| builtins.daemon_threaded   = daemon_threaded_wrapper | ||||
| builtins.sizeof_fmt        = sizeof_fmt_def | ||||
| builtins.event_sleep_time  = 0.05 | ||||
| 
 | ||||
| 
 | ||||
| def custom_except_hook(exc_type, exc_value, exc_traceback): | ||||
|     if issubclass(exc_type, KeyboardInterrupt): | ||||
|         sys.__excepthook__(exc_type, exc_value, exc_traceback) | ||||
|         return | ||||
| 
 | ||||
|     logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)) | ||||
| 
 | ||||
| 
 | ||||
| sys.excepthook = custom_except_hook | ||||
| @@ -7,14 +7,12 @@ import inspect | ||||
| # Application imports | ||||
| 
 | ||||
| from utils.ipc_server import IPCServer | ||||
| from core.controller import Controller | ||||
| from core.window import Window | ||||
| 
 | ||||
| 
 | ||||
| class AppLaunchException(Exception): | ||||
|     ... | ||||
| 
 | ||||
| class ControllerStartException(Exception): | ||||
|     ... | ||||
| 
 | ||||
| 
 | ||||
| class Application(IPCServer): | ||||
| @@ -38,25 +36,4 @@ class Application(IPCServer): | ||||
| 
 | ||||
|                 raise AppLaunchException(f"{app_name} IPC Server Exists: Will send path(s) to it and close...") | ||||
| 
 | ||||
| 
 | ||||
|         settings.create_window() | ||||
|         self._load_controller_and_builder() | ||||
| 
 | ||||
|     def _load_controller_and_builder(self): | ||||
|         controller = Controller(self.args, self.unknownargs) | ||||
|         if not controller: | ||||
|             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. | ||||
|         classes  = [controller] | ||||
|         handlers = {} | ||||
|         for c in classes: | ||||
|             methods = None | ||||
|             try: | ||||
|                 methods = inspect.getmembers(c, predicate=inspect.ismethod) | ||||
|                 handlers.update(methods) | ||||
|             except AppLaunchException as e: | ||||
|                 print(repr(e)) | ||||
| 
 | ||||
|         settings.get_builder().connect_signals(handlers) | ||||
|         Window(args, unknownargs) | ||||
| @@ -37,12 +37,13 @@ class Controller(UIMixin, SignalsMixins, Controller_Data): | ||||
|     """ Controller coordinates the mixins and is somewhat the root hub of it all. """ | ||||
| 
 | ||||
|     def __init__(self, args, unknownargs): | ||||
|         self.setup_controller_data() | ||||
| 
 | ||||
|         self._setup_styling() | ||||
|         self._setup_signals() | ||||
|         self._subscribe_to_events() | ||||
|         self._load_widgets() | ||||
| 
 | ||||
|         self.setup_controller_data() | ||||
|         self.generate_windows(self.fm_controller_data) | ||||
| 
 | ||||
|         if args.no_plugins == "false": | ||||
| @@ -69,6 +70,20 @@ class Controller(UIMixin, SignalsMixins, Controller_Data): | ||||
|         event_system.subscribe("do_action_from_menu_controls", self.do_action_from_menu_controls) | ||||
|         event_system.subscribe("set_clipboard_data", self.set_clipboard_data) | ||||
| 
 | ||||
|     def _load_glade_file(self): | ||||
|         self.builder = Gtk.Builder() | ||||
|         self.builder.add_from_file(settings.get_glade_file()) | ||||
|         self.builder.expose_object("main_window", self.window) | ||||
| 
 | ||||
|         self.core_widget = self.builder.get_object("core_widget") | ||||
| 
 | ||||
|         settings.set_builder(self.builder) | ||||
|         settings.register_signals_to_builder([self,]) | ||||
| 
 | ||||
|     def get_core_widget(self): | ||||
|         return self.core_widget | ||||
| 
 | ||||
| 
 | ||||
|     # NOTE: Really we will move these to the UI/(New) Window 'base' controller | ||||
|     #       after we're done cleaning and refactoring to use fewer mixins. | ||||
|     def _load_widgets(self): | ||||
| @@ -87,14 +102,6 @@ class Controller(UIMixin, SignalsMixins, Controller_Data): | ||||
|         self.message_dialog = MessageWidget() | ||||
| 
 | ||||
| 
 | ||||
|     def tear_down(self, widget=None, eve=None): | ||||
|         if not settings.is_trace_debug(): | ||||
|             self.fm_controller.save_state() | ||||
| 
 | ||||
|         settings.clear_pid() | ||||
|         time.sleep(event_sleep_time) | ||||
|         Gtk.main_quit() | ||||
| 
 | ||||
|     def reload_plugins(self, widget=None, eve=None): | ||||
|         self.plugins.reload_plugins() | ||||
| 
 | ||||
| @@ -143,6 +150,8 @@ class Controller(UIMixin, SignalsMixins, Controller_Data): | ||||
|             event_system.emit("show_plugins_popup") | ||||
|         if action == "messages_popup": | ||||
|             event_system.emit("show_messages_popup") | ||||
|         if action == "tear_down": | ||||
|             event_system.emit("tear_down") | ||||
| 
 | ||||
| 
 | ||||
|     @endpoint_registry.register(rule="go_home") | ||||
| @@ -1,7 +1,6 @@ | ||||
| # Python imports | ||||
| import sys | ||||
| import os | ||||
| import signal | ||||
| import subprocess | ||||
| from dataclasses import dataclass | ||||
| 
 | ||||
| @@ -9,12 +8,13 @@ from dataclasses import dataclass | ||||
| import gi | ||||
| gi.require_version('Gtk', '3.0') | ||||
| from gi.repository import Gtk | ||||
| from gi.repository import GLib | ||||
| 
 | ||||
| # Application imports | ||||
| from shellfm.windows.controller import WindowController | ||||
| from plugins.plugins_controller import PluginsController | ||||
| 
 | ||||
| # from factories.split_view_widget import SplitViewWidget | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @@ -40,14 +40,21 @@ class Controller_Data: | ||||
|     __slots__ = "settings", "builder", "logger", "keybindings", "trashman", "fm_controller", "window", "window1", "window2", "window3", "window4" | ||||
| 
 | ||||
|     def setup_controller_data(self) -> None: | ||||
|         self.builder            = settings.get_builder() | ||||
|         self.keybindings        = settings.get_keybindings() | ||||
|         self.window        = settings.get_main_window() | ||||
|         self.builder       = None | ||||
|         self.core_widget   = None | ||||
| 
 | ||||
|         self._load_glade_file() | ||||
|         self.fm_controller      = WindowController() | ||||
|         self.plugins            = PluginsController() | ||||
|         self.fm_controller_data = self.fm_controller.get_state_from_file() | ||||
| 
 | ||||
|         self.window             = settings.get_main_window() | ||||
| 
 | ||||
|         # self.pane_master        = self.builder.get_object("pane_master") | ||||
|         # self.pane_master.pack1(SplitViewWidget(), True, True) | ||||
|         # self.pane_master.pack2(SplitViewWidget(), True, True) | ||||
| 
 | ||||
| 
 | ||||
|         self.window1            = self.builder.get_object("window_1") | ||||
|         self.window2            = self.builder.get_object("window_2") | ||||
|         self.window3            = self.builder.get_object("window_3") | ||||
| @@ -77,14 +84,19 @@ class Controller_Data: | ||||
|         self.alt_down           = False | ||||
| 
 | ||||
|         # sys.excepthook = self.custom_except_hook | ||||
|         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 custom_except_hook(self, exc_type, exc_value, exc_traceback): | ||||
|         if issubclass(exc_type, KeyboardInterrupt): | ||||
|             sys.__excepthook__(exc_type, exc_value, exc_traceback) | ||||
|             return | ||||
| 
 | ||||
|         logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     def get_current_state(self) -> State: | ||||
|         ''' | ||||
|         Returns the state info most useful for any given context and action intent. | ||||
| @@ -88,7 +88,13 @@ class FileSystemActions(HandlerMixin, CRUDMixin): | ||||
| 
 | ||||
|     def open_with_files(self, app_info): | ||||
|         state = event_system.emit_and_await("get_current_state") | ||||
|         state.tab.app_chooser_exec(app_info, state.uris_raw) | ||||
|         uris  = state.uris_raw | ||||
| 
 | ||||
|         if not state.uris_raw: | ||||
|             uris = [f"file://{state.tab.get_current_directory()}"] | ||||
| 
 | ||||
|         state.tab.app_chooser_exec(app_info, uris) | ||||
| 
 | ||||
| 
 | ||||
|     def execute_files(self, in_terminal=False): | ||||
|         state       = event_system.emit_and_await("get_current_state") | ||||
| @@ -70,9 +70,6 @@ class HandlerMixin: | ||||
|                         type      = _file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE) | ||||
| 
 | ||||
|                         if type == Gio.FileType.DIRECTORY: | ||||
|                             # wid, tid = self.fm_controller.get_active_wid_and_tid() | ||||
|                             # tab      = self.get_fm_window(wid).get_tab_by_id(tid) | ||||
|                             # tab.delete_file( _file.get_path() ) | ||||
|                             state = event_system.emit_and_await("get_current_state") | ||||
|                             state.tab.delete_file( _file.get_path() ) | ||||
|                         else: | ||||
| @@ -98,8 +95,6 @@ class HandlerMixin: | ||||
| 
 | ||||
|                 type = file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE) | ||||
|                 if type == Gio.FileType.DIRECTORY: | ||||
|                     # wid, tid  = self.fm_controller.get_active_wid_and_tid() | ||||
|                     # tab       = self.get_fm_window(wid).get_tab_by_id(tid) | ||||
|                     state     = event_system.emit_and_await("get_current_state") | ||||
|                     tab       = state.tab | ||||
|                     fPath     = file.get_path() | ||||
| @@ -46,7 +46,7 @@ class KeyboardSignalsMixin: | ||||
|             if "alt" in keyname: | ||||
|                 self.alt_down     = False | ||||
| 
 | ||||
|         mapping = self.keybindings.lookup(event) | ||||
|         mapping = keybindings.lookup(event) | ||||
|         if mapping: | ||||
|             # See if in filemanager scope | ||||
|             try: | ||||
| @@ -62,8 +62,7 @@ class KeyboardSignalsMixin: | ||||
| 
 | ||||
|                 self.handle_plugin_key_event(sender, eve_type) | ||||
|         else: | ||||
|             if settings.is_debug(): | ||||
|                 print(f"on_global_key_release_controller > key > {keyname}") | ||||
|             logger.debug(f"on_global_key_release_controller > key > {keyname}") | ||||
| 
 | ||||
|             if self.ctrl_down: | ||||
|                 if keyname in ["1", "kp_1", "2", "kp_2", "3", "kp_3", "4", "kp_4"]: | ||||
| @@ -23,6 +23,12 @@ class WindowMixin(TabMixin): | ||||
|     """docstring for WindowMixin""" | ||||
| 
 | ||||
|     def generate_windows(self, session_json = None): | ||||
|         # for session in session_json: | ||||
|         #     nickname = session["window"]["Nickname"] | ||||
|         #     tabs     = session["window"]["tabs"] | ||||
|         #     isHidden = True if session["window"]["isHidden"] == "True" else False | ||||
|         #     event_system.emit("load-window-state", (nickname, tabs)) | ||||
| 
 | ||||
|         if session_json: | ||||
|             for j, value in enumerate(session_json): | ||||
|                 i = j + 1 | ||||
| @@ -54,8 +60,8 @@ 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 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)) | ||||
|                 logger.info("\n:  The saved session might be missing window data!  :\nLocation: ~/.config/solarfm/session.json\nFix: Back it up and delete it to reset.\n") | ||||
|                 logger.debug(repr(e)) | ||||
|         else: | ||||
|             for j in range(0, 4): | ||||
|                 i = j + 1 | ||||
| @@ -115,9 +121,7 @@ class WindowMixin(TabMixin): | ||||
|                     file_size = file_info.get_size() | ||||
|                     combined_size += file_size | ||||
|                 except WindowException as e: | ||||
|                     if settings.is_debug(): | ||||
|                         print(repr(e)) | ||||
| 
 | ||||
|                     logger.debug(repr(e)) | ||||
| 
 | ||||
|             formatted_size = sizeof_fmt(combined_size) | ||||
|             if tab.is_hiding_hidden(): | ||||
| @@ -204,7 +208,7 @@ class WindowMixin(TabMixin): | ||||
|                 event_system.emit("show_context_menu") | ||||
| 
 | ||||
|         except WindowException as e: | ||||
|             print(repr(e)) | ||||
|             logger.info(repr(e)) | ||||
|             self.display_message(settings.get_error_color(), f"{repr(e)}") | ||||
| 
 | ||||
|     def grid_icon_double_click(self, icons_grid, item, data=None): | ||||
| @@ -41,7 +41,7 @@ class ContextMenuWidget(Gtk.Menu): | ||||
|                 methods = inspect.getmembers(c, predicate=inspect.ismethod) | ||||
|                 handlers.update(methods) | ||||
|             except Exception as e: | ||||
|                 print(repr(e)) | ||||
|                 logger.debug(repr(e)) | ||||
| 
 | ||||
|         self._builder.connect_signals(handlers) | ||||
| 
 | ||||
| @@ -45,7 +45,7 @@ class AboutWidget: | ||||
|                 methods = inspect.getmembers(c, predicate=inspect.ismethod) | ||||
|                 handlers.update(methods) | ||||
|             except Exception as e: | ||||
|                 print(repr(e)) | ||||
|                 logger.debug(repr(e)) | ||||
| 
 | ||||
|         self._builder.connect_signals(handlers) | ||||
| 
 | ||||
| @@ -59,7 +59,7 @@ class FileExistsWidget: | ||||
|                 methods = inspect.getmembers(c, predicate=inspect.ismethod) | ||||
|                 handlers.update(methods) | ||||
|             except Exception as e: | ||||
|                 print(repr(e)) | ||||
|                 logger.debug(repr(e)) | ||||
| 
 | ||||
|         self._builder.connect_signals(handlers) | ||||
| 
 | ||||
| @@ -51,7 +51,7 @@ class RenameWidget: | ||||
|                 methods = inspect.getmembers(c, predicate=inspect.ismethod) | ||||
|                 handlers.update(methods) | ||||
|             except Exception as e: | ||||
|                 print(repr(e)) | ||||
|                 logger.debug(repr(e)) | ||||
| 
 | ||||
|         self._builder.connect_signals(handlers) | ||||
| 
 | ||||
| @@ -0,0 +1,3 @@ | ||||
| """ | ||||
|     FileView module | ||||
| """ | ||||
| @@ -0,0 +1,71 @@ | ||||
| # Python imports | ||||
|  | ||||
| # Lib imports | ||||
| import gi | ||||
| gi.require_version('Gtk', '3.0') | ||||
| from gi.repository import Gtk | ||||
|  | ||||
| # Application imports | ||||
| from shellfm.windows.tabs.tab import Tab | ||||
| from .icon_view import IconView | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class FileView(Gtk.ScrolledWindow): | ||||
|     """docstring for FileView.""" | ||||
|  | ||||
|     def __init__(self): | ||||
|         super(FileView, self).__init__() | ||||
|         self.tab_state  = Tab() | ||||
|         self.icon_view  = IconView(self.tab_state) | ||||
|         self.tab_widget = self.create_tab_widget() | ||||
|  | ||||
|         self._setup_styling() | ||||
|         self._setup_signals() | ||||
|  | ||||
|         self.add(self.icon_view) | ||||
|         self.show_all() | ||||
|  | ||||
|  | ||||
|     def _setup_styling(self): | ||||
|         self.set_hexpand(True) | ||||
|         self.set_vexpand(True) | ||||
|  | ||||
|     def _setup_signals(self): | ||||
|         # self.connect("update-tab-title", self._update_tab_title) | ||||
|         ... | ||||
|  | ||||
|     def _update_tab_title(self, widget=None, eve=None): | ||||
|         label = self.tab_widget.get_children()[0] | ||||
|         label.set_label(f"{self.tab_state.get_end_of_path()}") | ||||
|         label.set_width_chars( len(self.tab_state.get_end_of_path()) ) | ||||
|  | ||||
|     def set_path(self, path): | ||||
|         if path: | ||||
|             self.tab_state.set_path(path) | ||||
|             self.icon_view.load_store() | ||||
|             self._update_tab_title() | ||||
|  | ||||
|     def create_tab_widget(self): | ||||
|         button_box = Gtk.ButtonBox() | ||||
|         label      = Gtk.Label() | ||||
|         close      = Gtk.Button() | ||||
|         icon       = Gtk.Image(stock=Gtk.STOCK_CLOSE) | ||||
|  | ||||
|         label.set_label(f"{self.tab_state.get_end_of_path()}") | ||||
|         label.set_width_chars(len(self.tab_state.get_end_of_path())) | ||||
|         label.set_xalign(0.0) | ||||
|  | ||||
|         close.add(icon) | ||||
|         button_box.add(label) | ||||
|         button_box.add(close) | ||||
|  | ||||
|         close.connect("released", self.close_tab) | ||||
|  | ||||
|         button_box.show_all() | ||||
|         return button_box | ||||
|  | ||||
|  | ||||
|     def close_tab(self, widget, eve=None): | ||||
|         self.get_parent().emit('close-view', (self,)) | ||||
| @@ -0,0 +1,149 @@ | ||||
| # Python imports | ||||
|  | ||||
| # Lib imports | ||||
| import gi | ||||
| gi.require_version('Gtk', '3.0') | ||||
| from gi.repository import Gtk | ||||
|  | ||||
| # Application imports | ||||
| from .file_view import FileView | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class FilesWidget(Gtk.Notebook): | ||||
|     """docstring for FilesWidget.""" | ||||
|  | ||||
|     ccount = 0 | ||||
|  | ||||
|     def __new__(cls, *args, **kwargs): | ||||
|         obj        = super(FilesWidget, cls).__new__(cls) | ||||
|         cls.ccount += 1 | ||||
|  | ||||
|         return obj | ||||
|  | ||||
|     def __init__(self): | ||||
|         super(FilesWidget, self).__init__() | ||||
|  | ||||
|         self.set_group_name("file_window") | ||||
|  | ||||
|         self.NAME = f"window_{self.ccount}" | ||||
|         builder   = settings.get_builder() | ||||
|         builder.expose_object(self.NAME, self) | ||||
|  | ||||
|         self._add_action_widgets() | ||||
|         self._setup_styling() | ||||
|         self._setup_signals() | ||||
|  | ||||
|         self.show_all() | ||||
|  | ||||
|  | ||||
|     def _setup_styling(self): | ||||
|         self.set_scrollable(True) | ||||
|         self.set_show_tabs(True) | ||||
|         self.set_show_border(False) | ||||
|         self.set_hexpand(True) | ||||
|  | ||||
|         self.set_margin_top(5) | ||||
|         self.set_margin_bottom(5) | ||||
|         self.set_margin_start(5) | ||||
|         self.set_margin_end(5) | ||||
|  | ||||
|     def _setup_signals(self): | ||||
|         # self.connect("close-view", self.close_view) | ||||
|         event_system.subscribe("load-window-state", self.load_window_state) | ||||
|  | ||||
|     def _add_action_widgets(self): | ||||
|         start_box = Gtk.Box() | ||||
|         end_box   = Gtk.Box() | ||||
|  | ||||
|         search = Gtk.SearchEntry() | ||||
|         search.set_placeholder_text("Search...") | ||||
|         search.connect("changed", self._do_query) | ||||
|  | ||||
|         home_btn = Gtk.Button() | ||||
|         home_btn.set_image( Gtk.Image.new_from_icon_name("gtk-home", 4) ) | ||||
|         home_btn.set_always_show_image(True) | ||||
|         home_btn.connect("released", self.do_action, ("go_home_dir")) | ||||
|  | ||||
|         up_btn = Gtk.Button() | ||||
|         up_btn.set_image( Gtk.Image.new_from_icon_name("up", 4) ) | ||||
|         up_btn.set_always_show_image(True) | ||||
|         up_btn.connect("released", self.do_action, ("go_up_dir")) | ||||
|  | ||||
|         refresh_btn = Gtk.Button() | ||||
|         refresh_btn.set_image( Gtk.Image.new_from_icon_name("gtk-refresh", 4) ) | ||||
|         refresh_btn.set_always_show_image(True) | ||||
|         refresh_btn.connect("released", self.do_action, ("refresh_dir")) | ||||
|  | ||||
|         add_btn = Gtk.Button() | ||||
|         add_btn.set_image( Gtk.Image.new_from_icon_name("add", 4) ) | ||||
|         add_btn.set_always_show_image(True) | ||||
|         add_btn.connect("released", self.create_view) | ||||
|  | ||||
|         start_box.add(home_btn) | ||||
|         start_box.add(add_btn) | ||||
|         end_box.add(search) | ||||
|         end_box.add(up_btn) | ||||
|         end_box.add(refresh_btn) | ||||
|  | ||||
|         start_box.show_all() | ||||
|         end_box.show_all() | ||||
|  | ||||
|         # PACKTYPE: 0 Start, 1 = End | ||||
|         self.set_action_widget(start_box, 0) | ||||
|         self.set_action_widget(end_box, 1) | ||||
|  | ||||
|     def _do_query(self, widget): | ||||
|         text = widget.get_text() | ||||
|         page = self.get_nth_page( self.get_current_page() ) | ||||
|         page.icon_view.search_filter(text) | ||||
|  | ||||
|  | ||||
|     def load_window_state(self, win_name=None, tabs=None): | ||||
|         if win_name == self.NAME: | ||||
|             if len(tabs) > 0: | ||||
|                 for tab in tabs: | ||||
|                     self.create_view() | ||||
|                     self.load_tab(tab) | ||||
|             else: | ||||
|                 self.create_view() | ||||
|  | ||||
|     def create_view(self, widget = None): | ||||
|         file_view = FileView() | ||||
|         index = self.append_page(file_view, file_view.tab_widget) | ||||
|         self.set_current_page(index) | ||||
|  | ||||
|         return file_view | ||||
|  | ||||
|     def load_tab(self, path = None): | ||||
|         if path: | ||||
|             file_view = self.get_nth_page( self.get_current_page() ) | ||||
|             file_view.set_path(path) | ||||
|  | ||||
|  | ||||
|     def do_action(self, widget = None, action = None): | ||||
|         file_view = self.get_nth_page( self.get_current_page() ) | ||||
|  | ||||
|         if action == "refresh_dir": | ||||
|             file_view.icon_view.refresh_dir() | ||||
|  | ||||
|         if action == "go_up_dir": | ||||
|             file_view.icon_view.go_up_dir() | ||||
|  | ||||
|         if action == "go_home_dir": | ||||
|             file_view.icon_view.go_home_dir() | ||||
|  | ||||
|  | ||||
|     def close_view(self, parent, data = None): | ||||
|         widget = data[0] | ||||
|         page   = self.page_num(widget) | ||||
|  | ||||
|         # watcher  = widget.tab_state.get_dir_watcher() | ||||
|         # watcher.cancel() | ||||
|         if self.get_n_pages() > 1: | ||||
|             self.remove_page(page) | ||||
|  | ||||
|         # NOTE: Will prob try and call a window custom signal... | ||||
|         # self.fm_controller.save_state() | ||||
|         # self.set_window_title() | ||||
| @@ -0,0 +1,146 @@ | ||||
| # Python imports | ||||
|  | ||||
| # Lib imports | ||||
| import gi | ||||
| gi.require_version('Gtk', '3.0') | ||||
| gi.require_version('Gdk', '3.0') | ||||
| from gi.repository import Gtk | ||||
| from gi.repository import Gdk | ||||
| from gi.repository import GdkPixbuf | ||||
| from gi.repository import Gio | ||||
|  | ||||
| # Application imports | ||||
| from .signals.icon_view_signals_mixin import IconViewSignalsMixin | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class IconView(IconViewSignalsMixin, Gtk.FlowBox): | ||||
|     """docstring for IconView.""" | ||||
|  | ||||
|     def __init__(self, _tab_state): | ||||
|         super(IconView, self).__init__() | ||||
|         self.tab_state      = _tab_state | ||||
|         self.selected_files = [] | ||||
|         self.icon_theme     = settings.get_icon_theme() | ||||
|  | ||||
|         self._setup_styling() | ||||
|         self._setup_signals() | ||||
|         # self._setup_dnd() | ||||
|         self.load_store() | ||||
|  | ||||
|         self.show_all() | ||||
|  | ||||
|  | ||||
|     def _emmit_signal_to_file_view(self, signal_type): | ||||
|         self.get_parent().get_parent().emit(signal_type, None) | ||||
|  | ||||
|     def _setup_styling(self): | ||||
|         self.set_selection_mode(Gtk.SelectionMode.MULTIPLE) | ||||
|         self.set_valign(Gtk.Align.START) | ||||
|         self.set_column_spacing(15) | ||||
|         self.set_row_spacing(15) | ||||
|         self.set_homogeneous(True) | ||||
|  | ||||
|     def _setup_signals(self): | ||||
|         self.set_activate_on_single_click(False) | ||||
|         self.connect("child-activated", self.icon_double_click) | ||||
|  | ||||
|         # self.connect("drag-data-get",        self.on_drag_set) | ||||
|         # self.connect("drag-data-received",   self.on_drag_data_received) | ||||
|         # self.connect("drag-motion",          self.on_drag_motion) | ||||
|  | ||||
|  | ||||
|     # NOTE: This gets called by a txt box which then shows/hides the flowbox child | ||||
|     #       https://stackoverflow.com/questions/55828169/how-to-filter-gtk-flowbox-children-with-gtk-entrysearch | ||||
|     def search_filter(self, text): | ||||
|         def filter_func(fb_child, text): | ||||
|             if text.lower() in fb_child.get_name().lower(): | ||||
|                 return True | ||||
|             else: | ||||
|                 return False | ||||
|  | ||||
|         self.set_filter_func(filter_func, text) | ||||
|  | ||||
|  | ||||
|     def _setup_dnd(self): | ||||
|         URI_TARGET_TYPE  = 80 | ||||
|         uri_target       = Gtk.TargetEntry.new('text/uri-list', Gtk.TargetFlags(0), URI_TARGET_TYPE) | ||||
|         targets          = [ uri_target ] | ||||
|         action           = Gdk.DragAction.COPY | ||||
|         self.enable_model_drag_dest(targets, action) | ||||
|         self.enable_model_drag_source(0, targets, action) | ||||
|  | ||||
|     def _clear_children(self, widget: type) -> None: | ||||
|         ''' Clear children of a gtk widget. ''' | ||||
|         for child in widget.get_children(): | ||||
|             widget.remove(child) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| # class IconView(IconViewSignalsMixin, Gtk.IconView): | ||||
| #     """docstring for IconView.""" | ||||
| # | ||||
| #     def __init__(self, _tab_state): | ||||
| #         super(IconView, self).__init__() | ||||
| #         self.store          = None | ||||
| #         self.tab_state      = _tab_state | ||||
| #         self.selected_files = [] | ||||
| #         self.icon_theme     = Gtk.IconTheme.get_default() | ||||
| # | ||||
| #         self._setup_store() | ||||
| #         self._setup_styling() | ||||
| #         self._setup_signals() | ||||
| #         self._setup_dnd() | ||||
| #         self.load_store() | ||||
| # | ||||
| #         self.show_all() | ||||
| # | ||||
| # | ||||
| #     def _setup_store(self): | ||||
| #         self.store = Gtk.ListStore(GdkPixbuf.Pixbuf or GdkPixbuf.PixbufAnimation or None, str or None) | ||||
| #         self.set_model(self.store) | ||||
| #         self.set_pixbuf_column(0) | ||||
| #         self.set_text_column(1) | ||||
| # | ||||
| #     def _setup_styling(self): | ||||
| #         self.set_item_orientation(1) | ||||
| #         self.set_selection_mode(3) | ||||
| #         self.set_item_width(96) | ||||
| #         self.set_item_padding(8) | ||||
| #         self.set_margin(12) | ||||
| #         self.set_row_spacing(18) | ||||
| #         self.set_columns(-1) | ||||
| #         self.set_spacing(12) | ||||
| #         self.set_column_spacing(18) | ||||
| # | ||||
| #     def _setup_signals(self): | ||||
| #         # self.connect("button_release_event", self.icon_single_click) | ||||
| #         self.connect("item-activated",       self.icon_double_click) | ||||
| #         self.connect("selection-changed",    self.set_selected_items) | ||||
| #         # self.connect("drag-data-get",        self.on_drag_set) | ||||
| #         # self.connect("drag-data-received",   self.on_drag_data_received) | ||||
| #         # self.connect("drag-motion",          self.on_drag_motion) | ||||
| #         pass | ||||
| # | ||||
| #     def _setup_dnd(self): | ||||
| #         URI_TARGET_TYPE  = 80 | ||||
| #         uri_target       = Gtk.TargetEntry.new('text/uri-list', Gtk.TargetFlags(0), URI_TARGET_TYPE) | ||||
| #         targets          = [ uri_target ] | ||||
| #         action           = Gdk.DragAction.COPY | ||||
| #         self.enable_model_drag_dest(targets, action) | ||||
| #         self.enable_model_drag_source(0, targets, action) | ||||
| # | ||||
| #     def set_selected_items(self, icons_grid): | ||||
| #         self.selected_files = self.get_selected_items() | ||||
| @@ -0,0 +1,3 @@ | ||||
| """ | ||||
|     Signals module | ||||
| """ | ||||
| @@ -0,0 +1,183 @@ | ||||
| # Python imports | ||||
| import os | ||||
| import traceback | ||||
| import threading | ||||
| import subprocess | ||||
| import time | ||||
| from os.path import isdir | ||||
|  | ||||
| # Lib imports | ||||
| import gi | ||||
|  | ||||
| gi.require_version("Gtk", "3.0") | ||||
| gi.require_version('Gdk', '3.0') | ||||
| from gi.repository import Gtk | ||||
| from gi.repository import GLib | ||||
| from gi.repository import Gdk | ||||
| from gi.repository import GdkPixbuf | ||||
| from gi.repository import Gio | ||||
|  | ||||
|  | ||||
| # Application imports | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class FileView(Gtk.Box): | ||||
|     """docstring for FileView.""" | ||||
|  | ||||
|     def __init__(self, _icon = None, _label = None): | ||||
|         super(FileView, self).__init__() | ||||
|         self.icon  = None | ||||
|         self.label = _label | ||||
|  | ||||
|         self.label.set_line_wrap(True) | ||||
|         self.label.set_selectable(True) | ||||
|         self.label.set_max_width_chars(20) | ||||
|         self.label.set_justify(2) | ||||
|         self.label.set_line_wrap_mode(2) | ||||
|  | ||||
|         self.add(self.label) | ||||
|         self.set_orientation(1) | ||||
|  | ||||
|         self.show_all() | ||||
|  | ||||
|     def set_img(self, img): | ||||
|         self.icon  = img | ||||
|         self.add(img) | ||||
|         self.reorder_child(self.icon, 0) | ||||
|  | ||||
|  | ||||
|  | ||||
| class IconViewSignalsMixin: | ||||
|     """docstring for WidgetMixin""" | ||||
|  | ||||
|     def load_store(self): | ||||
|         self._clear_children(self) | ||||
|         self.search_filter("") | ||||
|  | ||||
|         dir   = self.tab_state.get_current_directory() | ||||
|         files = self.tab_state.get_files() | ||||
|  | ||||
|         for i, file in enumerate(files): | ||||
|             label = Gtk.Label(label=file[0]) | ||||
|             child = Gtk.FlowBoxChild() | ||||
|             child.set_name(file[0]) | ||||
|  | ||||
|             file_view = FileView(_label=label) | ||||
|             child.add( file_view ) | ||||
|             self.add(child) | ||||
|             self.create_icon(file_view, dir, file[0]) | ||||
|  | ||||
|     @threaded | ||||
|     def create_icon(self, file_view, dir, file): | ||||
|         icon = self.tab_state.create_icon(dir, file) | ||||
|         GLib.idle_add(self.update_store, *(file_view, icon, dir, file,)) | ||||
|  | ||||
|     def update_store(self, file_view, icon,  dir, file): | ||||
|         if not icon: | ||||
|             path = f"{dir}/{file}" | ||||
|             icon = self.get_system_thumbnail(path, self.tab_state.sys_icon_wh[0]) | ||||
|  | ||||
|         if not icon: | ||||
|             icon = GdkPixbuf.Pixbuf.new_from_file(self.tab_state.DEFAULT_ICON) | ||||
|  | ||||
|         img   = Gtk.Image.new_from_pixbuf(icon) | ||||
|         img.show() | ||||
|         file_view.set_img(img) | ||||
|         self.show_all() | ||||
|  | ||||
|  | ||||
|     def get_system_thumbnail(self, filename, size): | ||||
|         try: | ||||
|             gio_file  = Gio.File.new_for_path(filename) | ||||
|             info      = gio_file.query_info('standard::icon' , 0, None) | ||||
|             icon      = info.get_icon().get_names()[0] | ||||
|             icon_path = self.icon_theme.lookup_icon(icon , size , 0).get_filename() | ||||
|  | ||||
|             return GdkPixbuf.Pixbuf.new_from_file(icon_path) | ||||
|         except Exception: | ||||
|             ... | ||||
|  | ||||
|         return None | ||||
|  | ||||
|  | ||||
|     def icon_double_click(self, icons_grid, item, data=None): | ||||
|         try: | ||||
|             file_view  = item.get_children()[0] | ||||
|             file_name  = file_view.label.get_label() | ||||
|             dir        = self.tab_state.get_current_directory() | ||||
|             file       = f"{dir}/{file_name}" | ||||
|  | ||||
|             if isdir(file): | ||||
|                 self.tab_state.set_path(file) | ||||
|                 self.load_store() | ||||
|                 self._emmit_signal_to_file_view('update-tab-title') | ||||
|             else: | ||||
|                 event_system.emit("open_files", (self, file)) | ||||
|         except Exception as e: | ||||
|             traceback.print_exc() | ||||
|  | ||||
|  | ||||
|     def go_up_dir(self): | ||||
|         self.tab_state.pop_from_path() | ||||
|         self.load_store() | ||||
|         self._emmit_signal_to_file_view('update-tab-title') | ||||
|  | ||||
|     def go_home_dir(self): | ||||
|         self.tab_state.set_to_home() | ||||
|         self.load_store() | ||||
|         self._emmit_signal_to_file_view('update-tab-title') | ||||
|  | ||||
|     def go_to_path(self, path): | ||||
|         self.tab_state.set_path(path) | ||||
|         self.load_store() | ||||
|         self._emmit_signal_to_file_view('update-tab-title') | ||||
|  | ||||
|     def refresh_dir(self): | ||||
|         self.tab_state.load_directory() | ||||
|         self.load_store() | ||||
|  | ||||
|     # def on_drag_set(self, icons_grid, drag_context, data, info, time): | ||||
|     #     action    = icons_grid.get_name() | ||||
|     #     wid, tid  = action.split("|") | ||||
|     #     store     = icons_grid.get_model() | ||||
|     #     treePaths = icons_grid.get_selected_items() | ||||
|     #     # NOTE: Need URIs as URI format for DnD to work. Will strip 'file://' | ||||
|     #     # further down call chain when doing internal fm stuff. | ||||
|     #     uris      = self.format_to_uris(store, wid, tid, treePaths) | ||||
|     #     uris_text = '\n'.join(uris) | ||||
|     # | ||||
|     #     data.set_uris(uris) | ||||
|     #     data.set_text(uris_text, -1) | ||||
|     # | ||||
|     # def on_drag_motion(self, icons_grid, drag_context, x, y, data): | ||||
|     #     current   = '|'.join(self.fm_controller.get_active_wid_and_tid()) | ||||
|     #     target    = icons_grid.get_name() | ||||
|     #     wid, tid  = target.split("|") | ||||
|     #     store     = icons_grid.get_model() | ||||
|     #     treePath  = icons_grid.get_drag_dest_item().path | ||||
|     # | ||||
|     #     if treePath: | ||||
|     #         uri = self.format_to_uris(store, wid, tid, treePath)[0].replace("file://", "") | ||||
|     #         self.override_drop_dest = uri if isdir(uri) else None | ||||
|     # | ||||
|     #     if target not in current: | ||||
|     #         self.fm_controller.set_wid_and_tid(wid, tid) | ||||
|     # | ||||
|     # | ||||
|     # def on_drag_data_received(self, widget, drag_context, x, y, data, info, time): | ||||
|     #     if info == 80: | ||||
|     #         wid, tid  = self.fm_controller.get_active_wid_and_tid() | ||||
|     #         notebook  = self.builder.get_object(f"window_{wid}") | ||||
|     #         store, tab_label = self.get_store_and_label_from_notebook(notebook, f"{wid}|{tid}") | ||||
|     #         tab       = self.get_fm_window(wid).get_tab_by_id(tid) | ||||
|     # | ||||
|     #         uris = data.get_uris() | ||||
|     #         dest = f"{tab.get_current_directory()}" if not self.override_drop_dest else self.override_drop_dest | ||||
|     #         if len(uris) == 0: | ||||
|     #             uris = data.get_text().split("\n") | ||||
|     # | ||||
|     #         from_uri = '/'.join(uris[0].replace("file://", "").split("/")[:-1]) | ||||
|     #         if from_uri != dest: | ||||
|     #             self.move_files(uris, dest) | ||||
| @@ -60,7 +60,7 @@ class IOWidget(Gtk.Box): | ||||
|         self.add(stats) | ||||
| 
 | ||||
|     def do_cancel(self, widget, container, eve): | ||||
|         print(f"Canceling: [{self._action}] of {self._basename} ...") | ||||
|         logger.info(f"Canceling: [{self._action}] of {self._basename} ...") | ||||
|         eve.cancel() | ||||
| 
 | ||||
|     def update_progress(self, current, total, eve=None): | ||||
| @@ -75,7 +75,7 @@ class IOWidget(Gtk.Box): | ||||
|         if status: | ||||
|             self.delete_self() | ||||
|         else: | ||||
|             print(f"{self._action} of {self._basename} failed...") | ||||
|             logger.info(f"{self._action} of {self._basename} failed...") | ||||
| 
 | ||||
|     def delete_self(self, widget=None, eve=None): | ||||
|         self.get_parent().remove(self) | ||||
| @@ -16,18 +16,17 @@ class IOPopupWidget(Gtk.Popover): | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         super(IOPopupWidget, self).__init__() | ||||
|         self.builder = settings.get_builder() | ||||
|         self._builder = settings.get_builder() | ||||
| 
 | ||||
|         self.builder.expose_object(f"io_popup", self) | ||||
|         self._builder.expose_object(f"io_popup", self) | ||||
| 
 | ||||
|         self._setup_styling() | ||||
|         self._setup_signals() | ||||
|         self._load_widgets() | ||||
|         self.show_all() | ||||
| 
 | ||||
| 
 | ||||
|     def _setup_styling(self): | ||||
|         io_button = self.builder.get_object(f"io_button") | ||||
|         io_button = self._builder.get_object(f"io_button") | ||||
|         self.set_relative_to(io_button) | ||||
|         self.set_modal(True) | ||||
|         self.set_position(Gtk.PositionType.BOTTOM) | ||||
| @@ -40,7 +39,7 @@ class IOPopupWidget(Gtk.Popover): | ||||
|         vbox = Gtk.Box() | ||||
| 
 | ||||
|         vbox.set_orientation(Gtk.Orientation.VERTICAL) | ||||
|         self.builder.expose_object(f"io_list", vbox) | ||||
|         self._builder.expose_object(f"io_list", vbox) | ||||
|         self.add(vbox) | ||||
| 
 | ||||
|     def show_io_popup(self, widget=None, eve=None): | ||||
| @@ -28,7 +28,6 @@ class MessagePopupWidget(Gtk.Popover): | ||||
|         self._setup_styling() | ||||
|         self._setup_signals() | ||||
|         self._load_widgets() | ||||
|         self.show_all() | ||||
| 
 | ||||
| 
 | ||||
|     def _setup_styling(self): | ||||
| @@ -23,7 +23,6 @@ class PluginsPopupWidget(Gtk.Popover): | ||||
|         self._setup_styling() | ||||
|         self._setup_signals() | ||||
|         self._load_widgets() | ||||
|         self.show_all() | ||||
| 
 | ||||
| 
 | ||||
|     def _setup_styling(self): | ||||
							
								
								
									
										95
									
								
								src/versions/solarfm-0.0.1/solarfm/core/window.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								src/versions/solarfm-0.0.1/solarfm/core/window.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| # Python imports | ||||
| import time | ||||
| import signal | ||||
|  | ||||
| # Lib imports | ||||
| import gi | ||||
| import cairo | ||||
| gi.require_version('Gtk', '3.0') | ||||
| gi.require_version('Gdk', '3.0') | ||||
| from gi.repository import Gtk | ||||
| from gi.repository import Gdk | ||||
| from gi.repository import GLib | ||||
|  | ||||
| # Application imports | ||||
| from core.controller import Controller | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class Window(Gtk.ApplicationWindow): | ||||
|     """docstring for Window.""" | ||||
|  | ||||
|     def __init__(self, args, unknownargs): | ||||
|         super(Window, self).__init__() | ||||
|  | ||||
|         self._controller = None | ||||
|         settings.set_main_window(self) | ||||
|  | ||||
|         self._set_window_data() | ||||
|         self._setup_styling() | ||||
|         self._setup_signals() | ||||
|         self._subscribe_to_events() | ||||
|  | ||||
|         self._load_widgets(args, unknownargs) | ||||
|  | ||||
|         self.show() | ||||
|  | ||||
|  | ||||
|     def _setup_styling(self): | ||||
|         self.set_default_size(1670, 830) | ||||
|         self.set_title(f"{app_name}") | ||||
|         self.set_icon_from_file( settings.get_window_icon() ) | ||||
|         self.set_gravity(5)  # 5 = CENTER | ||||
|         self.set_position(1) # 1 = CENTER, 4 = CENTER_ALWAYS | ||||
|  | ||||
|     def _setup_signals(self): | ||||
|         self.connect("delete-event", self._tear_down) | ||||
|  | ||||
|         # self.connect("focus-out-event", self._controller.unset_keys_and_data) | ||||
|         # self.connect("key-press-event", self._controller.on_global_key_press_controller) | ||||
|         # self.connect("key-release-event", self._controller.on_global_key_release_controller) | ||||
|  | ||||
|         GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self._tear_down) | ||||
|  | ||||
|     def _subscribe_to_events(self): | ||||
|         event_system.subscribe("tear_down", self._tear_down) | ||||
|  | ||||
|     def _load_widgets(self, args, unknownargs): | ||||
|         self._controller = Controller(args, unknownargs) | ||||
|  | ||||
|         if not self._controller: | ||||
|             raise ControllerStartException("Controller exited and doesn't exist...") | ||||
|  | ||||
|         self.add( self._controller.get_core_widget() ) | ||||
|  | ||||
|     def _set_window_data(self) -> None: | ||||
|         screen = self.get_screen() | ||||
|         visual = screen.get_rgba_visual() | ||||
|  | ||||
|         if visual != None and screen.is_composited(): | ||||
|             self.set_visual(visual) | ||||
|             self.set_app_paintable(True) | ||||
|             self.connect("draw", self._area_draw) | ||||
|  | ||||
|         # bind css file | ||||
|         cssProvider  = Gtk.CssProvider() | ||||
|         cssProvider.load_from_path( settings.get_css_file() ) | ||||
|         screen       = Gdk.Screen.get_default() | ||||
|         styleContext = Gtk.StyleContext() | ||||
|         styleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER) | ||||
|  | ||||
|     def _area_draw(self, widget: Gtk.ApplicationWindow, cr: cairo.Context) -> None: | ||||
|         cr.set_source_rgba(0, 0, 0, 0.54) | ||||
|         cr.set_operator(cairo.OPERATOR_SOURCE) | ||||
|         cr.paint() | ||||
|         cr.set_operator(cairo.OPERATOR_OVER) | ||||
|  | ||||
|  | ||||
|     def _tear_down(self, widget=None, eve=None): | ||||
|         if not settings.is_trace_debug(): | ||||
|             self._controller.fm_controller.save_state() | ||||
|  | ||||
|         settings.clear_pid() | ||||
|         time.sleep(event_sleep_time) | ||||
|         Gtk.main_quit() | ||||
							
								
								
									
										3
									
								
								src/versions/solarfm-0.0.1/solarfm/factories/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/versions/solarfm-0.0.1/solarfm/factories/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| """ | ||||
|     Factories Module | ||||
| """ | ||||
| @@ -0,0 +1,33 @@ | ||||
| # Python imports | ||||
|  | ||||
| # Lib imports | ||||
| import gi | ||||
| gi.require_version('Gtk', '3.0') | ||||
| from gi.repository import Gtk | ||||
|  | ||||
| # Application imports | ||||
| from core.widgets.fm_widget.files_widget import FilesWidget | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class SplitViewWidget(Gtk.Paned): | ||||
|     def __init__(self): | ||||
|         super(SplitViewWidget, self).__init__() | ||||
|  | ||||
|         self._setup_styling() | ||||
|         self._setup_signals() | ||||
|         self._load_widgets() | ||||
|  | ||||
|         self.show_all() | ||||
|  | ||||
|  | ||||
|     def _setup_styling(self): | ||||
|         self.set_wide_handle(True) | ||||
|  | ||||
|     def _setup_signals(self): | ||||
|         ... | ||||
|  | ||||
|     def _load_widgets(self): | ||||
|         self.pack1(FilesWidget(), resize=False, shrink=False) | ||||
|         self.pack2(FilesWidget(), resize=True, shrink=False) | ||||
| @@ -82,8 +82,8 @@ class ManifestProcessor: | ||||
|                 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)) | ||||
|                     except ManifestProcessorException as e: | ||||
|                         logger.error(repr(e)) | ||||
| 
 | ||||
|         if "bind_keys" in keys: | ||||
|             if isinstance(requests["bind_keys"], list): | ||||
| @@ -24,7 +24,6 @@ class PluginBase: | ||||
|         self._event_system      = None | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     def set_fm_event_system(self, fm_event_system): | ||||
|         """ | ||||
|             Requests Key:  'pass_fm_events': "true" | ||||
| @@ -76,7 +75,7 @@ class PluginBase: | ||||
|                 methods = inspect.getmembers(c, predicate=inspect.ismethod) | ||||
|                 handlers.update(methods) | ||||
|             except Exception as e: | ||||
|                 print(repr(e)) | ||||
|                 logger.debug(repr(e)) | ||||
| 
 | ||||
|         builder.connect_signals(handlers) | ||||
| 
 | ||||
| @@ -32,7 +32,6 @@ class PluginsController: | ||||
| 
 | ||||
|         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   = [] | ||||
| @@ -54,7 +53,7 @@ class PluginsController: | ||||
|             self.reload_plugins(file) | ||||
| 
 | ||||
|     def load_plugins(self, file: str = None) -> None: | ||||
|         print(f"Loading plugins...") | ||||
|         logger.info(f"Loading plugins...") | ||||
|         parent_path = os.getcwd() | ||||
| 
 | ||||
|         for path, folder in [[join(self._plugins_path, item), item] if os.path.isdir(join(self._plugins_path, item)) else None for item in os.listdir(self._plugins_path)]: | ||||
| @@ -63,14 +62,14 @@ class PluginsController: | ||||
|                 manifest = ManifestProcessor(path, self._builder) | ||||
| 
 | ||||
|                 if not os.path.exists(target): | ||||
|                     raise InvalidPluginException("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...") | ||||
|                     raise FileNotFoundError("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...") | ||||
| 
 | ||||
|                 plugin, loading_data = manifest.get_loading_data() | ||||
|                 module               = self.load_plugin_module(path, folder, target) | ||||
|                 self.execute_plugin(module, plugin, loading_data) | ||||
|             except Exception as e: | ||||
|                 print(f"Malformed Plugin: Not loading -->: '{folder}' !") | ||||
|                 traceback.print_exc() | ||||
|             except InvalidPluginException as e: | ||||
|                 logger.info(f"Malformed Plugin: Not loading -->: '{folder}' !") | ||||
|                 logger.debug("Trace: ", traceback.print_exc()) | ||||
| 
 | ||||
|         os.chdir(parent_path) | ||||
| 
 | ||||
| @@ -111,13 +110,13 @@ class PluginsController: | ||||
|             plugin.reference.subscribe_to_events() | ||||
| 
 | ||||
|         if "bind_keys" in keys: | ||||
|             self._keybindings.append_bindings( loading_data["bind_keys"] ) | ||||
|             keybindings.append_bindings( loading_data["bind_keys"] ) | ||||
| 
 | ||||
|         plugin.reference.run() | ||||
|         self._plugin_collection.append(plugin) | ||||
| 
 | ||||
|     def reload_plugins(self, file: str = None) -> None: | ||||
|         print(f"Reloading plugins...") | ||||
|         logger.info(f"Reloading plugins...") | ||||
|         parent_path = os.getcwd() | ||||
| 
 | ||||
|         for plugin in self._plugin_collection: | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user