restructuring file handler logic for conflict control
This commit is contained in:
parent
a418df2ce7
commit
c4342c9099
@ -30,6 +30,11 @@ class Controller_Data:
|
|||||||
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")
|
||||||
|
|
||||||
|
self.exists_alert = self.builder.get_object("exists_alert")
|
||||||
|
self.exists_from_label = self.builder.get_object("exists_from_label")
|
||||||
|
self.exists_to_label = self.builder.get_object("exists_to_label")
|
||||||
|
self.exists_file_field = self.builder.get_object("exists_file_field")
|
||||||
|
|
||||||
self.bottom_size_label = self.builder.get_object("bottom_size_label")
|
self.bottom_size_label = self.builder.get_object("bottom_size_label")
|
||||||
self.bottom_file_count_label = self.builder.get_object("bottom_file_count_label")
|
self.bottom_file_count_label = self.builder.get_object("bottom_file_count_label")
|
||||||
self.bottom_path_label = self.builder.get_object("bottom_path_label")
|
self.bottom_path_label = self.builder.get_object("bottom_path_label")
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
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 Gio
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
|
|
||||||
@ -12,6 +13,33 @@ 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_widget.popup()
|
||||||
|
|
||||||
|
|
||||||
|
def show_exists_page(self, widget=None, eve=None):
|
||||||
|
# self.exists_alert = self.builder.get_object("exists_alert")
|
||||||
|
# self.exists_from_label = self.builder.get_object("exists_from_label")
|
||||||
|
# self.exists_to_label = self.builder.get_object("exists_to_label")
|
||||||
|
# self.exists_file_field = self.builder.get_object("exists_file_field")
|
||||||
|
|
||||||
|
|
||||||
|
response = self.exists_alert.run()
|
||||||
|
if response == Gtk.ResponseType.OK: # Rename
|
||||||
|
print(response)
|
||||||
|
return "rename", Gio.FileCreateFlags.NONE
|
||||||
|
if response == Gtk.ResponseType.ACCEPT: # Auto rename
|
||||||
|
return "rename_auto", Gio.FileCreateFlags.NONE
|
||||||
|
if response == Gtk.ResponseType.CLOSE: # Auto rename all
|
||||||
|
return "rename_auto_all", Gio.FileCreateFlags.NONE
|
||||||
|
if response == Gtk.ResponseType.YES: # Overwrite
|
||||||
|
return "overwrite", Gio.FileCreateFlags.OVERWRITE
|
||||||
|
if response == Gtk.ResponseType.APPLY: # Overwrite all
|
||||||
|
return "overwrite_all", Gio.FileCreateFlags.OVERWRITE
|
||||||
|
if response == Gtk.ResponseType.NO: # Skip
|
||||||
|
return "skip", Gio.FileCreateFlags.NONE
|
||||||
|
if response == Gtk.ResponseType.CANCEL: # Skip all
|
||||||
|
return "skip_all", Gio.FileCreateFlags.NONE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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()
|
||||||
|
@ -56,10 +56,10 @@ class WidgetFileActionMixin:
|
|||||||
view = self.get_fm_window(wid).get_view_by_id(tid)
|
view = self.get_fm_window(wid).get_view_by_id(tid)
|
||||||
target = f"{view.get_current_directory()}"
|
target = f"{view.get_current_directory()}"
|
||||||
|
|
||||||
if file_name != "":
|
if file_name:
|
||||||
file_name = "file://" + target + "/" + file_name
|
file_name = "file://" + target + "/" + file_name
|
||||||
if type == True: # Create File
|
if type == True: # Create File
|
||||||
self.handle_file([file_name], "create_file", target)
|
self.handle_file([file_name], "create_file")
|
||||||
else: # Create Folder
|
else: # Create Folder
|
||||||
self.handle_file([file_name], "create_dir")
|
self.handle_file([file_name], "create_dir")
|
||||||
|
|
||||||
@ -82,12 +82,9 @@ class WidgetFileActionMixin:
|
|||||||
store = iconview.get_model()
|
store = iconview.get_model()
|
||||||
uris = self.format_to_uris(store, wid, tid, self.selected_files)
|
uris = self.format_to_uris(store, wid, tid, self.selected_files)
|
||||||
|
|
||||||
f = Gio.File.new_for_uri(uris[0])
|
file = Gio.File.new_for_uri(uris[0])
|
||||||
app_info = appchooser_widget.get_app_info()
|
app_info = appchooser_widget.get_app_info()
|
||||||
app_info.launch([f], None)
|
app_info.launch([file], None)
|
||||||
|
|
||||||
def edit_files(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
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")
|
||||||
@ -119,6 +116,7 @@ class WidgetFileActionMixin:
|
|||||||
|
|
||||||
rname_to = rename_input.get_text().strip()
|
rname_to = rename_input.get_text().strip()
|
||||||
target = f"file://{view.get_current_directory()}/{rname_to}"
|
target = f"file://{view.get_current_directory()}/{rname_to}"
|
||||||
|
print(target)
|
||||||
self.handle_file([uri], "edit", target)
|
self.handle_file([uri], "edit", target)
|
||||||
|
|
||||||
self.show_edit_file_menu()
|
self.show_edit_file_menu()
|
||||||
@ -133,7 +131,7 @@ class WidgetFileActionMixin:
|
|||||||
wid, tid = self.window_controller.get_active_data()
|
wid, tid = self.window_controller.get_active_data()
|
||||||
iconview = self.builder.get_object(f"{wid}|{tid}|iconview")
|
iconview = self.builder.get_object(f"{wid}|{tid}|iconview")
|
||||||
store = iconview.get_model()
|
store = iconview.get_model()
|
||||||
paths = self.format_to_uris(store, wid, tid, self.selected_files, True)
|
paths = self.format_to_uris(store, wid, tid, self.selected_files)
|
||||||
|
|
||||||
save_target = archiver_dialogue.get_filename();
|
save_target = archiver_dialogue.get_filename();
|
||||||
start_itr, end_itr = self.arc_command_buffer.get_bounds()
|
start_itr, end_itr = self.arc_command_buffer.get_bounds()
|
||||||
@ -163,7 +161,7 @@ class WidgetFileActionMixin:
|
|||||||
def paste_files(self):
|
def paste_files(self):
|
||||||
wid, tid = self.window_controller.get_active_data()
|
wid, tid = self.window_controller.get_active_data()
|
||||||
view = self.get_fm_window(wid).get_view_by_id(tid)
|
view = self.get_fm_window(wid).get_view_by_id(tid)
|
||||||
target = f"{view.get_current_directory()}"
|
target = f"file://{view.get_current_directory()}"
|
||||||
|
|
||||||
if len(self.to_copy_files) > 0:
|
if len(self.to_copy_files) > 0:
|
||||||
self.handle_file(self.to_copy_files, "copy", target)
|
self.handle_file(self.to_copy_files, "copy", target)
|
||||||
@ -173,8 +171,8 @@ class WidgetFileActionMixin:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def move_file(self, view, files, target):
|
def move_files(self, files, target):
|
||||||
self.handle_file([files], "move", target)
|
self.handle_file(files, "move", target)
|
||||||
|
|
||||||
def delete_files(self):
|
def delete_files(self):
|
||||||
wid, tid = self.window_controller.get_active_data()
|
wid, tid = self.window_controller.get_active_data()
|
||||||
@ -195,75 +193,84 @@ class WidgetFileActionMixin:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE: Gio moves files by generating the target file path with name in it
|
# NOTE: While not fully race condition proof, we happy path it first
|
||||||
# We can't just give a base target directory and run with it.
|
# and then handle anything after as a conflict for renaming before
|
||||||
# Also, the display name is UTF-8 safe and meant for displaying in GUIs
|
# copy, move, or edit.
|
||||||
def handle_file(self, paths, action, _target_path=None):
|
def handle_file(self, paths, action, _target_path=None):
|
||||||
paths = self.preprocess_paths(paths)
|
paths = self.preprocess_paths(paths)
|
||||||
target = None
|
target = None
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
try:
|
try:
|
||||||
f = Gio.File.new_for_uri(path)
|
file = Gio.File.new_for_uri(path)
|
||||||
|
|
||||||
|
if action == "trash":
|
||||||
|
file.trash(cancellable=None)
|
||||||
|
|
||||||
|
if action == "delete":
|
||||||
|
# TODO: Add proper confirmation prompt befor doing either
|
||||||
|
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)
|
||||||
|
view.delete_file( file.get_path() )
|
||||||
|
else:
|
||||||
|
file.delete(cancellable=None)
|
||||||
|
|
||||||
|
if (action == "create_file" or action == "create_dir") and not file.query_exists():
|
||||||
if action == "create_file":
|
if action == "create_file":
|
||||||
f.create(Gio.FileCreateFlags.NONE, cancellable=None)
|
file.create(flags=Gio.FileCreateFlags.NONE, cancellable=None)
|
||||||
break
|
continue
|
||||||
if action == "create_dir":
|
if action == "create_dir":
|
||||||
f.make_directory(cancellable=None)
|
file.make_directory(cancellable=None)
|
||||||
break
|
continue
|
||||||
|
|
||||||
|
|
||||||
if _target_path:
|
if _target_path:
|
||||||
if os.path.isdir(_target_path):
|
if os.path.isdir(_target_path.split("file://")[1]):
|
||||||
info = f.query_info("standard::display-name", 0, cancellable=None)
|
info = file.query_info("standard::display-name", 0, cancellable=None)
|
||||||
_target = f"file://{_target_path}/{info.get_display_name()}"
|
_target = f"{_target_path}/{info.get_display_name()}"
|
||||||
target = Gio.File.new_for_uri(_target)
|
target = Gio.File.new_for_uri(_target)
|
||||||
else:
|
else:
|
||||||
target = Gio.File.new_for_uri(_target_path)
|
target = Gio.File.new_for_uri(_target_path)
|
||||||
|
|
||||||
# See if dragging to same directory then break
|
if target and not target.query_exists():
|
||||||
if action not in ["trash", "delete", "edit"] and \
|
type = file.query_file_type(flags=Gio.FileQueryInfoFlags.NONE)
|
||||||
(f.get_parent().get_path() == target.get_parent().get_path()):
|
|
||||||
break
|
|
||||||
|
|
||||||
type = f.query_file_type(flags=Gio.FileQueryInfoFlags.NONE, cancellable=None)
|
if type == Gio.FileType.DIRECTORY:
|
||||||
if not type == Gio.FileType.DIRECTORY:
|
|
||||||
if action == "delete":
|
|
||||||
f.delete(cancellable=None)
|
|
||||||
if action == "trash":
|
|
||||||
f.trash(cancellable=None)
|
|
||||||
if action == "copy":
|
|
||||||
f.copy(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
|
|
||||||
if action == "move" or action == "edit":
|
|
||||||
f.move(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
|
|
||||||
else:
|
|
||||||
# Yes, life is hopeless and there is no God. Blame Gio for this sinful shitshow. =/
|
|
||||||
wid, tid = self.window_controller.get_active_data()
|
wid, tid = self.window_controller.get_active_data()
|
||||||
view = self.get_fm_window(wid).get_view_by_id(tid)
|
view = self.get_fm_window(wid).get_view_by_id(tid)
|
||||||
fPath = f.get_path()
|
fPath = file.get_path()
|
||||||
tPath = None
|
tPath = None
|
||||||
state = True
|
state = True
|
||||||
|
|
||||||
if target:
|
|
||||||
tPath = target.get_path()
|
|
||||||
|
|
||||||
|
|
||||||
if action == "delete":
|
|
||||||
state = view.delete_file(fPath)
|
|
||||||
if action == "trash":
|
|
||||||
f.trash(cancellable=None)
|
|
||||||
if action == "copy":
|
if action == "copy":
|
||||||
state = view.copy_file(fPath, tPath)
|
tPath = target.get_path()
|
||||||
|
view.copy_file(fPath, tPath)
|
||||||
if action == "move" or action == "edit":
|
if action == "move" or action == "edit":
|
||||||
tPath = target.get_parent().get_path()
|
tPath = target.get_parent().get_path()
|
||||||
state = view.move_file(fPath, tPath)
|
view.move_file(fPath, tPath)
|
||||||
|
else:
|
||||||
|
if action == "copy":
|
||||||
|
file.copy(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
|
||||||
|
if action == "move" or action == "edit":
|
||||||
|
file.move(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
|
||||||
|
|
||||||
if not state:
|
|
||||||
raise GObject.GError("Failed to perform requested dir/file action!")
|
|
||||||
|
# NOTE: Past here, we need to handle detected conflicts.
|
||||||
|
# Maybe create a collection of file and target pares
|
||||||
|
# that then get passed to a handler who calls show_exists_page?
|
||||||
|
# type = None
|
||||||
|
# gio_flag = None
|
||||||
|
# self.exists_alert.hide()
|
||||||
|
# if not state:
|
||||||
|
# raise GObject.GError("Failed to perform requested dir/file action!")
|
||||||
except GObject.GError as e:
|
except GObject.GError as e:
|
||||||
raise OSError(e)
|
raise OSError(e)
|
||||||
|
|
||||||
|
|
||||||
def preprocess_paths(self, paths):
|
def preprocess_paths(self, paths):
|
||||||
if not isinstance(paths, list):
|
if not isinstance(paths, list):
|
||||||
paths = [paths]
|
paths = [paths]
|
||||||
|
@ -144,16 +144,11 @@ class WindowMixin(TabMixin):
|
|||||||
view = self.get_fm_window(wid).get_view_by_id(tid)
|
view = self.get_fm_window(wid).get_view_by_id(tid)
|
||||||
|
|
||||||
uris = data.get_uris()
|
uris = data.get_uris()
|
||||||
dest = view.get_current_directory()
|
dest = f"file://{view.get_current_directory()}"
|
||||||
|
|
||||||
if len(uris) > 0:
|
if len(uris) > 0:
|
||||||
if debug:
|
self.move_files(uris, dest)
|
||||||
print(f"Target Move Path: {dest}")
|
|
||||||
|
|
||||||
for uri in uris:
|
|
||||||
if debug:
|
|
||||||
print(f"URI: {uri}")
|
|
||||||
self.move_file(view, uri, dest)
|
|
||||||
|
|
||||||
def create_new_view_notebook(self, widget=None, wid=None, path=None):
|
def create_new_view_notebook(self, widget=None, wid=None, path=None):
|
||||||
self.create_tab(wid, path)
|
self.create_tab(wid, path)
|
||||||
|
Loading…
Reference in New Issue
Block a user