diff --git a/cocoa/base/ResultTable.h b/cocoa/base/ResultTable.h index 024ce097..d83d455d 100644 --- a/cocoa/base/ResultTable.h +++ b/cocoa/base/ResultTable.h @@ -13,7 +13,6 @@ http://www.hardcoded.net/licenses/bsd_license @interface ResultTable : HSTable { - NSSet *_deltaColumns; } - (id)initWithPyRef:(PyObject *)aPyRef view:(NSTableView *)aTableView; - (PyResultTable *)model; diff --git a/cocoa/base/ResultTable.m b/cocoa/base/ResultTable.m index 1e5fb643..e51eb630 100644 --- a/cocoa/base/ResultTable.m +++ b/cocoa/base/ResultTable.m @@ -20,16 +20,9 @@ http://www.hardcoded.net/licenses/bsd_license - (id)initWithPyRef:(PyObject *)aPyRef view:(NSTableView *)aTableView { self = [super initWithPyRef:aPyRef wrapperClass:[PyResultTable class] callbackClassName:@"ResultTableView" view:aTableView]; - _deltaColumns = [[NSSet setWithArray:[[self model] deltaColumns]] retain]; return self; } -- (void)dealloc -{ - [_deltaColumns release]; - [super dealloc]; -} - - (PyResultTable *)model { return (PyResultTable *)model; @@ -132,10 +125,8 @@ http://www.hardcoded.net/licenses/bsd_license color = [NSColor selectedTextColor]; } else if (isMarkable) { - if ([self deltaValuesMode]) { - if ([_deltaColumns containsObject:[column identifier]]) { - color = [NSColor orangeColor]; - } + if ([[self model] isDeltaAtRow:row column:[column identifier]]) { + color = [NSColor orangeColor]; } } else { diff --git a/cocoa/inter/result_table.py b/cocoa/inter/result_table.py index 91d36e9f..4a27205b 100644 --- a/cocoa/inter/result_table.py +++ b/cocoa/inter/result_table.py @@ -17,12 +17,13 @@ class PyResultTable(PyTable): def setDeltaValuesMode_(self, value: bool): self.model.delta_values = value - def deltaColumns(self) -> list: - return list(self.model.DELTA_COLUMNS) - def valueForRow_column_(self, row_index: int, column: str) -> object: return self.model.get_row_value(row_index, column) + def isDeltaAtRow_column_(self, row_index: int, column: str) -> bool: + row = self.model[row_index] + return row.is_cell_delta(column) + def renameSelected_(self, newname: str) -> bool: return self.model.rename_selected(newname) diff --git a/core/app.py b/core/app.py index f18bf59c..106e7f38 100644 --- a/core/app.py +++ b/core/app.py @@ -136,17 +136,25 @@ class DupeGuru(RegistrableApplication, Broadcaster): #--- Private def _get_dupe_sort_key(self, dupe, get_group, key, delta): - if key == 'percentage': - m = get_group().get_match_of(dupe) - return m.percentage - if key == 'dupe_count': - return 0 if key == 'marked': return self.results.is_marked(dupe) - r = cmp_value(dupe, key) - if delta and (key in self.result_table.DELTA_COLUMNS): - r -= cmp_value(get_group().ref, key) - return r + if key == 'percentage': + m = get_group().get_match_of(dupe) + result = m.percentage + elif key == 'dupe_count': + result = 0 + else: + result = cmp_value(dupe, key) + if delta: + refval = getattr(get_group().ref, key) + if key in self.result_table.DELTA_COLUMNS: + result -= refval + else: + # We use directly getattr() because cmp_value() does thing that we don't want to do + # when we want to determine whether two values are exactly the same. + same = getattr(dupe, key) == refval + result = (same, result) + return result def _get_group_sort_key(self, group, key): if key == 'percentage': diff --git a/core/gui/result_table.py b/core/gui/result_table.py index 3b91889c..a9862f71 100644 --- a/core/gui/result_table.py +++ b/core/gui/result_table.py @@ -21,6 +21,30 @@ class DupeRow(Row): self._dupe = dupe self._data = None self._data_delta = None + self._delta_columns = None + + def is_cell_delta(self, column_name): + """Returns whether a cell is in delta mode (orange color). + + If the result table is in delta mode, returns True if the column is one of the "delta + columns", that is, one of the columns that display a a differential value rather than an + absolute value. + + If not, returns True if the dupe's value is different from its ref value. + """ + if not self.table.delta_values: + return False + if self.isref: + return False + if self._delta_columns is None: + # table.DELTA_COLUMNS are always "delta" + self._delta_columns = self.table.DELTA_COLUMNS.copy() + dupe_info = self.data + ref_info = self._group.ref.get_display_info(group=self._group, delta=False) + for key, value in dupe_info.items(): + if ref_info[key] != value: + self._delta_columns.add(key) + return column_name in self._delta_columns @property def data(self): diff --git a/core/tests/app_test.py b/core/tests/app_test.py index f5755995..df989860 100644 --- a/core/tests/app_test.py +++ b/core/tests/app_test.py @@ -21,9 +21,6 @@ from jobprogress.job import Job from .base import DupeGuru, TestApp from .results_test import GetTestGroups from .. import app, fs, engine -from ..gui.details_panel import DetailsPanel -from ..gui.directory_tree import DirectoryTree -from ..gui.result_table import ResultTable from ..scanner import ScanType def add_fake_files_to_directories(directories, files): diff --git a/core/tests/base.py b/core/tests/base.py index 7c1f94f3..badf56a0 100644 --- a/core/tests/base.py +++ b/core/tests/base.py @@ -62,15 +62,6 @@ class DupeGuru(DupeGuruBase): def __init__(self): DupeGuruBase.__init__(self, DupeGuruView(), '/tmp') - def _get_dupe_sort_key(self, dupe, get_group, key, delta): - r = cmp_value(dupe, key) - if delta and (key in self.result_table.DELTA_COLUMNS): - r -= cmp_value(get_group().ref, key) - return r - - def _get_group_sort_key(self, group, key): - return cmp_value(group.ref, key) - def _prioritization_categories(self): return prioritize.all_categories() diff --git a/core/tests/result_table_test.py b/core/tests/result_table_test.py new file mode 100644 index 00000000..9640a433 --- /dev/null +++ b/core/tests/result_table_test.py @@ -0,0 +1,46 @@ +# Created By: Virgil Dupras +# Created On: 2013-07-28 +# Copyright 2013 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 .base import TestApp, GetTestGroups + +def app_with_results(): + app = TestApp() + objects, matches, groups = GetTestGroups() + app.app.results.groups = groups + app.rtable.refresh() + return app + +def test_delta_flags_delta_mode_off(): + app = app_with_results() + # When the delta mode is off, we never have delta values flags + app.rtable.delta_values = False + # Ref file, always false anyway + assert not app.rtable[0].is_cell_delta('size') + # False because delta mode is off + assert not app.rtable[1].is_cell_delta('size') + +def test_delta_flags_delta_mode_on_delta_columns(): + # When the delta mode is on, delta columns always have a delta flag, except for ref rows + app = app_with_results() + app.rtable.delta_values = True + # Ref file, always false anyway + assert not app.rtable[0].is_cell_delta('size') + # But for a dupe, the flag is on + assert app.rtable[1].is_cell_delta('size') + +def test_delta_flags_delta_mode_on_non_delta_columns(): + # When the delta mode is on, non-delta columns have a delta flag if their value differs from + # their ref. + app = app_with_results() + app.rtable.delta_values = True + # "bar bleh" != "foo bar", flag on + assert app.rtable[1].is_cell_delta('name') + # "ibabtu" row, but it's a ref, flag off + assert not app.rtable[3].is_cell_delta('name') + # "ibabtu" == "ibabtu", flag off + assert not app.rtable[4].is_cell_delta('name') diff --git a/help/de/results.rst b/help/de/results.rst index 8e32804a..6ea9e045 100644 --- a/help/de/results.rst +++ b/help/de/results.rst @@ -28,6 +28,8 @@ Markierung und Auswahl Ein **markiertes** Duplikat ist ein Duplikat, dessen kleine Box ein Häkchen hat. Ein **ausgewähltes** Duplikat ist hervorgehoben. Mehrfachauswahl wird in dupeGuru über den normalen Weg erreicht (Shift/Command/Steuerung Klick). Sie können die Markierung aller Duplikate umschalten, indem sie **Leertaste** drücken. +.. todo:: Add "Non-numerical delta" information. + Nur Duplikate anzeigen ---------------------- diff --git a/help/en/results.rst b/help/en/results.rst index b9272cec..93676a71 100644 --- a/help/en/results.rst +++ b/help/en/results.rst @@ -48,12 +48,29 @@ The dupeGuru results, when in normal mode, are sorted according to duplicate gro Delta Values ------------ -If you turn this switch on, some columns will display the value relative to the duplicate's reference instead of the absolute values. These delta values will also be displayed in a different color so you can spot them easily. For example, if a duplicate is 1.2 MB and its reference is 1.4 MB, the Size column will display -0.2 MB. +If you turn this switch on, numerical columns will display the value relative to the duplicate's +reference instead of the absolute values. These delta values will also be displayed in a different +color, orange, so you can spot them easily. For example, if a duplicate is 1.2 MB and its reference +is 1.4 MB, the Size column will display -0.2 MB. + +Moreover, non-numerical values will also be in orange if their value is different from their +reference, and stay black if their value is the same. Combined with column sorting in Dupes Only +mode, this allows for very powerful post-scan filtering. Dupes Only and Delta Values --------------------------- -The Dupes Only mode unveil its true power when you use it with the Delta Values switch turned on. When you turn it on, relative values will be displayed instead of absolute ones. So if, for example, you want to remove from your results all duplicates that are more than 300 KB away from their reference, you could sort the dupes only results by Size, select all duplicates under -300 in the Size column, delete them, and then do the same for duplicates over 300 at the bottom of the list. +The Dupes Only mode unveil its true power when you use it with the Delta Values switch turned on. +When you turn it on, relative values will be displayed instead of absolute ones. So if, for example, +you want to remove from your results all duplicates that are more than 300 KB away from their +reference, you could sort the dupes only results by Size, select all duplicates under -300 in the +Size column, delete them, and then do the same for duplicates over 300 at the bottom of the list. + +Same thing for non-numerical values: When Dupes Only and Delta Values are enabled at the same time, +column sorting groups rows depending on whether they're orange or not. Example: You ran a contents +scan, but you would only like to delete duplicates that have the same filename? Sort by filename +and all dupes with their filename attribute being the same as the reference will be grouped +together, their value being in black. You could also use it to change the reference priority of your duplicate list. When you make a fresh scan, if there are no reference folders, the reference file of every group is the biggest file. If @@ -61,9 +78,10 @@ you want to change that, for example, to the latest modification time, you can s results by modification time in **descending** order, select all duplicates with a modification time delta value higher than 0 and click on **Make Selected into Reference**. The reason why you must make the sort order descending is because if 2 files among the same duplicate group are selected -when you click on **Make Selected into Reference**, only the first of the list will be made reference, -the other will be ignored. And since you want the last modified file to be reference, having the -sort order descending assures you that the first item of the list will be the last modified. +when you click on **Make Selected into Reference**, only the first of the list will be made +reference, the other will be ignored. And since you want the last modified file to be reference, +having the sort order descending assures you that the first item of the list will be the last +modified. Filtering --------- diff --git a/help/fr/results.rst b/help/fr/results.rst index 26a15743..5413141d 100644 --- a/help/fr/results.rst +++ b/help/fr/results.rst @@ -54,6 +54,8 @@ Les deux modes ensemble Quand on active ces deux modes ensemble, il est alors possible de faire de la sélection de ficher assez avancée parce que le tri de fichier se fait alors en fonction des valeurs delta. Il devient alors possible de, par exemple, sélectionner tous les fichiers qui ont une différence de plus de 300 KB par rapport à leur référence, ou d'autres trucs comme ça. +.. todo:: Add "Non-numerical delta" information. + Filtrer les résultats ---------------------- diff --git a/help/hy/results.rst b/help/hy/results.rst index 644f75a1..e3ea5496 100755 --- a/help/hy/results.rst +++ b/help/hy/results.rst @@ -56,6 +56,8 @@ dupeGuru-ի արդյունքները, երբ այն նորմալ եղանակո Կարող եք նաև օգտագործել սա՝ փոխելու համար կրկնօրինակման ցանկի հղման առաջնայնությունը։ Նոր ստուգումը կատարելուց հետո, եթե չլինեն հղվող թղթապանակներ, հղվող ֆայլը ամեն խմբի կլինի ամենամեծ ֆայլը։ Եթե ցանկանում եք փոխել այն, օրինակի համար, ըստ վերջին փոփոխման ժամանակի, կարող եք դասավորել միայն սխալները արդյունքները ըստ փոփոխման ժամանակի **նվազման** կարգով, ընտրեք բոլոր կրկնօրինակները, որոնց փոփոխման դելտա ժամանակը բարձր է 0-ից և սեղմեք **Դարձնել ընտրվածը հղում**։ Պատճառը,որ դասավորում եք ըստ նվազման կարգի այն է, որ եթե 2 ֆայլերից, որոնք կընտրվեն նույն կրկնօրինակ խմբում, երբ սեղմեք **Դարձնել ընտրվածը հղում**, ապա ցանկի միայն առաջինը կդառնա հղում, մյուսները կանտեսվեն։ Եվ մինչև Դուք ցանկանաք վերջին փոփոխված ֆայլը դարձնել փոփոխված՝ ունենալով դասավորման նվազման կարգը, ապա ցանկի առաջին ֆայլը կլինի վերջին փոփոխված ֆայլը։ +.. todo:: Add "Non-numerical delta" information. + Ֆիլտրում --------- diff --git a/help/ru/results.rst b/help/ru/results.rst index 128532d6..5d7ba3f6 100755 --- a/help/ru/results.rst +++ b/help/ru/results.rst @@ -56,6 +56,8 @@ DupeGuru результаты, когда в нормальном режиме, Вы можете также использовать его для изменения ссылки приоритет повторяющиеся список. Когда вы делаете свежие сканирования, если Есть нет ссылки папки, ссылке на файл каждой группы является самой большой файл. Если вы хотите изменить, что, например, в последней модификации время, вы можете отсортировать дубликаты только результаты по времени модификации в **убывания** порядке выберите все дубликаты со временем изменения дельты значение больше 0 и нажмите **Убедитесь, выбранной ссылки**. Причина, почему вы должны сделать порядок сортировки по убыванию, потому что если 2 файла среди таких же дубликат группы выбираются при нажатии на **Сделать выбранной ссылки**, только первый из списка будут сделаны ссылки, другие будут проигнорированы . И так как вы хотите Последнее изменение файла для ссылки, имеющие порядок сортировки по убыванию уверяет вас, что первым пунктом в списке будет последнего изменения. +.. todo:: Add "Non-numerical delta" information. + Фильтрация ---------- diff --git a/help/uk/results.rst b/help/uk/results.rst index 65e4ebeb..ea29fc8c 100755 --- a/help/uk/results.rst +++ b/help/uk/results.rst @@ -56,6 +56,8 @@ DupeGuru результати, коли в нормальному режимі, Ви можете також використовувати його для зміни посилання пріоритет повторювані список. Коли ви робите свіжі сканування, якщо Є немає посилання папки, заслання на файл кожної групи є найбільшою файл. Якщо ви хочете змінити, що, наприклад, в останній модифікації час, ви можете відсортувати дублікати тільки результати за часом модифікації в **убування порядку** , виберіть всі дублікати з часом зміни дельти значення більше 0 і натисніть **Переконайтеся, обраної посилання**. Причина, чому ви повинні зробити порядок сортування за спаданням, тому що якщо 2 файли серед таких же дублікат групи вибираються при натисканні на **Зробити вибраної посилання**, тільки перший із списку будуть зроблені посилання, інші будуть проігноровані . І так як ви хочете Остання зміна файлу для посилання, які мають порядок сортування за спаданням запевняє вас, що першим пунктом у списку буде останньої зміни. +.. todo:: Add "Non-numerical delta" information. + Фільтрація ----------- diff --git a/qt/base/results_model.py b/qt/base/results_model.py index 8214438e..24380853 100644 --- a/qt/base/results_model.py +++ b/qt/base/results_model.py @@ -31,7 +31,7 @@ class ResultsModel(Table): elif role == Qt.ForegroundRole: if row.isref: return QBrush(Qt.blue) - elif self.model.delta_values and column.name in self.model.DELTA_COLUMNS: + elif row.is_cell_delta(column.name): return QBrush(QColor(255, 142, 40)) # orange elif role == Qt.FontRole: isBold = row.isref