diff --git a/README.md b/README.md index 74808c6..c533e1f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ sudo apt-get install python3 wget ffmpegthumbnailer steamcmd # TODO diff --git a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/shellfm/windows/view/utils/Settings.py b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/shellfm/windows/view/utils/Settings.py index 211f2d7..088cde0 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/shellfm/windows/view/utils/Settings.py +++ b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/shellfm/windows/view/utils/Settings.py @@ -25,7 +25,7 @@ class Settings: REMUX_FOLDER = USER_HOME + "/.remuxs" # Remuxed files folder STEAM_BASE_URL = "https://steamcdn-a.akamaihd.net/steam/apps/" - ICON_DIRS = ["/usr/share/pixmaps", "/usr/share/icons", USER_HOME + "/.icons" ,] + ICON_DIRS = ["/usr/share/pixmaps", "/usr/share/icons", f"{USER_HOME}/.icons" ,] BASE_THUMBS_PTH = USER_HOME + "/.thumbnails" # Used for thumbnail generation ABS_THUMBS_PTH = BASE_THUMBS_PTH + "/normal" # Used for thumbnail generation STEAM_ICONS_PTH = BASE_THUMBS_PTH + "/steam_icons" 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 dd0f5d8..5354c5e 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 @@ -1,5 +1,5 @@ # Python imports -import sys, traceback, threading, subprocess, signal, inspect, os, time +import sys, traceback, threading, signal, inspect, os, time # Lib imports import gi @@ -153,6 +153,10 @@ class Controller(WidgetFileActionMixin, PaneMixin, WindowMixin, ShowHideMixin, \ self.trash_files() if action == "go_to_trash": self.builder.get_object("path_entry").set_text(self.trash_files_path) + if action == "restore_from_trash": + self.restore_trash_files() + if action == "empty_trash": + self.empty_trash() if action == "create": 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 bdb20d7..ccace6d 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 @@ -5,6 +5,7 @@ from gi.repository import GLib # Application imports from shellfm import WindowController +from trasher.xdgtrash import XDGTrash @@ -15,8 +16,10 @@ class Controller_Data: def setup_controller_data(self): self.window_controller = WindowController() - self.state = self.window_controller.load_state() + self.trashman = XDGTrash() + self.trashman.regenerate() + self.state = self.window_controller.load_state() self.builder = self.settings.builder self.logger = self.settings.logger 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 1528625..96f503d 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 @@ -19,6 +19,20 @@ class WidgetFileActionMixin: num /= 1024.0 return f"{num:.1f} Yi{suffix}" + 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, view): if view.get_dir_watcher(): @@ -196,10 +210,16 @@ class WidgetFileActionMixin: wid, tid, view, iconview, store = self.get_current_state() uris = self.format_to_uris(store, wid, tid, self.selected_files, True) for uri in uris: - file = Gio.File.new_for_path(uri) - file.trash(cancellable=None) + self.trashman.trash(uri, False) + def restore_trash_files(self): + wid, tid, view, iconview, store = self.get_current_state() + uris = self.format_to_uris(store, wid, tid, self.selected_files, True) + for uri in uris: + self.trashman.restore(filename=uri.split("/")[-1], verbose=False) + def empty_trash(self): + self.trashman.empty(verbose=False) def create_files(self): diff --git a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/mixins/WindowMixin.py b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/mixins/WindowMixin.py index 026f741..aa8687f 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/mixins/WindowMixin.py +++ b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/signal_classes/mixins/WindowMixin.py @@ -81,11 +81,19 @@ class WindowMixin(TabMixin): def set_bottom_labels(self, view): _wid, _tid, _view, iconview, store = self.get_current_state() selected_files = iconview.get_selected_items() - path_file = Gio.File.new_for_path( view.get_current_directory()) + current_directory = view.get_current_directory() + path_file = Gio.File.new_for_path( current_directory) mount_file = path_file.query_filesystem_info(attributes="filesystem::*", cancellable=None) formatted_mount_free = self.sizeof_fmt( int(mount_file.get_attribute_as_string("filesystem::free")) ) formatted_mount_size = self.sizeof_fmt( int(mount_file.get_attribute_as_string("filesystem::size")) ) + if self.trash_files_path == current_directory: + self.builder.get_object("restore_from_trash").show() + self.builder.get_object("empty_trash").show() + else: + self.builder.get_object("restore_from_trash").hide() + self.builder.get_object("empty_trash").hide() + # If something selected self.bottom_size_label.set_label(f"{formatted_mount_free} free / {formatted_mount_size}") self.bottom_path_label.set_label(view.get_current_directory()) @@ -94,7 +102,7 @@ class WindowMixin(TabMixin): combined_size = 0 for uri in uris: file_info = Gio.File.new_for_path(uri).query_info(attributes="standard::size", - flags=Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, + flags=Gio.FileQueryInfoFlags.NONE, cancellable=None) file_size = file_info.get_size() combined_size += file_size diff --git a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/__init__.py b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/trash.py b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/trash.py new file mode 100755 index 0000000..be29701 --- /dev/null +++ b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/trash.py @@ -0,0 +1,46 @@ +# Python imports +import os + +# Lib imports + +# Application imports + + + + +class Trash(object): + """Base Trash class.""" + + def size_dir(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 + size_dir(item) + + return size + + def regenerate(self): + """Regenerate the trash and recreate metadata.""" + pass # Some backends don’t need regeneration. + + def empty(self, verbose): + """Empty the trash.""" + raise NotImplementedError(_('Backend didn’t implement this functionality')) + + def list(self, human=True): + """List the trash contents.""" + raise NotImplementedError(_('Backend didn’t implement this functionality')) + + def trash(self, filepath, verbose): + """Move specified file to trash.""" + raise NotImplementedError(_('Backend didn’t implement this functionality')) + + def restore(self, filename, verbose): + """Restore a file from trash.""" + raise NotImplementedError(_('Backend didn’t \ implement this functionality')) diff --git a/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/xdgtrash.py b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/xdgtrash.py new file mode 100755 index 0000000..02ff013 --- /dev/null +++ b/src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/xdgtrash.py @@ -0,0 +1,161 @@ +from .trash import Trash +import shutil +import os +import os.path +import datetime +import sys +import logging + +try: + import configparser +except ImportError: + import ConfigParser as configparser + + + + +class XDGTrash(Trash): + """XDG trash backend.""" + def __init__(self): + self.trashdir = None + self.filedir = None + self.infodir = None + + if os.getenv('XDG_DATA_HOME') is None: + self.trashdir = os.path.expanduser('~/.local/share/Trash') + else: + self.trashdir = os.getenv('XDG_DATA_HOME') + '/Trash' + + try: + if not os.path.exists(self.trashdir): + os.mkdir(self.trashdir) + except OSError: + self.trashdir = os.path.join('tmp' 'TRASH') + raise('Couldn’t access the proper directory, temporary trash is in in /tmp/TRASH') + + self.filedir = self.trashdir + '/files/' + self.infodir = self.trashdir + '/info/' + + def regenerate(self): + """Regenerate the trash and recreate metadata.""" + print('Regenerating the trash and recreating metadata...') + zerosize = False + + if not os.path.exists(self.trashdir): + os.mkdir(self.trashdir) + zerosize = True + + if ((not os.path.exists(self.filedir)) or + (not os.path.exists(self.infodir))): + os.mkdir(self.filedir) + os.mkdir(self.infodir) + zerosize = True + if not zerosize: + trashsize = (self.size_dir(self.filedir) + self.size_dir(self.infodir)) + else: + trashsize = 0 + + infofile = '[Cached]\nSize=' + str(trashsize) + '\n' + fh = open(os.path.join(self.trashdir, 'metadata'), 'w') + fh.write(infofile) + fh.close() + + def empty(self, verbose): + """Empty the trash.""" + print('emptying (verbose={})'.format(verbose)) + shutil.rmtree(self.filedir) + shutil.rmtree(self.infodir) + self.regenerate() + if verbose: + sys.stderr.write(_('emptied the trash\n')) + + def list(self, human=True): + """List the trash contents.""" + if human: + print('listing contents (on stdout; human=True)') + else: + print('listing contents (return; human=False)') + dirs = [] + files = [] + for f in os.listdir(self.filedir): + if os.path.isdir(self.filedir + f): + dirs.append(f) + else: + files.append(f) + + dirs.sort() + files.sort() + + allfiles = [] + for i in dirs: + allfiles.append(i + '/') + for i in files: + allfiles.append(i) + if human: + if allfiles != []: + print('\n'.join(allfiles)) + else: + return allfiles + + def trash(self, filepath, verbose): + """Move specified file to trash.""" + print('trashing file {} (verbose={})'.format(filepath, verbose)) + # Filename alteration, a big mess. + filename = os.path.basename(filepath) + fileext = os.path.splitext(filename) + + tomove = filename + collision = True + i = 1 + + while collision: + if os.path.lexists(self.filedir + tomove): + tomove = fileext[0] + ' ' + str(i) + fileext[1] + i = i + 1 + else: + collision = False + + infofile = """[Trash Info] +Path={} +DeletionDate={} +""".format(os.path.realpath(filepath), + datetime.datetime.now().strftime('%Y-%m-%dT%H:%m:%S')) + + os.rename(filepath, self.filedir + tomove) + + f = open(os.path.join(self.infodir, tomove + '.trashinfo'), 'w') + f.write(infofile) + f.close() + + self.regenerate() + + if verbose: + sys.stderr.write(_('trashed \'{}\'\n').format(filename)) + + def restore(self, filename, verbose, tocwd=False): + """Restore a file from trash.""" + print('restoring file {} (verbose={}, tocwd={})'.format(filename, verbose, tocwd)) + info = configparser.ConfigParser() + if os.path.exists(os.path.join(self.filedir, filename)): + info.read(os.path.join(self.infodir, filename + '.trashinfo')) + restname = os.path.basename(info.get('Trash Info', 'Path')) + + if tocwd: + restdir = os.path.abspath('.') + else: + restdir = os.path.dirname(info.get('Trash Info', 'Path')) + + restfile = os.path.join(restdir, restname) + if not os.path.exists(restdir): + raise TMError('restore', 'nodir', _('no such directory: {}' + ' -- cannot restore').format(restdir)) + os.rename(os.path.join(self.filedir, filename), restfile) + os.remove(os.path.join(self.infodir, filename + '.trashinfo')) + self.regenerate() + print('restored {} to {}'.format(filename, restfile)) + if verbose: + sys.stderr.write(_('restored {} to {}\n').format(filename, restfile)) + + else: + print('couldn\'t find {} in trash'.format(filename)) + raise TMError('restore', 'nofile', _('no such file in trash'))