mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-22 14:41:39 +00:00
Began the transition to a HSOutline based result outline. There's still a lot of glitches, the most glaring one being the lack of support for multiple selection.
This commit is contained in:
11
core/app.py
11
core/app.py
@@ -104,6 +104,11 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
||||
path = Path(str_path)
|
||||
return fs.get_file(path, self.directories.fileclasses)
|
||||
|
||||
def _job_completed(self, jobid):
|
||||
# Must be called by subclasses when they detect that an async job is completed.
|
||||
if jobid in (JOB_SCAN, JOB_LOAD, JOB_MOVE, JOB_DELETE):
|
||||
self.notify('results_changed')
|
||||
|
||||
@staticmethod
|
||||
def _open_path(path):
|
||||
raise NotImplementedError()
|
||||
@@ -233,6 +238,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
||||
if g not in changed_groups:
|
||||
self.results.make_ref(dupe)
|
||||
changed_groups.add(g)
|
||||
self.notify('results_changed')
|
||||
|
||||
def open_selected(self):
|
||||
if self.selected_dupes:
|
||||
@@ -283,6 +289,11 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
||||
self.results.groups = []
|
||||
self._start_job(JOB_SCAN, do)
|
||||
|
||||
def toggle_selected_mark_state(self):
|
||||
for dupe in self.selected_dupes:
|
||||
self.results.mark_toggle(dupe)
|
||||
self.notify('results_changed')
|
||||
|
||||
def without_ref(self, dupes):
|
||||
return [dupe for dupe in dupes if self.results.get_group_of_duplicate(dupe).ref is not dupe]
|
||||
|
||||
|
||||
@@ -162,57 +162,3 @@ class DupeGuru(app.DupeGuru):
|
||||
def sort_groups(self,key,asc):
|
||||
self.results.sort_groups(key,asc)
|
||||
|
||||
def ToggleSelectedMarkState(self):
|
||||
for dupe in self.selected_dupes:
|
||||
self.results.mark_toggle(dupe)
|
||||
|
||||
#---Data
|
||||
def GetOutlineViewMaxLevel(self, tag):
|
||||
if tag == 0:
|
||||
return 2
|
||||
elif tag == 2:
|
||||
return 1
|
||||
|
||||
def GetOutlineViewChildCounts(self, tag, node_path):
|
||||
if self.progress._job_running:
|
||||
return []
|
||||
if tag == 0: #Normal results
|
||||
assert not node_path # no other value is possible
|
||||
return [len(g.dupes) for g in self.results.groups]
|
||||
else: #Power Marker
|
||||
assert not node_path # no other value is possible
|
||||
return [0 for d in self.results.dupes]
|
||||
|
||||
def GetOutlineViewValues(self, tag, node_path):
|
||||
if self.progress._job_running:
|
||||
return
|
||||
if not node_path:
|
||||
return
|
||||
if tag in (0,2): #Normal results / Power Marker
|
||||
if tag == 0:
|
||||
g, d = self.GetObjects(node_path)
|
||||
if (d is None) and (g is not None):
|
||||
d = g.ref
|
||||
else:
|
||||
d = self.results.dupes[node_path[0]]
|
||||
g = self.results.get_group_of_duplicate(d)
|
||||
result = self._get_display_info(d, g, self.display_delta_values)
|
||||
return result
|
||||
|
||||
def GetOutlineViewMarked(self, tag, node_path):
|
||||
# 0=unmarked 1=marked 2=unmarkable
|
||||
if self.progress._job_running:
|
||||
return
|
||||
if not node_path:
|
||||
return 2
|
||||
if tag == 0: #Normal results
|
||||
g, d = self.GetObjects(node_path)
|
||||
else: #Power Marker
|
||||
d = self.results.dupes[node_path[0]]
|
||||
if (d is None) or (not self.results.is_markable(d)):
|
||||
return 2
|
||||
elif self.results.is_marked(d):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ from hsutil.cocoa.inter import signature, PyOutline, PyGUIObject, PyRegistrable
|
||||
|
||||
from .gui.details_panel import DetailsPanel
|
||||
from .gui.directory_tree import DirectoryTree
|
||||
from .gui.result_tree import ResultTree
|
||||
|
||||
# Fix py2app's problems on relative imports
|
||||
from core import app, app_cocoa, data, directories, engine, export, ignore, results, fs, scanner
|
||||
@@ -55,7 +56,7 @@ class PyDupeGuruBase(PyRegistrable):
|
||||
self.py.PurgeIgnoreList()
|
||||
|
||||
def toggleSelectedMark(self):
|
||||
self.py.ToggleSelectedMarkState()
|
||||
self.py.toggle_selected_mark_state()
|
||||
|
||||
def saveIgnoreList(self):
|
||||
self.py.save_ignore_list()
|
||||
@@ -126,21 +127,6 @@ class PyDupeGuruBase(PyRegistrable):
|
||||
def getOperationalErrorCount(self):
|
||||
return self.py.last_op_error_count
|
||||
|
||||
#---Data
|
||||
@signature('i@:i')
|
||||
def getOutlineViewMaxLevel_(self, tag):
|
||||
return self.py.GetOutlineViewMaxLevel(tag)
|
||||
|
||||
@signature('@@:i@')
|
||||
def getOutlineView_childCountsForPath_(self, tag, node_path):
|
||||
return self.py.GetOutlineViewChildCounts(tag, node_path)
|
||||
|
||||
def getOutlineView_valuesForIndexes_(self, tag, node_path):
|
||||
return self.py.GetOutlineViewValues(tag, node_path)
|
||||
|
||||
def getOutlineView_markedAtIndexes_(self, tag, node_path):
|
||||
return self.py.GetOutlineViewMarked(tag, node_path)
|
||||
|
||||
#---Properties
|
||||
def setMixFileKind_(self, mix_file_kind):
|
||||
self.py.scanner.mix_file_kind = mix_file_kind
|
||||
@@ -164,6 +150,9 @@ class PyDupeGuruBase(PyRegistrable):
|
||||
def cancelJob(self):
|
||||
self.py.progress.job_cancelled = True
|
||||
|
||||
def jobCompleted_(self, jobid):
|
||||
self.py._job_completed(jobid)
|
||||
|
||||
|
||||
class PyDetailsPanel(PyGUIObject):
|
||||
py_class = DetailsPanel
|
||||
@@ -182,3 +171,25 @@ class PyDirectoryOutline(PyOutline):
|
||||
def addDirectory_(self, path):
|
||||
self.py.add_directory(path)
|
||||
|
||||
|
||||
class PyResultOutline(PyOutline):
|
||||
py_class = ResultTree
|
||||
|
||||
@signature('v@:c')
|
||||
def setPowerMarkerMode_(self, value):
|
||||
self.py.power_marker = value
|
||||
|
||||
@signature('@@:@i')
|
||||
def valueForPath_column_(self, path, column):
|
||||
return self.py.get_node_value(path, column)
|
||||
|
||||
@signature('c@:@')
|
||||
def renameSelected_(self, newname):
|
||||
return self.py.app.RenameSelected(newname)
|
||||
|
||||
def sortBy_ascending_(self, key, asc):
|
||||
self.py.sort(key, asc)
|
||||
|
||||
def markSelected(self):
|
||||
self.py.app.toggle_selected_mark_state()
|
||||
|
||||
|
||||
@@ -21,3 +21,6 @@ class GUIObject(Listener):
|
||||
def dupes_selected(self):
|
||||
pass
|
||||
|
||||
def results_changed(self):
|
||||
pass
|
||||
|
||||
|
||||
98
core/gui/result_tree.py
Normal file
98
core/gui/result_tree.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2010-02-11
|
||||
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "HS" 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/hs_license
|
||||
|
||||
from hsgui.tree import Tree, Node
|
||||
|
||||
from .base import GUIObject
|
||||
|
||||
class DupeNode(Node):
|
||||
def __init__(self, app, group, dupe):
|
||||
Node.__init__(self, '')
|
||||
self._app = app
|
||||
self._group = group
|
||||
self._dupe = dupe
|
||||
self.data = app._get_display_info(dupe, group, False)
|
||||
self.data_delta = app._get_display_info(dupe, group, True)
|
||||
|
||||
@property
|
||||
def markable(self):
|
||||
return self._app.results.is_markable(self._dupe)
|
||||
|
||||
@property
|
||||
def marked(self):
|
||||
return self._app.results.is_marked(self._dupe)
|
||||
|
||||
|
||||
class ResultTree(GUIObject, Tree):
|
||||
def __init__(self, view, app):
|
||||
GUIObject.__init__(self, view, app)
|
||||
Tree.__init__(self)
|
||||
self._power_marker = False
|
||||
self.connect()
|
||||
self._refresh()
|
||||
self.view.refresh()
|
||||
|
||||
def _refresh(self):
|
||||
self.clear()
|
||||
if not self.power_marker:
|
||||
for group in self.app.results.groups:
|
||||
group_node = DupeNode(self.app, group, group.ref)
|
||||
self.append(group_node)
|
||||
for dupe in group.dupes:
|
||||
group_node.append(DupeNode(self.app, 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 = self.app.selected_dupes[0]
|
||||
node = self.find(lambda n: n is not self and n._dupe is to_find)
|
||||
self.selected = node
|
||||
|
||||
def get_node_value(self, path, column):
|
||||
try:
|
||||
node = self.get_node(path)
|
||||
except IndexError:
|
||||
return '---'
|
||||
if self.app.display_delta_values:
|
||||
return node.data_delta[column]
|
||||
else:
|
||||
return node.data[column]
|
||||
|
||||
def sort(self, key, asc):
|
||||
if self.power_marker:
|
||||
self.app.sort_dupes(key, asc)
|
||||
else:
|
||||
self.app.sort_groups(key, asc)
|
||||
|
||||
@property
|
||||
def power_marker(self):
|
||||
return self._power_marker
|
||||
|
||||
@power_marker.setter
|
||||
def power_marker(self, value):
|
||||
if value == self._power_marker:
|
||||
return
|
||||
self._power_marker = value
|
||||
self._refresh()
|
||||
self.view.refresh()
|
||||
|
||||
@Tree.selected.setter
|
||||
def selected(self, node):
|
||||
self._selected = node
|
||||
if node is None:
|
||||
self.app._select_dupes([])
|
||||
else:
|
||||
self.app._select_dupes([node._dupe])
|
||||
|
||||
#--- Event Handlers
|
||||
def results_changed(self):
|
||||
self._refresh()
|
||||
self.view.refresh()
|
||||
|
||||
@@ -243,16 +243,16 @@ class TCDupeGuru(TestCase):
|
||||
def test_toggleSelectedMark(self):
|
||||
app = self.app
|
||||
objects = self.objects
|
||||
app.ToggleSelectedMarkState()
|
||||
self.assertEqual(0,app.results.mark_count)
|
||||
app.toggle_selected_mark_state()
|
||||
eq_(app.results.mark_count, 0)
|
||||
app.SelectPowerMarkerNodePaths(r2np([0,2]))
|
||||
app.ToggleSelectedMarkState()
|
||||
self.assertEqual(2,app.results.mark_count)
|
||||
self.assert_(not app.results.is_marked(objects[0]))
|
||||
self.assert_(app.results.is_marked(objects[1]))
|
||||
self.assert_(not app.results.is_marked(objects[2]))
|
||||
self.assert_(not app.results.is_marked(objects[3]))
|
||||
self.assert_(app.results.is_marked(objects[4]))
|
||||
app.toggle_selected_mark_state()
|
||||
eq_(app.results.mark_count, 2)
|
||||
assert not app.results.is_marked(objects[0])
|
||||
assert app.results.is_marked(objects[1])
|
||||
assert not app.results.is_marked(objects[2])
|
||||
assert not app.results.is_marked(objects[3])
|
||||
assert app.results.is_marked(objects[4])
|
||||
|
||||
def test_refreshDetailsWithSelected(self):
|
||||
self.app.SelectPowerMarkerNodePaths(r2np([0,2]))
|
||||
|
||||
Reference in New Issue
Block a user