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. """ |     """ Controller coordinates the mixins and is somewhat the root hub of it all. """ | ||||||
|  |  | ||||||
|     def __init__(self, args, unknownargs): |     def __init__(self, args, unknownargs): | ||||||
|         self.setup_controller_data() |         self._setup_controller_data() | ||||||
|  |  | ||||||
|         self._setup_styling() |         self._setup_styling() | ||||||
|         self._setup_signals() |         self._setup_signals() | ||||||
|         self._subscribe_to_events() |         self._subscribe_to_events() | ||||||
|         self._load_widgets() |         self._load_widgets() | ||||||
|  |  | ||||||
|         self.generate_windows(self.fm_controller_data) |         self._generate_file_views(self.fm_controller_data) | ||||||
|  |  | ||||||
|         if args.no_plugins == "false": |         if args.no_plugins == "false": | ||||||
|             self.plugins.launch_plugins() |             self.plugins.launch_plugins() | ||||||
| @@ -65,8 +65,10 @@ class Controller(UIMixin, SignalsMixins, Controller_Data): | |||||||
|  |  | ||||||
|     def _subscribe_to_events(self): |     def _subscribe_to_events(self): | ||||||
|         event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc) |         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("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("get_current_state", self.get_current_state) | ||||||
|         event_system.subscribe("go_to_path", self.go_to_path) |         event_system.subscribe("go_to_path", self.go_to_path) | ||||||
|         event_system.subscribe("format_to_uris", self.format_to_uris) |         event_system.subscribe("format_to_uris", self.format_to_uris) | ||||||
| @@ -105,7 +107,6 @@ class Controller(UIMixin, SignalsMixins, Controller_Data): | |||||||
|         FileExistsWidget() |         FileExistsWidget() | ||||||
|         SaveLoadWidget() |         SaveLoadWidget() | ||||||
|  |  | ||||||
|  |  | ||||||
|     def reload_plugins(self, widget=None, eve=None): |     def reload_plugins(self, widget=None, eve=None): | ||||||
|         self.plugins.reload_plugins() |         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. """ |     """ 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" |     __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.window        = settings.get_main_window() | ||||||
|         self.builder       = None |         self.builder       = None | ||||||
|         self.core_widget   = 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.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.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      = 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.store          = state.icon_grid.get_model() | ||||||
|         state.message_dialog = MessageWidget() |         state.message_dialog = MessageWidget() | ||||||
|  |  | ||||||
| @@ -103,7 +104,7 @@ class Controller_Data: | |||||||
|         # if self.to_cut_files: |         # if self.to_cut_files: | ||||||
|         #     state.to_cut_files   = self.format_to_uris(state.store, state.wid, state.tid, self.to_cut_files, True) |         #     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 |         return state | ||||||
|  |  | ||||||
|     def format_to_uris(self, store, wid, tid, treePaths, use_just_path=False): |     def format_to_uris(self, store, wid, tid, treePaths, use_just_path=False): | ||||||
| @@ -125,6 +126,20 @@ class Controller_Data: | |||||||
|  |  | ||||||
|         return uris |         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: |     def clear_console(self) -> None: | ||||||
|         ''' Clears the terminal screen. ''' |         ''' Clears the terminal screen. ''' | ||||||
|         os.system('cls' if os.name == 'nt' else 'clear') |         os.system('cls' if os.name == 'nt' else 'clear') | ||||||
|   | |||||||
| @@ -22,53 +22,6 @@ class WindowException(Exception): | |||||||
| class WindowMixin(TabMixin): | class WindowMixin(TabMixin): | ||||||
|     """docstring for WindowMixin""" |     """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): |     def get_fm_window(self, wid): | ||||||
|         return self.fm_controller.get_window_by_nickname(f"window_{wid}") |         return self.fm_controller.get_window_by_nickname(f"window_{wid}") | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,13 +1,78 @@ | |||||||
| # Python imports | # 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 | # Application imports | ||||||
| from .mixins.ui.pane_mixin import PaneMixin | from .mixins.ui.pane_mixin import PaneMixin | ||||||
| from .mixins.ui.window_mixin import WindowMixin | 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, 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") |         state = event_system.emit_and_await("get_current_state") | ||||||
|         event_system.emit("clear_notebooks") |         event_system.emit("clear_notebooks") | ||||||
|         state.fm_controller.unload_tabs_and_windows() |         state.fm_controller.unload_tabs_and_windows() | ||||||
|         event_system.emit("generate_windows", (session_json,)) |         event_system.emit("generate_file_views", (session_json,)) | ||||||
|         gc.collect() |         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): |     def emit_and_await(self, event_type, data = None): | ||||||
|         """ NOTE: Should be used when signal has only one listener and vis-a-vis """ |         """ NOTE: Should be used when signal has only one listener and vis-a-vis """ | ||||||
|         if event_type in self.subscribers: |         if event_type in self.subscribers: | ||||||
|  |             response = None | ||||||
|             for fn in self.subscribers[event_type]: |             for fn in self.subscribers[event_type]: | ||||||
|                 if data: |                 if data: | ||||||
|                     if hasattr(data, '__iter__') and not type(data) is str: |                     if hasattr(data, '__iter__') and not type(data) is str: | ||||||
|                         return fn(*data) |                         response = fn(*data) | ||||||
|                     else: |                     else: | ||||||
|                         return fn(data) |                         response = fn(data) | ||||||
|                 else: |                 else: | ||||||
|                     return fn() |                     response = fn() | ||||||
|  |  | ||||||
|  |                 if not response in (None, ''): | ||||||
|  |                     break | ||||||
|  |  | ||||||
|  |             return response | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user