From bed8af256ca542dbc8634123d4e6ee5ba187d5db Mon Sep 17 00:00:00 2001
From: itdominator <1itdominator@gmail.com>
Date: Sun, 5 Dec 2021 21:18:39 -0600
Subject: [PATCH] Greatly improved file handling flow and added popup guards.
---
README.md | 1 -
.../shellfm/windows/view/utils/Launcher.py | 2 +-
.../new/solarfm/signal_classes/Controller.py | 1 -
.../solarfm/signal_classes/Controller_Data.py | 8 +-
.../signal_classes/KeyboardSignalsMixin.py | 2 +-
.../solarfm/signal_classes/ShowHideMixin.py | 73 ++---
.../mixins/WidgetFileActionMixin.py | 278 +++++++++++-------
user_config/solarfm/Main_Window.glade | 275 ++++++++++++++++-
user_config/solarfm/stylesheet.css | 4 +
9 files changed, 482 insertions(+), 162 deletions(-)
diff --git a/README.md b/README.md
index c67453d..ba3946c 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,6 @@ sudo apt-get install python3 wget ffmpegthumbnailer steamcmd
# TODO
-- Fix the wonky file handler situation and add prompt guards for actions.
- Add path bar search dropdown.
- Add "clear trash", "restore from trash" options.
- Add drive size free and consumed info to bottom bar.
diff --git a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/shellfm/windows/view/utils/Launcher.py b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/shellfm/windows/view/utils/Launcher.py
index 049af0e..8d7e5f0 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/shellfm/windows/view/utils/Launcher.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/shellfm/windows/view/utils/Launcher.py
@@ -44,7 +44,7 @@ class Launcher:
os.system(command)
else:
DEVNULL = open(os.devnull, 'w')
- subprocess.Popen(command, cwd=start_dir, shell=False, start_new_session=True, stdout=DEVNULL, stderr=DEVNULL, close_fds=True)
+ subprocess.Popen(command, cwd=start_dir, shell=True, start_new_session=True, stdout=DEVNULL, stderr=DEVNULL, close_fds=True)
def remux_video(self, hash, file):
diff --git a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/Controller.py b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/Controller.py
index 373c096..c000b11 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/Controller.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/Controller.py
@@ -127,7 +127,6 @@ class Controller(Controller_Data, ShowHideMixin, KeyboardSignalsMixin, \
if action == "execute_in_terminal":
self.execute_files(in_terminal=True)
if action == "rename":
- self.to_rename_files = self.selected_files
self.rename_files()
if action == "cut":
self.to_copy_files.clear()
diff --git a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/Controller_Data.py b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/Controller_Data.py
index cc72d9e..4fba1fc 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/Controller_Data.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/Controller_Data.py
@@ -31,10 +31,11 @@ class Controller_Data:
self.arc_command_buffer = self.builder.get_object("arc_command_buffer")
self.warning_alert = self.builder.get_object("warning_alert")
- 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.edit_file_menu = self.builder.get_object("edit_file_menu")
+ self.file_exists_dialog = self.builder.get_object("file_exists_dialog")
+ self.exists_file_label = self.builder.get_object("exists_file_label")
self.exists_file_field = self.builder.get_object("exists_file_field")
+ self.exists_file_rename_bttn = self.builder.get_object("exists_file_rename_bttn")
self.bottom_size_label = self.builder.get_object("bottom_size_label")
self.bottom_file_count_label = self.builder.get_object("bottom_file_count_label")
@@ -72,7 +73,6 @@ class Controller_Data:
self.notebooks = [self.window1, self.window2, self.window3, self.window4]
self.selected_files = []
- self.to_rename_files = []
self.to_copy_files = []
self.to_cut_files = []
diff --git a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/KeyboardSignalsMixin.py b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/KeyboardSignalsMixin.py
index 4913af1..2b14e08 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/KeyboardSignalsMixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/KeyboardSignalsMixin.py
@@ -73,7 +73,7 @@ class KeyboardSignalsMixin:
if keyname == "delete":
self.delete_files()
if keyname == "f2":
- self.do_edit_files()
+ self.rename_files()
if keyname == "f4":
wid, tid = self.window_controller.get_active_data()
view = self.get_fm_window(wid).get_view_by_id(tid)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/ShowHideMixin.py b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/ShowHideMixin.py
index 61de2dc..653f335 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/ShowHideMixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/ShowHideMixin.py
@@ -15,29 +15,32 @@ class ShowHideMixin:
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.file_exists_dialog.run()
+ self.file_exists_dialog.hide()
+ if response == Gtk.ResponseType.OK:
+ return "rename"
+ if response == Gtk.ResponseType.ACCEPT:
+ return "rename_auto"
+ if response == Gtk.ResponseType.CLOSE:
+ return "rename_auto_all"
+ if response == Gtk.ResponseType.YES:
+ return "overwrite"
+ if response == Gtk.ResponseType.APPLY:
+ return "overwrite_all"
+ if response == Gtk.ResponseType.NO:
+ return "skip"
+ if response == Gtk.ResponseType.REJECT:
+ return "skip_all"
- 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 hide_exists_page_rename(self, widget=None, eve=None):
+ self.file_exists_dialog.response(Gtk.ResponseType.OK)
+ def hide_exists_page_auto_rename(self, widget=None, eve=None):
+ self.file_exists_dialog.response(Gtk.ResponseType.ACCEPT)
+
+ def hide_exists_page_auto_rename_all(self, widget=None, eve=None):
+ self.file_exists_dialog.response(Gtk.ResponseType.CLOSE)
def show_about_page(self, widget=None, eve=None):
@@ -49,6 +52,7 @@ class ShowHideMixin:
def hide_about_page(self, widget=None, eve=None):
self.builder.get_object("about_page").hide()
+
def show_archiver_dialogue(self, widget=None, eve=None):
wid, tid = self.window_controller.get_active_data()
view = self.get_fm_window(wid).get_view_by_id(tid)
@@ -68,6 +72,7 @@ class ShowHideMixin:
def hide_archiver_dialogue(self, widget=None, eve=None):
self.builder.get_object("archiver_dialogue").hide()
+
def show_appchooser_menu(self, widget=None, eve=None):
appchooser_menu = self.builder.get_object("appchooser_menu")
appchooser_widget = self.builder.get_object("appchooser_widget")
@@ -86,36 +91,38 @@ class ShowHideMixin:
dialog = widget.get_parent().get_parent()
dialog.response(Gtk.ResponseType.OK)
+
def show_context_menu(self, widget=None, eve=None):
self.builder.get_object("context_menu").run()
def hide_context_menu(self, widget=None, eve=None):
self.builder.get_object("context_menu").hide()
+
def show_new_file_menu(self, widget=None, eve=None):
self.builder.get_object("new_file_menu").run()
def hide_new_file_menu(self, widget=None, eve=None):
self.builder.get_object("new_file_menu").hide()
+
def show_edit_file_menu(self, widget=None, eve=None):
- self.builder.get_object("edit_file_menu").run()
+ response = self.edit_file_menu.run()
+ if response == Gtk.ResponseType.CLOSE:
+ self.skip_edit = True
+ if response == Gtk.ResponseType.CANCEL:
+ self.cancel_edit = True
def hide_edit_file_menu(self, widget=None, eve=None):
- if widget:
- name = widget.get_name()
- if name == "rename":
- self.builder.get_object("edit_file_menu").hide()
- else:
- keyname = Gdk.keyval_name(eve.keyval).lower()
- if "return" in keyname or "enter" in keyname:
- self.builder.get_object("edit_file_menu").hide()
+ self.builder.get_object("edit_file_menu").hide()
+ def hide_edit_file_menu_enter_key(self, widget=None, eve=None):
+ keyname = Gdk.keyval_name(eve.keyval).lower()
+ if "return" in keyname or "enter" in keyname:
+ self.builder.get_object("edit_file_menu").hide()
def hide_edit_file_menu_skip(self, widget=None, eve=None):
- self.skip_edit = True
- self.builder.get_object("edit_file_menu").hide()
+ self.edit_file_menu.response(Gtk.ResponseType.CLOSE)
def hide_edit_file_menu_cancel(self, widget=None, eve=None):
- self.cancel_edit = True
- self.builder.get_object("edit_file_menu").hide()
+ self.edit_file_menu.response(Gtk.ResponseType.CANCEL)
diff --git a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/mixins/WidgetFileActionMixin.py b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/mixins/WidgetFileActionMixin.py
index 2a5f362..9cdfb0c 100644
--- a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/mixins/WidgetFileActionMixin.py
+++ b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/mixins/WidgetFileActionMixin.py
@@ -1,5 +1,5 @@
# Python imports
-import os
+import threading, os
# Lib imports
import gi
@@ -10,6 +10,12 @@ from gi.repository import Gtk, GObject, Gio
+def threaded(fn):
+ def wrapper(*args, **kwargs):
+ threading.Thread(target=fn, args=args, kwargs=kwargs).start()
+ return wrapper
+
+
class WidgetFileActionMixin:
def set_file_watcher(self, view):
if view.get_dir_watcher():
@@ -69,13 +75,18 @@ class WidgetFileActionMixin:
for file in uris:
view.open_file_locally(file)
+ @threaded
def open_with_files(self, appchooser_widget):
wid, tid, view, iconview, store = self.get_current_state()
- uris = self.format_to_uris(store, wid, tid, self.selected_files)
- file = Gio.File.new_for_uri(uris[0])
app_info = appchooser_widget.get_app_info()
+ uris = self.format_to_uris(store, wid, tid, self.selected_files, True)
+ files = []
- app_info.launch([file], None)
+ for uri in uris:
+ gio_file = Gio.File.new_for_path(uri)
+ files.append(gio_file)
+
+ app_info.launch(files, None)
def execute_files(self, in_terminal=False):
wid, tid, view, iconview, store = self.get_current_state()
@@ -85,64 +96,58 @@ class WidgetFileActionMixin:
for path in paths:
command = f"sh -c '{path}'" if not in_terminal else f"{view.terminal_app} -e '{path}'"
- view.execute(command.split(), current_dir)
+ view.execute(command, start_dir=view.get_current_directory(), use_os_system=False)
+ 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)
+ 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))
+ command = f"{view.terminal_app} -e '{command}'"
-
-
- ##################################################################################################
-
- # NOTE: Everything below is trash and needs yet another rewrite because it doesn't work properly.
-
- ##################################################################################################
+ view.execute(command, start_dir=None, use_os_system=True)
def rename_files(self):
rename_label = self.builder.get_object("file_to_rename_label")
rename_input = self.builder.get_object("new_rename_fname")
wid, tid, view, iconview, store = self.get_current_state()
- uris = self.format_to_uris(store, wid, tid, self.to_rename_files)
+ uris = self.format_to_uris(store, wid, tid, self.selected_files)
- # The rename button hides the rename dialog box which lets the loop continue.
- # Weirdly, the show at the end is needed to flow through all the list properly
- # than auto chosing the first rename entry you do.
for uri in uris:
entry = uri.split("/")[-1]
rename_label.set_label(entry)
rename_input.set_text(entry)
- if self.skip_edit:
- self.skip_edit = False
- self.show_edit_file_menu()
- # Yes...this step is required even with the above... =/
self.show_edit_file_menu()
-
if self.skip_edit:
+ self.skip_edit = False
continue
if self.cancel_edit:
+ self.cancel_edit = False
break
rname_to = rename_input.get_text().strip()
target = f"file://{view.get_current_directory()}/{rname_to}"
- self.handle_file([uri], "edit", target)
+ self.handle_file([uri], "rename", target)
- self.show_edit_file_menu()
self.skip_edit = False
self.cancel_edit = False
- self.hide_new_file_menu()
- self.to_rename_files.clear()
+ self.hide_edit_file_menu()
+ self.selected_files.clear()
def cut_files(self):
wid, tid, view, iconview, store = self.get_current_state()
- uris = self.format_to_uris(store, wid, tid, self.selected_files)
+ uris = self.format_to_uris(store, wid, tid, self.selected_files)
self.to_cut_files = uris
def copy_files(self):
wid, tid, view, iconview, store = self.get_current_state()
- print(self.selected_files)
- uris = self.format_to_uris(store, wid, tid, self.selected_files)
- print(uris)
+ uris = self.format_to_uris(store, wid, tid, self.selected_files)
self.to_copy_files = uris
def paste_files(self):
@@ -155,28 +160,35 @@ class WidgetFileActionMixin:
elif len(self.to_cut_files) > 0:
self.handle_file(self.to_cut_files, "move", target)
- 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)
-
- save_target = archiver_dialogue.get_filename();
- start_itr, end_itr = self.arc_command_buffer.get_bounds()
- command = self.arc_command_buffer.get_text(start_itr, end_itr, False)
-
- command = command.replace("%o", save_target)
- command = command.replace("%N", ' '.join(paths))
- final_command = f"terminator -e '{command}'"
- self.execute(final_command, start_dir=None, use_os_system=True)
-
def delete_files(self):
wid, tid, view, iconview, store = self.get_current_state()
- uris = self.format_to_uris(store, wid, tid, self.selected_files)
- self.handle_file(uris, "delete")
+ uris = self.format_to_uris(store, wid, tid, self.selected_files)
+ response = None
+
+ 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
+
def trash_files(self):
wid, tid, view, iconview, store = self.get_current_state()
uris = self.format_to_uris(store, wid, tid, self.selected_files)
- self.handle_file(uris, "trash")
+ for uri in uris:
+ file = Gio.File.new_for_uri(uri)
+ file.trash(cancellable=None)
@@ -191,104 +203,146 @@ class WidgetFileActionMixin:
target = f"{view.get_current_directory()}"
if file_name:
- file_name = "file://" + target + "/" + file_name
+ path = "file://{target}/{file_name}"
+
if type == True: # Create File
- self.handle_file([file_name], "create_file")
+ self.handle_file([path], "create_file")
else: # Create Folder
- self.handle_file([file_name], "create_dir")
+ self.handle_file([path], "create_dir")
fname_field.set_text("")
def move_files(self, files, target):
self.handle_file(files, "move", target)
- # NOTE: While not fully race condition proof, we happy path it first
- # and then handle anything after as a conflict for renaming before
- # copy, move, or edit. This is literally the oppopsite of what Gtk says to do.
- # But, they can't even delete directories properly. So... f**k them.
+ # 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.
def handle_file(self, paths, action, _target_path=None):
- paths = self.preprocess_paths(paths)
- target = None
- response = None
- self.warning_alert.format_secondary_text(f"Do you really want to {action} the {len(paths)} file(s)?")
+ target = None
+ _file = None
+ response = None
+ overwrite_all = False
+ rename_auto_all = False
for path in paths:
try:
file = Gio.File.new_for_uri(path)
- if action == "trash":
- file.trash(cancellable=None)
-
- if (action == "create_file" or action == "create_dir") and not file.query_exists():
- if action == "create_file":
- file.create(flags=Gio.FileCreateFlags.NONE, cancellable=None)
- continue
- if action == "create_dir":
- file.make_directory(cancellable=None)
- continue
-
-
if _target_path:
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()}"
- target = Gio.File.new_for_uri(_target)
+ _file = Gio.File.new_for_uri(_target)
else:
- target = Gio.File.new_for_uri(_target_path)
-
- if target and not target.query_exists():
- 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()
- tPath = None
- state = True
-
- if action == "copy":
- tPath = target.get_path()
- view.copy_file(fPath, tPath)
- if action == "move" or action == "edit":
- tPath = target.get_parent().get_path()
- 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)
+ _file = Gio.File.new_for_uri(_target_path)
+ else:
+ _file = Gio.File.new_for_uri(path)
+ 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()
+ if response == "overwrite_all":
+ overwrite_all = True
+ if response == "rename_auto_all":
+ rename_auto_all = True
- # 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?
+ 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)
- if action == "delete":
- 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 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.window_controller.get_active_data()
- view = self.get_fm_window(wid).get_view_by_id(tid)
- view.delete_file( file.get_path() )
+ view.delete_file( _file.get_path() )
else:
- file.delete(cancellable=None)
- 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.window_controller.get_active_data()
+ view = self.get_fm_window(wid).get_view_by_id(tid)
+ fPath = file.get_path()
+ tPath = None
+ state = True
+
+ if action == "copy":
+ tPath = target.get_path()
+ view.copy_file(fPath, tPath)
+ if action == "move" or action == "rename":
+ tPath = target.get_parent().get_path()
+ 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)
except GObject.GError as e:
raise OSError(e)
+ self.exists_file_rename_bttn.set_sensitive(False)
- def preprocess_paths(self, paths):
- if not isinstance(paths, list):
- paths = [paths]
- # Convert items such as pathlib paths to strings
- paths = [path.__fspath__() if hasattr(path, "__fspath__") else path for path in paths]
- return paths
+
+
+ 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()
+
+ 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)
diff --git a/user_config/solarfm/Main_Window.glade b/user_config/solarfm/Main_Window.glade
index 12f3b7d..cb4a4ee 100644
--- a/user_config/solarfm/Main_Window.glade
+++ b/user_config/solarfm/Main_Window.glade
@@ -729,7 +729,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
False
- True
+ False
1
@@ -773,6 +773,242 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
False
gtk-execute
+
True
False
@@ -856,12 +1092,15 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
True
@@ -1730,12 +1973,13 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
Open With
+ open_with
True
True
True
open_with_img
True
-
+
False
@@ -1786,7 +2030,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
20
rename_img2
True
-
+
False
@@ -1923,16 +2167,26 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
False
+ False
True
center
+ True
dialog
+ True
+ True
+ True
False
+ False
center
warning
Warning!
False
+ 5
+ 5
+ 5
+ 5
vertical
2
@@ -1982,5 +2236,8 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
button4
button3
+
diff --git a/user_config/solarfm/stylesheet.css b/user_config/solarfm/stylesheet.css
index 8be56ce..cc42a9a 100644
--- a/user_config/solarfm/stylesheet.css
+++ b/user_config/solarfm/stylesheet.css
@@ -39,6 +39,10 @@ notebook > header > tabs > tab:checked {
color: rgba(255, 255, 255, 0.5);
}
+.alert-border {
+ border: 2px solid rgba(116, 0, 0, 0.64);
+}
+
/* * {
background: rgba(0, 0, 0, 0.14);