1
0
mirror of https://github.com/arsenetar/dupeguru.git synced 2026-01-22 06:37:17 +00:00

Converted the result tree into a result table.

--HG--
rename : cocoa/base/PyResultTree.h => cocoa/base/PyResultTable.h
rename : cocoa/base/ResultOutline.h => cocoa/base/ResultTable.h
rename : cocoa/base/ResultOutline.m => cocoa/base/ResultTable.m
rename : core/gui/result_tree.py => core/gui/result_table.py
This commit is contained in:
Virgil Dupras
2010-09-24 15:48:59 +02:00
parent 9bd093a03c
commit 0d8ed92a68
15 changed files with 566 additions and 698 deletions

View File

@@ -15,7 +15,7 @@ from .gui.details_panel import DetailsPanel
from .gui.directory_tree import DirectoryTree
from .gui.problem_dialog import ProblemDialog
from .gui.problem_table import ProblemTable
from .gui.result_tree import ResultTree
from .gui.result_table import ResultTable
from .gui.stats_label import StatsLabel
# Fix py2app's problems on relative imports
@@ -163,8 +163,8 @@ class PyDirectoryOutline(PyOutline):
self.py.add_directory(path)
class PyResultOutline(PyOutline):
py_class = ResultTree
class PyResultTable(PyTable):
py_class = ResultTable
@signature('c@:')
def powerMarkerMode(self):
@@ -182,9 +182,9 @@ class PyResultOutline(PyOutline):
def setDeltaValuesMode_(self, value):
self.py.delta_values = value
@signature('@@:@i')
def valueForPath_column_(self, path, column):
return self.py.get_node_value(path, column)
@signature('@@:ii')
def valueForRow_column_(self, row_index, column):
return self.py.get_row_value(row_index, column)
@signature('c@:@')
def renameSelected_(self, newname):
@@ -200,8 +200,9 @@ class PyResultOutline(PyOutline):
def removeSelected(self):
self.py.app.remove_selected()
def rootChildrenCounts(self):
return self.py.root_children_counts()
@signature('i@:')
def selectedDupeCount(self):
return self.py.selected_dupe_count
# python --> cocoa
def invalidate_markings(self):

View File

