develop #11
| @@ -37,14 +37,14 @@ 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_controller_data() | ||||
|  | ||||
|         self._setup_styling() | ||||
|         self._setup_signals() | ||||
|         self._subscribe_to_events() | ||||
|         self._load_widgets() | ||||
|  | ||||
|         self.generate_windows(self.fm_controller_data) | ||||
|         self._generate_file_views(self.fm_controller_data) | ||||
|  | ||||
|         if args.no_plugins == "false": | ||||
|             self.plugins.launch_plugins() | ||||
| @@ -65,8 +65,10 @@ class Controller(UIMixin, SignalsMixins, Controller_Data): | ||||
|  | ||||
|     def _subscribe_to_events(self): | ||||
|         event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc) | ||||
|         event_system.subscribe("generate_windows", self.generate_windows) | ||||
|         event_system.subscribe("generate_file_views", self._generate_file_views) | ||||
|         event_system.subscribe("clear_notebooks", self.clear_notebooks) | ||||
|         event_system.subscribe("set_window_title", self._set_window_title) | ||||
|         event_system.subscribe("unset_selected_files_views", self._unset_selected_files_views) | ||||
|         event_system.subscribe("get_current_state", self.get_current_state) | ||||
|         event_system.subscribe("go_to_path", self.go_to_path) | ||||
|         event_system.subscribe("format_to_uris", self.format_to_uris) | ||||
| @@ -105,7 +107,6 @@ class Controller(UIMixin, SignalsMixins, Controller_Data): | ||||
|         FileExistsWidget() | ||||
|         SaveLoadWidget() | ||||
|  | ||||
|  | ||||
|     def reload_plugins(self, widget=None, eve=None): | ||||
|         self.plugins.reload_plugins() | ||||
|  | ||||
|   | ||||
| @@ -37,7 +37,7 @@ class Controller_Data: | ||||
|     """ Controller_Data contains most of the state of the app at ay given time. It also has some support methods. """ | ||||
|     __slots__ = "settings", "builder", "logger", "keybindings", "trashman", "fm_controller", "window", "window1", "window2", "window3", "window4" | ||||
|  | ||||
|     def setup_controller_data(self) -> None: | ||||
|     def _setup_controller_data(self) -> None: | ||||
|         self.window        = settings.get_main_window() | ||||
|         self.builder       = None | ||||
|         self.core_widget   = None | ||||
| @@ -87,6 +87,7 @@ class Controller_Data: | ||||
|         state.wid, state.tid = self.fm_controller.get_active_wid_and_tid() | ||||
|         state.tab            = self.get_fm_window(state.wid).get_tab_by_id(state.tid) | ||||
|         state.icon_grid      = self.builder.get_object(f"{state.wid}|{state.tid}|icon_grid") | ||||
|         # state.icon_grid      = event_system.emit_and_await("get_files_view_icon_grid", (state.wid, state.tid)) | ||||
|         state.store          = state.icon_grid.get_model() | ||||
|         state.message_dialog = MessageWidget() | ||||
|  | ||||
| @@ -103,7 +104,7 @@ class Controller_Data: | ||||
|         # 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) | ||||
|         event_system.emit("update_state_info_plugins", state) # NOTE: Need to remove after we convert plugins to use emit_and_await | ||||
|         return state | ||||
|  | ||||
|     def format_to_uris(self, store, wid, tid, treePaths, use_just_path=False): | ||||
| @@ -125,6 +126,20 @@ class Controller_Data: | ||||
|  | ||||
|         return uris | ||||
|  | ||||
|  | ||||
|     def get_fm_window(self, wid): | ||||
|         return self.fm_controller.get_window_by_nickname(f"window_{wid}") | ||||
|  | ||||
|     def _unset_selected_files_views(self): | ||||
|         for _notebook in self.notebooks: | ||||
|             ctx = _notebook.get_style_context() | ||||
|             ctx.remove_class("notebook-selected-focus") | ||||
|             ctx.add_class("notebook-unselected-focus") | ||||
|  | ||||
|     def _set_window_title(self, dir): | ||||
|         self.window.set_title(f"{app_name} ~ {dir}") | ||||
|  | ||||
|  | ||||
|     def clear_console(self) -> None: | ||||
|         ''' Clears the terminal screen. ''' | ||||
|         os.system('cls' if os.name == 'nt' else 'clear') | ||||
|   | ||||
| @@ -22,53 +22,6 @@ class WindowException(Exception): | ||||
| 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 | ||||
|                 notebook_tggl_button = self.builder.get_object(f"tggl_notebook_{i}") | ||||
|                 is_hidden = True if value["window"]["isHidden"] == "True" else False | ||||
|                 tabs      = value["window"]["tabs"] | ||||
|                 self.fm_controller.create_window() | ||||
|                 notebook_tggl_button.set_active(True) | ||||
|  | ||||
|                 if tabs: | ||||
|                     for tab in tabs: | ||||
|                         self.create_new_tab_notebook(None, i, tab) | ||||
|                 else: | ||||
|                     self.create_new_tab_notebook(None, i, None) | ||||
|  | ||||
|                 if is_hidden: | ||||
|                     self.toggle_notebook_pane(notebook_tggl_button) | ||||
|  | ||||
|             try: | ||||
|                 if not self.is_pane4_hidden: | ||||
|                     icon_grid = self.window4.get_children()[-1].get_children()[0] | ||||
|                 elif not self.is_pane3_hidden: | ||||
|                     icon_grid = self.window3.get_children()[-1].get_children()[0] | ||||
|                 elif not self.is_pane2_hidden: | ||||
|                     icon_grid = self.window2.get_children()[-1].get_children()[0] | ||||
|                 elif not self.is_pane1_hidden: | ||||
|                     icon_grid = self.window1.get_children()[-1].get_children()[0] | ||||
|  | ||||
|                 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: | ||||
|                 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 | ||||
|                 self.fm_controller.create_window() | ||||
|                 self.create_new_tab_notebook(None, i, None) | ||||
|  | ||||
|  | ||||
|     def get_fm_window(self, wid): | ||||
|         return self.fm_controller.get_window_by_nickname(f"window_{wid}") | ||||
|  | ||||
|   | ||||
| @@ -1,13 +1,78 @@ | ||||
| # Python imports | ||||
|  | ||||
| # Gtk 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 | ||||
|  | ||||
| # Application imports | ||||
| from .mixins.ui.pane_mixin import PaneMixin | ||||
| from .mixins.ui.window_mixin import WindowMixin | ||||
|  | ||||
| from .widgets.files_view.files_widget import FilesWidget | ||||
|  | ||||
|  | ||||
| class UIMixinException(Exception): | ||||
|     ... | ||||
|  | ||||
|  | ||||
| class UIMixin(PaneMixin, WindowMixin): | ||||
|     ... | ||||
| # class UIMixin(PaneMixin): | ||||
|     def _generate_file_views(self, session_json = None): | ||||
|         # self._wip_hybrid_approach_loading_process(session_json) | ||||
|         self._current_loading_process(session_json) | ||||
|  | ||||
|  | ||||
|     def _wip_hybrid_approach_loading_process(self, session_json = None): | ||||
|         for session in session_json: | ||||
|             self.fm_controller.create_window() | ||||
|             FilesWidget().set_fm_controller(self.fm_controller) | ||||
|  | ||||
|         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_files_view_state", (nickname, tabs)) | ||||
|  | ||||
|  | ||||
|     def _current_loading_process(self, session_json = None): | ||||
|         if session_json: | ||||
|             for j, value in enumerate(session_json): | ||||
|                 i = j + 1 | ||||
|                 notebook_tggl_button = self.builder.get_object(f"tggl_notebook_{i}") | ||||
|                 is_hidden = True if value["window"]["isHidden"] == "True" else False | ||||
|                 tabs      = value["window"]["tabs"] | ||||
|                 self.fm_controller.create_window() | ||||
|                 notebook_tggl_button.set_active(True) | ||||
|  | ||||
|                 if tabs: | ||||
|                     for tab in tabs: | ||||
|                         self.create_new_tab_notebook(None, i, tab) | ||||
|                 else: | ||||
|                     self.create_new_tab_notebook(None, i, None) | ||||
|  | ||||
|                 if is_hidden: | ||||
|                     self.toggle_notebook_pane(notebook_tggl_button) | ||||
|  | ||||
|             try: | ||||
|                 if not self.is_pane4_hidden: | ||||
|                     icon_grid = self.window4.get_children()[-1].get_children()[0] | ||||
|                 elif not self.is_pane3_hidden: | ||||
|                     icon_grid = self.window3.get_children()[-1].get_children()[0] | ||||
|                 elif not self.is_pane2_hidden: | ||||
|                     icon_grid = self.window2.get_children()[-1].get_children()[0] | ||||
|                 elif not self.is_pane1_hidden: | ||||
|                     icon_grid = self.window1.get_children()[-1].get_children()[0] | ||||
|  | ||||
|                 icon_grid.event(Gdk.Event().new(type=Gdk.EventType.BUTTON_RELEASE)) | ||||
|                 icon_grid.event(Gdk.Event().new(type=Gdk.EventType.BUTTON_RELEASE)) | ||||
|             except UIMixinException as 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 | ||||
|                 self.fm_controller.create_window() | ||||
|                 self.create_new_tab_notebook(None, i, None) | ||||
|   | ||||
| @@ -80,5 +80,5 @@ class SaveLoadWidget: | ||||
|         state = event_system.emit_and_await("get_current_state") | ||||
|         event_system.emit("clear_notebooks") | ||||
|         state.fm_controller.unload_tabs_and_windows() | ||||
|         event_system.emit("generate_windows", (session_json,)) | ||||
|         event_system.emit("generate_file_views", (session_json,)) | ||||
|         gc.collect() | ||||
|   | ||||
| @@ -0,0 +1,3 @@ | ||||
| """ | ||||
|     Files View 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 ...mixins.signals.file_action_signals_mixin import FileActionSignalsMixin | ||||
| from .window_mixin import WindowMixin | ||||
|  | ||||
|  | ||||
|  | ||||
| class FilesWidget(FileActionSignalsMixin, WindowMixin): | ||||
|     """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.INDEX   = self.ccount | ||||
|         self.NAME    = f"window_{self.INDEX}" | ||||
|         self.builder = Gtk.Builder() | ||||
|         self.files_view    = None | ||||
|         self.fm_controller = None | ||||
|  | ||||
|         self._setup_styling() | ||||
|         self._setup_signals() | ||||
|         self._subscribe_to_events() | ||||
|         self._load_widgets() | ||||
|  | ||||
|  | ||||
|     def _setup_styling(self): | ||||
|         ... | ||||
|  | ||||
|     def _setup_signals(self): | ||||
|         settings.register_signals_to_builder([self,], self.builder) | ||||
|  | ||||
|     def _subscribe_to_events(self): | ||||
|         event_system.subscribe("load_files_view_state", self._load_files_view_state) | ||||
|         event_system.subscribe("get_files_view_icon_grid", self._get_files_view_icon_grid) | ||||
|  | ||||
|     def _load_widgets(self): | ||||
|         _builder   = settings.get_builder() | ||||
|         self.files_view = _builder.get_object(f"{self.NAME}") | ||||
|  | ||||
|         self.files_view.set_group_name("files_widget") | ||||
|         self.builder.expose_object(f"{self.NAME}", self.files_view) | ||||
|  | ||||
|     def _load_files_view_state(self, win_name=None, tabs=None): | ||||
|         if win_name == self.NAME: | ||||
|             if tabs: | ||||
|                 for tab in tabs: | ||||
|                     self.create_new_tab_notebook(None, self.INDEX, tab) | ||||
|             else: | ||||
|                 self.create_new_tab_notebook(None, self.INDEX, None) | ||||
|  | ||||
|     def _get_files_view_icon_grid(self, win_index=None, tid=None): | ||||
|         if win_index == str(self.INDEX): | ||||
|             return self.builder.get_object(f"{self.INDEX}|{tid}|icon_grid") | ||||
|  | ||||
|  | ||||
|     def set_fm_controller(self, _fm_controller): | ||||
|         self.fm_controller = _fm_controller | ||||
| @@ -0,0 +1,105 @@ | ||||
| # Python imports | ||||
|  | ||||
| # Lib imports | ||||
| import gi | ||||
|  | ||||
| gi.require_version("Gtk", "3.0") | ||||
| from gi.repository import Gtk | ||||
| from gi.repository import GLib | ||||
|  | ||||
| # 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): | ||||
|         store.clear() | ||||
|         dir   = tab.get_current_directory() | ||||
|         files = tab.get_files() | ||||
|  | ||||
|         for file in files: | ||||
|             store.append([None, file[0]]) | ||||
|  | ||||
|         Gtk.main_iteration() | ||||
|         for i, file in enumerate(files): | ||||
|             self.create_icon(i, tab, store, dir, file[0]) | ||||
|  | ||||
|         # NOTE: Not likely called often from here but it could be useful | ||||
|         if save_state and not trace_debug: | ||||
|             self.fm_controller.save_state() | ||||
|  | ||||
|  | ||||
|     @daemon_threaded | ||||
|     def create_icon(self, i, tab, store, dir, file): | ||||
|         icon = tab.create_icon(dir, file) | ||||
|         GLib.idle_add(self.update_store, *(i, store, icon,)) | ||||
|  | ||||
|     def update_store(self, i, store, icon): | ||||
|         itr = store.get_iter(i) | ||||
|         store.set_value(itr, 0, icon) | ||||
|  | ||||
|     def create_tab_widget(self, tab): | ||||
|         return TabHeaderWidget(tab, 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) | ||||
|         self.builder.expose_object(f"{wid}|{tab.get_id()}", scroll) | ||||
|  | ||||
|         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] | ||||
|  | ||||
|         return store, tab_label | ||||
| @@ -0,0 +1,206 @@ | ||||
| # Python imports | ||||
| import os | ||||
|  | ||||
| # Lib imports | ||||
| import gi | ||||
| gi.require_version('Gtk', '3.0') | ||||
| from gi.repository import Gtk | ||||
|  | ||||
| # Application imports | ||||
| from .grid_mixin import GridMixin | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class TabMixin(GridMixin): | ||||
|     """docstring for TabMixin""" | ||||
|  | ||||
|     def create_tab(self, wid=None, tid=None, path=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.create_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) | ||||
|  | ||||
|         self.fm_controller.set_wid_and_tid(wid, tab.get_id()) | ||||
|         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()) | ||||
|         notebook.show_all() | ||||
|         notebook.set_current_page(index) | ||||
|  | ||||
|         ctx = notebook.get_style_context() | ||||
|         ctx.add_class("notebook-unselected-focus") | ||||
|         notebook.set_tab_reorderable(scroll, True) | ||||
|         self.load_store(tab, store) | ||||
|         # self.set_window_title() | ||||
|         event_system.emit("set_window_title", (tab.get_current_directory(),)) | ||||
|         self.set_file_watcher(tab) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     def close_tab(self, button, eve=None): | ||||
|         notebook = button.get_parent().get_parent() | ||||
|         wid      = int(notebook.get_name()[-1]) | ||||
|         tid      = self.get_id_from_tab_box(button.get_parent()) | ||||
|         scroll   = self.builder.get_object(f"{wid}|{tid}") | ||||
|         page     = notebook.page_num(scroll) | ||||
|         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) | ||||
|         notebook.remove_page(page) | ||||
|         if not settings.is_trace_debug(): | ||||
|             self.fm_controller.save_state() | ||||
|         self.set_window_title() | ||||
|  | ||||
|     # 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.is_trace_debug(): | ||||
|             self.fm_controller.save_state() | ||||
|  | ||||
|     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() | ||||
|  | ||||
|     def get_id_from_tab_box(self, tab_box): | ||||
|         return tab_box.get_children()[2].get_text() | ||||
|  | ||||
|     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) | ||||
|  | ||||
|     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.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.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): | ||||
|                 path_menu_buttons  = self.builder.get_object("path_menu_buttons") | ||||
|                 query              = widget.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 | ||||
|  | ||||
|                 if not show_path_menu: | ||||
|                     event_system.emit("hide_path_menu") | ||||
|                 else: | ||||
|                     event_system.emit("show_path_menu") | ||||
|                     widget.grab_focus_without_selecting() | ||||
|                     widget.set_position(-1) | ||||
|  | ||||
|             if path.endswith(".") or path == dir: | ||||
|                 return | ||||
|  | ||||
|             if not tab.set_path(path): | ||||
|                 return | ||||
|  | ||||
|         self.update_tab(tab_label, tab, store, wid, tid) | ||||
|  | ||||
|         try: | ||||
|             widget.grab_focus_without_selecting() | ||||
|             widget.set_position(-1) | ||||
|         except Exception as e: | ||||
|             pass | ||||
|  | ||||
|     def set_path_entry(self, button=None, eve=None): | ||||
|         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") | ||||
|  | ||||
|     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() | ||||
| @@ -0,0 +1,173 @@ | ||||
| # 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() | ||||
|  | ||||
|         event_system.emit("unset_selected_files_views") | ||||
|         ctx = self.files_view.get_style_context() | ||||
|         ctx.remove_class("notebook-unselected-focus") | ||||
|         ctx.add_class("notebook-selected-focus") | ||||
|  | ||||
|         event_system.emit("set_window_title", (dir,)) | ||||
|         self.set_bottom_labels(tab) | ||||
|  | ||||
|     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(),)) | ||||
|  | ||||
|     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 self.ctrl_down: | ||||
|                     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.get_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, icons_grid) | ||||
|  | ||||
|             fileName   = state.store[item][1] | ||||
|             dir        = state.tab.get_current_directory() | ||||
|             file       = f"{dir}/{fileName}" | ||||
|  | ||||
|             if isdir(file): | ||||
|                 state.tab.set_path(file) | ||||
|                 self.update_tab(tab_label, state.tab, state.store, state.wid, state.tid) | ||||
|             else: | ||||
|                 event_system.emit("open_files") | ||||
|         except WindowException as e: | ||||
|             traceback.print_exc() | ||||
|             self.display_message(settings.get_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: | ||||
|             path_at_loc           = icons_grid.get_item_at_pos(x, y)[0] | ||||
|             highlighted_item_path = icons_grid.get_drag_dest_item().path | ||||
|             if path_at_loc and path_at_loc == highlighted_item_path: | ||||
|                 uri = self.format_to_uris(store, wid, tid, highlighted_item_path)[0].replace("file://", "") | ||||
|                 self.override_drop_dest = uri if isdir(uri) else None | ||||
|         except Exception as e: | ||||
|             ... | ||||
|  | ||||
|         if target not in current: | ||||
|             self.fm_controller.set_wid_and_tid(wid, tid) | ||||
|  | ||||
|  | ||||
|     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)) | ||||
|  | ||||
|  | ||||
|     def create_new_tab_notebook(self, widget=None, wid=None, path=None): | ||||
|         self.create_tab(wid, None, path) | ||||
| @@ -38,11 +38,17 @@ class EventSystem: | ||||
|     def emit_and_await(self, event_type, data = None): | ||||
|         """ NOTE: Should be used when signal has only one listener and vis-a-vis """ | ||||
|         if event_type in self.subscribers: | ||||
|             response = None | ||||
|             for fn in self.subscribers[event_type]: | ||||
|                 if data: | ||||
|                     if hasattr(data, '__iter__') and not type(data) is str: | ||||
|                         return fn(*data) | ||||
|                         response = fn(*data) | ||||
|                     else: | ||||
|                         return fn(data) | ||||
|                         response = fn(data) | ||||
|                 else: | ||||
|                     return fn() | ||||
|                     response = fn() | ||||
|  | ||||
|                 if not response in (None, ''): | ||||
|                     break | ||||
|  | ||||
|             return response | ||||
|   | ||||
		Reference in New Issue
	
	Block a user