Add "clear trash", "restore from trash" options.
This commit is contained in:
		| @@ -13,7 +13,6 @@ sudo apt-get install python3 wget ffmpegthumbnailer steamcmd | ||||
|  | ||||
| # TODO | ||||
| <ul> | ||||
| <li>Add "clear trash", "restore from trash" options.</li> | ||||
| <li>Add simpleish plugin system to run bash/python scripts.</li> | ||||
| <li>Add DnD context awareness for over folder drop.</li> | ||||
| </ul> | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -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": | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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): | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										0
									
								
								src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/__init__.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										0
									
								
								src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/__init__.py
									
									
									
									
									
										Executable file
									
								
							
							
								
								
									
										46
									
								
								src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/trash.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										46
									
								
								src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/trash.py
									
									
									
									
									
										Executable file
									
								
							| @@ -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')) | ||||
							
								
								
									
										161
									
								
								src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/xdgtrash.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										161
									
								
								src/versions/solarfm-0.0.1/SolarFM/new/solarfm/trasher/xdgtrash.py
									
									
									
									
									
										Executable file
									
								
							| @@ -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')) | ||||
		Reference in New Issue
	
	Block a user