diff --git a/.gitignore b/.gitignore index cc28be0..2be1dc5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +cookies.txt + docs/ .idea/ *.zip diff --git a/plugins/thumbnailer/icons/icon.py b/plugins/thumbnailer/icons/icon.py index 4cc0781..79903ca 100644 --- a/plugins/thumbnailer/icons/icon.py +++ b/plugins/thumbnailer/icons/icon.py @@ -50,8 +50,8 @@ class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin): if not thumbnl: # TODO: Detect if not in a thread and use directly for speed get_system_thumbnail - thumbnl = self.get_system_thumbnail(full_path, self.sys_icon_wh[0]) - # thumbnl = self._get_system_thumbnail_gtk_thread(full_path, self.sys_icon_wh[0]) + # thumbnl = self.get_system_thumbnail(full_path, self.sys_icon_wh[0]) + thumbnl = self._get_system_thumbnail_gtk_thread(full_path, self.sys_icon_wh[0]) if not thumbnl: raise IconException("No known icons found.") @@ -144,6 +144,8 @@ class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin): event = threading.Event() GLib.idle_add(_call_gtk_thread, event, result) event.wait() + + event = None return result[0] @@ -156,6 +158,7 @@ class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin): if data: icon_path = data.get_filename() + return GdkPixbuf.Pixbuf.new_from_file_at_size(icon_path, width = size, height = size) raise IconException("No system icon found...") @@ -202,4 +205,4 @@ class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin): pixbuf = GdkPixbuf.Pixbuf.new_from_bytes(data, GdkPixbuf.Colorspace.RGB, False, 8, w, h, w * 3) - return pixbuf.scale_simple(wxh[0], wxh[1], 2) # BILINEAR = 2 \ No newline at end of file + return pixbuf.scale_simple(wxh[0], wxh[1], 2) # BILINEAR = 2 diff --git a/plugins/thumbnailer/plugin.py b/plugins/thumbnailer/plugin.py index 7dd778f..3478689 100644 --- a/plugins/thumbnailer/plugin.py +++ b/plugins/thumbnailer/plugin.py @@ -21,6 +21,10 @@ class Plugin(PluginBase): def run(self): self.icon_controller = IconController() self._event_system.subscribe("create-thumbnail", self.create_thumbnail) + self._event_system.subscribe("create-video-thumbnail", self.create_video_thumbnail) + self._event_system.subscribe("create-scaled-image", self.create_scaled_image) + self._event_system.subscribe("get-thumbnail-hash", self.get_thumbnail_hash) + self._event_system.subscribe("get-thumbnails-path", self.get_thumbnails_path) def generate_reference_ui_element(self): ... @@ -28,6 +32,18 @@ class Plugin(PluginBase): def create_thumbnail(self, dir, file) -> str: return self.icon_controller.create_icon(dir, file) + def create_video_thumbnail(self, file, scrub_percent, replace): + return self.icon_controller.create_video_thumbnail(file, scrub_percent, replace) + + def create_scaled_image(self, hash_img_pth): + return self.icon_controller.create_scaled_image(hash_img_pth) + + def get_thumbnail_hash(self, file): + return self.icon_controller.generate_hash_and_path(file) + + def get_thumbnails_path(self) -> str: + return self.icon_controller.ABS_THUMBS_PTH + def get_video_icons(self, dir) -> list: data = [] diff --git a/plugins/vod_thumbnailer/plugin.py b/plugins/vod_thumbnailer/plugin.py index 9fea674..521e326 100644 --- a/plugins/vod_thumbnailer/plugin.py +++ b/plugins/vod_thumbnailer/plugin.py @@ -26,6 +26,8 @@ def threaded(fn): return wrapper +class VODThumbnailerException(Exception): + ... class Plugin(PluginBase): @@ -94,32 +96,39 @@ class Plugin(PluginBase): file = self._file_name.get_text() dir = self._file_location.get_text() file_hash = self._file_hash.get_text() - hash_img_pth = f"{self._fm_state.tab.ABS_THUMBS_PTH}/{file_hash}.jpg" + hash_img_pth = f"{self.ABS_THUMBS_PTH}/{file_hash}.jpg" try: - self._fm_state.tab.create_video_thumbnail(f"{dir}/{file}", f"{scrub_percent}%", True) + self._event_system.emit_and_await("create-video-thumbnail", (f"{dir}/{file}", f"{scrub_percent}%", True,)) preview_pixbuf = GdkPixbuf.Pixbuf.new_from_file(hash_img_pth) self._thumbnail_preview_img.set_from_pixbuf(preview_pixbuf) - img_pixbuf = self._fm_state.tab.create_scaled_image(hash_img_pth) + img_pixbuf = self._event_system.emit_and_await("create-scaled-image", (hash_img_pth,)) tree_pth = self._fm_state.icon_grid.get_selected_items()[0] itr = self._fm_state.store.get_iter(tree_pth) pixbuff = self._fm_state.store.get(itr, 0)[0] self._fm_state.store.set(itr, 0, img_pixbuf) except Exception as e: - print(repr(e)) print("Couldn't regenerate thumbnail!") + print(repr(e)) def _set_ui_data(self): uri = self._fm_state.uris[0] path = self._fm_state.tab.get_current_directory() parts = uri.split("/") - file_hash = self._fm_state.tab.fast_hash(uri) - hash_img_pth = f"{self._fm_state.tab.ABS_THUMBS_PTH}/{file_hash}.jpg" + path_exists, + img_hash, + hash_img_pth = self._event_system.emit_and_await("get-thumbnail-hash", (uri,)) + + if not path_exists: + raise VODThumbnailerException(f"Could not generate file_hash from: {uri}") + + + self.ABS_THUMBS_PTH = self._event_system.emit_and_await("get-thumbnails-path") preview_pixbuf = GdkPixbuf.Pixbuf.new_from_file(hash_img_pth) self._thumbnail_preview_img.set_from_pixbuf(preview_pixbuf) self._file_name.set_text(parts[ len(parts) - 1 ]) self._file_location.set_text(path) - self._file_hash.set_text(file_hash) + self._file_hash.set_text(img_hash) diff --git a/plugins/youtube_download/plugin.py b/plugins/youtube_download/plugin.py index 58df3b3..b5decd4 100644 --- a/plugins/youtube_download/plugin.py +++ b/plugins/youtube_download/plugin.py @@ -47,4 +47,4 @@ class Plugin(PluginBase): @threaded def _download(self, dir): - subprocess.Popen([f'{self.path}/download.sh', dir]) + subprocess.Popen([f'{self.path}/download.sh', dir], start_new_session=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, close_fds=True) diff --git a/src/solarfm/app.py b/src/solarfm/app.py index 417b2cc..b8510be 100644 --- a/src/solarfm/app.py +++ b/src/solarfm/app.py @@ -44,7 +44,8 @@ class Application: def ipc_realization_check(self, ipc_server): try: ipc_server.create_ipc_listener() - except Exception: + except Exception as e: + print(e) ipc_server.send_test_ipc_message() try: diff --git a/src/solarfm/core/controller_data.py b/src/solarfm/core/controller_data.py index 35e2618..aa215d7 100644 --- a/src/solarfm/core/controller_data.py +++ b/src/solarfm/core/controller_data.py @@ -54,6 +54,7 @@ class Controller_Data: self.ctrl_down = False self.shift_down = False self.alt_down = False + self.was_midified_key = None self._state = State() self.message_dialog = MessageWidget() @@ -88,12 +89,6 @@ class Controller_Data: state.selected_files = event_system.emit_and_await("get_selected_files") - # if self.to_copy_files: - # state.to_copy_files = self.format_to_uris(state.store, state.wid, state.tid, self.to_copy_files, True) - # - # if self.to_cut_files: - # state.to_cut_files = self.format_to_uris(state.store, state.wid, state.tid, self.to_cut_files, True) - event_system.emit("update_state_info_plugins", state) # NOTE: Need to remove after we convert plugins to use emit_and_await return state diff --git a/src/solarfm/core/mixins/signals/file_action_signals_mixin.py b/src/solarfm/core/mixins/signals/file_action_signals_mixin.py index 9acce62..69e928c 100644 --- a/src/solarfm/core/mixins/signals/file_action_signals_mixin.py +++ b/src/solarfm/core/mixins/signals/file_action_signals_mixin.py @@ -20,6 +20,8 @@ class FileActionSignalsMixin: if tab.get_dir_watcher(): watcher = tab.get_dir_watcher() watcher.cancel() + watcher.disconnect(watcher.watch_id) + watcher.run_dispose() if settings_manager.is_debug(): logger.debug(f"Watcher Is Cancelled: {watcher.is_cancelled()}") @@ -30,8 +32,9 @@ class FileActionSignalsMixin: wid = tab.get_wid() tid = tab.get_id() - dir_watcher.connect("changed", self.dir_watch_updates, *(f"{wid}|{tid}",)) + watch_id = dir_watcher.connect("changed", self.dir_watch_updates, *(f"{wid}|{tid}",)) tab.set_dir_watcher(dir_watcher) + dir_watcher.watch_id = watch_id def dir_watch_updates(self, file_monitor, file, other_file = None, eve_type = None, tab_widget_id = None): if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED, @@ -46,7 +49,6 @@ class FileActionSignalsMixin: GLib.source_remove(timeout_id) timeout_id = GLib.timeout_add(0, self.update_on_soft_lock_end, 600, *(tab_widget_id,)) - self.soft_update_lock[tab_widget_id] = { "timeout_id": timeout_id } def update_on_soft_lock_end(self, timout_ms, tab_widget_id): @@ -68,14 +70,6 @@ class FileActionSignalsMixin: if [wid, tid] in [state.wid, state.tid]: self.set_bottom_labels(tab) - wid, tid = None, None - notebook = None - tab = None - icon_grid = None - store = None - _store, tab_widget_id_label = None, None - state = None - return False def do_file_search(self, widget, eve = None): diff --git a/src/solarfm/core/mixins/signals/keyboard_signals_mixin.py b/src/solarfm/core/mixins/signals/keyboard_signals_mixin.py index 03446d0..eed30c5 100644 --- a/src/solarfm/core/mixins/signals/keyboard_signals_mixin.py +++ b/src/solarfm/core/mixins/signals/keyboard_signals_mixin.py @@ -34,7 +34,7 @@ class KeyboardSignalsMixin: self.alt_down = False def on_global_key_press_controller(self, eve, user_data): - keyname = Gdk.keyval_name(user_data.keyval).lower() + keyname = Gdk.keyval_name(user_data.keyval).lower() modifiers = Gdk.ModifierType(user_data.get_state() & ~Gdk.ModifierType.LOCK_MASK) self.was_midified_key = True if modifiers != 0 else False diff --git a/src/solarfm/core/mixins/ui/grid_mixin.py b/src/solarfm/core/mixins/ui/grid_mixin.py deleted file mode 100644 index 497c6ac..0000000 --- a/src/solarfm/core/mixins/ui/grid_mixin.py +++ /dev/null @@ -1,135 +0,0 @@ -# Python imports -import asyncio - -# Lib imports -import gi - -gi.require_version("Gtk", "3.0") -from gi.repository import Gtk -from gi.repository import GLib -from gi.repository import Gio - -# Application imports -from ...widgets.tab_header_widget import TabHeaderWidget -from ...widgets.icon_grid_widget import IconGridWidget -from ...widgets.icon_tree_widget import IconTreeWidget - - - -class GridMixin: - """docstring for GridMixin""" - - def load_store(self, tab, store, save_state = False, use_generator = False): - dir = tab.get_current_directory() - files = tab.get_files() - - for file in files: - store.append([None, file[0]]) - - Gtk.main_iteration() - self.generate_icons(tab, store, dir, files) - - # NOTE: Not likely called often from here but it could be useful - if save_state and not trace_debug: - self.fm_controller.save_state() - - dir = None - files = None - - def generate_icons(self, tab, store, dir, files): - for i, file in enumerate(files): - self.make_and_load_icon( i, store, tab, dir, file[0]) - - def update_store(self, i, store, icon): - itr = store.get_iter(i) - GLib.idle_add(self.insert_store, store, itr, icon.copy()) - itr = None - del icon - - @daemon_threaded - def make_and_load_icon(self, i, store, tab, dir, file): - icon = tab.create_icon(dir, file) - GLib.idle_add(self.update_store, i, store, icon) - icon = None - - def get_icon(self, tab, dir, file): - tab.create_icon(dir, file) - - def insert_store(self, store, itr, icon): - store.set_value(itr, 0, icon) - - # Note: If the function returns GLib.SOURCE_REMOVE or False it is automatically removed from the list of event sources and will not be called again. - return False - - def do_ui_update(self): - Gtk.main_iteration() - # Note: If the function returns GLib.SOURCE_REMOVE or False it is automatically removed from the list of event sources and will not be called again. - return False - - def create_tab_widget(self): - return TabHeaderWidget(self.close_tab) - - def create_scroll_and_store(self, tab, wid, use_tree_view = False): - scroll = Gtk.ScrolledWindow() - - if not use_tree_view: - grid = self.create_icon_grid_widget() - else: - # TODO: Fix global logic to make the below work too - grid = self.create_icon_tree_widget() - - scroll.add(grid) - scroll.set_name(f"{wid}|{tab.get_id()}") - grid.set_name(f"{wid}|{tab.get_id()}") - self.builder.expose_object(f"{wid}|{tab.get_id()}|icon_grid", grid, use_gtk = False) - self.builder.expose_object(f"{wid}|{tab.get_id()}", scroll, use_gtk = False) - - return scroll, grid.get_store() - - def create_icon_grid_widget(self): - grid = IconGridWidget() - grid._setup_additional_signals( - self.grid_icon_single_click, - self.grid_icon_double_click, - self.grid_set_selected_items, - self.grid_on_drag_set, - self.grid_on_drag_data_received, - self.grid_on_drag_motion - ) - - return grid - - def create_icon_tree_widget(self): - grid = IconTreeWidget() - grid._setup_additional_signals( - self.grid_icon_single_click, - self.grid_icon_double_click, - self.grid_on_drag_set, - self.grid_on_drag_data_received, - self.grid_on_drag_motion - ) - - grid.columns_autosize() - return grid - - def get_store_and_label_from_notebook(self, notebook, _name): - icon_grid = None - tab_label = None - store = None - - for obj in notebook.get_children(): - icon_grid = obj.get_children()[0] - name = icon_grid.get_name() - if name == _name: - store = icon_grid.get_model() - tab_label = notebook.get_tab_label(obj).get_children()[0] - - icon_grid = None - return store, tab_label - - def get_icon_grid_from_notebook(self, notebook, _name): - for obj in notebook.get_children(): - icon_grid = obj.get_children()[0] - name = icon_grid.get_name() - if name == _name: - return icon_grid \ No newline at end of file diff --git a/src/solarfm/core/mixins/ui/tab_mixin.py b/src/solarfm/core/mixins/ui/tab_mixin.py deleted file mode 100644 index 70e53fe..0000000 --- a/src/solarfm/core/mixins/ui/tab_mixin.py +++ /dev/null @@ -1,315 +0,0 @@ -# Python imports -import os -import gc -import time - -# Lib imports -import gi -gi.require_version('Gtk', '3.0') -from gi.repository import Gtk -from gi.repository import GLib - -# Application imports -from .grid_mixin import GridMixin - - - -class TabMixin(GridMixin): - """docstring for TabMixin""" - - def create_tab(self, wid: int = None, tid: int = None, path: str = None): - if not wid: - wid, tid = self.fm_controller.get_active_wid_and_tid() - - notebook = self.builder.get_object(f"window_{wid}") - path_entry = self.builder.get_object(f"path_entry") - tab = self.fm_controller.add_tab_for_window_by_nickname(f"window_{wid}") - tab.logger = logger - - tab.set_wid(wid) - if not path: - if wid and tid: - _tab = self.get_fm_window(wid).get_tab_by_id(tid) - tab.set_path(_tab.get_current_directory()) - else: - tab.set_path(path) - - tab_widget = self.get_tab_widget(tab) - scroll, store = self.create_scroll_and_store(tab, wid) - index = notebook.append_page(scroll, tab_widget) - notebook.set_tab_detachable(scroll, True) - notebook.set_tab_reorderable(scroll, True) - - self.fm_controller.set_wid_and_tid(wid, tab.get_id()) - # path_entry.set_text(tab.get_current_directory()) - event_system.emit("go_to_path", (tab.get_current_directory(),)) # NOTE: Not efficent if I understand how - notebook.show_all() - notebook.set_current_page(index) - - ctx = notebook.get_style_context() - ctx.add_class("notebook-unselected-focus") - self.load_store(tab, store) - event_system.emit("set_window_title", (tab.get_current_directory(),)) - self.set_file_watcher(tab) - - tab_widget = None - scroll, store = None, None - index = None - notebook = None - path_entry = None - tab = None - ctx = None - - - def get_tab_widget(self, tab): - tab_widget = self.create_tab_widget() - tab_widget.tab = tab - - tab_widget.label.set_label(f"{tab.get_end_of_path()}") - tab_widget.label.set_width_chars(len(tab.get_end_of_path())) - - return tab_widget - - def close_tab(self, button, eve = None): - notebook = button.get_parent().get_parent() - if notebook.get_n_pages() == 1: - notebook = None - return - - tab_box = button.get_parent() - wid = int(notebook.get_name()[-1]) - tid = self.get_id_from_tab_box(tab_box) - scroll = self.builder.get_object(f"{wid}|{tid}", use_gtk = False) - icon_grid = scroll.get_children()[0] - store = icon_grid.get_store() - tab = self.get_fm_window(wid).get_tab_by_id(tid) - watcher = tab.get_dir_watcher() - - watcher.cancel() - self.get_fm_window(wid).delete_tab_by_id(tid) - - self.builder.dereference_object(f"{wid}|{tid}|icon_grid") - self.builder.dereference_object(f"{wid}|{tid}") - - iter = store.get_iter_first() - while iter: - next_iter = store.iter_next(iter) - store.unref_node(iter) - iter = next_iter - - store.clear() - store.run_dispose() - - icon_grid.set_model(None) - icon_grid.run_dispose() - scroll.run_dispose() - tab_box.run_dispose() - - iter = None - wid, tid = None, None - store = None - icon_grid = None - scroll = None - tab_box = None - watcher = None - tab = None - notebook = None - - if not settings_manager.is_trace_debug(): - self.fm_controller.save_state() - - self.set_window_title() - gc.collect() - - # NOTE: Not actually getting called even tho set in the glade file... - def on_tab_dnded(self, notebook, page, x, y): - ... - - def on_tab_reorder(self, child, page_num, new_index): - wid, tid = page_num.get_name().split("|") - window = self.get_fm_window(wid) - tab = None - - for i, tab in enumerate(window.get_all_tabs()): - if tab.get_id() == tid: - _tab = window.get_tab_by_id(tid) - watcher = _tab.get_dir_watcher() - watcher.cancel() - window.get_all_tabs().insert(new_index, window.get_all_tabs().pop(i)) - - tab = window.get_tab_by_id(tid) - self.set_file_watcher(tab) - if not settings_manager.is_trace_debug(): - self.fm_controller.save_state() - - wid, tid = None, None - window = None - tab = None - - - def on_tab_switch_update(self, notebook, content = None, index = None): - self.selected_files.clear() - wid, tid = content.get_children()[0].get_name().split("|") - - self.fm_controller.set_wid_and_tid(wid, tid) - self.set_path_text(wid, tid) - self.set_window_title() - - wid, tid = None, None - - def get_id_from_tab_box(self, tab_box): - return tab_box.tab.get_id() - - def get_tab_label(self, notebook, icon_grid): - return notebook.get_tab_label(icon_grid.get_parent()).get_children()[0] - - def get_tab_close(self, notebook, icon_grid): - return notebook.get_tab_label(icon_grid.get_parent()).get_children()[1] - - def get_tab_icon_grid_from_notebook(self, notebook): - return notebook.get_children()[1].get_children()[0] - - def refresh_tab(data = None): - state = self.get_current_state() - state.tab.load_directory() - self.load_store(state.tab, state.store) - - state = None - - def update_tab(self, tab_label, tab, store, wid, tid): - self.load_store(tab, store) - self.set_path_text(wid, tid) - - char_width = len(tab.get_end_of_path()) - tab_label.set_width_chars(char_width) - tab_label.set_label(tab.get_end_of_path()) - self.set_window_title() - self.set_file_watcher(tab) - if not settings_manager.is_trace_debug(): - self.fm_controller.save_state() - - def do_action_from_bar_controls(self, widget, eve = None): - action = widget.get_name() - 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) - - if action == "create_tab": - dir = tab.get_current_directory() - self.create_tab(wid, None, dir) - if not settings_manager.is_trace_debug(): - self.fm_controller.save_state() - - return - if action == "go_up": - tab.pop_from_path() - if action == "go_home": - tab.set_to_home() - if action == "refresh_tab": - tab.load_directory() - if action == "path_entry": - focused_obj = self.window.get_focus() - dir = f"{tab.get_current_directory()}/" - path = widget.get_text() - - if isinstance(focused_obj, Gtk.Entry): - self.process_path_menu(widget, tab, dir) - - action = None - store = None - - if path.endswith(".") or path == dir: - tab_label = None - notebook = None - wid, tid = None, None - path = None - tab = None - return - - if not tab.set_path(path): - tab_label = None - notebook = None - wid, tid = None, None - path = None - tab = None - return - - icon_grid = self.get_icon_grid_from_notebook(notebook, f"{wid}|{tid}") - icon_grid.clear_and_set_new_store() - self.update_tab(tab_label, tab, icon_grid.get_store(), wid, tid) - - action = None - wid, tid = None, None - notebook = None - store, tab_label = None, None - path = None - tab = None - icon_grid = None - - - def process_path_menu(self, gtk_entry, tab, dir): - path_menu_buttons = self.builder.get_object("path_menu_buttons") - query = gtk_entry.get_text().replace(dir, "") - files = tab.get_files() + tab.get_hidden() - - self.clear_children(path_menu_buttons) - show_path_menu = False - for file, hash, size in files: - if os.path.isdir(f"{dir}{file}"): - if query.lower() in file.lower(): - button = Gtk.Button(label=file) - button.show() - button.connect("clicked", self.set_path_entry) - path_menu_buttons.add(button) - show_path_menu = True - - query = None - files = None - - if not show_path_menu: - path_menu_buttons = None - event_system.emit("hide_path_menu") - else: - event_system.emit("show_path_menu") - buttons = path_menu_buttons.get_children() - path_menu_buttons = None - - if len(buttons) == 1: - self.slowed_focus(buttons[0]) - - @daemon_threaded - def slowed_focus(self, button): - time.sleep(0.05) - GLib.idle_add(self.do_focused_click, *(button,)) - - def do_focused_click(self, button): - button.grab_focus() - button.clicked() - return False - - def set_path_entry(self, button = None, eve = None): - self.path_auto_filled = True - state = self.get_current_state() - path = f"{state.tab.get_current_directory()}/{button.get_label()}" - path_entry = self.builder.get_object("path_entry") - - path_entry.set_text(path) - path_entry.grab_focus_without_selecting() - path_entry.set_position(-1) - event_system.emit("hide_path_menu") - - state = None - path = None - path_entry = None - - - def show_hide_hidden_files(self): - wid, tid = self.fm_controller.get_active_wid_and_tid() - tab = self.get_fm_window(wid).get_tab_by_id(tid) - tab.set_hiding_hidden(not tab.is_hiding_hidden()) - tab.load_directory() - self.builder.get_object("refresh_tab").released() - - wid, tid = None, None - tab = None \ No newline at end of file diff --git a/src/solarfm/core/mixins/ui/window_mixin.py b/src/solarfm/core/mixins/ui/window_mixin.py deleted file mode 100644 index 618b9bc..0000000 --- a/src/solarfm/core/mixins/ui/window_mixin.py +++ /dev/null @@ -1,204 +0,0 @@ -# Python imports -import copy -import traceback -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 Gdk -from gi.repository import Gio - -# Application imports -from .tab_mixin import TabMixin - - -class WindowException(Exception): - ... - - -class WindowMixin(TabMixin): - """docstring for WindowMixin""" - - def get_fm_window(self, wid): - return self.fm_controller.get_window_by_nickname(f"window_{wid}") - - def set_bottom_labels(self, tab): - event_system.emit("set_bottom_labels", (tab,)) - - def set_window_title(self): - wid, tid = self.fm_controller.get_active_wid_and_tid() - notebook = self.builder.get_object(f"window_{wid}") - tab = self.get_fm_window(wid).get_tab_by_id(tid) - dir = tab.get_current_directory() - - for _notebook in self.notebooks: - ctx = _notebook.get_style_context() - ctx.remove_class("notebook-selected-focus") - ctx.add_class("notebook-unselected-focus") - - ctx = notebook.get_style_context() - ctx.remove_class("notebook-unselected-focus") - ctx.add_class("notebook-selected-focus") - - self.window.set_title(f"{app_name} ~ {dir}") - self.set_bottom_labels(tab) - - wid, tid = None, None - notebook = None - tab = None - dir = None - - def set_path_text(self, wid, tid): - path_entry = self.builder.get_object("path_entry") - tab = self.get_fm_window(wid).get_tab_by_id(tid) - path_entry.set_text(tab.get_current_directory()) - - path_entry = None - tab = None - - def grid_set_selected_items(self, icons_grid): - new_items = icons_grid.get_selected_items() - items_size = len(new_items) - selected_items = event_system.emit_and_await("get_selected_files") - - if items_size == 1: - # NOTE: If already in selection, likely dnd else not so wont readd - if new_items[0] in selected_items: - self.dnd_left_primed += 1 - # NOTE: If in selection but trying to just select an already selected item. - if self.dnd_left_primed > 1: - self.dnd_left_primed = 0 - selected_items.clear() - - # NOTE: Likely trying dnd, just readd to selection the former set. - # Prevents losing highlighting of grid selected. - for path in selected_items: - icons_grid.select_path(path) - - if items_size > 0: - event_system.emit("set_selected_files", (new_items,)) - else: - self.dnd_left_primed = 0 - selected_items.clear() - - def grid_icon_single_click(self, icons_grid, eve): - try: - event_system.emit("hide_path_menu") - wid, tid = icons_grid.get_name().split("|") - self.fm_controller.set_wid_and_tid(wid, tid) - self.set_path_text(wid, tid) - self.set_window_title() - - if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 1: # l-click - if eve.state & Gdk.ModifierType.CONTROL_MASK: - self.dnd_left_primed = 0 - - if self.single_click_open: # FIXME: need to find a way to pass the model index - self.grid_icon_double_click(icons_grid) - elif eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 3: # r-click - event_system.emit("show_context_menu") - - except WindowException as e: - logger.info(repr(e)) - self.display_message(settings.theming.error_color, f"{repr(e)}") - - def grid_icon_double_click(self, icons_grid, item, data=None): - try: - if self.ctrl_down and self.shift_down: - self.unset_keys_and_data() - self.execute_files(in_terminal=True) - return - elif self.ctrl_down: - self.unset_keys_and_data() - self.execute_files() - return - - state = self.get_current_state() - notebook = self.builder.get_object(f"window_{state.wid}") - tab_label = self.get_tab_label(notebook, state.icon_grid) - - fileName = state.store[item][1] - dir = state.tab.get_current_directory() - file = f"{dir}/{fileName}" - - if isdir(file): - state.tab.set_path(file) - state.icon_grid.clear_and_set_new_store() - self.update_tab(tab_label, state.tab, state.icon_grid.get_store(), state.wid, state.tid) - else: - event_system.emit("open_files") - - state = None - notebook = None - tab_label = None - except WindowException as e: - traceback.print_exc() - self.display_message(settings.theming.error_color, f"{repr(e)}") - - - - def grid_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 grid_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() - path_at_loc = None - - try: - data = icons_grid.get_dest_item_at_pos(x, y) - path_at_loc = data[0] - drop_position = data[1] - highlighted_item_path = icons_grid.get_drag_dest_item().path - if path_at_loc and path_at_loc == highlighted_item_path and drop_position == Gtk.IconViewDropPosition.DROP_INTO: - uri = self.format_to_uris(store, wid, tid, highlighted_item_path)[0].replace("file://", "") - self.override_drop_dest = uri if isdir(uri) else None - else: - self.override_drop_dest = None - except Exception as e: - self.override_drop_dest = None - - if target not in current: - self.fm_controller.set_wid_and_tid(wid, tid) - - current = None - target = None - wid, tid = None, None - store = None - path_at_loc = None - - - def grid_on_drag_data_received(self, widget, drag_context, x, y, data, info, time): - if info == 80: - uris = data.get_uris() - state = event_system.emit_and_await("get_current_state") - dest = f"{state.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: - event_system.emit("move_files", (uris, dest)) - - Gtk.drag_finish(drag_context, True, False, time) - return - - Gtk.drag_finish(drag_context, False, False, time) - - def create_new_tab_notebook(self, widget=None, wid=None, path=None): - self.create_tab(wid, None, path) \ No newline at end of file diff --git a/src/solarfm/core/ui_mixin.py b/src/solarfm/core/ui_mixin.py index 212802b..d6d71cd 100644 --- a/src/solarfm/core/ui_mixin.py +++ b/src/solarfm/core/ui_mixin.py @@ -10,7 +10,7 @@ from gi.repository import GLib # Application imports from .mixins.ui.pane_mixin import PaneMixin -from .mixins.ui.window_mixin import WindowMixin +from .widgets.files_view.window_mixin import WindowMixin from .widgets.files_view.files_widget import FilesWidget diff --git a/src/solarfm/core/widgets/files_view/grid_mixin.py b/src/solarfm/core/widgets/files_view/grid_mixin.py index 083fb81..0b5a7ec 100644 --- a/src/solarfm/core/widgets/files_view/grid_mixin.py +++ b/src/solarfm/core/widgets/files_view/grid_mixin.py @@ -20,81 +20,41 @@ class GridMixin: """docstring for GridMixin""" def load_store(self, tab, store, save_state = False, use_generator = False): - # dir = tab.get_current_directory() - # file = Gio.File.new_for_path(dir) - # dir_list = Gtk.DirectoryList.new("standard::*", file) - # store.set(dir_list) - - # file = Gio.File.new_for_path(dir) - # for file in file.enumerate_children("standard::*", Gio.FILE_ATTRIBUTE_STANDARD_NAME, None): - # store.append(file) - - # return - dir = tab.get_current_directory() files = tab.get_files() for file in files: store.append([None, file[0]]) - Gtk.main_iteration() - self.generate_icons(tab, store, dir, files) - # GLib.Thread("", self.generate_icons, tab, store, dir, files) + self.load_icons(tab, store, dir, files, self.update_store) # NOTE: Not likely called often from here but it could be useful if save_state and not trace_debug: self.fm_controller.save_state() - dir = None - files = None - @daemon_threaded - def generate_icons(self, tab, store, dir, files): - for i, file in enumerate(files): - # GLib.Thread(f"{i}", self.make_and_load_icon, i, store, tab, dir, file[0]) - self.make_and_load_icon( i, store, tab, dir, file[0]) + def load_icons(self, tab, store, dir, files, callback): + icons = [] + for file in files: + icons.append( + tab.create_icon(dir, file[0]) + ) - def update_store(self, i, store, icon): - itr = store.get_iter(i) - GLib.idle_add(self.insert_store, store, itr, icon) - itr = None + GLib.idle_add(callback, store, icons) - @daemon_threaded - def make_and_load_icon(self, i, store, tab, dir, file): - icon = tab.create_icon(dir, file) - self.update_store(i, store, icon) - icon = None - - def get_icon(self, tab, dir, file): - tab.create_icon(dir, file) - - - # @daemon_threaded - # def generate_icons(self, tab, store, dir, files): - # try: - # loop = asyncio.get_running_loop() - # except RuntimeError: - # loop = None - - # if loop and loop.is_running(): - # loop = asyncio.get_event_loop() - # loop.create_task( self.create_icons(tab, store, dir, files) ) - # else: - # asyncio.run( self.create_icons(tab, store, dir, files) ) - - # async def create_icons(self, tab, store, dir, files): - # icons = [self.get_icon(tab, dir, file[0]) for file in files] - # data = await asyncio.gather(*icons) - # tasks = [self.update_store(i, store, icon) for i, icon in enumerate(data)] - # asyncio.gather(*tasks) - - # async def update_store(self, i, store, icon): - # itr = store.get_iter(i) - # GLib.idle_add(self.insert_store, store, itr, icon) - - # async def get_icon(self, tab, dir, file): - # return tab.create_icon(dir, file) + def update_store(self, store, icons): + for i, icon in enumerate(icons): + try: + itr = store.get_iter(i) + store.set_value(itr, 0, icon) + icon.run_dispose() + except: + icon.run_dispose() + continue + store.run_dispose() + del icons + del store def insert_store(self, store, itr, icon): store.set_value(itr, 0, icon) @@ -104,6 +64,7 @@ class GridMixin: def do_ui_update(self): Gtk.main_iteration() + # Note: If the function returns GLib.SOURCE_REMOVE or False it is automatically removed from the list of event sources and will not be called again. return False def create_tab_widget(self): @@ -164,6 +125,7 @@ class GridMixin: store = icon_grid.get_model() tab_label = notebook.get_tab_label(obj).get_children()[0] + icon_grid = None return store, tab_label def get_icon_grid_from_notebook(self, notebook, _name): @@ -171,4 +133,4 @@ class GridMixin: icon_grid = obj.get_children()[0] name = icon_grid.get_name() if name == _name: - return icon_grid + return icon_grid \ No newline at end of file diff --git a/src/solarfm/core/widgets/files_view/tab_mixin.py b/src/solarfm/core/widgets/files_view/tab_mixin.py index 8a49f94..91241d8 100644 --- a/src/solarfm/core/widgets/files_view/tab_mixin.py +++ b/src/solarfm/core/widgets/files_view/tab_mixin.py @@ -1,7 +1,7 @@ # Python imports import os -import gc import time +import gc # Lib imports import gi @@ -22,9 +22,9 @@ class TabMixin(GridMixin): wid, tid = self.fm_controller.get_active_wid_and_tid() notebook = self.builder.get_object(f"window_{wid}") - # path_entry = self.builder.get_object(f"path_entry") + path_entry = self.builder.get_object(f"path_entry") tab = self.fm_controller.add_tab_for_window_by_nickname(f"window_{wid}") - # tab.logger = logger + tab.logger = logger tab.set_wid(wid) if not path: @@ -41,8 +41,8 @@ class TabMixin(GridMixin): notebook.set_tab_reorderable(scroll, True) self.fm_controller.set_wid_and_tid(wid, tab.get_id()) - # path_entry.set_text(tab.get_current_directory()) - event_system.emit("go_to_path", (tab.get_current_directory(),)) # NOTE: Not efficent if I understand how + path_entry.set_text(tab.get_current_directory()) + # event_system.emit("go_to_path", (tab.get_current_directory(),)) # NOTE: Not efficent if I understand how notebook.show_all() notebook.set_current_page(index) @@ -52,18 +52,10 @@ class TabMixin(GridMixin): event_system.emit("set_window_title", (tab.get_current_directory(),)) self.set_file_watcher(tab) - tab_widget = None - scroll, store = None, None - index = None - notebook = None - # path_entry = None - tab = None - ctx = None - def get_tab_widget(self, tab): - tab_widget = self.create_tab_widget() - tab_widget.tab_id = tab.get_id() + tab_widget = self.create_tab_widget() + tab_widget.tab = tab tab_widget.label.set_label(f"{tab.get_end_of_path()}") tab_widget.label.set_width_chars(len(tab.get_end_of_path())) @@ -72,59 +64,57 @@ class TabMixin(GridMixin): def close_tab(self, button, eve = None): notebook = button.get_parent().get_parent() - if notebook.get_n_pages() == 1: - notebook = None - return + if notebook.get_n_pages() == 1: return tab_box = button.get_parent() wid = int(notebook.get_name()[-1]) tid = self.get_id_from_tab_box(tab_box) scroll = self.builder.get_object(f"{wid}|{tid}", use_gtk = False) icon_grid = scroll.get_children()[0] - store = icon_grid.get_model() tab = self.get_fm_window(wid).get_tab_by_id(tid) watcher = tab.get_dir_watcher() watcher.cancel() + watcher.disconnect(watcher.watch_id) + watcher.run_dispose() self.get_fm_window(wid).delete_tab_by_id(tid) + logger.debug(f"Reference count for watcher is: {watcher.__grefcount__}") + logger.debug(f"Reference count for tab_box is: {tab_box.__grefcount__}") + logger.debug(f"Reference count for icon_grid is: {icon_grid.__grefcount__}") + logger.debug(f"Reference count for scroll is: {scroll.__grefcount__}") + + tab_box.clear_signals_and_data() + icon_grid.clear_signals_and_data() + self.builder.dereference_object(f"{wid}|{tid}|icon_grid") self.builder.dereference_object(f"{wid}|{tid}") - iter = store.get_iter_first() - while iter: - next_iter = store.iter_next(iter) - store.unref_node(iter) - iter = next_iter + notebook.remove_page( notebook.page_num(scroll) ) - store.clear() - store.run_dispose() - - icon_grid.set_model(None) + tab_box.tab = None + tab_box.unparent() + tab_box.run_dispose() icon_grid.run_dispose() scroll.run_dispose() - tab_box.run_dispose() + icon_grid.unparent() + scroll.unparent() + + Gtk.main_iteration_do(False) + gc.collect() + + logger.debug(f"Reference count for tab_box is: {tab_box.__grefcount__}") + logger.debug(f"Reference count for icon_grid is: {icon_grid.__grefcount__}") + logger.debug(f"Reference count for scroll is: {scroll.__grefcount__}") - iter = None - wid, tid = None, None - store = None - icon_grid = None - scroll = None - tab_box = None - watcher = None - tab = None - notebook = None if not settings_manager.is_trace_debug(): self.fm_controller.save_state() self.set_window_title() - gc.collect() - - # NOTE: Not actually getting called even tho set in the glade file... def on_tab_dnded(self, notebook, page, x, y): - ... + logger.info("Create new window on tab dnd outside stub...") def on_tab_reorder(self, child, page_num, new_index): wid, tid = page_num.get_name().split("|") @@ -136,6 +126,8 @@ class TabMixin(GridMixin): _tab = window.get_tab_by_id(tid) watcher = _tab.get_dir_watcher() watcher.cancel() + watcher.disconnect(watcher.watch_id) + watcher.run_dispose() window.get_all_tabs().insert(new_index, window.get_all_tabs().pop(i)) tab = window.get_tab_by_id(tid) @@ -143,20 +135,14 @@ class TabMixin(GridMixin): if not settings_manager.is_trace_debug(): self.fm_controller.save_state() - wid, tid = None, None - window = None - tab = None - - def on_tab_switch_update(self, notebook, content = None, index = None): self.selected_files.clear() - wid, tid = content.get_children()[0].tab.get_name().split("|") + wid, tid = content.get_children()[0].get_name().split("|") + self.fm_controller.set_wid_and_tid(wid, tid) self.set_path_text(wid, tid) self.set_window_title() - wid, tid = None, None - def get_id_from_tab_box(self, tab_box): return tab_box.tab.get_id() @@ -237,7 +223,7 @@ class TabMixin(GridMixin): icon_grid = self.get_icon_grid_from_notebook(notebook, f"{wid}|{tid}") icon_grid.clear_and_set_new_store() - self.update_tab(tab_label, tab, store, wid, tid) + self.update_tab(tab_label, tab, icon_grid.get_store(), wid, tid) action = None wid, tid = None, None @@ -264,15 +250,16 @@ class TabMixin(GridMixin): path_menu_buttons.add(button) show_path_menu = True - path_menu_buttons = None - query = None - files = None + query = None + files = None if not show_path_menu: + path_menu_buttons = None event_system.emit("hide_path_menu") else: event_system.emit("show_path_menu") buttons = path_menu_buttons.get_children() + path_menu_buttons = None if len(buttons) == 1: self.slowed_focus(buttons[0]) @@ -285,7 +272,6 @@ class TabMixin(GridMixin): def do_focused_click(self, button): button.grab_focus() button.clicked() - return False def set_path_entry(self, button = None, eve = None): @@ -303,6 +289,7 @@ class TabMixin(GridMixin): path = None path_entry = None + def show_hide_hidden_files(self): wid, tid = self.fm_controller.get_active_wid_and_tid() tab = self.get_fm_window(wid).get_tab_by_id(tid) @@ -311,4 +298,4 @@ class TabMixin(GridMixin): self.builder.get_object("refresh_tab").released() wid, tid = None, None - tab = None + tab = None \ No newline at end of file diff --git a/src/solarfm/core/widgets/files_view/window_mixin.py b/src/solarfm/core/widgets/files_view/window_mixin.py index 23a12ab..618b9bc 100644 --- a/src/solarfm/core/widgets/files_view/window_mixin.py +++ b/src/solarfm/core/widgets/files_view/window_mixin.py @@ -34,12 +34,16 @@ class WindowMixin(TabMixin): tab = self.get_fm_window(wid).get_tab_by_id(tid) dir = tab.get_current_directory() - event_system.emit("unset_selected_files_views") - ctx = self.files_view.get_style_context() + for _notebook in self.notebooks: + ctx = _notebook.get_style_context() + ctx.remove_class("notebook-selected-focus") + ctx.add_class("notebook-unselected-focus") + + ctx = notebook.get_style_context() ctx.remove_class("notebook-unselected-focus") ctx.add_class("notebook-selected-focus") - event_system.emit("set_window_title", (dir,)) + self.window.set_title(f"{app_name} ~ {dir}") self.set_bottom_labels(tab) wid, tid = None, None @@ -48,10 +52,12 @@ class WindowMixin(TabMixin): dir = None def set_path_text(self, wid, tid): - tab = self.get_fm_window(wid).get_tab_by_id(tid) - event_system.emit("go_to_path", (tab.get_current_directory(),)) + path_entry = self.builder.get_object("path_entry") + tab = self.get_fm_window(wid).get_tab_by_id(tid) + path_entry.set_text(tab.get_current_directory()) - tab = None + path_entry = None + tab = None def grid_set_selected_items(self, icons_grid): new_items = icons_grid.get_selected_items() @@ -110,7 +116,6 @@ class WindowMixin(TabMixin): self.execute_files() return - state = self.get_current_state() notebook = self.builder.get_object(f"window_{state.wid}") tab_label = self.get_tab_label(notebook, state.icon_grid) @@ -125,6 +130,10 @@ class WindowMixin(TabMixin): self.update_tab(tab_label, state.tab, state.icon_grid.get_store(), state.wid, state.tid) else: event_system.emit("open_files") + + state = None + notebook = None + tab_label = None except WindowException as e: traceback.print_exc() self.display_message(settings.theming.error_color, f"{repr(e)}") @@ -192,4 +201,4 @@ class WindowMixin(TabMixin): Gtk.drag_finish(drag_context, False, False, time) def create_new_tab_notebook(self, widget=None, wid=None, path=None): - self.create_tab(wid, None, path) + self.create_tab(wid, None, path) \ No newline at end of file diff --git a/src/solarfm/core/widgets/icon_grid_widget.py b/src/solarfm/core/widgets/icon_grid_widget.py index 6114077..8263656 100644 --- a/src/solarfm/core/widgets/icon_grid_widget.py +++ b/src/solarfm/core/widgets/icon_grid_widget.py @@ -1,4 +1,5 @@ # Python imports +import gc # Lib imports import gi @@ -20,6 +21,8 @@ class IconGridWidget(Gtk.IconView): def __init__(self): super(IconGridWidget, self).__init__() + self._handler_ids = [] + self._setup_styling() self._setup_signals() self._set_up_dnd() @@ -52,12 +55,12 @@ class IconGridWidget(Gtk.IconView): grid_on_drag_data_received, grid_on_drag_motion): - self.connect("button_release_event", grid_icon_single_click) - self.connect("item-activated", grid_icon_double_click) - self.connect("selection-changed", grid_set_selected_items) - self.connect("drag-data-get", grid_on_drag_set) - self.connect("drag-data-received", grid_on_drag_data_received) - self.connect("drag-motion", grid_on_drag_motion) + self._handler_ids.append(self.connect("button_release_event", grid_icon_single_click)) + self._handler_ids.append(self.connect("item-activated", grid_icon_double_click)) + self._handler_ids.append(self.connect("selection-changed", grid_set_selected_items)) + self._handler_ids.append(self.connect("drag-data-get", grid_on_drag_set)) + self._handler_ids.append(self.connect("drag-data-received", grid_on_drag_data_received)) + self._handler_ids.append(self.connect("drag-motion", grid_on_drag_motion)) def _load_widgets(self): self.clear_and_set_new_store() @@ -75,20 +78,45 @@ class IconGridWidget(Gtk.IconView): return self.get_model() def clear_and_set_new_store(self): + self._clear_store() + self.set_model( + Gtk.ListStore( + GdkPixbuf.Pixbuf or GdkPixbuf.PixbufAnimation or None, str or None + ) + ) + + def clear_signals_and_data(self): + self.unset_model_drag_dest() + self.unset_model_drag_source() + + for handle_id in self._handler_ids: + self.disconnect(handle_id) + + self._handler_ids.clear() + self._clear_store() + + def _clear_store(self): store = self.get_model() - if store: - iter = store.get_iter_first() - while iter: - next_iter = store.iter_next(iter) - store.unref_node(iter) - iter = next_iter - - store.clear() - store.run_dispose() - store = None - self.set_model(None) - store = Gtk.ListStore(GdkPixbuf.Pixbuf or GdkPixbuf.PixbufAnimation or None, str or None) - # store = Gtk.ListStore(Gtk.DirectoryList) - self.set_model(store) - store = None \ No newline at end of file + + if not store: return + + iter = store.get_iter_first() + while iter: + icon = store.get_value(iter, 0) + + store.set_value(iter, 0, None) + store.set_value(iter, 1, None) + + iter = store.iter_next(iter) + + if icon: + logger.debug(f"Reference count for icon is: {icon.__grefcount__}") + icon.run_dispose() + del icon + + store.clear() + store.run_dispose() + del store + + gc.collect() diff --git a/src/solarfm/core/widgets/tab_header_widget.py b/src/solarfm/core/widgets/tab_header_widget.py index 729db6f..0bcab0c 100644 --- a/src/solarfm/core/widgets/tab_header_widget.py +++ b/src/solarfm/core/widgets/tab_header_widget.py @@ -15,7 +15,7 @@ class TabHeaderWidget(Gtk.Box): def __init__(self, close_tab): super(TabHeaderWidget, self).__init__() - self._close_tab = close_tab # NOTE: Close method in tab_mixin + self._close_tab = close_tab # NOTE: Close method is from tab_mixin self._setup_styling() self._setup_signals() @@ -30,19 +30,28 @@ class TabHeaderWidget(Gtk.Box): ... def _load_widgets(self): - self.label = Gtk.Label() - close = Gtk.Button() - icon = Gtk.Image(stock=Gtk.STOCK_CLOSE) + self.label = Gtk.Label() + self.close_btn = Gtk.Button() + icon = Gtk.Image(stock = Gtk.STOCK_CLOSE) self.label.set_xalign(0.0) self.label.set_margin_left(25) self.label.set_margin_right(25) self.label.set_hexpand(True) - close.connect("released", self._close_tab) + self._handler_id = self.close_btn.connect("released", self._close_tab) - close.add(icon) + self.close_btn.add(icon) self.add(self.label) - self.add(close) + self.add(self.close_btn) - self.show_all() \ No newline at end of file + self.show_all() + + def clear_signals_and_data(self): + self.close_btn.disconnect(self._handler_id) + self._close_tab = None + self._handler_id = None + + for child in self.get_children(): + child.unparent() + child.run_dispose() diff --git a/src/solarfm/shellfm/windows/tabs/utils/launcher.py b/src/solarfm/shellfm/windows/tabs/utils/launcher.py index 1b9d772..84ede79 100644 --- a/src/solarfm/shellfm/windows/tabs/utils/launcher.py +++ b/src/solarfm/shellfm/windows/tabs/utils/launcher.py @@ -17,22 +17,25 @@ class Launcher: lowerName = file.lower() command = [] - if lowerName.endswith(self.fvideos): - command = [self.media_app, file] - elif lowerName.endswith(self.fimages): - command = [self.image_app, file] - elif lowerName.endswith(self.fmusic): - command = [self.music_app, file] - elif lowerName.endswith(self.foffice): - command = [self.office_app, file] - elif lowerName.endswith(self.fcode): - command = [self.code_app, file] - elif lowerName.endswith(self.ftext): - command = [self.text_app, file] - elif lowerName.endswith(self.fpdf): - command = [self.pdf_app, file] - elif lowerName.endswith("placeholder-until-i-can-get-a-use-pref-fm-flag"): - command = [self.file_manager_app, file] + if self.use_defined_launchers: + if lowerName.endswith(self.fvideos): + command = [self.media_app, file] + elif lowerName.endswith(self.fimages): + command = [self.image_app, file] + elif lowerName.endswith(self.fmusic): + command = [self.music_app, file] + elif lowerName.endswith(self.foffice): + command = [self.office_app, file] + elif lowerName.endswith(self.fcode): + command = [self.code_app, file] + elif lowerName.endswith(self.ftext): + command = [self.text_app, file] + elif lowerName.endswith(self.fpdf): + command = [self.pdf_app, file] + elif lowerName.endswith("placeholder-until-i-can-get-a-use-pref-fm-flag"): + command = [self.file_manager_app, file] + else: + command = ["xdg-open", file] else: command = ["xdg-open", file] @@ -42,7 +45,7 @@ class Launcher: def execute(self, command, start_dir=os.getenv("HOME"), use_shell=False): try: logger.debug(command) - subprocess.Popen(command, cwd=start_dir, shell=use_shell, start_new_session=True, stdout=None, stderr=None, close_fds=True) + subprocess.Popen(command, cwd=start_dir, shell=use_shell, start_new_session=True, stdout=subprocess.DEVNULL, stderr=None, close_fds=True) except ShellFMLauncherException as e: logger.error(f"Couldn't execute: {command}") logger.error(e) @@ -50,8 +53,7 @@ class Launcher: # TODO: Return std(out/in/err) handlers along with subprocess instead of sinking to null def execute_and_return_thread_handler(self, command, start_dir=os.getenv("HOME"), use_shell=False): try: - DEVNULL = open(os.devnull, 'w') - return subprocess.Popen(command, cwd=start_dir, shell=use_shell, start_new_session=False, stdout=DEVNULL, stderr=DEVNULL, close_fds=False) + return subprocess.Popen(command, cwd=start_dir, shell=use_shell, start_new_session=False, stdout=subprocess.DEVNULL, stderr=None, close_fds=False) except ShellFMLauncherException as e: logger.error(f"Couldn't execute and return thread: {command}") logger.error(e) @@ -80,7 +82,7 @@ class Launcher: command += [remux_vid_pth] try: - proc = subprocess.Popen(command) + proc = subprocess.Popen(command, stdout=subprocess.DEVNULL, stderr=None) proc.wait() except ShellFMLauncherException as e: logger.error(message) diff --git a/src/solarfm/shellfm/windows/tabs/utils/settings.py b/src/solarfm/shellfm/windows/tabs/utils/settings.py index 3be7131..dbc059d 100644 --- a/src/solarfm/shellfm/windows/tabs/utils/settings.py +++ b/src/solarfm/shellfm/windows/tabs/utils/settings.py @@ -41,6 +41,7 @@ class Settings: lock_folder = False if config["lock_folder"] in ["false", ""] else True locked_folders = config["locked_folders"].split("::::") mplayer_options = config["mplayer_options"].split() + use_defined_launchers = config["use_defined_launchers"] music_app = config["music_app"] media_app = config["media_app"] image_app = config["image_app"] diff --git a/src/solarfm/utils/ipc_server.py b/src/solarfm/utils/ipc_server.py index 8fecbf2..f1cfa23 100644 --- a/src/solarfm/utils/ipc_server.py +++ b/src/solarfm/utils/ipc_server.py @@ -14,9 +14,9 @@ from .singleton import Singleton class IPCServer(Singleton): """ Create a listener so that other SolarFM instances send requests back to existing instance. """ - def __init__(self, ipc_address: str = '127.0.0.1', conn_type: str = "socket"): + def __init__(self, ipc_address: str = '127.0.0.1', conn_type: str = "local_network_unsecured"): self.is_ipc_alive = False - self._ipc_port = 4848 + self._ipc_port = 0 # Use 0 to let Listener chose port self._ipc_address = ipc_address self._conn_type = conn_type self._ipc_authkey = b'' + bytes(f'{app_name}-ipc', 'utf-8') diff --git a/src/solarfm/utils/settings_manager/options/config.py b/src/solarfm/utils/settings_manager/options/config.py index f8719d6..f173ae3 100644 --- a/src/solarfm/utils/settings_manager/options/config.py +++ b/src/solarfm/utils/settings_manager/options/config.py @@ -16,6 +16,7 @@ class Config: lock_folder: str = "false" locked_folders: list = field(default_factory=lambda: ["venv", "flasks"]) mplayer_options: str = "-quiet -really-quiet -xy 1600 -geometry 50%:50%" + use_defined_launchers: bool = False music_app: str = "deadbeef" media_app: str = "mpv" image_app: str = "mirage"