Skip to content

Commit 727a6c6

Browse files
committed
Provide backup cleanup command
Fixes #469
1 parent 4eb45ea commit 727a6c6

File tree

14 files changed

+648
-509
lines changed

14 files changed

+648
-509
lines changed

LICENSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT license.
22

3-
Copyright (c) 2013 - 2024 Isaac Muse <[email protected]>
3+
Copyright (c) 2013 - 2025 Isaac Muse <[email protected]>
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
66

docs/src/markdown/about/changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- **NEW**: Drop Python 3.8 support.
66
- **NEW**: Upgrade dependencies to pick up bug fixes.
7+
- **NEW**: Add a command under the main menu to delete backups recursively under the current working directory.
78
- **FIX**: Custom focus is not needed to focus the app from macOS dock anymore.
89
- **FIX**: Fix some issues with context menu right click selection in list controls.
910

docs/src/markdown/preferences.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ The **Backups** panel allows you to configure where Rummage creates backups. You
186186
placed in the same folder as the original source, or if they are put into a subfolder. You can also configure the name
187187
of the subfolder used or the extension used when not writing to a subfolder.
188188

189+
If you need to quickly delete all backups, you can select `File -> Delete Backups` from the main menu.
190+
189191
## Import/Export Settings
190192

191193
If desired, Rummage's settings can be exported to a JSON file or imported from a JSON file. This can be particularly

gui.fbp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
22
<wxFormBuilder_Project>
33
<FileVersion major="1" minor="18"/>
4-
<object class="Project" expanded="true">
4+
<object class="Project" expanded="false">
55
<property name="code_generation">Python</property>
66
<property name="cpp_class_decoration"></property>
77
<property name="cpp_disconnect_events">1</property>
@@ -5103,6 +5103,24 @@
51035103
<property name="name">m_separator41</property>
51045104
<property name="permission">none</property>
51055105
</object>
5106+
<object class="wxMenuItem" expanded="false">
5107+
<property name="bitmap"></property>
5108+
<property name="checked">0</property>
5109+
<property name="enabled">1</property>
5110+
<property name="help"></property>
5111+
<property name="id">wxID_ANY</property>
5112+
<property name="kind">wxITEM_NORMAL</property>
5113+
<property name="label">Delete Backups</property>
5114+
<property name="name">m_delete_backups_menuitem</property>
5115+
<property name="permission">none</property>
5116+
<property name="shortcut"></property>
5117+
<property name="unchecked_bitmap"></property>
5118+
<event name="OnMenuSelection">on_delete_backups</event>
5119+
</object>
5120+
<object class="separator" expanded="false">
5121+
<property name="name">m_separator5</property>
5122+
<property name="permission">none</property>
5123+
</object>
51065124
<object class="wxMenuItem" expanded="false">
51075125
<property name="bitmap"></property>
51085126
<property name="checked">0</property>

mkdocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ repo_url: https://github.com/facelessuser/Rummage
44
edit_uri: tree/master/docs/src/markdown
55
site_description: A search and replace tool written in Python.
66
copyright: |
7-
Copyright &copy; 2013 - 2024 <a href="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/facelessuser" target="_blank" rel="noopener">Isaac Muse</a>
7+
Copyright &copy; 2013 - 2025 <a href="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/facelessuser" target="_blank" rel="noopener">Isaac Muse</a>
88
99
docs_dir: docs/src/markdown
1010
theme:

rummage/lib/gui/data/docs/.dochash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
373dd3370b7e5a84494d1b170b10d294
1+
27006063770f8c91bfb5fdc28ad86a1a

rummage/lib/gui/data/docs/about/changelog.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ <h2 id="422">4.22</h2>
2929
<ul>
3030
<li><strong>NEW</strong>: Drop Python 3.8 support.</li>
3131
<li><strong>NEW</strong>: Upgrade dependencies to pick up bug fixes.</li>
32+
<li><strong>NEW</strong>: Add a command under the main menu to delete backups recursively under the current working directory.</li>
3233
<li><strong>FIX</strong>: Custom focus is not needed to focus the app from macOS dock anymore.</li>
3334
<li><strong>FIX</strong>: Fix some issues with context menu right click selection in list controls.</li>
3435
</ul>

rummage/lib/gui/data/docs/preferences.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ <h2 id="backups">Backups</h2>
252252
<p>The <strong>Backups</strong> panel allows you to configure where Rummage creates backups. You can control whether backups are all
253253
placed in the same folder as the original source, or if they are put into a subfolder. You can also configure the name
254254
of the subfolder used or the extension used when not writing to a subfolder.</p>
255+
<p>If you need to quickly delete all backups, you can select <code>File -&gt; Delete Backups</code> from the main menu.</p>
255256
<h2 id="importexport-settings">Import/Export Settings</h2>
256257
<p>If desired, Rummage's settings can be exported to a <abbr title="JavaScript Object Notation">JSON</abbr> file or imported from a <abbr title="JavaScript Object Notation">JSON</abbr> file. This can be particularly
257258
useful for importing regular expression patterns from one system into another system's existing regular expression list.

