Bringing to latest changes #3

Merged
itdominator merged 41 commits from develop into master 2022-07-16 19:14:30 +00:00
71 changed files with 637 additions and 592 deletions
Showing only changes of commit 8eccdfce7c - Show all commits

View File

@ -4,15 +4,17 @@ import builtins
# Lib imports # Lib imports
# Application imports # Application imports
from context.ipc_server_mixin import IPCServerMixin from ipc_server import IPCServer
class Builtins(IPCServerMixin): class EventSystem(IPCServer):
""" Inheret IPCServerMixin. Create an pub/sub systems. """ """ Inheret IPCServerMixin. Create an pub/sub systems. """
def __init__(self): def __init__(self):
super(EventSystem, self).__init__()
# NOTE: The format used is list of [type, target, (data,)] Where: # NOTE: The format used is list of [type, target, (data,)] Where:
# type is useful context for control flow, # type is useful context for control flow,
# target is the method to call, # target is the method to call,
@ -20,11 +22,7 @@ class Builtins(IPCServerMixin):
# Where data may be any kind of data # Where data may be any kind of data
self._gui_events = [] self._gui_events = []
self._module_events = [] self._module_events = []
self.is_ipc_alive = False
self.ipc_authkey = b'solarfm-ipc'
self.ipc_address = '127.0.0.1'
self.ipc_port = 4848
self.ipc_timeout = 15.0
# Makeshift fake "events" type system FIFO # Makeshift fake "events" type system FIFO
@ -70,7 +68,7 @@ class Builtins(IPCServerMixin):
# NOTE: Just reminding myself we can add to builtins two different ways... # NOTE: Just reminding myself we can add to builtins two different ways...
# __builtins__.update({"event_system": Builtins()}) # __builtins__.update({"event_system": Builtins()})
builtins.app_name = "SolarFM" builtins.app_name = "SolarFM"
builtins.event_system = Builtins() builtins.event_system = EventSystem()
builtins.event_sleep_time = 0.2 builtins.event_sleep_time = 0.2
builtins.debug = False builtins.debug = False
builtins.trace_debug = False builtins.trace_debug = False

View File