@@ -9,14 +9,14 @@
from operator import attrgetter
from hsgui.tree import Tree, Node
from hsgui.table import GUITable, Row
from .base import GUIObject
class DupeNode(Node):
def __init__(self, app, group, dupe):
Node.__init__(self, '')
self._app = app
class DupeRow(Row):
def __init__(self, table, group, dupe):
Row.__init__(self, table)
self._app = table.app
self._group = group
self._dupe = dupe
self._data = None
@@ -34,6 +34,10 @@ class DupeNode(Node):
self._data_delta = self._app._get_display_info(self._dupe, self._group, True)
return self._data_delta
@property
def isref(self):
return self._dupe is self._group.ref
@property
def markable(self):
return self._app.results.is_markable(self._dupe)
@@ -47,10 +51,10 @@ class DupeNode(Node):
self._app.mark_dupe(self._dupe, value)
class ResultTree(GUIObject, Tree):
class ResultTable(GUIObject, GUITable):
def __init__(self, view, app):
GUIObject.__init__(self, view, app)
Tree.__init__(self)
GUITable.__init__(self)
self._power_marker = False
self._delta_values = False
self._sort_descriptors = (0, True)
@@ -58,60 +62,54 @@ class ResultTree(GUIObject, Tree):
#--- Override
def connect(self):
GUIObject.connect(self)
self._refresh()
self.refresh()
self.view.refresh()
def _select_nodes(self, nodes):
Tree._select_nodes(self, nodes)
self.app._select_dupes(list(map(attrgetter('_dupe'), nodes)))
def _restore_selection(self, previous_selection):
if self.app.selected_dupes:
to_find = set(self.app.selected_dupes)
indexes = [i for i, r in enumerate(self) if r._dupe in to_find]
self.selected_indexes = indexes
#--- Private
def _refresh(self):
self.clear()
def _update_selection(self):
rows = self.selected_rows
self.app._select_dupes(list(map(attrgetter('_dupe'), rows)))
def _fill(self):
if not self.power_marker:
for group in self.app.results.groups:
group_node = DupeNode(self.app, group, group.ref)
self.append(group_node)
self.append(DupeRow(self, group, group.ref))
for dupe in group.dupes:
group_node.append(DupeNode(self.app, group, dupe))
self.append(DupeRow(self, group, dupe))
else:
for dupe in self.app.results.dupes:
group = self.app.results.get_group_of_duplicate(dupe)
self.append(DupeNode(self.app, group, dupe))
if self.app.selected_dupes:
to_find = set(self.app.selected_dupes)
nodes = list(self.findall(lambda n: n is not self and n._dupe in to_find))
self.selected_nodes = nodes
self.append(DupeRow(self, group, dupe))
#--- Public
def get_node_value(self, path, column):
def get_row_value(self, index, column):
try:
node = self.get_node(path)
row = self[index]
except IndexError:
return '---'
if self.delta_values:
return node.data_delta[column]
return row.data_delta[column]
else:
return node.data[column]
return row.data[column]
def rename_selected(self, newname):
node = self.selected_node
node._data = None
node._data_delta = None
row = self.selected_row
row._data = None
row._data_delta = None
return self.app.rename_selected(newname)
def root_children_counts(self):
# This is a speed optimization for cases where there's a lot of results so that there is
# not thousands of children_count queries when expandAll is called.
return [len(node) for node in self]
def sort(self, key, asc):
if self.power_marker:
self.app.results.sort_dupes(key, asc, self.delta_values)
else:
self.app.results.sort_groups(key, asc)
self._sort_descriptors = (key, asc)
self._refresh()
self.refresh()
self.view.refresh()
#--- Properties
@@ -126,7 +124,7 @@ class ResultTree(GUIObject, Tree):
self._power_marker = value
key, asc = self._sort_descriptors
self.sort(key, asc)
self._refresh()
self.refresh()
self.view.refresh()
@property
@@ -138,22 +136,26 @@ class ResultTree(GUIObject, Tree):
if value == self._delta_values:
return
self._delta_values = value
self._refresh()
self.refresh()
self.view.refresh()
@property
def selected_dupe_count(self):
return sum(1 for row in self.selected_rows if not row.isref)
#--- Event Handlers
def marking_changed(self):
self.view.invalidate_markings()
def results_changed(self):
self._refresh()
self.refresh()
self.view.refresh()
def results_changed_but_keep_selection(self):
# What we want to to here is that instead of restoring selected *dupes* after refresh, we
# restore selected *paths*.
paths = self.selected_paths
self._refresh()
self.selected_paths = paths
indexes = self.selected_indexes
self.refresh()
self.select(indexes)
self.view.refresh()

View File