rummage/lib/gui/dialogs/delete_dialog.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import wx
2222
import os
2323
import time
24+
import shutil
2425
import threading
2526
from ..localization import _
2627
from .. import gui
@@ -68,7 +69,10 @@ def retry(self):
6869
if self.recycle:
6970
send2trash(self.trouble_file)
7071
else:
71-
os.remove(self.trouble_file)
72+
if os.path.isdir(self.trouble_file):
73+
shutil.rmtree(self.trouble_file)
74+
else:
75+
os.remove(self.trouble_file)
7276
deleted = True
7377
self.trouble_file = None
7478
except Exception:
@@ -88,7 +92,10 @@ def run(self):
8892
if self.recycle:
8993
send2trash(f)
9094
else:
91-
os.remove(f)
95+
if os.path.isdir(f):
96+
shutil.rmtree(f)
97+
else:
98+
os.remove(f)
9299
self.count += 1
93100
except Exception:
94101
self.trouble_file = f
@@ -115,7 +122,7 @@ def __init__(self, parent, file_list, recycle):
115122
self.file = None
116123
self.busy = False
117124
self.thread = None
118-
self.total = len(file_list)
125+
self.total = len(file_list) if isinstance(file_list, list) else 0
119126
self.message = None
120127
self.processing = False
121128
self.handling = False
@@ -128,15 +135,18 @@ def __init__(self, parent, file_list, recycle):
128135
title = None
129136
if self.action == ACTION_DELETE:
130137
title = _("Deleting Files")
131-
self.message = _("Deleting %d/%d...")
138+
self.message = _("Deleting %d/%d...") if self.total else _("Deleting %d...")
132139
elif self.action == ACTION_RECYCLE:
133140
title = _("Recycling Files")
134-
self.message = _("Recycling %d/%d...")
141+
self.message = _("Recycling %d/%d...") if self.total else _("Recycling %d...")
135142

136143
self.localize()
137144
self.refresh_localization(title)
138145
if self.message:
139-
self.m_progress_label.SetLabel(self.message % (0, self.total))
146+
if self.total:
147+
self.m_progress_label.SetLabel(self.message % (0, self.total))
148+
else:
149+
self.m_progress_label.SetLabel(self.message % 0)
140150

141151
# Ensure good sizing of frame
142152
self.m_progress.SetValue(0)
@@ -201,7 +211,10 @@ def on_idle(self, event):
201211
ratio = (float(count) / float(self.total)) if self.total else 0
202212
percent = int(ratio * 100)
203213
self.m_progress.SetValue(percent)
204-
self.m_progress_label.SetLabel(self.message % (count, self.total))
214+
if self.total:
215+
self.m_progress_label.SetLabel(self.message % (count, self.total))
216+
else:
217+
self.m_progress_label.SetLabel(self.message % count)
205218

206219
if self.thread.request is True:
207220
self.thread.request = False
@@ -226,7 +239,10 @@ def on_idle(self, event):
226239
if not self.thread.is_alive():
227240
self.processing = False
228241
self.m_progress.SetValue(100)
229-
self.m_progress_label.SetLabel(self.message % (self.thread.count, self.total))
242+
if self.total:
243+
self.m_progress_label.SetLabel(self.message % (self.thread.count, self.total))
244+
else:
245+
self.m_progress_label.SetLabel(self.message % self.thread.count)
230246

231247
if not self.thread.abort:
232248
if self.thread.errors:

rummage/lib/gui/dialogs/rummage_dialog.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from ..actions import export_csv
3737
from ..actions import fileops
3838
from ..actions import updates
39+
from wcmatch import glob
3940
try:
4041
from ..gui import GUI_PATCHED # noqa: F401
4142
except ImportError as e:
@@ -599,6 +600,7 @@ def localize(self):
599600
self.MENU_EXPORT_RESULTS = _("Export Results")
600601
self.MENU_EXPORT_SETTINGS = _("Export Settings")
601602
self.MENU_IMPORT_SETTINGS = _("Import Settings")
603+
self.MENU_DELETE_BACKUPS = _("Delete Backups")
602604
self.MENU_FILE = _("File")
603605
self.MENU_VIEW = _("View")
604606
self.MENU_HELP = _("Help")
@@ -686,6 +688,7 @@ def refresh_localization(self):
686688
self.m_export_csv_menuitem.SetItemLabel(self.MENU_CSV)
687689
self.m_export_settings_menuitem.SetItemLabel(self.MENU_EXPORT_SETTINGS)
688690
self.m_import_settings_menuitem.SetItemLabel(self.MENU_IMPORT_SETTINGS)
691+
self.m_delete_backups_menuitem.SetItemLabel(self.MENU_DELETE_BACKUPS)
689692
self.m_log_menuitem.SetItemLabel(self.MENU_OPEN_LOG)
690693
self.m_about_menuitem.SetItemLabel(self.MENU_ABOUT)
691694
self.m_update_menuitem.SetItemLabel(self.MENU_UPDATE)
@@ -2337,6 +2340,27 @@ def on_export_settings(self, event):
23372340
exporter.ShowModal()
23382341
exporter.Destroy()
23392342

2343+
def on_delete_backups(self, event):
2344+
"""Delete Backups."""
2345+
2346+
from ..dialogs.delete_dialog import DeleteDialog
2347+
2348+
if not yesno(_("Are you sure you wish to delete all backup files/folders?")):
2349+
return
2350+
2351+
target = self.m_searchin_text.GetValue()
2352+
backup_folder = bool(Settings.get_backup_type())
2353+
backup_location = Settings.get_backup_folder() if backup_folder else Settings.get_backup_ext()
2354+
if backup_folder:
2355+
pattern = f'**/{backup_location}/'
2356+
flags = glob.G
2357+
else:
2358+
pattern = f'**/*.{backup_location}'
2359+
flags = glob.G | glob.O
2360+
dlg = DeleteDialog(self, glob.iglob(pattern, flags=flags, root_dir=target), False)
2361+
dlg.ShowModal()
2362+
dlg.Destroy()
2363+
23402364
def on_import_settings(self, event):
23412365
"""Import settings."""
23422366

0 commit comments

Comments
 (0)