@ -1,56 +1,3 @@
# Python imports """
import os, inspect, time Base module
"""
# Lib imports
# Application imports
from utils.settings import Settings
from context.controller import Controller
from __builtins__ import Builtins
class Main(Builtins):
""" Create Settings and Controller classes. Bind signal to Builder. Inherit from Builtins to bind global methods and classes. """
def __init__(self, args, unknownargs):
if not debug:
event_system.create_ipc_server()
time.sleep(0.2)
if not trace_debug and not debug:
if not event_system.is_ipc_alive:
if unknownargs:
for arg in unknownargs:
if os.path.isdir(arg):
message = f"FILE|{arg}"
event_system.send_ipc_message(message)
if args.new_tab and os.path.isdir(args.new_tab):
message = f"FILE|{args.new_tab}"
event_system.send_ipc_message(message)
raise Exception("IPC Server Exists: Will send path(s) to it and close...")
settings = Settings()
settings.create_window()
controller = Controller(args, unknownargs, settings)
if not controller:
raise Exception("Controller exited and doesn't exist...")
# Gets the methods from the classes and sets to handler.
# Then, builder connects to any signals it needs.
classes = [controller]
handlers = {}
for c in classes:
methods = None
try:
methods = inspect.getmembers(c, predicate=inspect.ismethod)
handlers.update(methods)
except Exception as e:
print(repr(e))
settings.builder.connect_signals(handlers)

View File

@ -15,7 +15,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from __init__ import Main from app import Application
if __name__ == "__main__": if __name__ == "__main__":
@ -35,7 +35,7 @@ if __name__ == "__main__":
# Read arguments (If any...) # Read arguments (If any...)
args, unknownargs = parser.parse_known_args() args, unknownargs = parser.parse_known_args()
Main(args, unknownargs) Application(args, unknownargs)
Gtk.main() Gtk.main()
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()

View File

@ -11,7 +11,7 @@ from __builtins__ import EventSystem
class Main(EventSystem): class Application(EventSystem):
""" Create Settings and Controller classes. Bind signal to Builder. Inherit from Builtins to bind global methods and classes. """ """ Create Settings and Controller classes. Bind signal to Builder. Inherit from Builtins to bind global methods and classes. """
def __init__(self, args, unknownargs): def __init__(self, args, unknownargs):

View File

@ -50,7 +50,7 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
def tear_down(self, widget=None, eve=None): def tear_down(self, widget=None, eve=None):
event_system.send_ipc_message("close server") event_system.send_ipc_message("close server")
self.window_controller.save_state() self.fm_controller.save_state()
time.sleep(event_sleep_time) time.sleep(event_sleep_time)
Gtk.main_quit() Gtk.main_quit()
@ -75,21 +75,21 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
def handle_gui_event_and_set_message(self, type, target, parameters): def handle_gui_event_and_set_message(self, type, target, parameters):
method = getattr(self.__class__, f"{target}") method = getattr(self.__class__, f"{target}")
data = method(*(self, *parameters)) data = method(*(self, *parameters))
self.plugins.set_message_on_plugin(type, data) self.plugins.send_message_to_plugin(type, data)
def open_terminal(self, widget=None, eve=None): def open_terminal(self, widget=None, eve=None):
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
dir = view.get_current_directory() dir = tab.get_current_directory()
view.execute(f"{view.terminal_app}", dir) tab.execute(f"{tab.terminal_app}", dir)
def save_load_session(self, action="save_session"): def save_load_session(self, action="save_session"):
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
save_load_dialog = self.builder.get_object("save_load_dialog") save_load_dialog = self.builder.get_object("save_load_dialog")
if action == "save_session": if action == "save_session":
self.window_controller.save_state() self.fm_controller.save_state()
return return
elif action == "save_session_as": elif action == "save_session_as":
save_load_dialog.set_action(Gtk.FileChooserAction.SAVE) save_load_dialog.set_action(Gtk.FileChooserAction.SAVE)
@ -98,16 +98,16 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
else: else:
raise Exception(f"Unknown action given: {action}") raise Exception(f"Unknown action given: {action}")
save_load_dialog.set_current_folder(view.get_current_directory()) save_load_dialog.set_current_folder(tab.get_current_directory())
save_load_dialog.set_current_name("session.json") save_load_dialog.set_current_name("session.json")
response = save_load_dialog.run() response = save_load_dialog.run()
if response == Gtk.ResponseType.OK: if response == Gtk.ResponseType.OK:
if action == "save_session": if action == "save_session_as":
path = f"{save_load_dialog.get_current_folder()}/{save_load_dialog.get_current_name()}" path = f"{save_load_dialog.get_current_folder()}/{save_load_dialog.get_current_name()}"
self.window_controller.save_state(path) self.fm_controller.save_state(path)
elif action == "load_session": elif action == "load_session":
path = f"{save_load_dialog.get_file().get_path()}" path = f"{save_load_dialog.get_file().get_path()}"
session_json = self.window_controller.load_state(path) session_json = self.fm_controller.load_state(path)
self.load_session(session_json) self.load_session(session_json)
if (response == Gtk.ResponseType.CANCEL) or (response == Gtk.ResponseType.DELETE_EVENT): if (response == Gtk.ResponseType.CANCEL) or (response == Gtk.ResponseType.DELETE_EVENT):
pass pass
@ -118,13 +118,13 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
if debug: if debug:
print(f"Session Data: {session_json}") print(f"Session Data: {session_json}")
self.ctrlDown = False self.ctrl_down = False
self.shiftDown = False self.shift_down = False
self.altDown = False self.alt_down = False
for notebook in self.notebooks: for notebook in self.notebooks:
self.clear_children(notebook) self.clear_children(notebook)
self.window_controller.unload_views_and_windows() self.fm_controller.unload_tabs_and_windows()
self.generate_windows(session_json) self.generate_windows(session_json)
gc.collect() gc.collect()
@ -165,7 +165,6 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
self.restore_trash_files() self.restore_trash_files()
if action == "empty_trash": if action == "empty_trash":
self.empty_trash() self.empty_trash()
if action == "create": if action == "create":
self.show_new_file_menu() self.show_new_file_menu()
if action in ["save_session", "save_session_as", "load_session"]: if action in ["save_session", "save_session_as", "load_session"]:

View File

@ -17,9 +17,9 @@ class Controller_Data:
def setup_controller_data(self, _settings): def setup_controller_data(self, _settings):
self.trashman = XDGTrash() self.trashman = XDGTrash()
self.window_controller = WindowController() self.fm_controller = WindowController()
self.plugins = Plugins(_settings) self.plugins = Plugins(_settings)
self.state = self.window_controller.load_state() self.state = self.fm_controller.load_state()
self.trashman.regenerate() self.trashman.regenerate()
self.settings = _settings self.settings = _settings
@ -31,8 +31,8 @@ class Controller_Data:
self.window2 = self.builder.get_object("window_2") self.window2 = self.builder.get_object("window_2")
self.window3 = self.builder.get_object("window_3") self.window3 = self.builder.get_object("window_3")
self.window4 = self.builder.get_object("window_4") self.window4 = self.builder.get_object("window_4")
self.message_widget = self.builder.get_object("message_widget") self.message_popup_widget = self.builder.get_object("message_popup_widget")
self.message_view = self.builder.get_object("message_view") self.message_text_view = self.builder.get_object("message_text_view")
self.message_buffer = self.builder.get_object("message_buffer") self.message_buffer = self.builder.get_object("message_buffer")
self.arc_command_buffer = self.builder.get_object("arc_command_buffer") self.arc_command_buffer = self.builder.get_object("arc_command_buffer")
@ -92,18 +92,18 @@ class Controller_Data:
self.override_drop_dest = None self.override_drop_dest = None
self.is_searching = False self.is_searching = False
self.search_iconview = None self.search_icon_grid = None
self.search_view = None self.search_tab = None
self.skip_edit = False self.skip_edit = False
self.cancel_edit = False self.cancel_edit = False
self.ctrlDown = False self.ctrl_down = False
self.shiftDown = False self.shift_down = False
self.altDown = False self.alt_down = False
self.success = "#88cc27" self.success_color = self.settings.get_success_color()
self.warning = "#ffa800" self.warning_color = self.settings.get_warning_color()
self.error = "#ff0000" self.error_color = self.settings.get_error_color()
sys.excepthook = self.custom_except_hook sys.excepthook = self.custom_except_hook
self.window.connect("delete-event", self.tear_down) self.window.connect("delete-event", self.tear_down)
@ -117,13 +117,13 @@ class Controller_Data:
a (obj): self a (obj): self
Returns: Returns:
wid, tid, view, iconview, store wid, tid, tab, icon_grid, store
''' '''
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
iconview = self.builder.get_object(f"{wid}|{tid}|iconview") icon_grid = self.builder.get_object(f"{wid}|{tid}|icon_grid")
store = iconview.get_model() store = icon_grid.get_model()
return wid, tid, view, iconview, store return wid, tid, tab, icon_grid, store
def clear_console(self): def clear_console(self):

View File

@ -0,0 +1,3 @@
"""
Mixins module
"""

View File

@ -27,14 +27,14 @@ class ExceptionHookMixin:
def display_message(self, type, text, seconds=None): def display_message(self, type, text, seconds=None):
self.message_buffer.insert_at_cursor(text) self.message_buffer.insert_at_cursor(text)
self.message_widget.popup() self.message_popup_widget.popup()
if seconds: if seconds:
self.hide_message_timeout(seconds) self.hide_message_timeout(seconds)
@threaded @threaded
def hide_message_timeout(self, seconds=3): def hide_message_timeout(self, seconds=3):
time.sleep(seconds) time.sleep(seconds)
GLib.idle_add(self.message_widget.popdown) GLib.idle_add(self.message_popup_widget.popdown)
def save_debug_alerts(self, widget=None, eve=None): def save_debug_alerts(self, widget=None, eve=None):
start_itr, end_itr = self.message_buffer.get_bounds() start_itr, end_itr = self.message_buffer.get_bounds()

View File

@ -11,7 +11,7 @@ from gi.repository import Gtk, Gdk
class ShowHideMixin: class ShowHideMixin:
def show_messages_popup(self, type, text, seconds=None): def show_messages_popup(self, type, text, seconds=None):
self.message_widget.popup() self.message_popup_widget.popup()
def stop_file_searching(self, widget=None, eve=None): def stop_file_searching(self, widget=None, eve=None):
self.is_searching = False self.is_searching = False
@ -48,7 +48,7 @@ class ShowHideMixin:
def show_about_page(self, widget=None, eve=None): def show_about_page(self, widget=None, eve=None):
about_page = self.builder.get_object("about_page") about_page = self.builder.get_object("about_page")
response = about_page.run() response = about_page.run()
if (response == Gtk.ResponseType.CANCEL) or (response == Gtk.ResponseType.DELETE_EVENT): if response in [Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT]:
self.hide_about_page() self.hide_about_page()
def hide_about_page(self, widget=None, eve=None): def hide_about_page(self, widget=None, eve=None):
@ -56,11 +56,11 @@ class ShowHideMixin:
def show_archiver_dialogue(self, widget=None, eve=None): def show_archiver_dialogue(self, widget=None, eve=None):
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
archiver_dialogue = self.builder.get_object("archiver_dialogue") archiver_dialogue = self.builder.get_object("archiver_dialogue")
archiver_dialogue.set_action(Gtk.FileChooserAction.SAVE) archiver_dialogue.set_action(Gtk.FileChooserAction.SAVE)
archiver_dialogue.set_current_folder(view.get_current_directory()) archiver_dialogue.set_current_folder(tab.get_current_directory())
archiver_dialogue.set_current_name("arc.7z") archiver_dialogue.set_current_name("arc.7z")
response = archiver_dialogue.run() response = archiver_dialogue.run()
@ -137,7 +137,7 @@ class ShowHideMixin:
def hide_edit_file_menu_enter_key(self, widget=None, eve=None): def hide_edit_file_menu_enter_key(self, widget=None, eve=None):
keyname = Gdk.keyval_name(eve.keyval).lower() keyname = Gdk.keyval_name(eve.keyval).lower()
if "return" in keyname or "enter" in keyname: if keyname in ["return", "enter"]:
self.builder.get_object("edit_file_menu").hide() self.builder.get_object("edit_file_menu").hide()
def hide_edit_file_menu_skip(self, widget=None, eve=None): def hide_edit_file_menu_skip(self, widget=None, eve=None):

View File

@ -0,0 +1,3 @@
"""
UI module
"""

View File

@ -39,8 +39,6 @@ class PaneMixin:
def toggle_notebook_pane(self, widget, eve=None): def toggle_notebook_pane(self, widget, eve=None):
name = widget.get_name() name = widget.get_name()
pane_index = int(name[-1]) pane_index = int(name[-1])
pane = None
master_pane = self.builder.get_object("pane_master") master_pane = self.builder.get_object("pane_master")
pane = self.builder.get_object("pane_top") if pane_index in [1, 2] else self.builder.get_object("pane_bottom") pane = self.builder.get_object("pane_top") if pane_index in [1, 2] else self.builder.get_object("pane_bottom")
@ -50,16 +48,12 @@ class PaneMixin:
self._save_state(state, pane_index) self._save_state(state, pane_index)
return return
child = None child = pane.get_child1() if pane_index in [1, 3] else pane.get_child2()
if pane_index in [1, 3]:
child = pane.get_child1()
elif pane_index in [2, 4]:
child = pane.get_child2()
self.toggle_pane(child) self.toggle_pane(child)
self._save_state(state, pane_index) self._save_state(state, pane_index)
def _save_state(self, state, pane_index): def _save_state(self, state, pane_index):
window = self.window_controller.get_window_by_index(pane_index - 1) window = self.fm_controller.get_window_by_index(pane_index - 1)
window.set_is_hidden(state) window.set_is_hidden(state)
self.window_controller.save_state() self.fm_controller.save_state()

View File

@ -18,128 +18,128 @@ class TabMixin(WidgetMixin):
def create_tab(self, wid, path=None): def create_tab(self, wid, path=None):
notebook = self.builder.get_object(f"window_{wid}") 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")
view = self.window_controller.add_view_for_window_by_nickname(f"window_{wid}") tab = self.fm_controller.add_tab_for_window_by_nickname(f"window_{wid}")
view.logger = self.logger tab.logger = self.logger
view.set_wid(wid) tab.set_wid(wid)
if path: view.set_path(path) if path: tab.set_path(path)
tab = self.create_tab_widget(view) tab_widget = self.create_tab_widget(tab)
scroll, store = self.create_grid_iconview_widget(view, wid) scroll, store = self.create_icon_grid_widget(tab, wid)
# scroll, store = self.create_grid_treeview_widget(view, wid) # TODO: Fix global logic to make the below work too
index = notebook.append_page(scroll, tab) # scroll, store = self.create_icon_tree_widget(tab, wid)
index = notebook.append_page(scroll, tab_widget)
self.window_controller.set__wid_and_tid(wid, view.get_id()) self.fm_controller.set__wid_and_tid(wid, tab.get_id())
path_entry.set_text(view.get_current_directory()) path_entry.set_text(tab.get_current_directory())
notebook.show_all() notebook.show_all()
notebook.set_current_page(index) notebook.set_current_page(index)
ctx = notebook.get_style_context() ctx = notebook.get_style_context()
ctx.add_class("notebook-unselected-focus") ctx.add_class("notebook-unselected-focus")
notebook.set_tab_reorderable(scroll, True) notebook.set_tab_reorderable(scroll, True)
self.load_store(view, store) self.load_store(tab, store)
self.set_window_title() self.set_window_title()
self.set_file_watcher(view) self.set_file_watcher(tab)
def close_tab(self, button, eve=None): def close_tab(self, button, eve=None):
notebook = button.get_parent().get_parent() notebook = button.get_parent().get_parent()
tid = self.get_id_from_tab_box(button.get_parent())
wid = int(notebook.get_name()[-1]) wid = int(notebook.get_name()[-1])
tid = self.get_id_from_tab_box(button.get_parent())
scroll = self.builder.get_object(f"{wid}|{tid}") scroll = self.builder.get_object(f"{wid}|{tid}")
page = notebook.page_num(scroll) page = notebook.page_num(scroll)
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
watcher = view.get_dir_watcher() watcher = tab.get_dir_watcher()
watcher.cancel() watcher.cancel()
self.get_fm_window(wid).delete_view_by_id(tid) self.get_fm_window(wid).delete_tab_by_id(tid)
notebook.remove_page(page) notebook.remove_page(page)
self.window_controller.save_state() self.fm_controller.save_state()
self.set_window_title() self.set_window_title()
def on_tab_reorder(self, child, page_num, new_index): def on_tab_reorder(self, child, page_num, new_index):
wid, tid = page_num.get_name().split("|") wid, tid = page_num.get_name().split("|")
window = self.get_fm_window(wid) window = self.get_fm_window(wid)
view = None tab = None
for i, view in enumerate(window.get_all_views()): for i, tab in enumerate(window.get_all_tabs()):
if view.get_id() == tid: if tab.get_id() == tid:
_view = window.get_view_by_id(tid) _tab = window.get_tab_by_id(tid)
watcher = _view.get_dir_watcher() watcher = _tab.get_dir_watcher()
watcher.cancel() watcher.cancel()
window.get_all_views().insert(new_index, window.get_all_views().pop(i)) window.get_all_tabs().insert(new_index, window.get_all_tabs().pop(i))
view = window.get_view_by_id(tid) tab = window.get_tab_by_id(tid)
self.set_file_watcher(view) self.set_file_watcher(tab)
self.window_controller.save_state() self.fm_controller.save_state()
def on_tab_switch_update(self, notebook, content=None, index=None): def on_tab_switch_update(self, notebook, content=None, index=None):
self.selected_files.clear() self.selected_files.clear()
wid, tid = content.get_children()[0].get_name().split("|") wid, tid = content.get_children()[0].get_name().split("|")
self.window_controller.set__wid_and_tid(wid, tid) self.fm_controller.set__wid_and_tid(wid, tid)
self.set_path_text(wid, tid) self.set_path_text(wid, tid)
self.set_window_title() self.set_window_title()
def get_id_from_tab_box(self, tab_box): def get_id_from_tab_box(self, tab_box):
tid = tab_box.get_children()[2] return tab_box.get_children()[2].get_text()
return tid.get_text()
def get_tab_label(self, notebook, iconview): def get_tab_label(self, notebook, icon_grid):
return notebook.get_tab_label(iconview.get_parent()).get_children()[0] return notebook.get_tab_label(icon_grid.get_parent()).get_children()[0]
def get_tab_close(self, notebook, iconview): def get_tab_close(self, notebook, icon_grid):
return notebook.get_tab_label(iconview.get_parent()).get_children()[1] return notebook.get_tab_label(icon_grid.get_parent()).get_children()[1]
def get_tab_iconview_from_notebook(self, notebook): def get_tab_icon_grid_from_notebook(self, notebook):
return notebook.get_children()[1].get_children()[0] return notebook.get_children()[1].get_children()[0]
def refresh_tab(data=None): def refresh_tab(data=None):
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
view.load_directory() tab.load_directory()
self.load_store(view, store) self.load_store(tab, store)
def update_view(self, tab_label, view, store, wid, tid): def update_tab(self, tab_label, tab, store, wid, tid):
self.load_store(view, store) self.load_store(tab, store)
self.set_path_text(wid, tid) self.set_path_text(wid, tid)
char_width = len(view.get_end_of_path()) char_width = len(tab.get_end_of_path())
tab_label.set_width_chars(char_width) tab_label.set_width_chars(char_width)
tab_label.set_label(view.get_end_of_path()) tab_label.set_label(tab.get_end_of_path())
self.set_window_title() self.set_window_title()
self.set_file_watcher(view) self.set_file_watcher(tab)
self.window_controller.save_state() self.fm_controller.save_state()
def do_action_from_bar_controls(self, widget, eve=None): def do_action_from_bar_controls(self, widget, eve=None):
action = widget.get_name() action = widget.get_name()
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
notebook = self.builder.get_object(f"window_{wid}") notebook = self.builder.get_object(f"window_{wid}")
store, tab_label = self.get_store_and_label_from_notebook(notebook, f"{wid}|{tid}") store, tab_label = self.get_store_and_label_from_notebook(notebook, f"{wid}|{tid}")
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
if action == "go_up":
view.pop_from_path()
if action == "go_home":
view.set_to_home()
if action == "refresh_view":
view.load_directory()
if action == "create_tab": if action == "create_tab":
dir = view.get_current_directory() dir = tab.get_current_directory()
self.create_tab(wid, dir) self.create_tab(wid, dir)
self.window_controller.save_state() self.fm_controller.save_state()
return 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": if action == "path_entry":
focused_obj = self.window.get_focus() focused_obj = self.window.get_focus()
dir = f"{view.get_current_directory()}/" dir = f"{tab.get_current_directory()}/"
path = widget.get_text() path = widget.get_text()
if isinstance(focused_obj, Gtk.Entry): if isinstance(focused_obj, Gtk.Entry):
button_box = self.path_menu.get_children()[0].get_children()[0].get_children()[0] path_menu_buttons = self.builder.get_object("path_menu_buttons")
query = widget.get_text().replace(dir, "") query = widget.get_text().replace(dir, "")
files = view.get_files() + view.get_hidden() files = tab.get_files() + tab.get_hidden()
self.clear_children(button_box) self.clear_children(path_menu_buttons)
show_path_menu = False show_path_menu = False
for file, hash in files: for file, hash in files:
if os.path.isdir(f"{dir}{file}"): if os.path.isdir(f"{dir}{file}"):
@ -147,7 +147,7 @@ class TabMixin(WidgetMixin):
button = Gtk.Button(label=file) button = Gtk.Button(label=file)
button.show() button.show()
button.connect("clicked", self.set_path_entry) button.connect("clicked", self.set_path_entry)
button_box.add(button) path_menu_buttons.add(button)
show_path_menu = True show_path_menu = True
if not show_path_menu: if not show_path_menu:
@ -160,11 +160,10 @@ class TabMixin(WidgetMixin):
if path.endswith(".") or path == dir: if path.endswith(".") or path == dir:
return return
traversed = view.set_path(path) if not tab.set_path(path):
if not traversed:
return return
self.update_view(tab_label, view, store, wid, tid) self.update_tab(tab_label, tab, store, wid, tid)
try: try:
widget.grab_focus_without_selecting() widget.grab_focus_without_selecting()
@ -173,8 +172,8 @@ class TabMixin(WidgetMixin):
pass pass
def set_path_entry(self, button=None, eve=None): def set_path_entry(self, button=None, eve=None):
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
path = f"{view.get_current_directory()}/{button.get_label()}" path = f"{tab.get_current_directory()}/{button.get_label()}"
path_entry = self.builder.get_object("path_entry") path_entry = self.builder.get_object("path_entry")
path_entry.set_text(path) path_entry.set_text(path)
path_entry.grab_focus_without_selecting() path_entry.grab_focus_without_selecting()
@ -182,23 +181,22 @@ class TabMixin(WidgetMixin):
self.path_menu.popdown() self.path_menu.popdown()
def keyboard_close_tab(self): def keyboard_close_tab(self):
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
notebook = self.builder.get_object(f"window_{wid}") notebook = self.builder.get_object(f"window_{wid}")
scroll = self.builder.get_object(f"{wid}|{tid}") scroll = self.builder.get_object(f"{wid}|{tid}")
page = notebook.page_num(scroll) page = notebook.page_num(scroll)
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
watcher = view.get_dir_watcher() watcher = tab.get_dir_watcher()
watcher.cancel() watcher.cancel()
self.get_fm_window(wid).delete_view_by_id(tid) self.get_fm_window(wid).delete_tab_by_id(tid)
notebook.remove_page(page) notebook.remove_page(page)
self.window_controller.save_state() self.fm_controller.save_state()
self.set_window_title() self.set_window_title()
# File control events
def show_hide_hidden_files(self): def show_hide_hidden_files(self):
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
view.set_hiding_hidden(not view.is_hiding_hidden()) tab.set_hiding_hidden(not tab.is_hiding_hidden())
view.load_directory() tab.load_directory()
self.builder.get_object("refresh_view").released() self.builder.get_object("refresh_tab").released()

View File

@ -40,29 +40,24 @@ class WidgetFileActionMixin:
return size return size
def set_file_watcher(self, view): def set_file_watcher(self, tab):
if view.get_dir_watcher(): if tab.get_dir_watcher():
watcher = view.get_dir_watcher() watcher = tab.get_dir_watcher()
watcher.cancel() watcher.cancel()
if debug: if debug:
print(f"Watcher Is Cancelled: {watcher.is_cancelled()}") print(f"Watcher Is Cancelled: {watcher.is_cancelled()}")
cur_dir = view.get_current_directory() cur_dir = tab.get_current_directory()
# Temp updating too much with current events we are checking for.
# Seems to cause invalid iter errors in WidbetMixin > update_store
if cur_dir == "/tmp":
watcher = None
return
dir_watcher = Gio.File.new_for_path(cur_dir) \ dir_watcher = Gio.File.new_for_path(cur_dir) \
.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, Gio.Cancellable()) .monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, Gio.Cancellable())
wid = view.get_wid() wid = tab.get_wid()
tid = view.get_id() tid = tab.get_id()
dir_watcher.connect("changed", self.dir_watch_updates, (f"{wid}|{tid}",)) dir_watcher.connect("changed", self.dir_watch_updates, (f"{wid}|{tid}",))
view.set_dir_watcher(dir_watcher) tab.set_dir_watcher(dir_watcher)
# NOTE: Too lazy to impliment a proper update handler and so just regen store and update view. # NOTE: Too lazy to impliment a proper update handler and so just regen store and update tab.
# Use a lock system to prevent too many update calls for certain instances but user can manually refresh if they have urgency # Use a lock system to prevent too many update calls for certain instances but user can manually refresh if they have urgency
def dir_watch_updates(self, file_monitor, file, other_file=None, eve_type=None, data=None): def dir_watch_updates(self, file_monitor, file, other_file=None, eve_type=None, data=None):
if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED, if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED,
@ -72,46 +67,46 @@ class WidgetFileActionMixin:
print(eve_type) print(eve_type)
if eve_type in [Gio.FileMonitorEvent.MOVED_IN, Gio.FileMonitorEvent.MOVED_OUT]: if eve_type in [Gio.FileMonitorEvent.MOVED_IN, Gio.FileMonitorEvent.MOVED_OUT]:
self.update_on_end_soft_lock(data[0]) self.update_on_soft_lock_end(data[0])
elif data[0] in self.soft_update_lock.keys(): elif data[0] in self.soft_update_lock.keys():
self.soft_update_lock[data[0]]["last_update_time"] = time.time() self.soft_update_lock[data[0]]["last_update_time"] = time.time()
else: else:
self.soft_lock_countdown(data[0]) self.soft_lock_countdown(data[0])
@threaded @threaded
def soft_lock_countdown(self, tab): def soft_lock_countdown(self, tab_widget):
self.soft_update_lock[tab] = { "last_update_time": time.time()} self.soft_update_lock[tab_widget] = { "last_update_time": time.time()}
lock = True lock = True
while lock: while lock:
time.sleep(0.6) time.sleep(0.6)
last_update_time = self.soft_update_lock[tab]["last_update_time"] last_update_time = self.soft_update_lock[tab_widget]["last_update_time"]
current_time = time.time() current_time = time.time()
if (current_time - last_update_time) > 0.6: if (current_time - last_update_time) > 0.6:
lock = False lock = False
self.soft_update_lock.pop(tab, None) self.soft_update_lock.pop(tab_widget, None)
GLib.idle_add(self.update_on_end_soft_lock, *(tab,)) GLib.idle_add(self.update_on_soft_lock_end, *(tab_widget,))
def update_on_end_soft_lock(self, tab): def update_on_soft_lock_end(self, tab_widget):
wid, tid = tab.split("|") wid, tid = tab_widget.split("|")
notebook = self.builder.get_object(f"window_{wid}") notebook = self.builder.get_object(f"window_{wid}")
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
iconview = self.builder.get_object(f"{wid}|{tid}|iconview") icon_grid = self.builder.get_object(f"{wid}|{tid}|icon_grid")
store = iconview.get_model() store = icon_grid.get_model()
_store, tab_label = self.get_store_and_label_from_notebook(notebook, f"{wid}|{tid}") _store, tab_widget_label = self.get_store_and_label_from_notebook(notebook, f"{wid}|{tid}")
view.load_directory() tab.load_directory()
self.load_store(view, store) self.load_store(tab, store)
tab_label.set_label(view.get_end_of_path()) tab_widget_label.set_label(tab.get_end_of_path())
_wid, _tid, _view, _iconview, _store = self.get_current_state() _wid, _tid, _tab, _icon_grid, _store = self.get_current_state()
if [wid, tid] in [_wid, _tid]: if [wid, tid] in [_wid, _tid]:
self.set_bottom_labels(view) self.set_bottom_labels(tab)
def popup_search_files(self, wid, keyname): def popup_search_files(self, wid, keyname):
@ -123,43 +118,43 @@ class WidgetFileActionMixin:
def do_file_search(self, widget, eve=None): def do_file_search(self, widget, eve=None):
query = widget.get_text() query = widget.get_text()
self.search_iconview.unselect_all() self.search_icon_grid.unselect_all()
for i, file in enumerate(self.search_view.get_files()): for i, file in enumerate(self.search_tab.get_files()):
if query and query in file[0].lower(): if query and query in file[0].lower():
path = Gtk.TreePath().new_from_indices([i]) path = Gtk.TreePath().new_from_indices([i])
self.search_iconview.select_path(path) self.search_icon_grid.select_path(path)
items = self.search_iconview.get_selected_items() items = self.search_icon_grid.get_selected_items()
if len(items) == 1: if len(items) == 1:
self.search_iconview.scroll_to_path(items[0], True, 0.5, 0.5) self.search_icon_grid.scroll_to_path(items[0], True, 0.5, 0.5)
def open_files(self): def open_files(self):
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
uris = self.format_to_uris(store, wid, tid, self.selected_files, True) uris = self.format_to_uris(store, wid, tid, self.selected_files, True)
for file in uris: for file in uris:
view.open_file_locally(file) tab.open_file_locally(file)
def open_with_files(self, appchooser_widget): def open_with_files(self, appchooser_widget):
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
app_info = appchooser_widget.get_app_info() app_info = appchooser_widget.get_app_info()
uris = self.format_to_uris(store, wid, tid, self.selected_files) uris = self.format_to_uris(store, wid, tid, self.selected_files)
view.app_chooser_exec(app_info, uris) tab.app_chooser_exec(app_info, uris)
def execute_files(self, in_terminal=False): def execute_files(self, in_terminal=False):
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
paths = self.format_to_uris(store, wid, tid, self.selected_files, True) paths = self.format_to_uris(store, wid, tid, self.selected_files, True)
current_dir = view.get_current_directory() current_dir = tab.get_current_directory()
command = None command = None
for path in paths: for path in paths:
command = f"exec '{path}'" if not in_terminal else f"{view.terminal_app} -e '{path}'" command = f"exec '{path}'" if not in_terminal else f"{tab.terminal_app} -e '{path}'"
view.execute(command, start_dir=view.get_current_directory(), use_os_system=False) tab.execute(command, start_dir=tab.get_current_directory(), use_os_system=False)
def archive_files(self, archiver_dialogue): def archive_files(self, archiver_dialogue):
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
paths = self.format_to_uris(store, wid, tid, self.selected_files, True) paths = self.format_to_uris(store, wid, tid, self.selected_files, True)
save_target = archiver_dialogue.get_filename(); save_target = archiver_dialogue.get_filename();
@ -167,14 +162,14 @@ class WidgetFileActionMixin:
pre_command = self.arc_command_buffer.get_text(sItr, eItr, False) pre_command = self.arc_command_buffer.get_text(sItr, eItr, False)
pre_command = pre_command.replace("%o", save_target) pre_command = pre_command.replace("%o", save_target)
pre_command = pre_command.replace("%N", ' '.join(paths)) pre_command = pre_command.replace("%N", ' '.join(paths))
command = f"{view.terminal_app} -e '{pre_command}'" command = f"{tab.terminal_app} -e '{pre_command}'"
view.execute(command, start_dir=None, use_os_system=True) tab.execute(command, start_dir=None, use_os_system=True)
def rename_files(self): def rename_files(self):
rename_label = self.builder.get_object("file_to_rename_label") rename_label = self.builder.get_object("file_to_rename_label")
rename_input = self.builder.get_object("new_rename_fname") rename_input = self.builder.get_object("new_rename_fname")
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
uris = self.format_to_uris(store, wid, tid, self.selected_files, True) uris = self.format_to_uris(store, wid, tid, self.selected_files, True)
for uri in uris: for uri in uris:
@ -191,7 +186,7 @@ class WidgetFileActionMixin:
break break
rname_to = rename_input.get_text().strip() rname_to = rename_input.get_text().strip()
target = f"{view.get_current_directory()}/{rname_to}" target = f"{tab.get_current_directory()}/{rname_to}"
self.handle_files([uri], "rename", target) self.handle_files([uri], "rename", target)
@ -201,27 +196,27 @@ class WidgetFileActionMixin:
self.selected_files.clear() self.selected_files.clear()
def cut_files(self): def cut_files(self):
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
uris = self.format_to_uris(store, wid, tid, self.selected_files, True) uris = self.format_to_uris(store, wid, tid, self.selected_files, True)
self.to_cut_files = uris self.to_cut_files = uris
def copy_files(self): def copy_files(self):
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
uris = self.format_to_uris(store, wid, tid, self.selected_files, True) uris = self.format_to_uris(store, wid, tid, self.selected_files, True)
self.to_copy_files = uris self.to_copy_files = uris
def paste_files(self): def paste_files(self):
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
target = f"{view.get_current_directory()}" target = f"{tab.get_current_directory()}"
if len(self.to_copy_files) > 0: if self.to_copy_files:
self.handle_files(self.to_copy_files, "copy", target) self.handle_files(self.to_copy_files, "copy", target)
elif len(self.to_cut_files) > 0: elif self.to_cut_files:
self.handle_files(self.to_cut_files, "move", target) self.handle_files(self.to_cut_files, "move", target)
def delete_files(self): def delete_files(self):
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
uris = self.format_to_uris(store, wid, tid, self.selected_files, True) uris = self.format_to_uris(store, wid, tid, self.selected_files, True)
response = None response = None
@ -236,7 +231,7 @@ class WidgetFileActionMixin:
type = file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE) type = file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
if type == Gio.FileType.DIRECTORY: if type == Gio.FileType.DIRECTORY:
view.delete_file( file.get_path() ) tab.delete_file( file.get_path() )
else: else:
file.delete(cancellable=None) file.delete(cancellable=None)
else: else:
@ -244,13 +239,13 @@ class WidgetFileActionMixin:
def trash_files(self): def trash_files(self):
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
uris = self.format_to_uris(store, wid, tid, self.selected_files, True) uris = self.format_to_uris(store, wid, tid, self.selected_files, True)
for uri in uris: for uri in uris:
self.trashman.trash(uri, False) self.trashman.trash(uri, False)
def restore_trash_files(self): def restore_trash_files(self):
wid, tid, view, iconview, store = self.get_current_state() wid, tid, tab, icon_grid, store = self.get_current_state()
uris = self.format_to_uris(store, wid, tid, self.selected_files, True) uris = self.format_to_uris(store, wid, tid, self.selected_files, True)
for uri in uris: for uri in uris:
self.trashman.restore(filename=uri.split("/")[-1], verbose=False) self.trashman.restore(filename=uri.split("/")[-1], verbose=False)
@ -264,9 +259,9 @@ class WidgetFileActionMixin:
file_name = fname_field.get_text().strip() file_name = fname_field.get_text().strip()
type = self.builder.get_object("context_menu_type_toggle").get_state() type = self.builder.get_object("context_menu_type_toggle").get_state()
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
target = f"{view.get_current_directory()}" target = f"{tab.get_current_directory()}"
if file_name: if file_name:
path = f"{target}/{file_name}" path = f"{target}/{file_name}"
@ -331,9 +326,9 @@ class WidgetFileActionMixin:
type = _file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE) type = _file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
if type == Gio.FileType.DIRECTORY: if type == Gio.FileType.DIRECTORY:
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
view.delete_file( _file.get_path() ) tab.delete_file( _file.get_path() )
else: else:
_file.delete(cancellable=None) _file.delete(cancellable=None)
@ -358,16 +353,16 @@ class WidgetFileActionMixin:
type = file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE) type = file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
if type == Gio.FileType.DIRECTORY: if type == Gio.FileType.DIRECTORY:
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
fPath = file.get_path() fPath = file.get_path()
tPath = target.get_path() tPath = target.get_path()
state = True state = True
if action == "copy": if action == "copy":
view.copy_file(fPath, tPath) tab.copy_file(fPath, tPath)
if action == "move" or action == "rename": if action == "move" or action == "rename":
view.move_file(fPath, tPath) tab.move_file(fPath, tPath)
else: else:
if action == "copy": if action == "copy":
file.copy(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None) file.copy(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)

View File

@ -22,29 +22,29 @@ def threaded(fn):
class WidgetMixin: class WidgetMixin:
"""docstring for WidgetMixin""" """docstring for WidgetMixin"""
def load_store(self, view, store, save_state=False): def load_store(self, tab, store, save_state=False):
store.clear() store.clear()
dir = view.get_current_directory() dir = tab.get_current_directory()
files = view.get_files() files = tab.get_files()
for i, file in enumerate(files): for i, file in enumerate(files):
store.append([None, file[0]]) store.append([None, file[0]])
self.create_icon(i, view, store, dir, file[0]) self.create_icon(i, tab, store, dir, file[0])
# NOTE: Not likely called often from here but it could be useful # NOTE: Not likely called often from here but it could be useful
if save_state: if save_state:
self.window_controller.save_state() self.fm_controller.save_state()
@threaded @threaded
def create_icon(self, i, view, store, dir, file): def create_icon(self, i, tab, store, dir, file):
icon = view.create_icon(dir, file) icon = tab.create_icon(dir, file)
fpath = f"{dir}/{file}" fpath = f"{dir}/{file}"
GLib.idle_add(self.update_store, (i, store, icon, view, fpath,)) GLib.idle_add(self.update_store, (i, store, icon, tab, fpath,))
# NOTE: Might need to keep an eye on this throwing invalid iters when too # NOTE: Might need to keep an eye on this throwing invalid iters when too
# many updates are happening to a folder. Example: /tmp # many updates are happening to a folder. Example: /tmp
def update_store(self, item): def update_store(self, item):
i, store, icon, view, fpath = item i, store, icon, tab, fpath = item
itr = None itr = None
try: try:
@ -60,12 +60,12 @@ class WidgetMixin:
return return
if not icon: if not icon:
icon = self.get_system_thumbnail(fpath, view.SYS_ICON_WH[0]) icon = self.get_system_thumbnail(fpath, tab.SYS_ICON_WH[0])
if not icon: if not icon:
if fpath.endswith(".gif"): if fpath.endswith(".gif"):
icon = GdkPixbuf.PixbufAnimation.get_static_image(fpath) icon = GdkPixbuf.PixbufAnimation.get_static_image(fpath)
else: else:
icon = GdkPixbuf.Pixbuf.new_from_file(view.DEFAULT_ICON) icon = GdkPixbuf.Pixbuf.new_from_file(tab.DEFAULT_ICON)
store.set_value(itr, 0, icon) store.set_value(itr, 0, icon)
@ -90,31 +90,29 @@ class WidgetMixin:
return None return None
def create_tab_widget(self, tab):
tab_widget = Gtk.ButtonBox()
def create_tab_widget(self, view):
tab = Gtk.ButtonBox()
label = Gtk.Label() label = Gtk.Label()
tid = Gtk.Label() tid = Gtk.Label()
close = Gtk.Button() close = Gtk.Button()
icon = Gtk.Image(stock=Gtk.STOCK_CLOSE) icon = Gtk.Image(stock=Gtk.STOCK_CLOSE)
label.set_label(f"{view.get_end_of_path()}") label.set_label(f"{tab.get_end_of_path()}")
label.set_width_chars(len(view.get_end_of_path())) label.set_width_chars(len(tab.get_end_of_path()))
label.set_xalign(0.0) label.set_xalign(0.0)
tid.set_label(f"{view.get_id()}") tid.set_label(f"{tab.get_id()}")
close.add(icon) close.add(icon)
tab.add(label) tab_widget.add(label)
tab.add(close) tab_widget.add(close)
tab.add(tid) tab_widget.add(tid)
close.connect("released", self.close_tab) close.connect("released", self.close_tab)
tab.show_all() tab_widget.show_all()
tid.hide() tid.hide()
return tab return tab_widget
def create_grid_iconview_widget(self, view, wid): def create_icon_grid_widget(self, tab, wid):
scroll = Gtk.ScrolledWindow() scroll = Gtk.ScrolledWindow()
grid = Gtk.IconView() grid = Gtk.IconView()
store = Gtk.ListStore(GdkPixbuf.Pixbuf or GdkPixbuf.PixbufAnimation or None, str) store = Gtk.ListStore(GdkPixbuf.Pixbuf or GdkPixbuf.PixbufAnimation or None, str)
@ -150,17 +148,16 @@ class WidgetMixin:
grid.show_all() grid.show_all()
scroll.add(grid) scroll.add(grid)
grid.set_name(f"{wid}|{view.get_id()}") grid.set_name(f"{wid}|{tab.get_id()}")
scroll.set_name(f"{wid}|{view.get_id()}") scroll.set_name(f"{wid}|{tab.get_id()}")
self.builder.expose_object(f"{wid}|{view.get_id()}|iconview", grid) self.builder.expose_object(f"{wid}|{tab.get_id()}|icon_grid", grid)
self.builder.expose_object(f"{wid}|{view.get_id()}", scroll) self.builder.expose_object(f"{wid}|{tab.get_id()}", scroll)
return scroll, store return scroll, store
def create_grid_treeview_widget(self, view, wid): def create_icon_tree_widget(self, tab, wid):
scroll = Gtk.ScrolledWindow() scroll = Gtk.ScrolledWindow()
grid = Gtk.TreeView() grid = Gtk.TreeView()
store = Gtk.ListStore(GdkPixbuf.Pixbuf or GdkPixbuf.PixbufAnimation or None, str) store = Gtk.TreeStore(GdkPixbuf.Pixbuf or GdkPixbuf.PixbufAnimation or None, str)
# store = Gtk.TreeStore(GdkPixbuf.Pixbuf or None, str)
column = Gtk.TreeViewColumn("Icons") column = Gtk.TreeViewColumn("Icons")
icon = Gtk.CellRendererPixbuf() icon = Gtk.CellRendererPixbuf()
name = Gtk.CellRendererText() name = Gtk.CellRendererText()
@ -199,23 +196,23 @@ class WidgetMixin:
grid.show_all() grid.show_all()
scroll.add(grid) scroll.add(grid)
grid.set_name(f"{wid}|{view.get_id()}") grid.set_name(f"{wid}|{tab.get_id()}")
scroll.set_name(f"{wid}|{view.get_id()}") scroll.set_name(f"{wid}|{tab.get_id()}")
grid.columns_autosize() grid.columns_autosize()
self.builder.expose_object(f"{wid}|{view.get_id()}", scroll) self.builder.expose_object(f"{wid}|{tab.get_id()}", scroll)
return scroll, store return scroll, store
def get_store_and_label_from_notebook(self, notebook, _name): def get_store_and_label_from_notebook(self, notebook, _name):
icon_view = None icon_grid = None
tab_label = None tab_label = None
store = None store = None
for obj in notebook.get_children(): for obj in notebook.get_children():
icon_view = obj.get_children()[0] icon_grid = obj.get_children()[0]
name = icon_view.get_name() name = icon_grid.get_name()
if name == _name: if name == _name:
store = icon_view.get_model() store = icon_grid.get_model()
tab_label = notebook.get_tab_label(obj).get_children()[0] tab_label = notebook.get_tab_label(obj).get_children()[0]
return store, tab_label return store, tab_label

View File

@ -10,9 +10,6 @@ from gi.repository import Gdk, Gio
# Application imports # Application imports
from .tab_mixin import TabMixin from .tab_mixin import TabMixin
from .widget_mixin import WidgetMixin
class WindowMixin(TabMixin): class WindowMixin(TabMixin):
@ -22,47 +19,46 @@ class WindowMixin(TabMixin):
if session_json: if session_json:
for j, value in enumerate(session_json): for j, value in enumerate(session_json):
i = j + 1 i = j + 1
isHidden = True if value[0]["window"]["isHidden"] == "True" else False notebook_tggl_button = self.builder.get_object(f"tggl_notebook_{i}")
object = self.builder.get_object(f"tggl_notebook_{i}") is_hidden = True if value[0]["window"]["isHidden"] == "True" else False
views = value[0]["window"]["views"] tabs = value[0]["window"]["tabs"]
self.window_controller.create_window() self.fm_controller.create_window()
object.set_active(True) notebook_tggl_button.set_active(True)
for view in views: for tab in tabs:
self.create_new_view_notebook(None, i, view) self.create_new_tab_notebook(None, i, tab)
if isHidden: if is_hidden:
self.toggle_notebook_pane(object) self.toggle_notebook_pane(notebook_tggl_button)
try: try:
if not self.is_pane4_hidden: if not self.is_pane4_hidden:
icon_view = self.window4.get_children()[1].get_children()[0] icon_grid = self.window4.get_children()[1].get_children()[0]
icon_view.event(Gdk.Event().new(type=Gdk.EventType.BUTTON_RELEASE))
elif not self.is_pane3_hidden: elif not self.is_pane3_hidden:
icon_view = self.window3.get_children()[1].get_children()[0] icon_grid = self.window3.get_children()[1].get_children()[0]
icon_view.event(Gdk.Event().new(type=Gdk.EventType.BUTTON_RELEASE))
elif not self.is_pane2_hidden: elif not self.is_pane2_hidden:
icon_view = self.window2.get_children()[1].get_children()[0] icon_grid = self.window2.get_children()[1].get_children()[0]
icon_view.event(Gdk.Event().new(type=Gdk.EventType.BUTTON_RELEASE))
elif not self.is_pane1_hidden: elif not self.is_pane1_hidden:
icon_view = self.window1.get_children()[1].get_children()[0] icon_grid = self.window1.get_children()[1].get_children()[0]
icon_view.event(Gdk.Event().new(type=Gdk.EventType.BUTTON_RELEASE))
icon_grid.event(Gdk.Event().new(type=Gdk.EventType.BUTTON_RELEASE))
icon_grid.event(Gdk.Event().new(type=Gdk.EventType.BUTTON_RELEASE))
except Exception as e: except Exception as e:
print("\n: The saved session might be missing window data! :\nLocation: ~/.config/solarfm/session.json\nFix: Back it up and delete it to reset.\n") print("\n: The saved session might be missing window data! :\nLocation: ~/.config/solarfm/session.json\nFix: Back it up and delete it to reset.\n")
print(repr(e)) print(repr(e))
else: else:
for j in range(0, 4): for j in range(0, 4):
i = j + 1 i = j + 1
self.window_controller.create_window() self.fm_controller.create_window()
self.create_new_view_notebook(None, i, None) self.create_new_tab_notebook(None, i, None)
def get_fm_window(self, wid): def get_fm_window(self, wid):
return self.window_controller.get_window_by_nickname(f"window_{wid}") return self.fm_controller.get_window_by_nickname(f"window_{wid}")
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):
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
dir = view.get_current_directory() dir = tab.get_current_directory()
uris = [] uris = []
for path in treePaths: for path in treePaths:
@ -80,10 +76,10 @@ class WindowMixin(TabMixin):
return uris return uris
def set_bottom_labels(self, view): def set_bottom_labels(self, tab):
_wid, _tid, _view, icon_view, store = self.get_current_state() _wid, _tid, _tab, icon_grid, store = self.get_current_state()
selected_files = icon_view.get_selected_items() selected_files = icon_grid.get_selected_items()
current_directory = view.get_current_directory() current_directory = tab.get_current_directory()
path_file = Gio.File.new_for_path(current_directory) path_file = Gio.File.new_for_path(current_directory)
mount_file = path_file.query_filesystem_info(attributes="filesystem::*", cancellable=None) mount_file = path_file.query_filesystem_info(attributes="filesystem::*", cancellable=None)
formatted_mount_free = self.sizeof_fmt( int(mount_file.get_attribute_as_string("filesystem::free")) ) formatted_mount_free = self.sizeof_fmt( int(mount_file.get_attribute_as_string("filesystem::free")) )
@ -98,8 +94,8 @@ class WindowMixin(TabMixin):
# If something selected # If something selected
self.bottom_size_label.set_label(f"{formatted_mount_free} free / {formatted_mount_size}") self.bottom_size_label.set_label(f"{formatted_mount_free} free / {formatted_mount_size}")
self.bottom_path_label.set_label(view.get_current_directory()) self.bottom_path_label.set_label(tab.get_current_directory())
if len(selected_files) > 0: if selected_files:
uris = self.format_to_uris(store, _wid, _tid, selected_files, True) uris = self.format_to_uris(store, _wid, _tid, selected_files, True)
combined_size = 0 combined_size = 0
for uri in uris: for uri in uris:
@ -115,29 +111,29 @@ class WindowMixin(TabMixin):
formatted_size = self.sizeof_fmt(combined_size) formatted_size = self.sizeof_fmt(combined_size)
if view.get_hidden(): if tab.is_hiding_hidden():
self.bottom_path_label.set_label(f" {len(uris)} / {view.get_files_count()} ({formatted_size})") self.bottom_path_label.set_label(f" {len(uris)} / {tab.get_files_count()} ({formatted_size})")
else: else:
self.bottom_path_label.set_label(f" {len(uris)} / {view.get_not_hidden_count()} ({formatted_size})") self.bottom_path_label.set_label(f" {len(uris)} / {tab.get_not_hidden_count()} ({formatted_size})")
return return
# If nothing selected # If nothing selected
if view.get_hidden(): if tab.get_hidden():
if view.get_hidden_count() > 0: if tab.get_hidden_count() > 0:
self.bottom_file_count_label.set_label(f"{view.get_not_hidden_count()} visible ({view.get_hidden_count()} hidden)") self.bottom_file_count_label.set_label(f"{tab.get_not_hidden_count()} visible ({tab.get_hidden_count()} hidden)")
else: else:
self.bottom_file_count_label.set_label(f"{view.get_files_count()} items") self.bottom_file_count_label.set_label(f"{tab.get_files_count()} items")
else: else:
self.bottom_file_count_label.set_label(f"{view.get_files_count()} items") self.bottom_file_count_label.set_label(f"{tab.get_files_count()} items")
def set_window_title(self): def set_window_title(self):
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
notebook = self.builder.get_object(f"window_{wid}") notebook = self.builder.get_object(f"window_{wid}")
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
dir = view.get_current_directory() dir = tab.get_current_directory()
for _notebook in self.notebooks: for _notebook in self.notebooks:
ctx = _notebook.get_style_context() ctx = _notebook.get_style_context()
@ -149,73 +145,73 @@ class WindowMixin(TabMixin):
ctx.add_class("notebook-selected-focus") ctx.add_class("notebook-selected-focus")
self.window.set_title(f"SolarFM ~ {dir}") self.window.set_title(f"SolarFM ~ {dir}")
self.set_bottom_labels(view) self.set_bottom_labels(tab)
def set_path_text(self, wid, tid): def set_path_text(self, wid, tid):
path_entry = self.builder.get_object("path_entry") path_entry = self.builder.get_object("path_entry")
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
path_entry.set_text(view.get_current_directory()) path_entry.set_text(tab.get_current_directory())
def grid_set_selected_items(self, iconview): def grid_set_selected_items(self, icons_grid):
self.selected_files = iconview.get_selected_items() self.selected_files = icons_grid.get_selected_items()
def grid_cursor_toggled(self, iconview): def grid_cursor_toggled(self, icons_grid):
print("wat...") print("wat...")
def grid_icon_single_click(self, iconview, eve): def grid_icon_single_click(self, icons_grid, eve):
try: try:
self.path_menu.popdown() self.path_menu.popdown()
wid, tid = iconview.get_name().split("|") wid, tid = icons_grid.get_name().split("|")
self.window_controller.set__wid_and_tid(wid, tid) self.fm_controller.set__wid_and_tid(wid, tid)
self.set_path_text(wid, tid) self.set_path_text(wid, tid)
self.set_window_title() self.set_window_title()
if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 1: # l-click if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 1: # l-click
if self.single_click_open: # FIXME: need to find a way to pass the model index if self.single_click_open: # FIXME: need to find a way to pass the model index
self.grid_icon_double_click(iconview) self.grid_icon_double_click(icons_grid)
elif eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 3: # r-click elif eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 3: # r-click
self.show_context_menu() self.show_context_menu()
except Exception as e: except Exception as e:
print(repr(e)) print(repr(e))
self.display_message(self.error, f"{repr(e)}") self.display_message(self.error_color, f"{repr(e)}")
def grid_icon_double_click(self, iconview, item, data=None): def grid_icon_double_click(self, icons_grid, item, data=None):
try: try:
if self.ctrlDown and self.shiftDown: if self.ctrl_down and self.shift_down:
self.unset_keys_and_data() self.unset_keys_and_data()
self.execute_files(in_terminal=True) self.execute_files(in_terminal=True)
return return
elif self.ctrlDown: elif self.ctrl_down:
self.unset_keys_and_data() self.unset_keys_and_data()
self.execute_files() self.execute_files()
return return
wid, tid, view, _iconview, store = self.get_current_state() wid, tid, tab, _icons_grid, store = self.get_current_state()
notebook = self.builder.get_object(f"window_{wid}") notebook = self.builder.get_object(f"window_{wid}")
tab_label = self.get_tab_label(notebook, iconview) tab_label = self.get_tab_label(notebook, icons_grid)
fileName = store[item][1] fileName = store[item][1]
dir = view.get_current_directory() dir = tab.get_current_directory()
file = f"{dir}/{fileName}" file = f"{dir}/{fileName}"
if isdir(file): if isdir(file):
view.set_path(file) tab.set_path(file)
self.update_view(tab_label, view, store, wid, tid) self.update_tab(tab_label, tab, store, wid, tid)
else: else:
self.open_files() self.open_files()
except Exception as e: except Exception as e:
self.display_message(self.error, f"{repr(e)}") self.display_message(self.error_color, f"{repr(e)}")
def grid_on_drag_set(self, iconview, drag_context, data, info, time): def grid_on_drag_set(self, icons_grid, drag_context, data, info, time):
action = iconview.get_name() action = icons_grid.get_name()
wid, tid = action.split("|") wid, tid = action.split("|")
store = iconview.get_model() store = icons_grid.get_model()
treePaths = iconview.get_selected_items() treePaths = icons_grid.get_selected_items()
# NOTE: Need URIs as URI format for DnD to work. Will strip 'file://' # NOTE: Need URIs as URI format for DnD to work. Will strip 'file://'
# further down call chain when doing internal fm stuff. # further down call chain when doing internal fm stuff.
uris = self.format_to_uris(store, wid, tid, treePaths) uris = self.format_to_uris(store, wid, tid, treePaths)
@ -224,30 +220,30 @@ class WindowMixin(TabMixin):
data.set_uris(uris) data.set_uris(uris)
data.set_text(uris_text, -1) data.set_text(uris_text, -1)
def grid_on_drag_motion(self, iconview, drag_context, x, y, data): def grid_on_drag_motion(self, icons_grid, drag_context, x, y, data):
current = '|'.join(self.window_controller.get_active_wid_and_tid()) current = '|'.join(self.fm_controller.get_active_wid_and_tid())
target = iconview.get_name() target = icons_grid.get_name()
wid, tid = target.split("|") wid, tid = target.split("|")
store = iconview.get_model() store = icons_grid.get_model()
treePath = iconview.get_drag_dest_item().path treePath = icons_grid.get_drag_dest_item().path
if treePath: if treePath:
uri = self.format_to_uris(store, wid, tid, treePath)[0].replace("file://", "") uri = self.format_to_uris(store, wid, tid, treePath)[0].replace("file://", "")
self.override_drop_dest = uri if isdir(uri) else None self.override_drop_dest = uri if isdir(uri) else None
if target not in current: if target not in current:
self.window_controller.set__wid_and_tid(wid, tid) self.fm_controller.set__wid_and_tid(wid, tid)
def grid_on_drag_data_received(self, widget, drag_context, x, y, data, info, time): def grid_on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
if info == 80: if info == 80:
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
notebook = self.builder.get_object(f"window_{wid}") notebook = self.builder.get_object(f"window_{wid}")
store, tab_label = self.get_store_and_label_from_notebook(notebook, f"{wid}|{tid}") store, tab_label = self.get_store_and_label_from_notebook(notebook, f"{wid}|{tid}")
view = self.get_fm_window(wid).get_view_by_id(tid) tab = self.get_fm_window(wid).get_tab_by_id(tid)
uris = data.get_uris() uris = data.get_uris()
dest = f"{view.get_current_directory()}" if not self.override_drop_dest else self.override_drop_dest dest = f"{tab.get_current_directory()}" if not self.override_drop_dest else self.override_drop_dest
if len(uris) == 0: if len(uris) == 0:
uris = data.get_text().split("\n") uris = data.get_text().split("\n")
@ -256,5 +252,5 @@ class WindowMixin(TabMixin):
self.move_files(uris, dest) self.move_files(uris, dest)
def create_new_view_notebook(self, widget=None, wid=None, path=None): def create_new_tab_notebook(self, widget=None, wid=None, path=None):
self.create_tab(wid, path) self.create_tab(wid, path)

View File

@ -0,0 +1,3 @@
"""
Signals module
"""

View File

@ -13,7 +13,7 @@ class IPCSignalsMixin:
print(message) print(message)
def handle_file_from_ipc(self, path): def handle_file_from_ipc(self, path):
wid, tid = self.window_controller.get_active_wid_and_tid() wid, tid = self.fm_controller.get_active_wid_and_tid()
notebook = self.builder.get_object(f"window_{wid}") notebook = self.builder.get_object(f"window_{wid}")
if notebook.is_visible(): if notebook.is_visible():
self.create_tab(wid, path) self.create_tab(wid, path)

View File

@ -17,20 +17,20 @@ class KeyboardSignalsMixin:
""" KeyboardSignalsMixin keyboard hooks controller. """ """ KeyboardSignalsMixin keyboard hooks controller. """
def unset_keys_and_data(self, widget=None, eve=None): def unset_keys_and_data(self, widget=None, eve=None):
self.ctrlDown = False self.ctrl_down = False
self.shiftDown = False self.shift_down = False
self.altDown = False self.alt_down = False
self.is_searching = False self.is_searching = False
def global_key_press_controller(self, eve, user_data): def global_key_press_controller(self, eve, user_data):
keyname = Gdk.keyval_name(user_data.keyval).lower() keyname = Gdk.keyval_name(user_data.keyval).lower()
if "control" in keyname or "alt" in keyname or "shift" in keyname: if keyname.replace("_l", "").replace("_r", "") in ["control", "alt", "shift"]:
if "control" in keyname: if "control" in keyname:
self.ctrlDown = True self.ctrl_down = True
if "shift" in keyname: if "shift" in keyname:
self.shiftDown = True self.shift_down = True
if "alt" in keyname: if "alt" in keyname:
self.altDown = True self.alt_down = True
# NOTE: Yes, this should actually be mapped to some key controller setting # NOTE: Yes, this should actually be mapped to some key controller setting
# file or something. Sue me. # file or something. Sue me.
@ -39,84 +39,56 @@ class KeyboardSignalsMixin:
if debug: if debug:
print(f"global_key_release_controller > key > {keyname}") print(f"global_key_release_controller > key > {keyname}")
if "control" in keyname or "alt" in keyname or "shift" in keyname: if keyname.replace("_l", "").replace("_r", "") in ["control", "alt", "shift"]:
if "control" in keyname: if "control" in keyname:
self.ctrlDown = False self.ctrl_down = False
if "shift" in keyname: if "shift" in keyname:
self.shiftDown = False self.shift_down = False
if "alt" in keyname: if "alt" in keyname:
self.altDown = False self.alt_down = False
if self.ctrl_down and self.shift_down and keyname == "t":
if self.ctrlDown and self.shiftDown and keyname == "t":
self.unset_keys_and_data() self.unset_keys_and_data()
self.trash_files() self.trash_files()
if self.ctrl_down:
if re.fullmatch(valid_keyvalue_pat, keyname): if keyname in ["1", "kp_1", "2", "kp_2", "3", "kp_3", "4", "kp_4"]:
if not self.is_searching and not self.ctrlDown \ self.builder.get_object(f"tggl_notebook_{keyname.strip('kp_')}").released()
and not self.shiftDown and not self.altDown: if keyname == "q":
focused_obj = self.window.get_focus()
if isinstance(focused_obj, Gtk.IconView):
self.is_searching = True
wid, tid, self.search_view, self.search_iconview, store = self.get_current_state()
self.unset_keys_and_data()
self.popup_search_files(wid, keyname)
return
if (self.ctrlDown and keyname in ["1", "kp_1"]):
self.builder.get_object("tggl_notebook_1").released()
if (self.ctrlDown and keyname in ["2", "kp_2"]):
self.builder.get_object("tggl_notebook_2").released()
if (self.ctrlDown and keyname in ["3", "kp_3"]):
self.builder.get_object("tggl_notebook_3").released()
if (self.ctrlDown and keyname in ["4", "kp_4"]):
self.builder.get_object("tggl_notebook_4").released()
if self.ctrlDown and keyname == "q":
self.tear_down() self.tear_down()
if (self.ctrlDown and keyname == "slash") or keyname == "home": if keyname == "slash" or keyname == "home":
self.builder.get_object("go_home").released() self.builder.get_object("go_home").released()
if (self.ctrlDown and keyname == "r") or keyname == "f5": if keyname == "r" or keyname == "f5":
self.builder.get_object("refresh_view").released() self.builder.get_object("refresh_tab").released()
if (self.ctrlDown and keyname == "up") or (self.ctrlDown and keyname == "u"): if keyname == "up" or keyname == "u":
self.builder.get_object("go_up").released() self.builder.get_object("go_up").released()
if self.ctrlDown and keyname == "l": if keyname == "l":
self.unset_keys_and_data() self.unset_keys_and_data()
self.builder.get_object("path_entry").grab_focus() self.builder.get_object("path_entry").grab_focus()
if self.ctrlDown and keyname == "t": if keyname == "t":
self.builder.get_object("create_tab").released() self.builder.get_object("create_tab").released()
if self.ctrlDown and keyname == "o": if keyname == "o":
self.unset_keys_and_data() self.unset_keys_and_data()
self.open_files() self.open_files()
if self.ctrlDown and keyname == "w": if keyname == "w":
self.keyboard_close_tab() self.keyboard_close_tab()
if self.ctrlDown and keyname == "h": if keyname == "h":
self.show_hide_hidden_files() self.show_hide_hidden_files()
if (self.ctrlDown and keyname == "e"): if keyname == "e":
self.unset_keys_and_data() self.unset_keys_and_data()
self.rename_files() self.rename_files()
if self.ctrlDown and keyname == "c": if keyname == "c":
self.copy_files() self.copy_files()
self.to_cut_files.clear() self.to_cut_files.clear()
if self.ctrlDown and keyname == "x": if keyname == "x":
self.to_copy_files.clear() self.to_copy_files.clear()
self.cut_files() self.cut_files()
if self.ctrlDown and keyname == "v": if keyname == "v":
self.paste_files() self.paste_files()
if self.ctrlDown and keyname == "n": if keyname == "n":
self.unset_keys_and_data() self.unset_keys_and_data()
self.show_new_file_menu() self.show_new_file_menu()
if keyname in ["alt_l", "alt_r"]:
top_main_menubar = self.builder.get_object("top_main_menubar")
if top_main_menubar.is_visible():
top_main_menubar.hide()
else:
top_main_menubar.show()
if keyname == "delete": if keyname == "delete":
self.unset_keys_and_data() self.unset_keys_and_data()
self.delete_files() self.delete_files()
@ -126,3 +98,17 @@ class KeyboardSignalsMixin:
if keyname == "f4": if keyname == "f4":
self.unset_keys_and_data() self.unset_keys_and_data()
self.open_terminal() self.open_terminal()
if keyname in ["alt_l", "alt_r"]:
top_main_menubar = self.builder.get_object("top_main_menubar")
top_main_menubar.hide() if top_main_menubar.is_visible() else top_main_menubar.show()
if re.fullmatch(valid_keyvalue_pat, keyname):
if not self.is_searching and not self.ctrl_down \
and not self.shift_down and not self.alt_down:
focused_obj = self.window.get_focus()
if isinstance(focused_obj, Gtk.IconView):
self.is_searching = True
wid, tid, self.search_tab, self.search_icon_grid, store = self.get_current_state()
self.unset_keys_and_data()
self.popup_search_files(wid, keyname)
return

View File

@ -1,5 +1,5 @@
# Python imports # Python imports
import threading, time import os, threading, time
from multiprocessing.connection import Listener, Client from multiprocessing.connection import Listener, Client
# Lib imports # Lib imports
@ -15,12 +15,32 @@ def threaded(fn):
class IPCServerMixin: class IPCServer:
""" Create a listener so that other SolarFM instances send requests back to existing instance. """ """ Create a listener so that other SolarFM instances send requests back to existing instance. """
def __init__(self, conn_type="socket"):
self.is_ipc_alive = False
self._conn_type = conn_type
self.ipc_authkey = b'solarfm-ipc'
self.ipc_timeout = 15.0
if conn_type == "socket":
self.ipc_address = '/tmp/solarfm-ipc.sock'
else:
self.ipc_address = '127.0.0.1'
self.ipc_port = 4848
@threaded @threaded
def create_ipc_server(self): def create_ipc_server(self):
if self._conn_type == "socket":
if os.path.exists(self.ipc_address):
return
listener = Listener(address=self.ipc_address, family="AF_UNIX", authkey=self.ipc_authkey)
else:
listener = Listener((self.ipc_address, self.ipc_port), authkey=self.ipc_authkey) listener = Listener((self.ipc_address, self.ipc_port), authkey=self.ipc_authkey)
self.is_ipc_alive = True self.is_ipc_alive = True
while True: while True:
conn = listener.accept() conn = listener.accept()
@ -58,7 +78,12 @@ class IPCServerMixin:
def send_ipc_message(self, message="Empty Data..."): def send_ipc_message(self, message="Empty Data..."):
try: try:
if self._conn_type == "socket":
conn = Client(address=self.ipc_address, family="AF_UNIX", authkey=self.ipc_authkey)
else:
conn = Client((self.ipc_address, self.ipc_port), authkey=self.ipc_authkey) conn = Client((self.ipc_address, self.ipc_port), authkey=self.ipc_authkey)
conn.send(message) conn.send(message)
conn.send('close connection') conn.send('close connection')
except Exception as e: except Exception as e:

View File

@ -13,8 +13,6 @@ from gi.repository import Gtk, Gio
class Plugin: class Plugin:
name = None name = None
module = None module = None
gtk_socket_id = None
gtk_socket = None
reference = None reference = None
@ -23,8 +21,7 @@ class Plugins:
def __init__(self, settings): def __init__(self, settings):
self._settings = settings self._settings = settings
self._plugin_list_widget = self._settings.get_builder().get_object("plugin_list") self._builder = self._settings.get_builder()
self._plugin_list_socket = self._settings.get_builder().get_object("plugin_socket")
self._plugins_path = self._settings.get_plugins_path() self._plugins_path = self._settings.get_plugins_path()
self._plugins_dir_watcher = None self._plugins_dir_watcher = None
self._plugin_collection = [] self._plugin_collection = []
@ -56,26 +53,18 @@ class Plugins:
if isdir(path): if isdir(path):
os.chdir(path) os.chdir(path)
gtk_socket = Gtk.Socket().new()
self._plugin_list_socket.add(gtk_socket)
# NOTE: Must get ID after adding socket to window. Else issues....
gtk_socket_id = gtk_socket.get_id()
sys.path.insert(0, path) sys.path.insert(0, path)
spec = importlib.util.spec_from_file_location(file, join(path, "__main__.py")) spec = importlib.util.spec_from_file_location(file, join(path, "__main__.py"))
module = importlib.util.module_from_spec(spec) app = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) spec.loader.exec_module(app)
ref = module.Main(gtk_socket_id, event_system) plugin_reference = app.Plugin(self._builder, event_system)
plugin = Plugin() plugin = Plugin()
plugin.name = ref.get_plugin_name() plugin.name = plugin_reference.get_plugin_name()
plugin.module = path plugin.module = path
plugin.gtk_socket_id = gtk_socket_id plugin.reference = plugin_reference
plugin.gtk_socket = gtk_socket
plugin.reference = ref
self._plugin_collection.append(plugin) self._plugin_collection.append(plugin)
gtk_socket.show_all()
except Exception as e: except Exception as e:
print("Malformed plugin! Not loading!") print("Malformed plugin! Not loading!")
traceback.print_exc() traceback.print_exc()
@ -84,14 +73,9 @@ class Plugins:
def reload_plugins(self, file=None): def reload_plugins(self, file=None):
print(f"Reloading plugins...") print(f"Reloading plugins... stub.")
# if self._plugin_collection:
# to_unload = []
# for dir in self._plugin_collection:
# if not os.path.isdir(os.path.join(self._plugins_path, dir)):
# to_unload.append(dir)
def set_message_on_plugin(self, type, data): def send_message_to_plugin(self, type, data):
print("Trying to send message to plugin...") print("Trying to send message to plugin...")
for plugin in self._plugin_collection: for plugin in self._plugin_collection:
if type in plugin.name: if type in plugin.name:

View File

@ -0,0 +1,3 @@
"""
Root of ShellFM
"""

View File

@ -0,0 +1,3 @@
"""
Window module
"""

View File

@ -40,20 +40,20 @@ class WindowController:
return window return window
def add_view_for_window(self, win_id): def add_tab_for_window(self, win_id):
for window in self._windows: for window in self._windows:
if window.get_id() == win_id: if window.get_id() == win_id:
return window.create_view() return window.create_tab()
def add_view_for_window_by_name(self, name): def add_tab_for_window_by_name(self, name):
for window in self._windows: for window in self._windows:
if window.get_name() == name: if window.get_name() == name:
return window.create_view() return window.create_tab()
def add_view_for_window_by_nickname(self, nickname): def add_tab_for_window_by_nickname(self, nickname):
for window in self._windows: for window in self._windows:
if window.get_nickname() == nickname: if window.get_nickname() == nickname:
return window.create_view() return window.create_tab()
def pop_window(self): def pop_window(self):
self._windows.pop() self._windows.pop()
@ -116,33 +116,33 @@ class WindowController:
print(f"Name: {window.get_name()}") print(f"Name: {window.get_name()}")
print(f"Nickname: {window.get_nickname()}") print(f"Nickname: {window.get_nickname()}")
print(f"Is Hidden: {window.is_hidden()}") print(f"Is Hidden: {window.is_hidden()}")
print(f"View Count: {window.get_views_count()}") print(f"Tab Count: {window.get_tabs_count()}")
print("\n-------------------------\n") print("\n-------------------------\n")
def list_files_from_views_of_window(self, win_id): def list_files_from_tabs_of_window(self, win_id):
for window in self._windows: for window in self._windows:
if window.get_id() == win_id: if window.get_id() == win_id:
window.list_files_from_views() window.list_files_from_tabs()
break break
def get_views_count(self, win_id): def get_tabs_count(self, win_id):
for window in self._windows: for window in self._windows:
if window.get_id() == win_id: if window.get_id() == win_id:
return window.get_views_count() return window.get_tabs_count()
def get_views_from_window(self, win_id): def get_tabs_from_window(self, win_id):
for window in self._windows: for window in self._windows:
if window.get_id() == win_id: if window.get_id() == win_id:
return window.get_all_views() return window.get_all_tabs()
def unload_views_and_windows(self): def unload_tabs_and_windows(self):
for window in self._windows: for window in self._windows:
window.get_all_views().clear() window.get_all_tabs().clear()
self._windows.clear() self._windows.clear()
@ -153,9 +153,9 @@ class WindowController:
if len(self._windows) > 0: if len(self._windows) > 0:
windows = [] windows = []
for window in self._windows: for window in self._windows:
views = [] tabs = []
for view in window.get_all_views(): for tab in window.get_all_tabs():
views.append(view.get_current_directory()) tabs.append(tab.get_current_directory())
windows.append( windows.append(
[ [
@ -165,7 +165,7 @@ class WindowController:
"Name": window.get_name(), "Name": window.get_name(),
"Nickname": window.get_nickname(), "Nickname": window.get_nickname(),
"isHidden": f"{window.is_hidden()}", "isHidden": f"{window.is_hidden()}",
'views': views 'tabs': tabs
} }
} }
] ]

View File

@ -0,0 +1,3 @@
"""
Tabs module
"""

View File

@ -0,0 +1,3 @@
"""
Icons module
"""

View File

@ -0,0 +1,3 @@
"""
Icons mixins module
"""

View File

@ -18,7 +18,7 @@ from .icons.icon import Icon
from .path import Path from .path import Path
class View(Settings, FileHandler, Launcher, Icon, Path): class Tab(Settings, FileHandler, Launcher, Icon, Path):
def __init__(self): def __init__(self):
self.logger = None self.logger = None
self._id_length = 10 self._id_length = 10

View File

@ -0,0 +1,3 @@
"""
Utils module
"""

View File

@ -6,7 +6,7 @@ from random import randint
# Application imports # Application imports
from .views.view import View from .tabs.tab import Tab
class Window: class Window:
@ -16,45 +16,40 @@ class Window:
self._name = "" self._name = ""
self._nickname = "" self._nickname = ""
self._isHidden = False self._isHidden = False
self._views = [] self._tabs = []
self._generate_id() self._generate_id()
self._set_name() self._set_name()
def create_view(self): def create_tab(self):
view = View() tab = Tab()
self._views.append(view) self._tabs.append(tab)
return view return tab
def pop_view(self): def pop_tab(self):
self._views.pop() self._tabs.pop()
def delete_view_by_id(self, vid): def delete_tab_by_id(self, vid):
for view in self._views: for tab in self._tabs:
if view.get_id() == vid: if tab.get_id() == vid:
self._views.remove(view) self._tabs.remove(tab)
break break
def get_view_by_id(self, vid): def get_tab_by_id(self, vid):
for view in self._views: for tab in self._tabs:
if view.get_id() == vid: if tab.get_id() == vid:
return view return tab
def get_view_by_index(self, index): def get_tab_by_index(self, index):
return self._views[index] return self._tabs[index]
def get_views_count(self): def get_tabs_count(self):
return len(self._views) return len(self._tabs)
def get_all_views(self):
return self._views
def list_files_from_views(self):
for view in self._views:
print(view.get_files())
def get_all_tabs(self):
return self._tabs
def get_id(self): def get_id(self):
return self._id return self._id
@ -68,7 +63,9 @@ class Window:
def is_hidden(self): def is_hidden(self):
return self._isHidden return self._isHidden
def list_files_from_tabs(self):
for tab in self._tabs:
print(tab.get_files())
def set_nickname(self, nickname): def set_nickname(self, nickname):
@ -80,6 +77,7 @@ class Window:
def _set_name(self): def _set_name(self):
self._name = "window_" + self.get_id() self._name = "window_" + self.get_id()
def _random_with_N_digits(self, n): def _random_with_N_digits(self, n):
range_start = 10**(n-1) range_start = 10**(n-1)
range_end = (10**n)-1 range_end = (10**n)-1

View File

@ -0,0 +1,3 @@
"""
Trasher module
"""

View File

View File

View File

@ -0,0 +1,3 @@
"""
Utils module
"""

View File

@ -7,8 +7,8 @@ import gi, cairo
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0') gi.require_version('Gdk', '3.0')
from gi.repository import Gtk as gtk from gi.repository import Gtk
from gi.repository import Gdk as gdk from gi.repository import Gdk
# Application imports # Application imports
@ -17,36 +17,39 @@ from .logger import Logger
class Settings: class Settings:
def __init__(self): def __init__(self):
self.builder = gtk.Builder() self._SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__))
self._USER_HOME = path.expanduser('~')
self._CONFIG_PATH = f"{self._USER_HOME}/.config/{app_name.lower()}"
self._PLUGINS_PATH = f"{self._CONFIG_PATH}/plugins"
self._USR_SOLARFM = f"/usr/share/{app_name.lower()}"
self.SCRIPT_PTH = os.path.dirname(os.path.realpath(__file__)) self._CSS_FILE = f"{self._CONFIG_PATH}/stylesheet.css"
self.USER_HOME = path.expanduser('~') self._WINDOWS_GLADE = f"{self._CONFIG_PATH}/Main_Window.glade"
self.CONFIG_PATH = f"{self.USER_HOME}/.config/{app_name.lower()}" self._DEFAULT_ICONS = f"{self._CONFIG_PATH}/icons"
self.PLUGINS_PATH = f"{self.CONFIG_PATH}/plugins" self._WINDOW_ICON = f"{self._DEFAULT_ICONS}/{app_name.lower()}.png"
self.USR_SOLARFM = f"/usr/share/{app_name.lower()}"
if not os.path.exists(self._CONFIG_PATH):
os.mkdir(self._CONFIG_PATH)
if not os.path.exists(self._PLUGINS_PATH):
os.mkdir(self._PLUGINS_PATH)
if not os.path.exists(self._WINDOWS_GLADE):
self._WINDOWS_GLADE = f"{self._USR_SOLARFM}/Main_Window.glade"
if not os.path.exists(self._CSS_FILE):
self._CSS_FILE = f"{self._USR_SOLARFM}/stylesheet.css"
if not os.path.exists(self._WINDOW_ICON):
self._WINDOW_ICON = f"{self._USR_SOLARFM}/icons/{app_name.lower()}.png"
if not os.path.exists(self._DEFAULT_ICONS):
self._DEFAULT_ICONS = f"{self._USR_SOLARFM}/icons"
self._success_color = "#88cc27"
self._warning_color = "#ffa800"
self._error_color = "#ff0000"
self.CSS_FILE = f"{self.CONFIG_PATH}/stylesheet.css"
self.WINDOWS_GLADE = f"{self.CONFIG_PATH}/Main_Window.glade"
self.DEFAULT_ICONS = f"{self.CONFIG_PATH}/icons"
self.WINDOW_ICON = f"{self.DEFAULT_ICONS}/{app_name.lower()}.png"
self.main_window = None self.main_window = None
self.logger = Logger(self._CONFIG_PATH).get_logger()
if not os.path.exists(self.CONFIG_PATH): self.builder = Gtk.Builder()
os.mkdir(self.CONFIG_PATH) self.builder.add_from_file(self._WINDOWS_GLADE)
if not os.path.exists(self.PLUGINS_PATH):
os.mkdir(self.PLUGINS_PATH)
if not os.path.exists(self.WINDOWS_GLADE):
self.WINDOWS_GLADE = f"{self.USR_SOLARFM}/Main_Window.glade"
if not os.path.exists(self.CSS_FILE):
self.CSS_FILE = f"{self.USR_SOLARFM}/stylesheet.css"
if not os.path.exists(self.WINDOW_ICON):
self.WINDOW_ICON = f"{self.USR_SOLARFM}/icons/{app_name.lower()}.png"
if not os.path.exists(self.DEFAULT_ICONS):
self.DEFAULT_ICONS = f"{self.USR_SOLARFM}/icons"
self.logger = Logger(self.CONFIG_PATH).get_logger()
self.builder.add_from_file(self.WINDOWS_GLADE)
@ -56,7 +59,7 @@ class Settings:
self._set_window_data() self._set_window_data()
def _set_window_data(self): def _set_window_data(self):
self.main_window.set_icon_from_file(self.WINDOW_ICON) self.main_window.set_icon_from_file(self._WINDOW_ICON)
screen = self.main_window.get_screen() screen = self.main_window.get_screen()
visual = screen.get_rgba_visual() visual = screen.get_rgba_visual()
@ -66,11 +69,11 @@ class Settings:
self.main_window.connect("draw", self._area_draw) self.main_window.connect("draw", self._area_draw)
# bind css file # bind css file
cssProvider = gtk.CssProvider() cssProvider = Gtk.CssProvider()
cssProvider.load_from_path(self.CSS_FILE) cssProvider.load_from_path(self._CSS_FILE)
screen = gdk.Screen.get_default() screen = Gdk.Screen.get_default()
styleContext = gtk.StyleContext() styleContext = Gtk.StyleContext()
styleContext.add_provider_for_screen(screen, cssProvider, gtk.STYLE_PROVIDER_PRIORITY_USER) styleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
def _area_draw(self, widget, cr): def _area_draw(self, widget, cr):
cr.set_source_rgba(0, 0, 0, 0.54) cr.set_source_rgba(0, 0, 0, 0.54)
@ -92,4 +95,8 @@ class Settings:
def get_builder(self): return self.builder def get_builder(self): return self.builder
def get_logger(self): return self.logger def get_logger(self): return self.logger
def get_main_window(self): return self.main_window def get_main_window(self): return self.main_window
def get_plugins_path(self): return self.PLUGINS_PATH def get_plugins_path(self): return self._PLUGINS_PATH
def get_success_color(self): return self._success_color
def get_warning_color(self): return self._warning_color
def get_error_color(self): return self._error_color

View File

@ -0,0 +1,3 @@
"""
Base module
"""

View File

@ -15,7 +15,7 @@ gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
# Application imports # Application imports
from main import Main from app import Application
if __name__ == "__main__": if __name__ == "__main__":
@ -35,7 +35,7 @@ if __name__ == "__main__":
# Read arguments (If any...) # Read arguments (If any...)
args, unknownargs = parser.parse_known_args() args, unknownargs = parser.parse_known_args()
Main(args, unknownargs) Application(args, unknownargs)
Gtk.main() Gtk.main()
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()

View File

@ -0,0 +1,55 @@
# Python imports
import os, inspect, time
# Lib imports
# Application imports
from utils.settings import Settings
from context.controller import Controller
from __builtins__ import EventSystem
class Application(EventSystem):
""" Create Settings and Controller classes. Bind signal to Builder. Inherit from Builtins to bind global methods and classes. """
def __init__(self, args, unknownargs):
if not trace_debug:
event_system.create_ipc_server()
time.sleep(0.1)
if not event_system.is_ipc_alive:
if unknownargs:
for arg in unknownargs:
if os.path.isdir(arg):
message = f"FILE|{arg}"
event_system.send_ipc_message(message)
if args.new_tab and os.path.isdir(args.new_tab):
message = f"FILE|{args.new_tab}"
event_system.send_ipc_message(message)
raise Exception("IPC Server Exists: Will send path(s) to it and close...")
settings = Settings()
settings.create_window()
controller = Controller(args, unknownargs, settings)
if not controller:
raise Exception("Controller exited and doesn't exist...")
# Gets the methods from the classes and sets to handler.
# Then, builder connects to any signals it needs.
classes = [controller]
handlers = {}
for c in classes:
methods = None
try:
methods = inspect.getmembers(c, predicate=inspect.ismethod)
handlers.update(methods)
except Exception as e:
print(repr(e))
settings.builder.connect_signals(handlers)

View File

@ -0,0 +1,3 @@
"""
Mixins module
"""

View File

@ -0,0 +1,3 @@
"""
UI module
"""

View File

@ -0,0 +1,3 @@
"""
Signals module
"""

View File

@ -0,0 +1,3 @@
"""
Root of ShellFM
"""

View File

@ -0,0 +1,3 @@
"""
Window module
"""

View File

@ -0,0 +1,3 @@
"""
Tabs module
"""

View File

@ -0,0 +1,3 @@
"""
Trasher module
"""