2021-11-22 22:24:44 +00:00
|
|
|
# Python imports
|
2021-12-07 04:44:39 +00:00
|
|
|
import os
|
2021-11-22 22:24:44 +00:00
|
|
|
|
|
|
|
# Lib imports
|
2021-12-02 01:22:03 +00:00
|
|
|
import gi
|
|
|
|
gi.require_version('Gtk', '3.0')
|
|
|
|
from gi.repository import Gtk, GObject, Gio
|
2021-11-22 22:24:44 +00:00
|
|
|
|
|
|
|
# Application imports
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
|
2021-11-22 22:24:44 +00:00
|
|
|
class WidgetFileActionMixin:
|
|
|
|
def set_file_watcher(self, view):
|
|
|
|
if view.get_dir_watcher():
|
|
|
|
watcher = view.get_dir_watcher()
|
|
|
|
watcher.cancel()
|
|
|
|
if debug:
|
|
|
|
print(f"Watcher Is Cancelled: {watcher.is_cancelled()}")
|
|
|
|
|
2021-11-30 06:21:50 +00:00
|
|
|
cur_dir = view.get_current_directory()
|
|
|
|
# Temp updating too much with current events we are checking for.
|
|
|
|
# Causes invalid iter errors in WidbetMixin > update_store
|
|
|
|
if cur_dir == "/tmp":
|
|
|
|
watcher = None
|
|
|
|
return
|
|
|
|
|
|
|
|
dir_watcher = Gio.File.new_for_path(cur_dir) \
|
|
|
|
.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, Gio.Cancellable())
|
2021-11-22 22:24:44 +00:00
|
|
|
|
|
|
|
wid = view.get_wid()
|
|
|
|
tid = view.get_tab_id()
|
|
|
|
dir_watcher.connect("changed", self.dir_watch_updates, (f"{wid}|{tid}",))
|
|
|
|
view.set_dir_watcher(dir_watcher)
|
|
|
|
|
|
|
|
def dir_watch_updates(self, file_monitor, file, other_file=None, eve_type=None, data=None):
|
2021-11-30 02:42:14 +00:00
|
|
|
if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED,
|
|
|
|
Gio.FileMonitorEvent.RENAMED, Gio.FileMonitorEvent.MOVED_IN,
|
|
|
|
Gio.FileMonitorEvent.MOVED_OUT]:
|
2021-11-30 06:21:50 +00:00
|
|
|
if debug:
|
|
|
|
print(eve_type)
|
|
|
|
|
2021-11-22 22:24:44 +00:00
|
|
|
wid, tid = data[0].split("|")
|
|
|
|
notebook = self.builder.get_object(f"window_{wid}")
|
|
|
|
view = self.get_fm_window(wid).get_view_by_id(tid)
|
|
|
|
iconview = self.builder.get_object(f"{wid}|{tid}|iconview")
|
|
|
|
store = iconview.get_model()
|
|
|
|
_store, tab_label = self.get_store_and_label_from_notebook(notebook, f"{wid}|{tid}")
|
|
|
|
|
|
|
|
view.load_directory()
|
|
|
|
self.load_store(view, store)
|
|
|
|
tab_label.set_label(view.get_end_of_path())
|
2021-11-28 04:03:00 +00:00
|
|
|
self.set_bottom_labels(view)
|
2021-11-22 22:24:44 +00:00
|
|
|
|
2021-12-01 22:26:20 +00:00
|
|
|
|
|
|
|
|
2021-12-07 07:48:52 +00:00
|
|
|
def popup_search_files(self, wid, keyname):
|
|
|
|
entry = self.builder.get_object(f"win{wid}_search_field")
|
2021-12-11 04:39:30 +00:00
|
|
|
self.builder.get_object(f"win{wid}_search").popup()
|
2021-12-07 07:48:52 +00:00
|
|
|
entry.set_text(keyname)
|
|
|
|
entry.grab_focus_without_selecting()
|
2021-12-11 04:39:30 +00:00
|
|
|
entry.set_position(-1)
|
2021-12-07 07:48:52 +00:00
|
|
|
|
|
|
|
def do_file_search(self, widget, eve=None):
|
|
|
|
query = widget.get_text()
|
|
|
|
self.search_iconview.unselect_all()
|
|
|
|
for i, file in enumerate(self.search_view.files):
|
|
|
|
if query and query in file.lower():
|
|
|
|
path = Gtk.TreePath().new_from_indices([i])
|
|
|
|
self.search_iconview.select_path(path)
|
2021-12-02 01:22:03 +00:00
|
|
|
|
2021-12-11 03:05:38 +00:00
|
|
|
items = self.search_iconview.get_selected_items()
|
|
|
|
if len(items) == 1:
|
|
|
|
self.search_iconview.scroll_to_path(items[0], True, 0.5, 0.5)
|
|
|
|
|
|
|
|
|
2021-11-26 06:53:20 +00:00
|
|
|
def open_files(self):
|
2021-11-30 02:42:14 +00:00
|
|
|
wid, tid, view, iconview, store = self.get_current_state()
|
|
|
|
uris = self.format_to_uris(store, wid, tid, self.selected_files, True)
|
2021-11-26 06:53:20 +00:00
|
|
|
|
|
|
|
for file in uris:
|
|
|
|
view.open_file_locally(file)
|
|
|
|
|
2021-11-27 06:51:31 +00:00
|
|
|
def open_with_files(self, appchooser_widget):
|
2021-11-30 02:42:14 +00:00
|
|
|
wid, tid, view, iconview, store = self.get_current_state()
|
2021-11-27 06:51:31 +00:00
|
|
|
app_info = appchooser_widget.get_app_info()
|
2021-12-07 04:44:39 +00:00
|
|
|
uris = self.format_to_uris(store, wid, tid, self.selected_files)
|
2021-12-06 03:18:39 +00:00
|
|
|
|
2021-12-07 04:44:39 +00:00
|
|
|
app_info.launch_uris_async(uris)
|
2021-11-26 22:50:58 +00:00
|
|
|
|
2021-12-05 00:23:00 +00:00
|
|
|
def execute_files(self, in_terminal=False):
|
|
|
|
wid, tid, view, iconview, store = self.get_current_state()
|
|
|
|
paths = self.format_to_uris(store, wid, tid, self.selected_files, True)
|
|
|
|
current_dir = view.get_current_directory()
|
|
|
|
command = None
|
|
|
|
|
|
|
|
for path in paths:
|
2021-12-07 04:44:39 +00:00
|
|
|
command = f"exec '{path}'" if not in_terminal else f"{view.terminal_app} -e '{path}'"
|
2021-12-06 03:18:39 +00:00
|
|
|
view.execute(command, start_dir=view.get_current_directory(), use_os_system=False)
|
2021-12-05 00:23:00 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
def archive_files(self, archiver_dialogue):
|
|
|
|
wid, tid, view, iconview, store = self.get_current_state()
|
|
|
|
paths = self.format_to_uris(store, wid, tid, self.selected_files, True)
|
2021-12-05 00:23:00 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
save_target = archiver_dialogue.get_filename();
|
|
|
|
sItr, eItr = self.arc_command_buffer.get_bounds()
|
|
|
|
pre_command = self.arc_command_buffer.get_text(sItr, eItr, False)
|
|
|
|
pre_command = pre_command.replace("%o", save_target)
|
|
|
|
pre_command = pre_command.replace("%N", ' '.join(paths))
|
2021-12-06 03:23:30 +00:00
|
|
|
command = f"{view.terminal_app} -e '{pre_command}'"
|
2021-12-05 00:23:00 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
view.execute(command, start_dir=None, use_os_system=True)
|
2021-12-05 00:23:00 +00:00
|
|
|
|
2021-11-26 22:50:58 +00:00
|
|
|
def rename_files(self):
|
|
|
|
rename_label = self.builder.get_object("file_to_rename_label")
|
|
|
|
rename_input = self.builder.get_object("new_rename_fname")
|
2021-11-30 02:42:14 +00:00
|
|
|
wid, tid, view, iconview, store = self.get_current_state()
|
2021-12-06 03:18:39 +00:00
|
|
|
uris = self.format_to_uris(store, wid, tid, self.selected_files)
|
2021-11-26 22:50:58 +00:00
|
|
|
|
|
|
|
for uri in uris:
|
|
|
|
entry = uri.split("/")[-1]
|
|
|
|
rename_label.set_label(entry)
|
|
|
|
rename_input.set_text(entry)
|
|
|
|
|
2021-12-18 07:44:54 +00:00
|
|
|
self.show_edit_file_menu(rename_input)
|
2021-11-26 22:50:58 +00:00
|
|
|
if self.skip_edit:
|
2021-12-06 03:18:39 +00:00
|
|
|
self.skip_edit = False
|
2021-11-26 22:50:58 +00:00
|
|
|
continue
|
|
|
|
if self.cancel_edit:
|
2021-12-06 03:18:39 +00:00
|
|
|
self.cancel_edit = False
|
2021-11-26 22:50:58 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
rname_to = rename_input.get_text().strip()
|
|
|
|
target = f"file://{view.get_current_directory()}/{rname_to}"
|
2021-12-06 23:21:19 +00:00
|
|
|
self.handle_files([uri], "rename", target)
|
2021-11-26 22:50:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
self.skip_edit = False
|
|
|
|
self.cancel_edit = False
|
2021-12-06 03:18:39 +00:00
|
|
|
self.hide_edit_file_menu()
|
|
|
|
self.selected_files.clear()
|
2021-11-26 22:50:58 +00:00
|
|
|
|
|
|
|
def cut_files(self):
|
2021-11-30 02:42:14 +00:00
|
|
|
wid, tid, view, iconview, store = self.get_current_state()
|
2021-12-06 03:18:39 +00:00
|
|
|
uris = self.format_to_uris(store, wid, tid, self.selected_files)
|
2021-11-26 22:50:58 +00:00
|
|
|
self.to_cut_files = uris
|
2021-11-22 22:24:44 +00:00
|
|
|
|
2021-11-26 22:50:58 +00:00
|
|
|
def copy_files(self):
|
2021-11-30 02:42:14 +00:00
|
|
|
wid, tid, view, iconview, store = self.get_current_state()
|
2021-12-06 03:18:39 +00:00
|
|
|
uris = self.format_to_uris(store, wid, tid, self.selected_files)
|
2021-11-26 22:50:58 +00:00
|
|
|
self.to_copy_files = uris
|
2021-11-22 22:24:44 +00:00
|
|
|
|
|
|
|
def paste_files(self):
|
|
|
|
wid, tid = self.window_controller.get_active_data()
|
|
|
|
view = self.get_fm_window(wid).get_view_by_id(tid)
|
2021-11-29 22:49:56 +00:00
|
|
|
target = f"file://{view.get_current_directory()}"
|
2021-11-22 22:24:44 +00:00
|
|
|
|
|
|
|
if len(self.to_copy_files) > 0:
|
2021-12-06 23:21:19 +00:00
|
|
|
self.handle_files(self.to_copy_files, "copy", target)
|
2021-11-22 22:24:44 +00:00
|
|
|
elif len(self.to_cut_files) > 0:
|
2021-12-06 23:21:19 +00:00
|
|
|
self.handle_files(self.to_cut_files, "move", target)
|
2021-11-22 22:24:44 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
def delete_files(self):
|
2021-12-05 00:23:00 +00:00
|
|
|
wid, tid, view, iconview, store = self.get_current_state()
|
2021-12-06 03:18:39 +00:00
|
|
|
uris = self.format_to_uris(store, wid, tid, self.selected_files)
|
|
|
|
response = None
|
2021-11-22 22:24:44 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
self.warning_alert.format_secondary_text(f"Do you really want to delete the {len(uris)} file(s)?")
|
|
|
|
for uri in uris:
|
|
|
|
file = Gio.File.new_for_uri(uri)
|
|
|
|
|
|
|
|
if not response:
|
|
|
|
response = self.warning_alert.run()
|
|
|
|
self.warning_alert.hide()
|
|
|
|
if response == Gtk.ResponseType.YES:
|
|
|
|
type = file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
|
|
|
|
|
|
|
|
if type == Gio.FileType.DIRECTORY:
|
|
|
|
view.delete_file( file.get_path() )
|
|
|
|
else:
|
|
|
|
file.delete(cancellable=None)
|
|
|
|
else:
|
|
|
|
break
|
2021-11-26 22:50:58 +00:00
|
|
|
|
2021-11-22 22:24:44 +00:00
|
|
|
|
|
|
|
def trash_files(self):
|
2021-11-30 02:42:14 +00:00
|
|
|
wid, tid, view, iconview, store = self.get_current_state()
|
2021-11-22 22:24:44 +00:00
|
|
|
uris = self.format_to_uris(store, wid, tid, self.selected_files)
|
2021-12-06 03:18:39 +00:00
|
|
|
for uri in uris:
|
|
|
|
file = Gio.File.new_for_uri(uri)
|
|
|
|
file.trash(cancellable=None)
|
2021-11-22 22:24:44 +00:00
|
|
|
|
|
|
|
|
2021-11-26 22:50:58 +00:00
|
|
|
|
|
|
|
|
2021-12-06 23:21:19 +00:00
|
|
|
def create_files(self):
|
2021-12-05 00:23:00 +00:00
|
|
|
fname_field = self.builder.get_object("context_menu_fname")
|
|
|
|
file_name = fname_field.get_text().strip()
|
|
|
|
type = self.builder.get_object("context_menu_type_toggle").get_state()
|
|
|
|
|
|
|
|
wid, tid = self.window_controller.get_active_data()
|
|
|
|
view = self.get_fm_window(wid).get_view_by_id(tid)
|
|
|
|
target = f"{view.get_current_directory()}"
|
|
|
|
|
|
|
|
if file_name:
|
2021-12-06 23:21:19 +00:00
|
|
|
path = f"file://{target}/{file_name}"
|
2021-12-06 03:18:39 +00:00
|
|
|
|
2021-12-05 00:23:00 +00:00
|
|
|
if type == True: # Create File
|
2021-12-06 23:21:19 +00:00
|
|
|
self.handle_files([path], "create_file")
|
2021-12-05 00:23:00 +00:00
|
|
|
else: # Create Folder
|
2021-12-06 23:21:19 +00:00
|
|
|
self.handle_files([path], "create_dir")
|
2021-12-05 00:23:00 +00:00
|
|
|
|
|
|
|
fname_field.set_text("")
|
|
|
|
|
|
|
|
def move_files(self, files, target):
|
2021-12-06 23:21:19 +00:00
|
|
|
self.handle_files(files, "move", target)
|
2021-12-05 00:23:00 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
# NOTE: Gtk recommends using fail flow than pre check existence 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.
|
2021-12-06 23:21:19 +00:00
|
|
|
def handle_files(self, paths, action, _target_path=None):
|
2021-12-06 03:18:39 +00:00
|
|
|
target = None
|
|
|
|
_file = None
|
|
|
|
response = None
|
|
|
|
overwrite_all = False
|
|
|
|
rename_auto_all = False
|
2021-11-22 22:24:44 +00:00
|
|
|
|
|
|
|
for path in paths:
|
|
|
|
try:
|
2021-11-29 22:49:56 +00:00
|
|
|
file = Gio.File.new_for_uri(path)
|
|
|
|
|
2021-11-26 22:50:58 +00:00
|
|
|
if _target_path:
|
2021-11-29 22:49:56 +00:00
|
|
|
if os.path.isdir(_target_path.split("file://")[1]):
|
|
|
|
info = file.query_info("standard::display-name", 0, cancellable=None)
|
|
|
|
_target = f"{_target_path}/{info.get_display_name()}"
|
2021-12-06 03:18:39 +00:00
|
|
|
_file = Gio.File.new_for_uri(_target)
|
2021-11-26 22:50:58 +00:00
|
|
|
else:
|
2021-12-06 03:18:39 +00:00
|
|
|
_file = Gio.File.new_for_uri(_target_path)
|
|
|
|
else:
|
|
|
|
_file = Gio.File.new_for_uri(path)
|
|
|
|
|
2021-11-29 22:49:56 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
if _file.query_exists():
|
|
|
|
if not overwrite_all and not rename_auto_all:
|
|
|
|
self.exists_file_label.set_label(_file.get_basename())
|
|
|
|
self.exists_file_field.set_text(_file.get_basename())
|
|
|
|
response = self.show_exists_page()
|
2021-11-29 22:49:56 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
if response == "overwrite_all":
|
|
|
|
overwrite_all = True
|
|
|
|
if response == "rename_auto_all":
|
|
|
|
rename_auto_all = True
|
2021-11-29 22:49:56 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
if response == "rename":
|
|
|
|
base_path = _file.get_parent().get_path()
|
|
|
|
new_name = self.exists_file_field.get_text().strip()
|
|
|
|
rfPath = f"{base_path}/{new_name}"
|
|
|
|
_file = Gio.File.new_for_path(rfPath)
|
2021-12-02 01:22:03 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
if response == "rename_auto" or rename_auto_all:
|
|
|
|
_file = self.rename_proc(_file)
|
2021-12-02 01:22:03 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
if response == "overwrite" or overwrite_all:
|
2021-12-06 23:21:19 +00:00
|
|
|
type = _file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
|
2021-12-02 01:22:03 +00:00
|
|
|
|
|
|
|
if type == Gio.FileType.DIRECTORY:
|
2021-12-06 23:21:19 +00:00
|
|
|
wid, tid = self.window_controller.get_active_data()
|
|
|
|
view = self.get_fm_window(wid).get_view_by_id(tid)
|
2021-12-06 03:18:39 +00:00
|
|
|
view.delete_file( _file.get_path() )
|
2021-12-02 01:22:03 +00:00
|
|
|
else:
|
2021-12-06 03:18:39 +00:00
|
|
|
_file.delete(cancellable=None)
|
|
|
|
|
|
|
|
if response == "skip":
|
|
|
|
continue
|
|
|
|
if response == "skip_all":
|
2021-12-02 01:22:03 +00:00
|
|
|
break
|
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
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.window_controller.get_active_data()
|
|
|
|
view = self.get_fm_window(wid).get_view_by_id(tid)
|
|
|
|
fPath = file.get_path()
|
2021-12-07 03:59:52 +00:00
|
|
|
tPath = target.get_path()
|
2021-12-06 03:18:39 +00:00
|
|
|
state = True
|
|
|
|
|
|
|
|
if action == "copy":
|
|
|
|
view.copy_file(fPath, tPath)
|
|
|
|
if action == "move" or action == "rename":
|
|
|
|
view.move_file(fPath, tPath)
|
|
|
|
else:
|
|
|
|
if action == "copy":
|
|
|
|
file.copy(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
|
|
|
|
if action == "move" or action == "rename":
|
|
|
|
file.move(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
|
2021-12-02 01:22:03 +00:00
|
|
|
|
2021-11-22 22:24:44 +00:00
|
|
|
except GObject.GError as e:
|
2021-11-28 06:40:47 +00:00
|
|
|
raise OSError(e)
|
2021-11-22 22:24:44 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
self.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)
|
|
|
|
|
|
|
|
if debug:
|
|
|
|
print(f"Path: {full_path}")
|
|
|
|
print(f"Base Path: {base_path}")
|
|
|
|
print(f'Name: {file_name}')
|
|
|
|
print(f"Extension: {extension}")
|
|
|
|
|
|
|
|
i = 2
|
|
|
|
while target.query_exists():
|
|
|
|
target = Gio.File.new_for_path(f"{base_path}/{file_name}-copy{i}{extension}")
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
return target
|
|
|
|
|
|
|
|
|
|
|
|
def exists_rename_field_changed(self, widget):
|
|
|
|
nfile_name = widget.get_text().strip()
|
|
|
|
ofile_name = self.exists_file_label.get_label()
|
2021-11-29 22:49:56 +00:00
|
|
|
|
2021-12-06 03:18:39 +00:00
|
|
|
if nfile_name:
|
|
|
|
if nfile_name == ofile_name:
|
|
|
|
self.exists_file_rename_bttn.set_sensitive(False)
|
|
|
|
else:
|
|
|
|
self.exists_file_rename_bttn.set_sensitive(True)
|
|
|
|
else:
|
|
|
|
self.exists_file_rename_bttn.set_sensitive(False)
|