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
-- Add "clear trash", "restore from trash" options.
- Add simpleish plugin system to run bash/python scripts.
- Add DnD context awareness for over folder drop.
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'))