mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-22 14:41:39 +00:00
Added an Ignore List dialog.
This commit is contained in:
16
core/app.py
16
core/app.py
@@ -26,6 +26,7 @@ from hscommon.trans import tr
|
||||
from . import directories, results, scanner, export, fs
|
||||
from .gui.details_panel import DetailsPanel
|
||||
from .gui.directory_tree import DirectoryTree
|
||||
from .gui.ignore_list_dialog import IgnoreListDialog
|
||||
from .gui.problem_dialog import ProblemDialog
|
||||
from .gui.stats_label import StatsLabel
|
||||
|
||||
@@ -114,12 +115,12 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
||||
self.details_panel = DetailsPanel(self)
|
||||
self.directory_tree = DirectoryTree(self)
|
||||
self.problem_dialog = ProblemDialog(self)
|
||||
self.ignore_list_dialog = IgnoreListDialog(self)
|
||||
self.stats_label = StatsLabel(self)
|
||||
self.result_table = self._create_result_table()
|
||||
children = [self.result_table, self.directory_tree, self.stats_label, self.details_panel]
|
||||
for child in children:
|
||||
child.connect()
|
||||
# subclasses must create and connect self.result_table
|
||||
|
||||
#--- Virtual
|
||||
def _get_display_info(self, dupe, group, delta):
|
||||
@@ -257,6 +258,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
||||
if other is not dupe:
|
||||
self.scanner.ignore_list.Ignore(str(other.path), str(dupe.path))
|
||||
self.remove_duplicates(dupes)
|
||||
self.ignore_list_dialog.refresh()
|
||||
|
||||
def apply_filter(self, filter):
|
||||
self.results.apply_filter(None)
|
||||
@@ -271,16 +273,6 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
||||
while delete_if_empty(path, ['.DS_Store']):
|
||||
path = path[:-1]
|
||||
|
||||
def clear_ignore_list(self):
|
||||
if not self.scanner.ignore_list:
|
||||
msg = tr("The ignore list is already empty. Nothing to clear.")
|
||||
self.view.show_message(msg)
|
||||
return
|
||||
msg = tr("Do you really want to remove all %d items from the ignore list?") % len(self.scanner.ignore_list)
|
||||
if self.view.ask_yes_no(msg):
|
||||
self.scanner.ignore_list.Clear()
|
||||
self.view.show_message(tr("Ignore list cleared."))
|
||||
|
||||
def copy_or_move(self, dupe, copy: bool, destination: str, dest_type: DestType):
|
||||
source_path = dupe.path
|
||||
location_path = first(p for p in self.directories if dupe.path in p)
|
||||
@@ -398,6 +390,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
||||
self.notify('directories_changed')
|
||||
p = op.join(self.appdata, 'ignore_list.xml')
|
||||
self.scanner.ignore_list.load_from_xml(p)
|
||||
self.ignore_list_dialog.refresh()
|
||||
|
||||
def load_from(self, filename):
|
||||
def do(j):
|
||||
@@ -439,6 +432,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
||||
|
||||
def purge_ignore_list(self):
|
||||
self.scanner.ignore_list.Filter(lambda f,s:op.exists(f) and op.exists(s))
|
||||
self.ignore_list_dialog.refresh()
|
||||
|
||||
def remove_directories(self, indexes):
|
||||
try:
|
||||
|
||||
39
core/gui/ignore_list_dialog.py
Normal file
39
core/gui/ignore_list_dialog.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# Created On: 2012/03/13
|
||||
# Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
from hscommon.trans import tr
|
||||
from .ignore_list_table import IgnoreListTable
|
||||
|
||||
class IgnoreListDialog:
|
||||
#--- View interface
|
||||
# show()
|
||||
#
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.ignore_list = self.app.scanner.ignore_list
|
||||
self.ignore_list_table = IgnoreListTable(self)
|
||||
|
||||
def clear(self):
|
||||
if not self.ignore_list:
|
||||
return
|
||||
msg = tr("Do you really want to remove all %d items from the ignore list?") % len(self.ignore_list)
|
||||
if self.app.view.ask_yes_no(msg):
|
||||
self.ignore_list.Clear()
|
||||
self.refresh()
|
||||
|
||||
def refresh(self):
|
||||
self.ignore_list_table.refresh()
|
||||
|
||||
def remove_selected(self):
|
||||
for row in self.ignore_list_table.selected_rows:
|
||||
self.ignore_list.remove(row.path1_original, row.path2_original)
|
||||
self.refresh()
|
||||
|
||||
def show(self):
|
||||
self.view.show()
|
||||
|
||||
41
core/gui/ignore_list_table.py
Normal file
41
core/gui/ignore_list_table.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2012-03-13
|
||||
# Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
from hscommon.gui.table import GUITable, Row
|
||||
from hscommon.gui.column import Column, Columns
|
||||
from hscommon.trans import trget
|
||||
|
||||
coltr = trget('columns')
|
||||
|
||||
class IgnoreListTable(GUITable):
|
||||
COLUMNS = [
|
||||
# the str concat below saves us needless localization.
|
||||
Column('path1', coltr("File Path") + " 1"),
|
||||
Column('path2', coltr("File Path") + " 2"),
|
||||
]
|
||||
|
||||
def __init__(self, ignore_list_dialog):
|
||||
GUITable.__init__(self)
|
||||
self.columns = Columns(self)
|
||||
self.view = None
|
||||
self.dialog = ignore_list_dialog
|
||||
|
||||
#--- Override
|
||||
def _fill(self):
|
||||
for path1, path2 in self.dialog.ignore_list:
|
||||
self.append(IgnoreListRow(self, path1, path2))
|
||||
|
||||
|
||||
class IgnoreListRow(Row):
|
||||
def __init__(self, table, path1, path2):
|
||||
Row.__init__(self, table)
|
||||
self.path1_original = path1
|
||||
self.path2_original = path2
|
||||
self.path1 = str(path1)
|
||||
self.path2 = str(path2)
|
||||
|
||||
@@ -71,6 +71,25 @@ class IgnoreList:
|
||||
self._ignored[first] = matches
|
||||
self._count += 1
|
||||
|
||||
def remove(self, first, second):
|
||||
def inner(first, second):
|
||||
try:
|
||||
matches = self._ignored[first]
|
||||
if second in matches:
|
||||
matches.discard(second)
|
||||
if not matches:
|
||||
del self._ignored[first]
|
||||
self._count -= 1
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
if not inner(first, second):
|
||||
if not inner(second, first):
|
||||
raise ValueError()
|
||||
|
||||
def load_from_xml(self, infile):
|
||||
"""Loads the ignore list from a XML created with save_to_xml.
|
||||
|
||||
|
||||
@@ -152,6 +152,8 @@ class TestApp(TestAppBase):
|
||||
link_gui(self.pdialog.category_list)
|
||||
link_gui(self.pdialog.criteria_list)
|
||||
link_gui(self.pdialog.prioritization_list)
|
||||
link_gui(self.app.ignore_list_dialog)
|
||||
link_gui(self.app.ignore_list_dialog.ignore_list_table)
|
||||
|
||||
#--- Helpers
|
||||
def select_pri_criterion(self, name):
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import io
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
from pytest import raises
|
||||
from hscommon.testutil import eq_
|
||||
|
||||
from ..ignore import *
|
||||
@@ -147,3 +148,18 @@ def test_nonzero():
|
||||
assert not il
|
||||
il.Ignore('foo','bar')
|
||||
assert il
|
||||
|
||||
def test_remove():
|
||||
il = IgnoreList()
|
||||
il.Ignore('foo', 'bar')
|
||||
il.Ignore('foo', 'baz')
|
||||
il.remove('bar', 'foo')
|
||||
eq_(len(il), 1)
|
||||
assert not il.AreIgnored('foo', 'bar')
|
||||
|
||||
def test_remove_non_existant():
|
||||
il = IgnoreList()
|
||||
il.Ignore('foo', 'bar')
|
||||
il.Ignore('foo', 'baz')
|
||||
with raises(ValueError):
|
||||
il.remove('foo', 'bleh')
|
||||
|
||||
Reference in New Issue
Block a user