#!/usr/bin/env python
"""File Manager Utilities
Trash according to Freedesktop.org:
For every user2 a “home trash” directory MUST be available.
Its name and location are $XDG_DATA_HOME/Trash
The implementation also MUST check that this directory is not a symbolic link.
If these checks fail, the implementation MUST NOT use this directory.
See videothumb lines 226,227.
A trash directory contains two subdirectories, named info and files.
$trash/files directory contains the files that were trashed.
$trash/info directory contains an “information file” for every file
This file MUST have the same name as the deleted file with extension “.trashinfo”.
“Path” contains the original location of the file/directory.
“DeletionDate” contains the date and time when the file/directory was trashed.
When trashing a file the program MUST create the $trash/info first.
*H.Speksnijder 20141204, 20210414*
"""
# module unittest:no
import sys
import os
import shutil
import datetime
# pylint: disable=C0413
import gi
gi.require_version('Gtk', '3.0') # tell we want GTK3
from gi.repository import Gtk # pylint: disable=C0413
import dialogs
import gtkutils
# pylint: enable=C0413
def filesaveas(widget, getselected):
"""!Save movie with new name.
@param widget: Gtk.Widget, usually the button
@param getselected: function to get workdir & selected file from the treeview
"""
wdir, fn = getselected()
chooser = Gtk.FileChooserDialog(
title="File Save as",
parent=widget.get_toplevel(),
action=Gtk.FileChooserAction.SAVE)
chooser.add_buttons(
Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_SAVE, Gtk.ResponseType.OK)
chooser.set_default_response(Gtk.ResponseType.OK)
if chooser.run() == Gtk.ResponseType.OK:
newfn = chooser.get_filename()
print("save file ", wdir, fn, " as: ", newfn)
if os.path.exists(newfn):
dialogs.warning(widget, 'File already exist')
else:
dfn = os.path.join(wdir, fn)
try:
shutil.copy2(dfn, newfn)
except OSError as error:
dialogs.warning(widget, error)
chooser.hide()
def filerename(widget, getselected): # pylint: disable=W0613
"""!Rename, keep in the same directory !
@param widget: Gtk.Widget, usually the button
@param getselected: function to get workdir & selected file from the treeview
@return fnnew: string new filename.
"""
wdir, fn = getselected()
fnnew = None
dialog = Gtk.Dialog(
"Rename File",
widget.get_toplevel())
dialog.add_buttons(
Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OK, Gtk.ResponseType.OK)
entry = Gtk.Entry()
entry.set_text(fn)
box = dialog.get_content_area()
box.pack_start(entry, False, False, 0)
box.show_all()
if dialog.run() == Gtk.ResponseType.OK:
fnnew = entry.get_text()
if os.path.exists(fnnew):
dialogs.warning(widget, 'File already exist')
else:
dfn = os.path.join(wdir, fn)
dfnnew = os.path.join(wdir, fnnew)
try:
os.rename(dfn, dfnnew)
except OSError as error:
dialogs.warning(widget, error)
fnnew = None
dialog.destroy()
return fnnew
def filetrash(wdir, fn, trashdir):
"""!move file to trash
create trashinfo and create trashinfo file
@param wdir: string directory
@param fn: string filename
@param trashdir: string (usually /home/user/.local/share/Trash)
Note:
os.rename(dfn, trashfn) works only on the same filesystem
my photo/video collection lives in another partition and not
the same as the /home partition hence os.rename fails.
OLD:
with open(trashinfofn, 'w') as fp:
NEW: specify encoding see below.
"""
dfn = os.path.join(wdir, fn)
trashfn = os.path.join(trashdir, "files", fn)
trashinfofn = os.path.join(trashdir, 'info', fn + ".trashinfo")
timenow = datetime.datetime.now().isoformat()
infotxt = [
"[Trash Info]",
"Path=" + dfn,
"DeletionDate=" + timenow]
with open(trashinfofn, mode='w', encoding="utf8") as fp:
fp.write("\n".join(infotxt))
shutil.move(dfn, trashfn)
# --/--
def filedelete(widget, getselected, trashdir): # pylint: disable=W0613
"""!Delete the file
@param widget: Gtk.Widget, usually the button
@param getselected: function to get workdir & selected file from the treeview
@param trashdir: directory for trashcan
@return ret: boolean.
OLD usinf "string".format(x)
"Delete {}\nAre you sure?".format(fn)
NEW: use f-string (python 3.6)
"""
wdir, fn = getselected()
dfn = os.path.join(wdir, fn)
ret = False
msg = f"Delete {fn}\nAre you sure?"
if dialogs.warning(widget, msg):
try:
if trashdir:
filetrash(wdir, fn, trashdir)
else:
os.remove(dfn)
ret = True
except OSError as error:
dialogs.warning(widget, error)
ret = False
return ret
# =========== S E L E C T F O L D E R ========================================
def selectfolder(widget):
"""!Select a new forder
@param widget: Gtk.Widget.
@return dn: string directory name.
"""
dn = None
chooser = Gtk.FileChooserDialog(
title="Save",
action=Gtk.FileChooserAction.SELECT_FOLDER,
parent=widget.get_toplevel())
chooser.add_buttons(
Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_SAVE, Gtk.ResponseType.OK)
chooser.set_default_response(Gtk.ResponseType.OK)
if chooser.run() == Gtk.ResponseType.OK:
dn = chooser.get_filename()
print('--->folder chooser: ', dn)
chooser.hide()
return dn
# =========== M A I N =========================================================
def simplefileman():
"""Simple run mostly for testing."""
def getfilename(): # pylint: disable=C0111
"""!@return workdir, fn: tuple of string.
"""
fn = "green.png"
workdir = os.getcwd()
return workdir, fn
button1 = Gtk.Button(label='File Save as')
button1.connect("clicked", filesaveas, getfilename)
button3 = Gtk.Button(label='File Rename')
button3.connect("clicked", filerename, getfilename)
button4 = Gtk.Button(label='File Delete')
button4.connect('clicked', filedelete, getfilename)
hbox = Gtk.Box()
hbox.pack_start(button1, False, True, 0)
hbox.pack_start(button3, True, True, 0)
hbox.pack_start(button4, True, True, 0)
gtkutils.gtkwin(hbox, 300, 50, main=True)
Gtk.main()
if __name__ == "__main__":
# check we have Python3
if sys.version_info[0] < 3:
print("Error: Python version >=3 is required.")
sys.exit(1)
simplefileman()
sys.exit(0)