develop #11
|
@ -113,7 +113,7 @@ class Plugin(PluginBase):
|
||||||
self._archiver_dialogue.hide()
|
self._archiver_dialogue.hide()
|
||||||
|
|
||||||
def archive_files(self, save_target, state):
|
def archive_files(self, save_target, state):
|
||||||
paths = [shlex.quote(p) for p in state.selected_files]
|
paths = [shlex.quote(p) for p in state.uris]
|
||||||
|
|
||||||
sItr, eItr = self._arc_command_buffer.get_bounds()
|
sItr, eItr = self._arc_command_buffer.get_bounds()
|
||||||
pre_command = self._arc_command_buffer.get_text(sItr, eItr, False)
|
pre_command = self._arc_command_buffer.get_text(sItr, eItr, False)
|
||||||
|
|
|
@ -123,8 +123,8 @@ class Plugin(PluginBase):
|
||||||
GLib.idle_add(self._process_changes, (state))
|
GLib.idle_add(self._process_changes, (state))
|
||||||
|
|
||||||
def _process_changes(self, state):
|
def _process_changes(self, state):
|
||||||
if len(state.selected_files) == 1:
|
if len(state.uris) == 1:
|
||||||
uri = state.selected_files[0]
|
uri = state.uris[0]
|
||||||
path = state.tab.get_current_directory()
|
path = state.tab.get_current_directory()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ class Plugin(PluginBase):
|
||||||
def _process_changes(self, state):
|
def _process_changes(self, state):
|
||||||
self._fm_state = None
|
self._fm_state = None
|
||||||
|
|
||||||
if len(state.selected_files) == 1:
|
if len(state.uris) == 1:
|
||||||
self._fm_state = state
|
self._fm_state = state
|
||||||
self._set_ui_data()
|
self._set_ui_data()
|
||||||
response = self._thumbnailer_dialog.run()
|
response = self._thumbnailer_dialog.run()
|
||||||
|
@ -115,7 +115,7 @@ class Plugin(PluginBase):
|
||||||
print(video_data["videos"]) if not keys in ("", None) and "videos" in keys else ...
|
print(video_data["videos"]) if not keys in ("", None) and "videos" in keys else ...
|
||||||
|
|
||||||
def get_video_data(self):
|
def get_video_data(self):
|
||||||
uri = self._fm_state.selected_files[0]
|
uri = self._fm_state.uris[0]
|
||||||
path = self._fm_state.tab.get_current_directory()
|
path = self._fm_state.tab.get_current_directory()
|
||||||
parts = uri.split("/")
|
parts = uri.split("/")
|
||||||
_title = parts[ len(parts) - 1 ]
|
_title = parts[ len(parts) - 1 ]
|
||||||
|
|
|
@ -99,7 +99,7 @@ class Plugin(PluginBase):
|
||||||
def delete_files(self, widget = None, eve = None):
|
def delete_files(self, widget = None, eve = None):
|
||||||
self._event_system.emit("get_current_state")
|
self._event_system.emit("get_current_state")
|
||||||
state = self._fm_state
|
state = self._fm_state
|
||||||
uris = state.selected_files
|
uris = state.uris
|
||||||
response = None
|
response = None
|
||||||
|
|
||||||
state.message_dialog.format_secondary_text(f"Do you really want to delete the {len(uris)} file(s)?")
|
state.message_dialog.format_secondary_text(f"Do you really want to delete the {len(uris)} file(s)?")
|
||||||
|
@ -122,13 +122,13 @@ class Plugin(PluginBase):
|
||||||
def trash_files(self, widget = None, eve = None, verbocity = False):
|
def trash_files(self, widget = None, eve = None, verbocity = False):
|
||||||
self._event_system.emit("get_current_state")
|
self._event_system.emit("get_current_state")
|
||||||
state = self._fm_state
|
state = self._fm_state
|
||||||
for uri in state.selected_files:
|
for uri in state.uris:
|
||||||
self.trashman.trash(uri, verbocity)
|
self.trashman.trash(uri, verbocity)
|
||||||
|
|
||||||
def restore_trash_files(self, widget = None, eve = None, verbocity = False):
|
def restore_trash_files(self, widget = None, eve = None, verbocity = False):
|
||||||
self._event_system.emit("get_current_state")
|
self._event_system.emit("get_current_state")
|
||||||
state = self._fm_state
|
state = self._fm_state
|
||||||
for uri in state.selected_files:
|
for uri in state.uris:
|
||||||
self.trashman.restore(filename=uri.split("/")[-1], verbose = verbocity)
|
self.trashman.restore(filename=uri.split("/")[-1], verbose = verbocity)
|
||||||
|
|
||||||
def empty_trash(self, widget = None, eve = None, verbocity = False):
|
def empty_trash(self, widget = None, eve = None, verbocity = False):
|
||||||
|
|
|
@ -98,8 +98,8 @@ class Plugin(PluginBase):
|
||||||
def _process_changes(self, state):
|
def _process_changes(self, state):
|
||||||
self._fm_state = None
|
self._fm_state = None
|
||||||
|
|
||||||
if len(state.selected_files) == 1:
|
if len(state.uris) == 1:
|
||||||
if state.selected_files[0].lower().endswith(state.tab.fvideos):
|
if state.uris[0].lower().endswith(state.tab.fvideos):
|
||||||
self._fm_state = state
|
self._fm_state = state
|
||||||
self._set_ui_data()
|
self._set_ui_data()
|
||||||
response = self._thumbnailer_dialog.run()
|
response = self._thumbnailer_dialog.run()
|
||||||
|
@ -132,7 +132,7 @@ class Plugin(PluginBase):
|
||||||
|
|
||||||
|
|
||||||
def _set_ui_data(self):
|
def _set_ui_data(self):
|
||||||
uri = self._fm_state.selected_files[0]
|
uri = self._fm_state.uris[0]
|
||||||
path = self._fm_state.tab.get_current_directory()
|
path = self._fm_state.tab.get_current_directory()
|
||||||
parts = uri.split("/")
|
parts = uri.split("/")
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ from gi.repository import GLib
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from .controller_data import Controller_Data
|
from .controller_data import Controller_Data
|
||||||
|
from .fs_actions.file_system_actions import FileSystemActions
|
||||||
from .mixins.signals_mixins import SignalsMixins
|
from .mixins.signals_mixins import SignalsMixins
|
||||||
|
|
||||||
from .ui.dialogs.about_widget import AboutWidget
|
from .ui.dialogs.about_widget import AboutWidget
|
||||||
|
@ -57,17 +58,16 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
||||||
...
|
...
|
||||||
|
|
||||||
def _setup_signals(self):
|
def _setup_signals(self):
|
||||||
...
|
FileSystemActions()
|
||||||
|
|
||||||
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("clear_notebooks", self.clear_notebooks)
|
event_system.subscribe("clear_notebooks", self.clear_notebooks)
|
||||||
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("do_action_from_menu_controls", self.do_action_from_menu_controls)
|
event_system.subscribe("do_action_from_menu_controls", self.do_action_from_menu_controls)
|
||||||
# NOTE: Needs to be moved (probably just to file actions class) after reducing mixins usage
|
event_system.subscribe("set_clipboard_data", self.set_clipboard_data)
|
||||||
event_system.subscribe("open_with_files", self.open_with_files)
|
|
||||||
event_system.subscribe("generate_windows", self.generate_windows)
|
|
||||||
|
|
||||||
# NOTE: Really we will move these to the UI/(New) Window 'base' controller
|
# NOTE: Really we will move these to the UI/(New) Window 'base' controller
|
||||||
# after we're done cleaning and refactoring to use fewer mixins.
|
# after we're done cleaning and refactoring to use fewer mixins.
|
||||||
|
@ -96,36 +96,39 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
||||||
Gtk.main_quit()
|
Gtk.main_quit()
|
||||||
|
|
||||||
|
|
||||||
def do_action_from_menu_controls(self, widget, eve = None):
|
def do_action_from_menu_controls(self, _action=None, eve=None):
|
||||||
if not isinstance(widget, str):
|
if not _action:
|
||||||
action = widget.get_name()
|
return
|
||||||
|
|
||||||
|
if not isinstance(_action, str):
|
||||||
|
action = _action.get_name()
|
||||||
else:
|
else:
|
||||||
action = widget
|
action = _action
|
||||||
|
|
||||||
event_system.emit("hide_context_menu")
|
event_system.emit("hide_context_menu")
|
||||||
event_system.emit("hide_new_file_menu")
|
event_system.emit("hide_new_file_menu")
|
||||||
event_system.emit("hide_rename_file_menu")
|
event_system.emit("hide_rename_file_menu")
|
||||||
|
|
||||||
if action == "open":
|
if action == "open":
|
||||||
self.open_files()
|
event_system.emit("open_files")
|
||||||
if action == "open_with":
|
if action == "open_with":
|
||||||
event_system.emit("show_appchooser_menu")
|
event_system.emit("show_appchooser_menu")
|
||||||
if action == "execute":
|
if action == "execute":
|
||||||
self.execute_files()
|
event_system.emit("execute_files")
|
||||||
if action == "execute_in_terminal":
|
if action == "execute_in_terminal":
|
||||||
self.execute_files(in_terminal=True)
|
event_system.emit("execute_files", (True,))
|
||||||
if action == "rename":
|
if action == "rename":
|
||||||
self.rename_files()
|
event_system.emit("rename_files")
|
||||||
if action == "cut":
|
if action == "cut":
|
||||||
self.cut_files()
|
event_system.emit("cut_files")
|
||||||
if action == "copy":
|
if action == "copy":
|
||||||
self.copy_files()
|
event_system.emit("copy_files")
|
||||||
if action == "copy_name":
|
if action == "copy_name":
|
||||||
self.copy_name()
|
event_system.emit("copy_name")
|
||||||
if action == "paste":
|
if action == "paste":
|
||||||
self.paste_files()
|
event_system.emit("paste_files")
|
||||||
if action == "create":
|
if action == "create":
|
||||||
self.create_files()
|
event_system.emit("create_files")
|
||||||
if action in ["save_session", "save_session_as", "load_session"]:
|
if action in ["save_session", "save_session_as", "load_session"]:
|
||||||
event_system.emit("save_load_session", (action))
|
event_system.emit("save_load_session", (action))
|
||||||
|
|
||||||
|
@ -166,5 +169,5 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
||||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
||||||
tab.execute([f"{tab.terminal_app}"], start_dir=tab.get_current_directory())
|
tab.execute([f"{tab.terminal_app}"], start_dir=tab.get_current_directory())
|
||||||
|
|
||||||
def go_to_path(self, path):
|
def go_to_path(self, path: str):
|
||||||
self.path_entry.set_text(path)
|
self.path_entry.set_text(path)
|
||||||
|
|
|
@ -27,6 +27,7 @@ class State:
|
||||||
tab: type = None
|
tab: type = None
|
||||||
icon_grid: gi.overrides.Gtk.IconView = None
|
icon_grid: gi.overrides.Gtk.IconView = None
|
||||||
store: gi.overrides.Gtk.ListStore = None
|
store: gi.overrides.Gtk.ListStore = None
|
||||||
|
uris: [] = None
|
||||||
selected_files: [] = None
|
selected_files: [] = None
|
||||||
to_copy_files: [] = None
|
to_copy_files: [] = None
|
||||||
to_cut_files: [] = None
|
to_cut_files: [] = None
|
||||||
|
@ -104,7 +105,9 @@ class Controller_Data:
|
||||||
|
|
||||||
selected_files = state.icon_grid.get_selected_items()
|
selected_files = state.icon_grid.get_selected_items()
|
||||||
if selected_files:
|
if selected_files:
|
||||||
state.selected_files = self.format_to_uris(state.store, state.wid, state.tid, selected_files, True)
|
state.uris = self.format_to_uris(state.store, state.wid, state.tid, selected_files, True)
|
||||||
|
|
||||||
|
state.selected_files = self.selected_files
|
||||||
|
|
||||||
# if self.to_copy_files:
|
# if self.to_copy_files:
|
||||||
# state.to_copy_files = self.format_to_uris(state.store, state.wid, state.tid, self.to_copy_files, True)
|
# state.to_copy_files = self.format_to_uris(state.store, state.wid, state.tid, self.to_copy_files, True)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
FS Actions Module
|
||||||
|
"""
|
|
@ -0,0 +1,70 @@
|
||||||
|
# Python imports
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CRUDMixin:
|
||||||
|
"""docstring for CRUDMixin"""
|
||||||
|
|
||||||
|
def move_files(self, files, target):
|
||||||
|
self.handle_files(files, "move", target)
|
||||||
|
|
||||||
|
def paste_files(self):
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
target = f"{state.tab.get_current_directory()}"
|
||||||
|
|
||||||
|
if self._to_copy_files:
|
||||||
|
self.handle_files(self._to_copy_files, "copy", target)
|
||||||
|
elif self._to_cut_files:
|
||||||
|
self.handle_files(self._to_cut_files, "move", target)
|
||||||
|
|
||||||
|
def create_files(self):
|
||||||
|
fname_field = self._builder.get_object("new_fname_field")
|
||||||
|
cancel_creation = event_system.emit_and_await("show_new_file_menu", fname_field)
|
||||||
|
|
||||||
|
if cancel_creation:
|
||||||
|
event_system.emit("hide_new_file_menu")
|
||||||
|
return
|
||||||
|
|
||||||
|
file_name = fname_field.get_text().strip()
|
||||||
|
type = self._builder.get_object("new_file_toggle_type").get_state()
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
target = f"{state.tab.get_current_directory()}"
|
||||||
|
|
||||||
|
if file_name:
|
||||||
|
path = f"{target}/{file_name}"
|
||||||
|
|
||||||
|
if type == True: # Create File
|
||||||
|
self.handle_files([path], "create_file")
|
||||||
|
else: # Create Folder
|
||||||
|
self.handle_files([path], "create_dir")
|
||||||
|
|
||||||
|
event_system.emit("hide_new_file_menu")
|
||||||
|
|
||||||
|
def rename_files(self):
|
||||||
|
rename_label = self._builder.get_object("file_to_rename_label")
|
||||||
|
rename_input = self._builder.get_object("rename_fname")
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
|
||||||
|
for uri in state.uris:
|
||||||
|
entry = uri.split("/")[-1]
|
||||||
|
rename_label.set_label(entry)
|
||||||
|
rename_input.set_text(entry)
|
||||||
|
|
||||||
|
response = event_system.emit_and_await("show_rename_file_menu", rename_input)
|
||||||
|
if response == "skip_edit":
|
||||||
|
continue
|
||||||
|
if response == "cancel_edit":
|
||||||
|
break
|
||||||
|
|
||||||
|
rname_to = rename_input.get_text().strip()
|
||||||
|
if rname_to:
|
||||||
|
target = f"{state.tab.get_current_directory()}/{rname_to}"
|
||||||
|
self.handle_files([uri], "rename", target)
|
||||||
|
|
||||||
|
event_system.emit("hide_rename_file_menu")
|
||||||
|
event_system.emit_and_await("get_selected_files").clear()
|
|
@ -0,0 +1,95 @@
|
||||||
|
# Python imports
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
from .crud_mixin import CRUDMixin
|
||||||
|
from .handler_mixin import HandlerMixin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class FileSystemActions(HandlerMixin, CRUDMixin):
|
||||||
|
"""docstring for FileSystemActions"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(FileSystemActions, self).__init__()
|
||||||
|
self._setup_styling()
|
||||||
|
self._setup_signals()
|
||||||
|
self._subscribe_to_events()
|
||||||
|
self._load_widgets()
|
||||||
|
|
||||||
|
self._selected_files = []
|
||||||
|
self._to_copy_files = []
|
||||||
|
self._to_cut_files = []
|
||||||
|
|
||||||
|
self._builder = settings.get_builder()
|
||||||
|
|
||||||
|
|
||||||
|
def _setup_styling(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def _setup_signals(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def _subscribe_to_events(self):
|
||||||
|
event_system.subscribe("set_selected_files", self.set_selected_files)
|
||||||
|
event_system.subscribe("get_selected_files", self.get_selected_files)
|
||||||
|
|
||||||
|
event_system.subscribe("open_files", self.open_files)
|
||||||
|
event_system.subscribe("open_with_files", self.open_with_files)
|
||||||
|
event_system.subscribe("execute_files", self.execute_files)
|
||||||
|
|
||||||
|
event_system.subscribe("cut_files", self.cut_files)
|
||||||
|
event_system.subscribe("copy_files", self.copy_files)
|
||||||
|
event_system.subscribe("paste_files", self.paste_files)
|
||||||
|
event_system.subscribe("move_files", self.move_files)
|
||||||
|
event_system.subscribe("copy_name", self.copy_name)
|
||||||
|
event_system.subscribe("create_files", self.create_files)
|
||||||
|
event_system.subscribe("rename_files", self.rename_files)
|
||||||
|
|
||||||
|
def _load_widgets(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
def set_selected_files(self, selected_files: []):
|
||||||
|
self._selected_files = selected_files
|
||||||
|
|
||||||
|
def get_selected_files(self):
|
||||||
|
return self._selected_files
|
||||||
|
|
||||||
|
|
||||||
|
def cut_files(self):
|
||||||
|
self._to_copy_files.clear()
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
self._to_cut_files = state.uris
|
||||||
|
|
||||||
|
def copy_files(self):
|
||||||
|
self._to_cut_files.clear()
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
self._to_copy_files = state.uris
|
||||||
|
|
||||||
|
def copy_name(self):
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
if len(state.uris) == 1:
|
||||||
|
file_name = state.uris[0].split("/")[-1]
|
||||||
|
event_system.emit("set_clipboard_data", (file_name,))
|
||||||
|
|
||||||
|
|
||||||
|
def open_files(self):
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
for file in state.uris:
|
||||||
|
state.tab.open_file_locally(file)
|
||||||
|
|
||||||
|
def open_with_files(self, app_info):
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
state.tab.app_chooser_exec(app_info, state.uris)
|
||||||
|
|
||||||
|
def execute_files(self, in_terminal=False):
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
current_dir = state.tab.get_current_directory()
|
||||||
|
command = None
|
||||||
|
for path in state.uris:
|
||||||
|
command = f"{shlex.quote(path)}" if not in_terminal else f"{state.tab.terminal_app} -e {shlex.quote(path)}"
|
||||||
|
state.tab.execute(shlex.split(command), start_dir=state.tab.get_current_directory())
|
|
@ -0,0 +1,167 @@
|
||||||
|
# Python imports
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
import gi
|
||||||
|
gi.require_version('Gtk', '3.0')
|
||||||
|
from gi.repository import Gtk
|
||||||
|
from gi.repository import GObject
|
||||||
|
from gi.repository import Gio
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
from ..ui.io_widget import IOWidget
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class HandlerMixin:
|
||||||
|
"""docstring for HandlerMixin"""
|
||||||
|
|
||||||
|
# NOTE: Gtk recommends using fail flow than pre check which is more
|
||||||
|
# race condition proof. They're right; but, they can't even delete
|
||||||
|
# directories properly. So... f**k them. I'll do it my way.
|
||||||
|
def handle_files(self, paths, action, _target_path=None):
|
||||||
|
target = None
|
||||||
|
_file = None
|
||||||
|
response = None
|
||||||
|
overwrite_all = False
|
||||||
|
rename_auto_all = False
|
||||||
|
|
||||||
|
for path in paths:
|
||||||
|
try:
|
||||||
|
if "file://" in path:
|
||||||
|
path = path.split("file://")[1]
|
||||||
|
|
||||||
|
file = Gio.File.new_for_path(path)
|
||||||
|
if _target_path:
|
||||||
|
if file.get_parent().get_path() == _target_path:
|
||||||
|
raise Exception("Parent dir of target and file locations are the same! Won't copy or move!")
|
||||||
|
|
||||||
|
if os.path.isdir(_target_path):
|
||||||
|
info = file.query_info("standard::display-name", 0, cancellable=None)
|
||||||
|
_target = f"{_target_path}/{info.get_display_name()}"
|
||||||
|
_file = Gio.File.new_for_path(_target)
|
||||||
|
else:
|
||||||
|
_file = Gio.File.new_for_path(_target_path)
|
||||||
|
else:
|
||||||
|
_file = Gio.File.new_for_path(path)
|
||||||
|
|
||||||
|
|
||||||
|
if _file.query_exists():
|
||||||
|
if not overwrite_all and not rename_auto_all:
|
||||||
|
event_system.emit("setup_exists_data", (file, _file))
|
||||||
|
response = event_system.emit_and_await("show_exists_page")
|
||||||
|
|
||||||
|
if response == "overwrite_all":
|
||||||
|
overwrite_all = True
|
||||||
|
if response == "rename_auto_all":
|
||||||
|
rename_auto_all = True
|
||||||
|
|
||||||
|
if response == "rename":
|
||||||
|
base_path = _file.get_parent().get_path()
|
||||||
|
new_name = self._builder.get_object("exists_file_field").get_text().strip()
|
||||||
|
rfPath = f"{base_path}/{new_name}"
|
||||||
|
_file = Gio.File.new_for_path(rfPath)
|
||||||
|
|
||||||
|
if response == "rename_auto" or rename_auto_all:
|
||||||
|
_file = self.rename_proc(_file)
|
||||||
|
|
||||||
|
if response == "overwrite" or overwrite_all:
|
||||||
|
type = _file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
|
||||||
|
|
||||||
|
if type == Gio.FileType.DIRECTORY:
|
||||||
|
# wid, tid = self.fm_controller.get_active_wid_and_tid()
|
||||||
|
# tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
||||||
|
# tab.delete_file( _file.get_path() )
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
state.tab.delete_file( _file.get_path() )
|
||||||
|
else:
|
||||||
|
_file.delete(cancellable=None)
|
||||||
|
|
||||||
|
if response == "skip":
|
||||||
|
continue
|
||||||
|
if response == "skip_all":
|
||||||
|
break
|
||||||
|
|
||||||
|
if _target_path:
|
||||||
|
target = _file
|
||||||
|
else:
|
||||||
|
file = _file
|
||||||
|
|
||||||
|
|
||||||
|
if action == "create_file":
|
||||||
|
file.create(flags=Gio.FileCreateFlags.NONE, cancellable=None)
|
||||||
|
continue
|
||||||
|
if action == "create_dir":
|
||||||
|
file.make_directory(cancellable=None)
|
||||||
|
continue
|
||||||
|
|
||||||
|
type = file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
|
||||||
|
if type == Gio.FileType.DIRECTORY:
|
||||||
|
# wid, tid = self.fm_controller.get_active_wid_and_tid()
|
||||||
|
# tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
||||||
|
state = event_system.emit_and_await("get_current_state")
|
||||||
|
tab = state.tab
|
||||||
|
fPath = file.get_path()
|
||||||
|
tPath = target.get_path()
|
||||||
|
state = True
|
||||||
|
|
||||||
|
if action == "copy":
|
||||||
|
tab.copy_file(fPath, tPath)
|
||||||
|
if action == "move" or action == "rename":
|
||||||
|
tab.move_file(fPath, tPath)
|
||||||
|
else:
|
||||||
|
io_widget = IOWidget(action, file)
|
||||||
|
|
||||||
|
if action == "copy":
|
||||||
|
file.copy_async(destination=target,
|
||||||
|
flags=Gio.FileCopyFlags.BACKUP,
|
||||||
|
io_priority=98,
|
||||||
|
cancellable=io_widget.cancle_eve,
|
||||||
|
progress_callback=io_widget.update_progress,
|
||||||
|
callback=io_widget.finish_callback)
|
||||||
|
|
||||||
|
self._builder.get_object("io_list").add(io_widget)
|
||||||
|
if action == "move" or action == "rename":
|
||||||
|
file.move_async(destination=target,
|
||||||
|
flags=Gio.FileCopyFlags.BACKUP,
|
||||||
|
io_priority=98,
|
||||||
|
cancellable=io_widget.cancle_eve,
|
||||||
|
progress_callback=None,
|
||||||
|
# NOTE: progress_callback here causes seg fault when set
|
||||||
|
callback=io_widget.finish_callback)
|
||||||
|
|
||||||
|
self._builder.get_object("io_list").add(io_widget)
|
||||||
|
|
||||||
|
except GObject.GError as e:
|
||||||
|
raise OSError(e)
|
||||||
|
|
||||||
|
self._builder.get_object("exists_file_rename_bttn").set_sensitive(False)
|
||||||
|
|
||||||
|
def rename_proc(self, gio_file):
|
||||||
|
full_path = gio_file.get_path()
|
||||||
|
base_path = gio_file.get_parent().get_path()
|
||||||
|
file_name = os.path.splitext(gio_file.get_basename())[0]
|
||||||
|
extension = os.path.splitext(full_path)[-1]
|
||||||
|
target = Gio.File.new_for_path(full_path)
|
||||||
|
start = "-copy"
|
||||||
|
|
||||||
|
if settings.is_debug():
|
||||||
|
logger.debug(f"Path: {full_path}")
|
||||||
|
logger.debug(f"Base Path: {base_path}")
|
||||||
|
logger.debug(f'Name: {file_name}')
|
||||||
|
logger.debug(f"Extension: {extension}")
|
||||||
|
|
||||||
|
i = 2
|
||||||
|
while target.query_exists():
|
||||||
|
try:
|
||||||
|
value = file_name[(file_name.find(start)+len(start)):]
|
||||||
|
int(value)
|
||||||
|
file_name = file_name.split(start)[0]
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
target = Gio.File.new_for_path(f"{base_path}/{file_name}-copy{i}{extension}")
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return target
|
|
@ -1,40 +1,21 @@
|
||||||
# Python imports
|
# Python imports
|
||||||
import os
|
|
||||||
import time
|
import time
|
||||||
import shlex
|
|
||||||
|
|
||||||
# Lib imports
|
# Lib imports
|
||||||
import gi
|
import gi
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
from gi.repository import GObject
|
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
from gi.repository import Gio
|
from gi.repository import Gio
|
||||||
|
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from ...ui.io_widget import IOWidget
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FileActionSignalsMixin:
|
class FileActionSignalsMixin:
|
||||||
"""docstring for FileActionSignalsMixin"""
|
"""docstring for FileActionSignalsMixin"""
|
||||||
|
|
||||||
def get_dir_size(self, sdir):
|
|
||||||
"""Get the size of a directory. Based on code found online."""
|
|
||||||
size = os.path.getsize(sdir)
|
|
||||||
|
|
||||||
for item in os.listdir(sdir):
|
|
||||||
item = os.path.join(sdir, item)
|
|
||||||
|
|
||||||
if os.path.isfile(item):
|
|
||||||
size = size + os.path.getsize(item)
|
|
||||||
elif os.path.isdir(item):
|
|
||||||
size = size + self.get_dir_size(item)
|
|
||||||
|
|
||||||
return size
|
|
||||||
|
|
||||||
|
|
||||||
def set_file_watcher(self, tab):
|
def set_file_watcher(self, tab):
|
||||||
if tab.get_dir_watcher():
|
if tab.get_dir_watcher():
|
||||||
watcher = tab.get_dir_watcher()
|
watcher = tab.get_dir_watcher()
|
||||||
|
@ -120,256 +101,5 @@ class FileActionSignalsMixin:
|
||||||
icon_grid.select_path(path)
|
icon_grid.select_path(path)
|
||||||
|
|
||||||
items = icon_grid.get_selected_items()
|
items = icon_grid.get_selected_items()
|
||||||
if len(items) == 1:
|
if len(items) > 0:
|
||||||
icon_grid.scroll_to_path(items[-1], True, 0.5, 0.5)
|
icon_grid.scroll_to_path(items[0], False, 0.5, 0.5)
|
||||||
|
|
||||||
|
|
||||||
def open_files(self):
|
|
||||||
state = self.get_current_state()
|
|
||||||
uris = self.format_to_uris(state.store, state.wid, state.tid, self.selected_files, True)
|
|
||||||
|
|
||||||
for file in uris:
|
|
||||||
state.tab.open_file_locally(file)
|
|
||||||
|
|
||||||
def open_with_files(self, app_info):
|
|
||||||
state = self.get_current_state()
|
|
||||||
uris = self.format_to_uris(state.store, state.wid, state.tid, self.selected_files)
|
|
||||||
state.tab.app_chooser_exec(app_info, uris)
|
|
||||||
|
|
||||||
def execute_files(self, in_terminal=False):
|
|
||||||
state = self.get_current_state()
|
|
||||||
paths = self.format_to_uris(state.store, state.wid, state.tid, self.selected_files, True)
|
|
||||||
current_dir = state.tab.get_current_directory()
|
|
||||||
command = None
|
|
||||||
for path in paths:
|
|
||||||
command = f"{shlex.quote(path)}" if not in_terminal else f"{state.tab.terminal_app} -e {shlex.quote(path)}"
|
|
||||||
state.tab.execute(shlex.split(command), start_dir=state.tab.get_current_directory())
|
|
||||||
|
|
||||||
def rename_files(self):
|
|
||||||
rename_label = self.builder.get_object("file_to_rename_label")
|
|
||||||
rename_input = self.builder.get_object("rename_fname")
|
|
||||||
state = self.get_current_state()
|
|
||||||
uris = self.format_to_uris(state.store, state.wid, state.tid, self.selected_files, True)
|
|
||||||
|
|
||||||
for uri in uris:
|
|
||||||
entry = uri.split("/")[-1]
|
|
||||||
rename_label.set_label(entry)
|
|
||||||
rename_input.set_text(entry)
|
|
||||||
|
|
||||||
response = event_system.emit_and_await("show_rename_file_menu", rename_input)
|
|
||||||
if response == "skip_edit":
|
|
||||||
continue
|
|
||||||
if response == "cancel_edit":
|
|
||||||
break
|
|
||||||
|
|
||||||
rname_to = rename_input.get_text().strip()
|
|
||||||
if rname_to:
|
|
||||||
target = f"{state.tab.get_current_directory()}/{rname_to}"
|
|
||||||
self.handle_files([uri], "rename", target)
|
|
||||||
|
|
||||||
event_system.emit("hide_rename_file_menu")
|
|
||||||
self.selected_files.clear()
|
|
||||||
|
|
||||||
def cut_files(self):
|
|
||||||
self.to_copy_files.clear()
|
|
||||||
state = self.get_current_state()
|
|
||||||
uris = self.format_to_uris(state.store, state.wid, state.tid, self.selected_files, True)
|
|
||||||
self.to_cut_files = uris
|
|
||||||
|
|
||||||
def copy_name(self):
|
|
||||||
state = self.get_current_state()
|
|
||||||
uris = self.format_to_uris(state.store, state.wid, state.tid, self.selected_files, True)
|
|
||||||
if len(uris) == 1:
|
|
||||||
file_name = uris[0].split("/")[-1]
|
|
||||||
self.set_clipboard_data(file_name)
|
|
||||||
|
|
||||||
def copy_files(self):
|
|
||||||
self.to_cut_files.clear()
|
|
||||||
state = self.get_current_state()
|
|
||||||
uris = self.format_to_uris(state.store, state.wid, state.tid, self.selected_files, True)
|
|
||||||
self.to_copy_files = uris
|
|
||||||
|
|
||||||
def paste_files(self):
|
|
||||||
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
|
||||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
|
||||||
target = f"{tab.get_current_directory()}"
|
|
||||||
|
|
||||||
if self.to_copy_files:
|
|
||||||
self.handle_files(self.to_copy_files, "copy", target)
|
|
||||||
elif self.to_cut_files:
|
|
||||||
self.handle_files(self.to_cut_files, "move", target)
|
|
||||||
|
|
||||||
def create_files(self):
|
|
||||||
fname_field = self.builder.get_object("new_fname_field")
|
|
||||||
cancel_creation = event_system.emit_and_await("show_new_file_menu", fname_field)
|
|
||||||
|
|
||||||
if cancel_creation:
|
|
||||||
event_system.emit("hide_new_file_menu")
|
|
||||||
return
|
|
||||||
|
|
||||||
file_name = fname_field.get_text().strip()
|
|
||||||
type = self.builder.get_object("new_file_toggle_type").get_state()
|
|
||||||
|
|
||||||
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
|
||||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
|
||||||
target = f"{tab.get_current_directory()}"
|
|
||||||
|
|
||||||
if file_name:
|
|
||||||
path = f"{target}/{file_name}"
|
|
||||||
|
|
||||||
if type == True: # Create File
|
|
||||||
self.handle_files([path], "create_file")
|
|
||||||
else: # Create Folder
|
|
||||||
self.handle_files([path], "create_dir")
|
|
||||||
|
|
||||||
event_system.emit("hide_new_file_menu")
|
|
||||||
|
|
||||||
|
|
||||||
def move_files(self, files, target):
|
|
||||||
self.handle_files(files, "move", target)
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE: Gtk recommends using fail flow than pre check which is more
|
|
||||||
# race condition proof. They're right; but, they can't even delete
|
|
||||||
# directories properly. So... f**k them. I'll do it my way.
|
|
||||||
def handle_files(self, paths, action, _target_path=None):
|
|
||||||
target = None
|
|
||||||
_file = None
|
|
||||||
response = None
|
|
||||||
overwrite_all = False
|
|
||||||
rename_auto_all = False
|
|
||||||
|
|
||||||
for path in paths:
|
|
||||||
try:
|
|
||||||
if "file://" in path:
|
|
||||||
path = path.split("file://")[1]
|
|
||||||
|
|
||||||
file = Gio.File.new_for_path(path)
|
|
||||||
if _target_path:
|
|
||||||
if file.get_parent().get_path() == _target_path:
|
|
||||||
raise Exception("Parent dir of target and file locations are the same! Won't copy or move!")
|
|
||||||
|
|
||||||
if os.path.isdir(_target_path):
|
|
||||||
info = file.query_info("standard::display-name", 0, cancellable=None)
|
|
||||||
_target = f"{_target_path}/{info.get_display_name()}"
|
|
||||||
_file = Gio.File.new_for_path(_target)
|
|
||||||
else:
|
|
||||||
_file = Gio.File.new_for_path(_target_path)
|
|
||||||
else:
|
|
||||||
_file = Gio.File.new_for_path(path)
|
|
||||||
|
|
||||||
|
|
||||||
if _file.query_exists():
|
|
||||||
if not overwrite_all and not rename_auto_all:
|
|
||||||
event_system.emit("setup_exists_data", (file, _file))
|
|
||||||
response = event_system.emit_and_await("show_exists_page")
|
|
||||||
|
|
||||||
if response == "overwrite_all":
|
|
||||||
overwrite_all = True
|
|
||||||
if response == "rename_auto_all":
|
|
||||||
rename_auto_all = True
|
|
||||||
|
|
||||||
if response == "rename":
|
|
||||||
base_path = _file.get_parent().get_path()
|
|
||||||
new_name = self.builder.get_object("exists_file_field").get_text().strip()
|
|
||||||
rfPath = f"{base_path}/{new_name}"
|
|
||||||
_file = Gio.File.new_for_path(rfPath)
|
|
||||||
|
|
||||||
if response == "rename_auto" or rename_auto_all:
|
|
||||||
_file = self.rename_proc(_file)
|
|
||||||
|
|
||||||
if response == "overwrite" or overwrite_all:
|
|
||||||
type = _file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
|
|
||||||
|
|
||||||
if type == Gio.FileType.DIRECTORY:
|
|
||||||
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
|
||||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
|
||||||
tab.delete_file( _file.get_path() )
|
|
||||||
else:
|
|
||||||
_file.delete(cancellable=None)
|
|
||||||
|
|
||||||
if response == "skip":
|
|
||||||
continue
|
|
||||||
if response == "skip_all":
|
|
||||||
break
|
|
||||||
|
|
||||||
if _target_path:
|
|
||||||
target = _file
|
|
||||||
else:
|
|
||||||
file = _file
|
|
||||||
|
|
||||||
|
|
||||||
if action == "create_file":
|
|
||||||
file.create(flags=Gio.FileCreateFlags.NONE, cancellable=None)
|
|
||||||
continue
|
|
||||||
if action == "create_dir":
|
|
||||||
file.make_directory(cancellable=None)
|
|
||||||
continue
|
|
||||||
|
|
||||||
type = file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
|
|
||||||
if type == Gio.FileType.DIRECTORY:
|
|
||||||
wid, tid = self.fm_controller.get_active_wid_and_tid()
|
|
||||||
tab = self.get_fm_window(wid).get_tab_by_id(tid)
|
|
||||||
fPath = file.get_path()
|
|
||||||
tPath = target.get_path()
|
|
||||||
state = True
|
|
||||||
|
|
||||||
if action == "copy":
|
|
||||||
tab.copy_file(fPath, tPath)
|
|
||||||
if action == "move" or action == "rename":
|
|
||||||
tab.move_file(fPath, tPath)
|
|
||||||
else:
|
|
||||||
io_widget = IOWidget(action, file)
|
|
||||||
|
|
||||||
if action == "copy":
|
|
||||||
file.copy_async(destination=target,
|
|
||||||
flags=Gio.FileCopyFlags.BACKUP,
|
|
||||||
io_priority=98,
|
|
||||||
cancellable=io_widget.cancle_eve,
|
|
||||||
progress_callback=io_widget.update_progress,
|
|
||||||
callback=io_widget.finish_callback)
|
|
||||||
|
|
||||||
self.builder.get_object("io_list").add(io_widget)
|
|
||||||
if action == "move" or action == "rename":
|
|
||||||
file.move_async(destination=target,
|
|
||||||
flags=Gio.FileCopyFlags.BACKUP,
|
|
||||||
io_priority=98,
|
|
||||||
cancellable=io_widget.cancle_eve,
|
|
||||||
progress_callback=None,
|
|
||||||
# NOTE: progress_callback here causes seg fault when set
|
|
||||||
callback=io_widget.finish_callback)
|
|
||||||
|
|
||||||
self.builder.get_object("io_list").add(io_widget)
|
|
||||||
|
|
||||||
except GObject.GError as e:
|
|
||||||
raise OSError(e)
|
|
||||||
|
|
||||||
self.builder.get_object("exists_file_rename_bttn").set_sensitive(False)
|
|
||||||
|
|
||||||
def rename_proc(self, gio_file):
|
|
||||||
full_path = gio_file.get_path()
|
|
||||||
base_path = gio_file.get_parent().get_path()
|
|
||||||
file_name = os.path.splitext(gio_file.get_basename())[0]
|
|
||||||
extension = os.path.splitext(full_path)[-1]
|
|
||||||
target = Gio.File.new_for_path(full_path)
|
|
||||||
start = "-copy"
|
|
||||||
|
|
||||||
if settings.is_debug():
|
|
||||||
logger.debug(f"Path: {full_path}")
|
|
||||||
logger.debug(f"Base Path: {base_path}")
|
|
||||||
logger.debug(f'Name: {file_name}')
|
|
||||||
logger.debug(f"Extension: {extension}")
|
|
||||||
|
|
||||||
i = 2
|
|
||||||
while target.query_exists():
|
|
||||||
try:
|
|
||||||
value = file_name[(file_name.find(start)+len(start)):]
|
|
||||||
int(value)
|
|
||||||
file_name = file_name.split(start)[0]
|
|
||||||
except Exception as e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
target = Gio.File.new_for_path(f"{base_path}/{file_name}-copy{i}{extension}")
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
return target
|
|
||||||
|
|
|
@ -50,7 +50,11 @@ class KeyboardSignalsMixin:
|
||||||
if mapping:
|
if mapping:
|
||||||
try:
|
try:
|
||||||
# See if in filemanager scope
|
# See if in filemanager scope
|
||||||
|
try:
|
||||||
getattr(self, mapping)()
|
getattr(self, mapping)()
|
||||||
|
except Exception as e:
|
||||||
|
event_system.emit(mapping)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
# Must be plugins scope or we forgot to add method to file manager scope
|
# Must be plugins scope or we forgot to add method to file manager scope
|
||||||
|
|
|
@ -165,12 +165,14 @@ class WindowMixin(TabMixin):
|
||||||
|
|
||||||
if size == 1:
|
if size == 1:
|
||||||
# NOTE: If already in selection, likely dnd else not so wont readd
|
# NOTE: If already in selection, likely dnd else not so wont readd
|
||||||
if items[0] in self.selected_files:
|
# if items[0] in self.selected_files:
|
||||||
|
if items[0] in event_system.emit_and_await("get_selected_files"):
|
||||||
self.dnd_left_primed += 1
|
self.dnd_left_primed += 1
|
||||||
# NOTE: If in selection but trying to just select an already selected item.
|
# NOTE: If in selection but trying to just select an already selected item.
|
||||||
if self.dnd_left_primed > 1:
|
if self.dnd_left_primed > 1:
|
||||||
self.dnd_left_primed = 0
|
self.dnd_left_primed = 0
|
||||||
self.selected_files.clear()
|
event_system.emit_and_await("get_selected_files").clear()
|
||||||
|
# self.selected_files.clear()
|
||||||
|
|
||||||
# NOTE: Likely trying dnd, just readd to selection the former set.
|
# NOTE: Likely trying dnd, just readd to selection the former set.
|
||||||
# Prevents losing highlighting of grid selected.
|
# Prevents losing highlighting of grid selected.
|
||||||
|
@ -178,10 +180,11 @@ class WindowMixin(TabMixin):
|
||||||
icons_grid.select_path(path)
|
icons_grid.select_path(path)
|
||||||
|
|
||||||
if size > 0:
|
if size > 0:
|
||||||
self.selected_files = icons_grid.get_selected_items()
|
# self.selected_files = icons_grid.get_selected_items()
|
||||||
|
event_system.emit("set_selected_files", (icons_grid.get_selected_items(),))
|
||||||
else:
|
else:
|
||||||
self.dnd_left_primed = 0
|
self.dnd_left_primed = 0
|
||||||
self.selected_files.clear()
|
event_system.emit_and_await("get_selected_files").clear()
|
||||||
|
|
||||||
def grid_icon_single_click(self, icons_grid, eve):
|
def grid_icon_single_click(self, icons_grid, eve):
|
||||||
try:
|
try:
|
||||||
|
@ -228,7 +231,7 @@ class WindowMixin(TabMixin):
|
||||||
state.tab.set_path(file)
|
state.tab.set_path(file)
|
||||||
self.update_tab(tab_label, state.tab, state.store, state.wid, state.tid)
|
self.update_tab(tab_label, state.tab, state.store, state.wid, state.tid)
|
||||||
else:
|
else:
|
||||||
self.open_files()
|
event_system.emit("open_files")
|
||||||
except WindowException as e:
|
except WindowException as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
self.display_message(settings.get_error_color(), f"{repr(e)}")
|
self.display_message(settings.get_error_color(), f"{repr(e)}")
|
||||||
|
@ -282,7 +285,7 @@ class WindowMixin(TabMixin):
|
||||||
|
|
||||||
from_uri = '/'.join(uris[0].replace("file://", "").split("/")[:-1])
|
from_uri = '/'.join(uris[0].replace("file://", "").split("/")[:-1])
|
||||||
if from_uri != dest:
|
if from_uri != dest:
|
||||||
self.move_files(uris, dest)
|
event_system.emit("move_files", (uris, dest))
|
||||||
|
|
||||||
|
|
||||||
def create_new_tab_notebook(self, widget=None, wid=None, path=None):
|
def create_new_tab_notebook(self, widget=None, wid=None, path=None):
|
||||||
|
|
Loading…
Reference in New Issue