Added IO ui to stop move or copy, enhanced favorites plugin
This commit is contained in:
parent
4f9fe37613
commit
f51a860de5
|
@ -1,5 +1,5 @@
|
||||||
# Python imports
|
# Python imports
|
||||||
import os, threading, subprocess, time, inspect
|
import os, subprocess, time, inspect
|
||||||
|
|
||||||
# Lib imports
|
# Lib imports
|
||||||
import gi
|
import gi
|
||||||
|
@ -10,19 +10,6 @@ from gi.repository import Gtk
|
||||||
from plugins.plugin_base import PluginBase
|
from plugins.plugin_base import PluginBase
|
||||||
|
|
||||||
|
|
||||||
# NOTE: Threads WILL NOT die with parent's destruction.
|
|
||||||
def threaded(fn):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
# NOTE: Threads WILL die with parent's destruction.
|
|
||||||
def daemon_threaded(fn):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Plugin(PluginBase):
|
class Plugin(PluginBase):
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.38.2 -->
|
<!-- Generated with glade 3.40.0 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.24"/>
|
<requires lib="gtk+" version="3.24"/>
|
||||||
<object class="GtkListStore" id="favorites_store">
|
<object class="GtkListStore" id="favorites_store">
|
||||||
<columns>
|
<columns>
|
||||||
<!-- column-name Favorites -->
|
<!-- column-name Favorites -->
|
||||||
<column type="gchararray"/>
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name Path -->
|
||||||
|
<column type="gchararray"/>
|
||||||
</columns>
|
</columns>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkDialog" id="favorites_dialog">
|
<object class="GtkDialog" id="favorites_dialog">
|
||||||
<property name="width-request">320</property>
|
<property name="width-request">420</property>
|
||||||
<property name="height-request">450</property>
|
<property name="height-request">450</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="modal">True</property>
|
<property name="modal">True</property>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Python imports
|
# Python imports
|
||||||
import os, threading, subprocess, time, inspect, json
|
import os, inspect, json
|
||||||
|
|
||||||
# Lib imports
|
# Lib imports
|
||||||
import gi
|
import gi
|
||||||
|
@ -10,19 +10,6 @@ from gi.repository import Gtk
|
||||||
from plugins.plugin_base import PluginBase
|
from plugins.plugin_base import PluginBase
|
||||||
|
|
||||||
|
|
||||||
# NOTE: Threads WILL NOT die with parent's destruction.
|
|
||||||
def threaded(fn):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start()
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
# NOTE: Threads WILL die with parent's destruction.
|
|
||||||
def daemon_threaded(fn):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Plugin(PluginBase):
|
class Plugin(PluginBase):
|
||||||
|
@ -65,7 +52,8 @@ class Plugin(PluginBase):
|
||||||
with open(self._FAVORITES_FILE) as f:
|
with open(self._FAVORITES_FILE) as f:
|
||||||
self._favorites = json.load(f)
|
self._favorites = json.load(f)
|
||||||
for favorite in self._favorites:
|
for favorite in self._favorites:
|
||||||
self._favorites_store.append([favorite])
|
display, path = favorite
|
||||||
|
self._favorites_store.append([display, path])
|
||||||
else:
|
else:
|
||||||
with open(self._FAVORITES_FILE, 'a') as f:
|
with open(self._FAVORITES_FILE, 'a') as f:
|
||||||
f.write('[]')
|
f.write('[]')
|
||||||
|
@ -78,25 +66,29 @@ class Plugin(PluginBase):
|
||||||
button.connect("button-release-event", self._show_favorites_menu)
|
button.connect("button-release-event", self._show_favorites_menu)
|
||||||
return button
|
return button
|
||||||
|
|
||||||
@threaded
|
|
||||||
def _get_state(self, widget=None, eve=None):
|
def _get_state(self, widget=None, eve=None):
|
||||||
self._event_system.emit("get_current_state")
|
self._event_system.emit("get_current_state")
|
||||||
|
|
||||||
|
|
||||||
@threaded
|
|
||||||
def _set_current_dir_lbl(self, widget=None, eve=None):
|
def _set_current_dir_lbl(self, widget=None, eve=None):
|
||||||
self._current_dir_lbl.set_label(f"Current Directory:\n{self._fm_state.tab.get_current_directory()}")
|
self._current_dir_lbl.set_label(f"Current Directory:\n{self._fm_state.tab.get_current_directory()}")
|
||||||
|
|
||||||
def _add_to_favorite(self, state):
|
def _add_to_favorite(self, state):
|
||||||
current_directory = self._fm_state.tab.get_current_directory()
|
path = self._fm_state.tab.get_current_directory()
|
||||||
self._favorites_store.append([current_directory])
|
parts = path.split("/")
|
||||||
self._favorites.append(current_directory)
|
display = '/'.join(parts[-3:]) if len(parts) > 3 else path
|
||||||
|
|
||||||
|
self._favorites_store.append([display, path])
|
||||||
|
self._favorites.append([display, path])
|
||||||
self._save_favorites()
|
self._save_favorites()
|
||||||
|
|
||||||
def _remove_from_favorite(self, state):
|
def _remove_from_favorite(self, state):
|
||||||
path = self._favorites_store.get_value(self._selected, 0)
|
path = self._favorites_store.get_value(self._selected, 1)
|
||||||
self._favorites_store.remove(self._selected)
|
self._favorites_store.remove(self._selected)
|
||||||
self._favorites.remove(path)
|
|
||||||
|
for i, f in enumerate(self._favorites):
|
||||||
|
if f[1] == path:
|
||||||
|
self._favorites.remove( self._favorites[i] )
|
||||||
|
|
||||||
self._save_favorites()
|
self._save_favorites()
|
||||||
|
|
||||||
def _save_favorites(self):
|
def _save_favorites(self):
|
||||||
|
@ -104,7 +96,7 @@ class Plugin(PluginBase):
|
||||||
json.dump(self._favorites, outfile, separators=(',', ':'), indent=4)
|
json.dump(self._favorites, outfile, separators=(',', ':'), indent=4)
|
||||||
|
|
||||||
def _set_selected_path(self, widget=None, eve=None):
|
def _set_selected_path(self, widget=None, eve=None):
|
||||||
path = self._favorites_store.get_value(self._selected, 0)
|
path = self._favorites_store.get_value(self._selected, 1)
|
||||||
self._ui_objects[0].set_text(path)
|
self._ui_objects[0].set_text(path)
|
||||||
self._set_current_dir_lbl()
|
self._set_current_dir_lbl()
|
||||||
|
|
||||||
|
@ -119,5 +111,5 @@ class Plugin(PluginBase):
|
||||||
|
|
||||||
def _set_selected(self, user_data):
|
def _set_selected(self, user_data):
|
||||||
selected = user_data.get_selected()[1]
|
selected = user_data.get_selected()[1]
|
||||||
if selected:
|
if selected and not self._selected == selected:
|
||||||
self._selected = selected
|
self._selected = selected
|
||||||
|
|
|
@ -115,6 +115,9 @@ class ShowHideMixin:
|
||||||
if response == Gtk.ResponseType.CANCEL:
|
if response == Gtk.ResponseType.CANCEL:
|
||||||
self.cancel_edit = True
|
self.cancel_edit = True
|
||||||
|
|
||||||
|
def show_io_popup(self, widget=None, eve=None):
|
||||||
|
self.builder.get_object("io_popup").popup()
|
||||||
|
|
||||||
def hide_edit_file_menu(self, widget=None, eve=None):
|
def hide_edit_file_menu(self, widget=None, eve=None):
|
||||||
self.builder.get_object("edit_file_menu").hide()
|
self.builder.get_object("edit_file_menu").hide()
|
||||||
|
|
||||||
|
|
|
@ -320,16 +320,82 @@ class WidgetFileActionMixin:
|
||||||
if action == "move" or action == "rename":
|
if action == "move" or action == "rename":
|
||||||
tab.move_file(fPath, tPath)
|
tab.move_file(fPath, tPath)
|
||||||
else:
|
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)
|
||||||
|
|
||||||
if action == "copy":
|
if action == "copy":
|
||||||
file.copy(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
|
container, cancle_eve, update_progress, finish_callback = self.create_io_widget(action, file)
|
||||||
|
file.copy_async(destination=target, flags=Gio.FileCopyFlags.BACKUP,
|
||||||
|
io_priority=98, cancellable=cancle_eve,
|
||||||
|
progress_callback=update_progress, callback=finish_callback)
|
||||||
|
self.builder.get_object("io_list").add(container)
|
||||||
if action == "move" or action == "rename":
|
if action == "move" or action == "rename":
|
||||||
file.move(target, flags=Gio.FileCopyFlags.BACKUP, cancellable=None)
|
container, cancle_eve, update_progress, finish_callback = self.create_io_widget(action, file)
|
||||||
|
file.move(destination=target, flags=Gio.FileCopyFlags.BACKUP,
|
||||||
|
cancellable=cancle_eve, progress_callback=update_progress)
|
||||||
|
self.builder.get_object("io_list").add(container)
|
||||||
|
|
||||||
|
|
||||||
except GObject.GError as e:
|
except GObject.GError as e:
|
||||||
raise OSError(e)
|
raise OSError(e)
|
||||||
|
|
||||||
self.exists_file_rename_bttn.set_sensitive(False)
|
self.exists_file_rename_bttn.set_sensitive(False)
|
||||||
|
|
||||||
|
# NOTE: There is something not right about the way we are doing this.
|
||||||
|
# Calling cancel results in an error getting thrown to finish_callback
|
||||||
|
# and checking for task.had_error() is True and task.get_completed() is False
|
||||||
|
def create_io_widget(self, action, file):
|
||||||
|
cancle_eve = Gio.Cancellable.new()
|
||||||
|
container = Gtk.Box()
|
||||||
|
stats = Gtk.Box()
|
||||||
|
label = Gtk.Label()
|
||||||
|
progress = Gtk.ProgressBar()
|
||||||
|
cncl_button = Gtk.Button(label="Cancel")
|
||||||
|
del_button = Gtk.Button(label="Delete")
|
||||||
|
io_list = self.builder.get_object("io_list")
|
||||||
|
label.set_label(file.get_basename())
|
||||||
|
|
||||||
|
progress.set_show_text(True)
|
||||||
|
progress.set_text(f"{action.upper()}ING")
|
||||||
|
|
||||||
|
|
||||||
|
def do_cancel(widget, container, eve):
|
||||||
|
print(f"Canceling: [{action}] of {file.get_basename()} ...")
|
||||||
|
eve.cancel()
|
||||||
|
|
||||||
|
def update_progress(current, total, eve=None):
|
||||||
|
progress.set_fraction(current/total)
|
||||||
|
|
||||||
|
def finish_callback(file, task=None, eve=None):
|
||||||
|
io_list.remove(container)
|
||||||
|
# if not task.had_error():
|
||||||
|
# self.builder.get_object("io_list").remove(container)
|
||||||
|
# else:
|
||||||
|
# print(f"{action} of {file.get_basename()} failed...")
|
||||||
|
|
||||||
|
def delete_container(widget, eve):
|
||||||
|
io_list.remove(container)
|
||||||
|
|
||||||
|
|
||||||
|
if not action == "move":
|
||||||
|
stats.pack_end(cncl_button, False, False, 5)
|
||||||
|
cncl_button.connect("clicked", do_cancel, *(container, cancle_eve))
|
||||||
|
else:
|
||||||
|
stats.pack_end(del_button, False, False, 5)
|
||||||
|
del_button.connect("clicked", delete_container, ())
|
||||||
|
|
||||||
|
container.set_orientation(1)
|
||||||
|
stats.set_orientation(0)
|
||||||
|
stats.add(progress)
|
||||||
|
|
||||||
|
container.add(label)
|
||||||
|
container.add(stats)
|
||||||
|
container.show_all()
|
||||||
|
|
||||||
|
return container, cancle_eve, update_progress, finish_callback
|
||||||
|
|
||||||
|
|
||||||
def setup_exists_data(self, from_file, to_file):
|
def setup_exists_data(self, from_file, to_file):
|
||||||
from_info = from_file.query_info("standard::*,time::modified", 0, cancellable=None)
|
from_info = from_file.query_info("standard::*,time::modified", 0, cancellable=None)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.38.2 -->
|
<!-- Generated with glade 3.40.0 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<object class="GtkAboutDialog" id="about_page">
|
<object class="GtkAboutDialog" id="about_page">
|
||||||
|
@ -482,6 +482,11 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="stock">gtk-execute</property>
|
<property name="stock">gtk-execute</property>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkImage" id="io_img">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="stock">gtk-stop</property>
|
||||||
|
</object>
|
||||||
<object class="GtkTextBuffer" id="message_buffer"/>
|
<object class="GtkTextBuffer" id="message_buffer"/>
|
||||||
<object class="GtkImage" id="rename_img">
|
<object class="GtkImage" id="rename_img">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -824,7 +829,23 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
|
||||||
<property name="spacing">5</property>
|
<property name="spacing">5</property>
|
||||||
<property name="layout-style">start</property>
|
<property name="layout-style">start</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="plugins_buttoin">
|
<object class="GtkButton" id="io_button">
|
||||||
|
<property name="label" translatable="yes">I/O</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="receives-default">True</property>
|
||||||
|
<property name="image">io_img</property>
|
||||||
|
<property name="always-show-image">True</property>
|
||||||
|
<signal name="released" handler="show_io_popup" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="plugins_button">
|
||||||
<property name="label" translatable="yes">Plugins</property>
|
<property name="label" translatable="yes">Plugins</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
|
@ -834,7 +855,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="position">0</property>
|
<property name="position">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
@ -850,7 +871,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="position">1</property>
|
<property name="position">2</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
@ -866,7 +887,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="position">2</property>
|
<property name="position">3</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
@ -882,7 +903,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="position">3</property>
|
<property name="position">4</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
@ -898,7 +919,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="position">4</property>
|
<property name="position">5</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
@ -1832,6 +1853,23 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
|
||||||
<class name="alert-border"/>
|
<class name="alert-border"/>
|
||||||
</style>
|
</style>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkPopover" id="io_popup">
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="relative-to">io_button</property>
|
||||||
|
<property name="position">bottom</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="io_list">
|
||||||
|
<property name="width-request">320</property>
|
||||||
|
<property name="height-request">320</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
<object class="GtkPopover" id="message_popup_widget">
|
<object class="GtkPopover" id="message_popup_widget">
|
||||||
<property name="width-request">320</property>
|
<property name="width-request">320</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
|
@ -2099,7 +2137,7 @@ SolarFM is developed on Atom, git, and using Python 3+ with Gtk GObject introspe
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkPopover" id="plugin_controls">
|
<object class="GtkPopover" id="plugin_controls">
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="relative-to">plugins_buttoin</property>
|
<property name="relative-to">plugins_button</property>
|
||||||
<signal name="button-release-event" handler="hide_plugins_popup" swapped="no"/>
|
<signal name="button-release-event" handler="hide_plugins_popup" swapped="no"/>
|
||||||
<signal name="key-release-event" handler="hide_plugins_popup" swapped="no"/>
|
<signal name="key-release-event" handler="hide_plugins_popup" swapped="no"/>
|
||||||
<child>
|
<child>
|
||||||
|
|
Loading…
Reference in New Issue