@@ -23,7 +23,7 @@ from .. import app, fs, engine
from ..app import DupeGuru as DupeGuruBase
from ..gui.details_panel import DetailsPanel
from ..gui.directory_tree import DirectoryTree
from ..gui.result_tree import ResultTree
from ..gui.result_table import ResultTable
class DupeGuru(DupeGuruBase):
def __init__(self):
@@ -166,11 +166,11 @@ class TCDupeGuruWithResults(TestCase):
self.dpanel = DetailsPanel(self.dpanel_gui, self.app)
self.dtree_gui = CallLogger()
self.dtree = DirectoryTree(self.dtree_gui, self.app)
self.rtree_gui = CallLogger()
self.rtree = ResultTree(self.rtree_gui, self.app)
self.rtable_gui = CallLogger()
self.rtable = ResultTable(self.rtable_gui, self.app)
self.dpanel.connect()
self.dtree.connect()
self.rtree.connect()
self.rtable.connect()
tmppath = self.tmppath()
io.mkdir(tmppath + 'foo')
io.mkdir(tmppath + 'bar')
@@ -217,42 +217,35 @@ class TCDupeGuruWithResults(TestCase):
def test_GetObjects(self):
objects = self.objects
groups = self.groups
n = self.rtree.get_node([0])
assert n._group is groups[0]
assert n._dupe is objects[0]
n = self.rtree.get_node([0, 0])
assert n._group is groups[0]
assert n._dupe is objects[1]
n = self.rtree.get_node([1, 0])
assert n._group is groups[1]
assert n._dupe is objects[4]
r = self.rtable[0]
assert r._group is groups[0]
assert r._dupe is objects[0]
r = self.rtable[1]
assert r._group is groups[0]
assert r._dupe is objects[1]
r = self.rtable[4]
assert r._group is groups[1]
assert r._dupe is objects[4]
def test_GetObjects_after_sort(self):
objects = self.objects
groups = self.groups[:] # we need an un-sorted reference
self.rtree.sort(0, False) #0 = Filename
n = self.rtree.get_node([0, 0])
assert n._group is groups[1]
assert n._dupe is objects[4]
def test_selected_result_node_paths(self):
# app.selected_dupes is correctly converted into node paths
paths = [[0, 0], [0, 1], [1]]
self.rtree.selected_paths = paths
eq_(self.rtree.selected_paths, paths)
self.rtable.sort(0, False) #0 = Filename
r = self.rtable[1]
assert r._group is groups[1]
assert r._dupe is objects[4]
def test_selected_result_node_paths_after_deletion(self):
# cases where the selected dupes aren't there are correctly handled
paths = [[0, 0], [0, 1], [1]]
self.rtree.selected_paths = paths
self.rtable.select([1, 2, 3])
self.app.remove_selected()
# The first 2 dupes have been removed. The 3rd one is a ref. it stays there, in first pos.
eq_(self.rtree.selected_paths, [[0, 0]]) # no exception
eq_(self.rtable.selected_indexes, [1]) # no exception
def test_selectResultNodePaths(self):
app = self.app
objects = self.objects
self.rtree.selected_paths = [[0, 0], [0, 1]]
self.rtable.select([1, 2])
eq_(len(app.selected_dupes), 2)
assert app.selected_dupes[0] is objects[1]
assert app.selected_dupes[1] is objects[2]
@@ -260,7 +253,7 @@ class TCDupeGuruWithResults(TestCase):
def test_selectResultNodePaths_with_ref(self):
app = self.app
objects = self.objects
self.rtree.selected_paths = [[0, 0], [0, 1], [1]]
self.rtable.select([1, 2, 3])
eq_(len(app.selected_dupes), 3)
assert app.selected_dupes[0] is objects[1]
assert app.selected_dupes[1] is objects[2]
@@ -270,9 +263,9 @@ class TCDupeGuruWithResults(TestCase):
app = self.app
objects = self.objects
groups = self.groups[:] #To keep the old order in memory
self.rtree.sort(0, False) #0 = Filename
self.rtable.sort(0, False) #0 = Filename
#Now, the group order is supposed to be reversed
self.rtree.selected_paths = [[0, 0], [1], [1, 0]]
self.rtable.select([1, 2, 3])
eq_(len(app.selected_dupes), 3)
assert app.selected_dupes[0] is objects[4]
assert app.selected_dupes[1] is groups[0].ref
@@ -282,39 +275,26 @@ class TCDupeGuruWithResults(TestCase):
# app.selected_dupes is correctly converted into paths
app = self.app
objects = self.objects
self.rtree.power_marker = True
self.rtree.selected_paths = [[0], [1], [2]]
self.rtree.power_marker = False
eq_(self.rtree.selected_paths, [[0, 0], [0, 1], [1, 0]])
self.rtable.power_marker = True
self.rtable.select([0, 1, 2])
self.rtable.power_marker = False
eq_(self.rtable.selected_indexes, [1, 2, 4])
def test_selected_powermarker_node_paths_after_deletion(self):
# cases where the selected dupes aren't there are correctly handled
app = self.app
objects = self.objects
self.rtree.power_marker = True
self.rtree.selected_paths = [[0], [1], [2]]
self.rtable.power_marker = True
self.rtable.select([0, 1, 2])
app.remove_selected()
eq_(self.rtree.selected_paths, []) # no exception
def test_selectPowerMarkerRows(self):
app = self.app
objects = self.objects
self.rtree.selected_paths = [[0, 0], [0, 1], [1, 0]]
eq_(len(app.selected_dupes), 3)
assert app.selected_dupes[0] is objects[1]
assert app.selected_dupes[1] is objects[2]
assert app.selected_dupes[2] is objects[4]
def test_selectPowerMarkerRows_empty(self):
self.rtree.selected_paths = []
eq_(len(self.app.selected_dupes), 0)
eq_(self.rtable.selected_indexes, []) # no exception
def test_selectPowerMarkerRows_after_sort(self):
app = self.app
objects = self.objects
self.rtree.power_marker = True
self.rtree.sort(0, False) #0 = Filename
self.rtree.selected_paths = [[0], [1], [2]]
self.rtable.power_marker = True
self.rtable.sort(0, False) #0 = Filename
self.rtable.select([0, 1, 2])
eq_(len(app.selected_dupes), 3)
assert app.selected_dupes[0] is objects[4]
assert app.selected_dupes[1] is objects[2]
@@ -325,7 +305,7 @@ class TCDupeGuruWithResults(TestCase):
objects = self.objects
app.toggle_selected_mark_state()
eq_(app.results.mark_count, 0)
self.rtree.selected_paths = [[0, 0], [1, 0]]
self.rtable.select([1, 4])
app.toggle_selected_mark_state()
eq_(app.results.mark_count, 2)
assert not app.results.is_marked(objects[0])
@@ -335,10 +315,10 @@ class TCDupeGuruWithResults(TestCase):
assert app.results.is_marked(objects[4])
def test_refreshDetailsWithSelected(self):
self.rtree.selected_paths = [[0, 0], [1, 0]]
self.rtable.select([1, 4])
eq_(self.dpanel.row(0), ('Filename', 'bar bleh', 'foo bar'))
self.check_gui_calls(self.dpanel_gui, ['refresh'])
self.rtree.selected_paths = []
self.rtable.select([])
eq_(self.dpanel.row(0), ('Filename', '---', '---'))
self.check_gui_calls(self.dpanel_gui, ['refresh'])
@@ -346,7 +326,7 @@ class TCDupeGuruWithResults(TestCase):
app = self.app
objects = self.objects
groups = self.groups
self.rtree.selected_paths = [[0, 0], [1, 0]]
self.rtable.select([1, 4])
app.make_selected_reference()
assert groups[0].ref is objects[1]
assert groups[1].ref is objects[4]
@@ -355,7 +335,7 @@ class TCDupeGuruWithResults(TestCase):
app = self.app
objects = self.objects
groups = self.groups
self.rtree.selected_paths = [[0, 0], [0, 1], [1, 0]]
self.rtable.select([1, 2, 4])
#Only [0, 0] and [1, 0] must go ref, not [0, 1] because it is a part of the same group
app.make_selected_reference()
assert groups[0].ref is objects[1]
@@ -363,7 +343,7 @@ class TCDupeGuruWithResults(TestCase):
def test_removeSelected(self):
app = self.app
self.rtree.selected_paths = [[0, 0], [1, 0]]
self.rtable.select([1, 4])
app.remove_selected()
eq_(len(app.results.dupes), 1) # the first path is now selected
app.remove_selected()
@@ -386,10 +366,10 @@ class TCDupeGuruWithResults(TestCase):
def test_ignore(self):
app = self.app
self.rtree.selected_path = [1, 0] #The dupe of the second, 2 sized group
self.rtable.select([4]) #The dupe of the second, 2 sized group
app.add_selected_to_ignore_list()
eq_(len(app.scanner.ignore_list), 1)
self.rtree.selected_path = [0, 0] #first dupe of the 3 dupes group
self.rtable.select([1]) #first dupe of the 3 dupes group
app.add_selected_to_ignore_list()
#BOTH the ref and the other dupe should have been added
eq_(len(app.scanner.ignore_list), 3)
@@ -416,7 +396,7 @@ class TCDupeGuruWithResults(TestCase):
app = self.app
app.scanner.ignore_list.Ignore = FakeIgnore
self.rtree.selected_path = [1, 0]
self.rtable.select([4])
app.add_selected_to_ignore_list()
@@ -440,14 +420,14 @@ class TCDupeGuru_renameSelected(TestCase):
self.groups = groups
self.p = p
self.files = files
self.rtree_gui = CallLogger()
self.rtree = ResultTree(self.rtree_gui, self.app)
self.rtree.connect()
self.rtable_gui = CallLogger()
self.rtable = ResultTable(self.rtable_gui, self.app)
self.rtable.connect()
def test_simple(self):
app = self.app
g = self.groups[0]
self.rtree.selected_path = [0, 0]
self.rtable.select([1])
assert app.rename_selected('renamed')
names = io.listdir(self.p)
assert 'renamed' in names
@@ -457,7 +437,7 @@ class TCDupeGuru_renameSelected(TestCase):
def test_none_selected(self):
app = self.app
g = self.groups[0]
self.rtree.selected_paths = []
self.rtable.select([])
self.mock(logging, 'warning', log_calls(lambda msg: None))
assert not app.rename_selected('renamed')
msg = logging.warning.calls[0]['msg']
@@ -470,7 +450,7 @@ class TCDupeGuru_renameSelected(TestCase):
def test_name_already_exists(self):
app = self.app
g = self.groups[0]
self.rtree.selected_path = [0, 0]
self.rtable.select([1])
self.mock(logging, 'warning', log_calls(lambda msg: None))
assert not app.rename_selected('foo bar 1')
msg = logging.warning.calls[0]['msg']