mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-25 16:11:39 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b182585d46 | ||
|
|
e8f92535d3 | ||
|
|
d62c3663e9 | ||
|
|
6b0bfda9fb | ||
|
|
7477330961 | ||
|
|
1f71157063 | ||
|
|
905988c592 | ||
|
|
310951bfa8 | ||
|
|
64c1087856 | ||
|
|
cab6d924aa | ||
|
|
c3a972d39b | ||
|
|
33d44d4d24 | ||
|
|
fd89cf2482 | ||
|
|
112ffb981f | ||
|
|
514426b980 | ||
|
|
a4bf1c8be6 | ||
|
|
9b82e1478f | ||
|
|
d5f145d57e | ||
|
|
bab891ee74 | ||
|
|
a65fd7d0d0 | ||
|
|
46836cc805 | ||
|
|
42559f13d8 | ||
|
|
87351b5920 | ||
|
|
e68dcf189c | ||
|
|
5d62b8389c | ||
|
|
c50aebe76d | ||
|
|
a610f3fde7 | ||
|
|
626391a1d9 | ||
|
|
1bedfe75ea |
2
.hgtags
2
.hgtags
@@ -9,3 +9,5 @@ cbcf9c80fee4c908ef2efbf1c143c9e47676c9b2 pe1.8.0
|
|||||||
0e923897a3389331d4ab3debbc40b8dd616199d9 pe1.8.1
|
0e923897a3389331d4ab3debbc40b8dd616199d9 pe1.8.1
|
||||||
2c454eca9ebe93b6cf34916068f828a6a39e3eaf me5.7.1
|
2c454eca9ebe93b6cf34916068f828a6a39e3eaf me5.7.1
|
||||||
19e40bab20521d4256acf325dba9b32e95e135c5 pe1.8.2
|
19e40bab20521d4256acf325dba9b32e95e135c5 pe1.8.2
|
||||||
|
7b7c5a66ebee4e4b8125330d24fe9ce1a070ff25 se2.9.2
|
||||||
|
1cef6d39855f85d4be728646bc78b860e6d4e398 pe1.8.3
|
||||||
|
|||||||
@@ -8,11 +8,6 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
/* ResultsChangedNotification happens on major changes, which requires a complete reload of the data*/
|
|
||||||
#define ResultsChangedNotification @"ResultsChangedNotification"
|
|
||||||
/* ResultsChangedNotification happens on minor changes, which requires buffer flush*/
|
|
||||||
#define ResultsUpdatedNotification @"ResultsUpdatedNotification"
|
|
||||||
#define ResultsMarkingChangedNotification @"ResultsMarkingChangedNotification"
|
|
||||||
#define RegistrationRequired @"RegistrationRequired"
|
#define RegistrationRequired @"RegistrationRequired"
|
||||||
#define JobStarted @"JobStarted"
|
#define JobStarted @"JobStarted"
|
||||||
#define JobInProgress @"JobInProgress"
|
#define JobInProgress @"JobInProgress"
|
||||||
|
|||||||
@@ -14,9 +14,16 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
self = [super initWithNibName:@"DetailsPanel" pyClassName:@"PyDetailsPanel" pyParent:aPy];
|
self = [super initWithNibName:@"DetailsPanel" pyClassName:@"PyDetailsPanel" pyParent:aPy];
|
||||||
[self window]; //So the detailsTable is initialized.
|
[self window]; //So the detailsTable is initialized.
|
||||||
|
[self connect];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[self disconnect];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
- (PyDetailsPanel *)py
|
- (PyDetailsPanel *)py
|
||||||
{
|
{
|
||||||
return (PyDetailsPanel *)py;
|
return (PyDetailsPanel *)py;
|
||||||
|
|||||||
@@ -13,9 +13,16 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
self = [super initWithPyClassName:@"PyDirectoryOutline" pyParent:aPyParent view:aOutlineView];
|
self = [super initWithPyClassName:@"PyDirectoryOutline" pyParent:aPyParent view:aOutlineView];
|
||||||
[outlineView registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
|
[outlineView registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
|
||||||
|
[self connect];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[self disconnect];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
- (PyDirectoryOutline *)py
|
- (PyDirectoryOutline *)py
|
||||||
{
|
{
|
||||||
return (PyDirectoryOutline *)py;
|
return (PyDirectoryOutline *)py;
|
||||||
|
|||||||
@@ -23,27 +23,17 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
- (NSNumber *)doScan;
|
- (NSNumber *)doScan;
|
||||||
|
|
||||||
- (NSArray *)selectedPowerMarkerNodePaths;
|
|
||||||
- (void)selectPowerMarkerNodePaths:(NSArray *)aIndexPaths;
|
|
||||||
- (NSArray *)selectedResultNodePaths;
|
|
||||||
- (void)selectResultNodePaths:(NSArray *)aIndexPaths;
|
|
||||||
|
|
||||||
- (void)toggleSelectedMark;
|
- (void)toggleSelectedMark;
|
||||||
- (void)markAll;
|
- (void)markAll;
|
||||||
- (void)markInvert;
|
- (void)markInvert;
|
||||||
- (void)markNone;
|
- (void)markNone;
|
||||||
|
|
||||||
- (void)addSelectedToIgnoreList;
|
- (void)addSelectedToIgnoreList;
|
||||||
- (void)removeSelected;
|
|
||||||
- (void)openSelected;
|
- (void)openSelected;
|
||||||
- (NSNumber *)renameSelected:(NSString *)aNewName;
|
|
||||||
- (void)revealSelected;
|
- (void)revealSelected;
|
||||||
- (void)makeSelectedReference;
|
- (void)makeSelectedReference;
|
||||||
- (void)applyFilter:(NSString *)filter;
|
- (void)applyFilter:(NSString *)filter;
|
||||||
|
|
||||||
- (void)sortGroupsBy:(NSNumber *)aIdentifier ascending:(NSNumber *)aAscending;
|
|
||||||
- (void)sortDupesBy:(NSNumber *)aIdentifier ascending:(NSNumber *)aAscending;
|
|
||||||
|
|
||||||
- (void)copyOrMove:(NSNumber *)aCopy markedTo:(NSString *)destination recreatePath:(NSNumber *)aRecreateType;
|
- (void)copyOrMove:(NSNumber *)aCopy markedTo:(NSString *)destination recreatePath:(NSNumber *)aRecreateType;
|
||||||
- (void)deleteMarked;
|
- (void)deleteMarked;
|
||||||
- (void)removeMarked;
|
- (void)removeMarked;
|
||||||
@@ -51,13 +41,11 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
//Data
|
//Data
|
||||||
- (NSNumber *)getIgnoreListCount;
|
- (NSNumber *)getIgnoreListCount;
|
||||||
- (NSNumber *)getMarkCount;
|
- (NSNumber *)getMarkCount;
|
||||||
- (NSString *)getStatLine;
|
|
||||||
- (NSNumber *)getOperationalErrorCount;
|
- (NSNumber *)getOperationalErrorCount;
|
||||||
|
|
||||||
//Scanning options
|
//Scanning options
|
||||||
- (void)setMinMatchPercentage:(NSNumber *)percentage;
|
- (void)setMinMatchPercentage:(NSNumber *)percentage;
|
||||||
- (void)setMixFileKind:(NSNumber *)mix_file_kind;
|
- (void)setMixFileKind:(NSNumber *)mix_file_kind;
|
||||||
- (void)setDisplayDeltaValues:(NSNumber *)display_delta_values;
|
|
||||||
- (void)setEscapeFilterRegexp:(NSNumber *)escape_filter_regexp;
|
- (void)setEscapeFilterRegexp:(NSNumber *)escape_filter_regexp;
|
||||||
- (void)setRemoveEmptyFolders:(NSNumber *)remove_empty_folders;
|
- (void)setRemoveEmptyFolders:(NSNumber *)remove_empty_folders;
|
||||||
- (void)setSizeThreshold:(NSInteger)size_threshold;
|
- (void)setSizeThreshold:(NSInteger)size_threshold;
|
||||||
|
|||||||
24
cocoa/base/PyResultTree.h
Normal file
24
cocoa/base/PyResultTree.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "PyOutline.h"
|
||||||
|
|
||||||
|
@interface PyResultTree : PyOutline
|
||||||
|
- (BOOL)powerMarkerMode;
|
||||||
|
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode;
|
||||||
|
- (BOOL)deltaValuesMode;
|
||||||
|
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode;
|
||||||
|
|
||||||
|
- (NSString *)valueForPath:(NSArray *)aPath column:(NSInteger)aColumn;
|
||||||
|
- (BOOL)renameSelected:(NSString *)aNewName;
|
||||||
|
- (void)sortBy:(NSInteger)aIdentifier ascending:(BOOL)aAscending;
|
||||||
|
- (void)markSelected;
|
||||||
|
- (void)removeSelected;
|
||||||
|
- (NSArray *)rootChildrenCounts;
|
||||||
|
@end
|
||||||
14
cocoa/base/PyStatsLabel.h
Normal file
14
cocoa/base/PyStatsLabel.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "PyGUI.h"
|
||||||
|
|
||||||
|
@interface PyStatsLabel : PyGUI
|
||||||
|
- (NSString *)display;
|
||||||
|
@end
|
||||||
26
cocoa/base/ResultOutline.h
Normal file
26
cocoa/base/ResultOutline.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "HSOutline.h"
|
||||||
|
#import "PyResultTree.h"
|
||||||
|
|
||||||
|
@interface ResultOutline : HSOutline
|
||||||
|
{
|
||||||
|
NSIndexSet *_deltaColumns;
|
||||||
|
NSArray *_rootChildrenCounts;
|
||||||
|
}
|
||||||
|
- (PyResultTree *)py;
|
||||||
|
- (BOOL)powerMarkerMode;
|
||||||
|
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode;
|
||||||
|
- (BOOL)deltaValuesMode;
|
||||||
|
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode;
|
||||||
|
- (void)setDeltaColumns:(NSIndexSet *)aDeltaColumns;
|
||||||
|
- (NSInteger)selectedDupeCount;
|
||||||
|
- (void)removeSelected;
|
||||||
|
@end;
|
||||||
207
cocoa/base/ResultOutline.m
Normal file
207
cocoa/base/ResultOutline.m
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "ResultOutline.h"
|
||||||
|
#import "Dialogs.h"
|
||||||
|
#import "Utils.h"
|
||||||
|
#import "Consts.h"
|
||||||
|
|
||||||
|
@implementation ResultOutline
|
||||||
|
- (id)initWithPyParent:(id)aPyParent view:(HSOutlineView *)aOutlineView
|
||||||
|
{
|
||||||
|
self = [super initWithPyClassName:@"PyResultOutline" pyParent:aPyParent view:aOutlineView];
|
||||||
|
_rootChildrenCounts = nil;
|
||||||
|
[self connect];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[self disconnect];
|
||||||
|
[_deltaColumns release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (PyResultTree *)py
|
||||||
|
{
|
||||||
|
return (PyResultTree *)py;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Public */
|
||||||
|
- (BOOL)powerMarkerMode
|
||||||
|
{
|
||||||
|
return [[self py] powerMarkerMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode
|
||||||
|
{
|
||||||
|
[[self py] setPowerMarkerMode:aPowerMarkerMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)deltaValuesMode
|
||||||
|
{
|
||||||
|
return [[self py] deltaValuesMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode
|
||||||
|
{
|
||||||
|
[[self py] setDeltaValuesMode:aDeltaValuesMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDeltaColumns:(NSIndexSet *)aDeltaColumns
|
||||||
|
{
|
||||||
|
[_deltaColumns release];
|
||||||
|
_deltaColumns = [aDeltaColumns retain];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)selectedDupeCount
|
||||||
|
{
|
||||||
|
NSArray *selected = [self selectedIndexPaths];
|
||||||
|
if ([self powerMarkerMode]) {
|
||||||
|
return [selected count];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NSInteger r = 0;
|
||||||
|
for (NSIndexPath *path in selected) {
|
||||||
|
if ([path length] == 2) {
|
||||||
|
r++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeSelected
|
||||||
|
{
|
||||||
|
NSInteger selectedDupeCount = [self selectedDupeCount];
|
||||||
|
if (!selectedDupeCount)
|
||||||
|
return;
|
||||||
|
NSString *msg = [NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",selectedDupeCount];
|
||||||
|
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) // NO
|
||||||
|
return;
|
||||||
|
[[self py] removeSelected];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Datasource */
|
||||||
|
- (NSInteger)outlineView:(NSOutlineView *)aOutlineView numberOfChildrenOfItem:(id)item
|
||||||
|
{
|
||||||
|
NSIndexPath *path = item;
|
||||||
|
if ((path != nil) && ([path length] == 1)) {
|
||||||
|
if (_rootChildrenCounts == nil) {
|
||||||
|
_rootChildrenCounts = [[[self py] rootChildrenCounts] retain];
|
||||||
|
}
|
||||||
|
NSInteger index = [path indexAtPosition:0];
|
||||||
|
return n2i([_rootChildrenCounts objectAtIndex:index]);
|
||||||
|
}
|
||||||
|
return [super outlineView:aOutlineView numberOfChildrenOfItem:item];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)column byItem:(id)item
|
||||||
|
{
|
||||||
|
NSIndexPath *path = item;
|
||||||
|
NSString *identifier = [column identifier];
|
||||||
|
if ([identifier isEqual:@"marked"]) {
|
||||||
|
return b2n([self boolProperty:@"marked" valueAtPath:path]);
|
||||||
|
}
|
||||||
|
NSInteger columnId = [identifier integerValue];
|
||||||
|
return [[self py] valueForPath:p2a(path) column:columnId];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)outlineView:(NSOutlineView *)aOutlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
|
||||||
|
{
|
||||||
|
if ([[tableColumn identifier] isEqual:@"0"]) {
|
||||||
|
NSIndexPath *path = item;
|
||||||
|
NSString *oldName = [[self py] valueForPath:p2a(path) column:0];
|
||||||
|
NSString *newName = object;
|
||||||
|
if (![newName isEqual:oldName]) {
|
||||||
|
BOOL renamed = [[self py] renameSelected:newName];
|
||||||
|
if (!renamed) {
|
||||||
|
[Dialogs showMessage:[NSString stringWithFormat:@"The name '%@' already exists.", newName]];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[self refreshItemAtPath:path];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[super outlineView:aOutlineView setObjectValue:object forTableColumn:tableColumn byItem:item];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delegate */
|
||||||
|
- (void)outlineView:(NSOutlineView *)aOutlineView didClickTableColumn:(NSTableColumn *)tableColumn
|
||||||
|
{
|
||||||
|
if ([[outlineView sortDescriptors] count] < 1)
|
||||||
|
return;
|
||||||
|
NSSortDescriptor *sd = [[outlineView sortDescriptors] objectAtIndex:0];
|
||||||
|
[[self py] sortBy:[[sd key] integerValue] ascending:[sd ascending]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
|
||||||
|
{
|
||||||
|
NSIndexPath *path = item;
|
||||||
|
BOOL isMarkable = [self boolProperty:@"markable" valueAtPath:path];
|
||||||
|
if ([[tableColumn identifier] isEqual:@"marked"]) {
|
||||||
|
[cell setEnabled:isMarkable];
|
||||||
|
}
|
||||||
|
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
|
||||||
|
// Determine if the text color will be blue due to directory being reference.
|
||||||
|
NSTextFieldCell *textCell = cell;
|
||||||
|
if (isMarkable) {
|
||||||
|
[textCell setTextColor:[NSColor blackColor]];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[textCell setTextColor:[NSColor blueColor]];
|
||||||
|
}
|
||||||
|
if (([self deltaValuesMode]) && ([self powerMarkerMode] || ([path length] > 1))) {
|
||||||
|
NSInteger i = [[tableColumn identifier] integerValue];
|
||||||
|
if ([_deltaColumns containsIndex:i]) {
|
||||||
|
[textCell setTextColor:[NSColor orangeColor]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)tableViewHadDeletePressed:(NSTableView *)tableView
|
||||||
|
{
|
||||||
|
[self removeSelected];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)tableViewHadSpacePressed:(NSTableView *)tableView
|
||||||
|
{
|
||||||
|
[[self py] markSelected];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* don't calls saveEdits and cancelEdits */
|
||||||
|
- (void)outlineViewDidEndEditing:(HSOutlineView *)outlineView
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)outlineViewCancelsEdition:(HSOutlineView *)outlineView
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Python --> Cocoa */
|
||||||
|
- (void)refresh /* Override */
|
||||||
|
{
|
||||||
|
[_rootChildrenCounts release];
|
||||||
|
_rootChildrenCounts = nil;
|
||||||
|
[super refresh];
|
||||||
|
[outlineView expandItem:nil expandChildren:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)invalidateMarkings
|
||||||
|
{
|
||||||
|
for (NSMutableDictionary *props in [itemData objectEnumerator]) {
|
||||||
|
[props removeObjectForKey:@"marked"];
|
||||||
|
}
|
||||||
|
[outlineView setNeedsDisplay:YES];
|
||||||
|
}
|
||||||
|
@end
|
||||||
@@ -7,43 +7,34 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "Outline.h"
|
#import "HSOutlineView.h"
|
||||||
|
#import "StatsLabel.h"
|
||||||
|
#import "ResultOutline.h"
|
||||||
#import "PyDupeGuru.h"
|
#import "PyDupeGuru.h"
|
||||||
|
|
||||||
@interface MatchesView : OutlineView
|
|
||||||
- (void)keyDown:(NSEvent *)theEvent;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface ResultWindowBase : NSWindowController
|
@interface ResultWindowBase : NSWindowController
|
||||||
{
|
{
|
||||||
@protected
|
@protected
|
||||||
IBOutlet PyDupeGuruBase *py;
|
IBOutlet PyDupeGuruBase *py;
|
||||||
IBOutlet id app;
|
IBOutlet id app;
|
||||||
IBOutlet NSSegmentedControl *deltaSwitch;
|
IBOutlet NSSegmentedControl *deltaSwitch;
|
||||||
IBOutlet MatchesView *matches;
|
IBOutlet HSOutlineView *matches;
|
||||||
IBOutlet NSSegmentedControl *pmSwitch;
|
IBOutlet NSSegmentedControl *pmSwitch;
|
||||||
IBOutlet NSTextField *stats;
|
IBOutlet NSTextField *stats;
|
||||||
IBOutlet NSMenu *columnsMenu;
|
IBOutlet NSMenu *columnsMenu;
|
||||||
IBOutlet NSSearchField *filterField;
|
IBOutlet NSSearchField *filterField;
|
||||||
|
|
||||||
BOOL _powerMode;
|
|
||||||
BOOL _displayDelta;
|
|
||||||
NSMutableArray *_resultColumns;
|
NSMutableArray *_resultColumns;
|
||||||
NSMutableIndexSet *_deltaColumns;
|
|
||||||
NSWindowController *preferencesPanel;
|
NSWindowController *preferencesPanel;
|
||||||
|
ResultOutline *outline;
|
||||||
|
StatsLabel *statsLabel;
|
||||||
}
|
}
|
||||||
/* Helpers */
|
/* Helpers */
|
||||||
- (void)fillColumnsMenu;
|
- (void)fillColumnsMenu;
|
||||||
- (NSTableColumn *)getColumnForIdentifier:(NSInteger)aIdentifier title:(NSString *)aTitle width:(NSInteger)aWidth refCol:(NSTableColumn *)aColumn;
|
- (NSTableColumn *)getColumnForIdentifier:(NSInteger)aIdentifier title:(NSString *)aTitle width:(NSInteger)aWidth refCol:(NSTableColumn *)aColumn;
|
||||||
- (NSArray *)getColumnsOrder;
|
- (NSArray *)getColumnsOrder;
|
||||||
- (NSDictionary *)getColumnsWidth;
|
- (NSDictionary *)getColumnsWidth;
|
||||||
- (NSArray *)getSelected:(BOOL)aDupesOnly;
|
|
||||||
- (NSArray *)getSelectedPaths:(BOOL)aDupesOnly;
|
|
||||||
- (void)initResultColumns;
|
- (void)initResultColumns;
|
||||||
- (void)updatePySelection;
|
|
||||||
- (void)performPySelection:(NSArray *)aIndexPaths;
|
|
||||||
- (void)refreshStats;
|
|
||||||
- (void)reloadMatches;
|
|
||||||
- (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth;
|
- (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth;
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
@@ -59,7 +50,6 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
- (IBAction)markInvert:(id)sender;
|
- (IBAction)markInvert:(id)sender;
|
||||||
- (IBAction)markNone:(id)sender;
|
- (IBAction)markNone:(id)sender;
|
||||||
- (IBAction)markSelected:(id)sender;
|
- (IBAction)markSelected:(id)sender;
|
||||||
- (IBAction)markToggle:(id)sender;
|
|
||||||
- (IBAction)moveMarked:(id)sender;
|
- (IBAction)moveMarked:(id)sender;
|
||||||
- (IBAction)openClicked:(id)sender;
|
- (IBAction)openClicked:(id)sender;
|
||||||
- (IBAction)openSelected:(id)sender;
|
- (IBAction)openSelected:(id)sender;
|
||||||
@@ -69,6 +59,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
- (IBAction)resetColumnsToDefault:(id)sender;
|
- (IBAction)resetColumnsToDefault:(id)sender;
|
||||||
- (IBAction)revealSelected:(id)sender;
|
- (IBAction)revealSelected:(id)sender;
|
||||||
- (IBAction)showPreferencesPanel:(id)sender;
|
- (IBAction)showPreferencesPanel:(id)sender;
|
||||||
|
- (IBAction)startDuplicateScan:(id)sender;
|
||||||
- (IBAction)switchSelected:(id)sender;
|
- (IBAction)switchSelected:(id)sender;
|
||||||
- (IBAction)toggleColumn:(id)sender;
|
- (IBAction)toggleColumn:(id)sender;
|
||||||
- (IBAction)toggleDelta:(id)sender;
|
- (IBAction)toggleDelta:(id)sender;
|
||||||
|
|||||||
@@ -14,66 +14,29 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
#import "Consts.h"
|
#import "Consts.h"
|
||||||
|
|
||||||
@implementation MatchesView
|
|
||||||
- (void)keyDown:(NSEvent *)theEvent
|
|
||||||
{
|
|
||||||
unichar key = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
|
|
||||||
// get flags and strip the lower 16 (device dependant) bits
|
|
||||||
NSUInteger flags = ( [theEvent modifierFlags] & 0x00FF );
|
|
||||||
if (((key == NSDeleteFunctionKey) || (key == NSDeleteCharacter)) && (flags == 0))
|
|
||||||
[self sendAction:@selector(removeSelected:) to:[self delegate]];
|
|
||||||
else
|
|
||||||
if ((key == 0x20) && (flags == 0)) // Space
|
|
||||||
[self sendAction:@selector(markSelected:) to:[self delegate]];
|
|
||||||
else
|
|
||||||
[super keyDown:theEvent];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
|
|
||||||
{
|
|
||||||
if (![[tableColumn identifier] isEqual:@"0"])
|
|
||||||
return; //We only want to cover renames.
|
|
||||||
OVNode *node = item;
|
|
||||||
NSString *oldName = [[node buffer] objectAtIndex:0];
|
|
||||||
NSString *newName = object;
|
|
||||||
if (![newName isEqual:oldName])
|
|
||||||
{
|
|
||||||
BOOL renamed = n2b([(PyDupeGuruBase *)py renameSelected:newName]);
|
|
||||||
if (renamed)
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
|
|
||||||
else
|
|
||||||
[Dialogs showMessage:[NSString stringWithFormat:@"The name '%@' already exists.",newName]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation ResultWindowBase
|
@implementation ResultWindowBase
|
||||||
- (void)awakeFromNib
|
- (void)awakeFromNib
|
||||||
{
|
{
|
||||||
_displayDelta = NO;
|
|
||||||
_powerMode = NO;
|
|
||||||
[self window];
|
[self window];
|
||||||
preferencesPanel = [[NSWindowController alloc] initWithWindowNibName:@"Preferences"];
|
preferencesPanel = [[NSWindowController alloc] initWithWindowNibName:@"Preferences"];
|
||||||
|
outline = [[ResultOutline alloc] initWithPyParent:py view:matches];
|
||||||
|
statsLabel = [[StatsLabel alloc] initWithPyParent:py labelView:stats];
|
||||||
[self initResultColumns];
|
[self initResultColumns];
|
||||||
[self fillColumnsMenu];
|
[self fillColumnsMenu];
|
||||||
[deltaSwitch setSelectedSegment:0];
|
[deltaSwitch setSelectedSegment:0];
|
||||||
[pmSwitch setSelectedSegment:0];
|
[pmSwitch setSelectedSegment:0];
|
||||||
[py setDisplayDeltaValues:b2n(_displayDelta)];
|
|
||||||
[matches setTarget:self];
|
[matches setTarget:self];
|
||||||
[matches setDoubleAction:@selector(openClicked:)];
|
[matches setDoubleAction:@selector(openClicked:)];
|
||||||
[self refreshStats];
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registrationRequired:) name:RegistrationRequired object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registrationRequired:) name:RegistrationRequired object:nil];
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobCompleted:) name:JobCompletedNotification object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobCompleted:) name:JobCompletedNotification object:nil];
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobStarted:) name:JobStarted object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobStarted:) name:JobStarted object:nil];
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobInProgress:) name:JobInProgress object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobInProgress:) name:JobInProgress object:nil];
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsMarkingChanged:) name:ResultsMarkingChangedNotification object:nil];
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsChanged:) name:ResultsChangedNotification object:nil];
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsUpdated:) name:ResultsUpdatedNotification object:nil];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
|
[outline release];
|
||||||
[preferencesPanel release];
|
[preferencesPanel release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
@@ -130,34 +93,6 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *)getSelected:(BOOL)aDupesOnly
|
|
||||||
{
|
|
||||||
if (_powerMode)
|
|
||||||
aDupesOnly = NO;
|
|
||||||
NSIndexSet *indexes = [matches selectedRowIndexes];
|
|
||||||
NSMutableArray *nodeList = [NSMutableArray array];
|
|
||||||
OVNode *node;
|
|
||||||
NSInteger i = [indexes firstIndex];
|
|
||||||
while (i != NSNotFound)
|
|
||||||
{
|
|
||||||
node = [matches itemAtRow:i];
|
|
||||||
if (!aDupesOnly || ([node level] > 1))
|
|
||||||
[nodeList addObject:node];
|
|
||||||
i = [indexes indexGreaterThanIndex:i];
|
|
||||||
}
|
|
||||||
return nodeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSArray *)getSelectedPaths:(BOOL)aDupesOnly
|
|
||||||
{
|
|
||||||
NSMutableArray *r = [NSMutableArray array];
|
|
||||||
NSArray *selected = [self getSelected:aDupesOnly];
|
|
||||||
for (OVNode *node in selected) {
|
|
||||||
[r addObject:p2a([node indexPath])];
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)initResultColumns
|
- (void)initResultColumns
|
||||||
{
|
{
|
||||||
// Virtual
|
// Virtual
|
||||||
@@ -172,12 +107,13 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
}
|
}
|
||||||
//Add columns and set widths
|
//Add columns and set widths
|
||||||
for (NSString *colId in aColumnsOrder) {
|
for (NSString *colId in aColumnsOrder) {
|
||||||
if ([colId isEqual:@"mark"]) {
|
NSInteger colIndex = [colId integerValue];
|
||||||
|
if ((colIndex == 0) && (![colId isEqual:@"0"])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
NSTableColumn *col = [_resultColumns objectAtIndex:[colId intValue]];
|
NSTableColumn *col = [_resultColumns objectAtIndex:colIndex];
|
||||||
NSNumber *width = [aColumnsWidth objectForKey:[col identifier]];
|
NSNumber *width = [aColumnsWidth objectForKey:[col identifier]];
|
||||||
NSMenuItem *mi = [columnsMenu itemWithTag:[colId intValue]];
|
NSMenuItem *mi = [columnsMenu itemWithTag:colIndex];
|
||||||
if (width) {
|
if (width) {
|
||||||
[col setWidth:[width floatValue]];
|
[col setWidth:[width floatValue]];
|
||||||
}
|
}
|
||||||
@@ -185,43 +121,6 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updatePySelection
|
|
||||||
{
|
|
||||||
NSArray *selection;
|
|
||||||
if (_powerMode) {
|
|
||||||
selection = [py selectedPowerMarkerNodePaths];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
selection = [py selectedResultNodePaths];
|
|
||||||
}
|
|
||||||
[matches selectNodePaths:selection];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)performPySelection:(NSArray *)aIndexPaths
|
|
||||||
{
|
|
||||||
if (_powerMode) {
|
|
||||||
[py selectPowerMarkerNodePaths:aIndexPaths];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[py selectResultNodePaths:aIndexPaths];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)refreshStats
|
|
||||||
{
|
|
||||||
[stats setStringValue:[py getStatLine]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reload the matches outline and restore selection from py */
|
|
||||||
- (void)reloadMatches
|
|
||||||
{
|
|
||||||
[matches setDelegate:nil];
|
|
||||||
[matches reloadData];
|
|
||||||
[matches expandItem:nil expandChildren:YES];
|
|
||||||
[matches setDelegate:self];
|
|
||||||
[self updatePySelection];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
- (IBAction)clearIgnoreList:(id)sender
|
- (IBAction)clearIgnoreList:(id)sender
|
||||||
{
|
{
|
||||||
@@ -235,21 +134,12 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
- (IBAction)changeDelta:(id)sender
|
- (IBAction)changeDelta:(id)sender
|
||||||
{
|
{
|
||||||
_displayDelta = [deltaSwitch selectedSegment] == 1;
|
[outline setDeltaValuesMode:[deltaSwitch selectedSegment] == 1];
|
||||||
[py setDisplayDeltaValues:b2n(_displayDelta)];
|
|
||||||
[self reloadMatches];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)changePowerMarker:(id)sender
|
- (IBAction)changePowerMarker:(id)sender
|
||||||
{
|
{
|
||||||
_powerMode = [pmSwitch selectedSegment] == 1;
|
[outline setPowerMarkerMode:[pmSwitch selectedSegment] == 1];
|
||||||
if (_powerMode)
|
|
||||||
[matches setTag:2];
|
|
||||||
else
|
|
||||||
[matches setTag:0];
|
|
||||||
[matches expandItem:nil expandChildren:YES];
|
|
||||||
[self outlineView:matches didClickTableColumn:nil];
|
|
||||||
[self updatePySelection];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)copyMarked:(id)sender
|
- (IBAction)copyMarked:(id)sender
|
||||||
@@ -294,52 +184,37 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
[py setEscapeFilterRegexp:b2n(!n2b([ud objectForKey:@"useRegexpFilter"]))];
|
[py setEscapeFilterRegexp:b2n(!n2b([ud objectForKey:@"useRegexpFilter"]))];
|
||||||
[py applyFilter:[filterField stringValue]];
|
[py applyFilter:[filterField stringValue]];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)ignoreSelected:(id)sender
|
- (IBAction)ignoreSelected:(id)sender
|
||||||
{
|
{
|
||||||
NSArray *nodeList = [self getSelected:YES];
|
NSInteger selectedDupeCount = [outline selectedDupeCount];
|
||||||
if (![nodeList count])
|
if (!selectedDupeCount)
|
||||||
return;
|
return;
|
||||||
NSString *msg = [NSString stringWithFormat:@"All selected %d matches are going to be ignored in all subsequent scans. Continue?",[nodeList count]];
|
NSString *msg = [NSString stringWithFormat:@"All selected %d matches are going to be ignored in all subsequent scans. Continue?",selectedDupeCount];
|
||||||
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) // NO
|
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) // NO
|
||||||
return;
|
return;
|
||||||
[py addSelectedToIgnoreList];
|
[py addSelectedToIgnoreList];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)markAll:(id)sender
|
- (IBAction)markAll:(id)sender
|
||||||
{
|
{
|
||||||
[py markAll];
|
[py markAll];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)markInvert:(id)sender
|
- (IBAction)markInvert:(id)sender
|
||||||
{
|
{
|
||||||
[py markInvert];
|
[py markInvert];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)markNone:(id)sender
|
- (IBAction)markNone:(id)sender
|
||||||
{
|
{
|
||||||
[py markNone];
|
[py markNone];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)markSelected:(id)sender
|
- (IBAction)markSelected:(id)sender
|
||||||
{
|
{
|
||||||
[self performPySelection:[self getSelectedPaths:YES]];
|
|
||||||
[py toggleSelectedMark];
|
[py toggleSelectedMark];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)markToggle:(id)sender
|
|
||||||
{
|
|
||||||
OVNode *node = [matches itemAtRow:[matches clickedRow]];
|
|
||||||
[self performPySelection:[NSArray arrayWithObject:p2a([node indexPath])]];
|
|
||||||
[py toggleSelectedMark];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)moveMarked:(id)sender
|
- (IBAction)moveMarked:(id)sender
|
||||||
@@ -373,15 +248,9 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
- (IBAction)openSelected:(id)sender
|
- (IBAction)openSelected:(id)sender
|
||||||
{
|
{
|
||||||
[self performPySelection:[self getSelectedPaths:NO]];
|
|
||||||
[py openSelected];
|
[py openSelected];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)refresh:(id)sender
|
|
||||||
{
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)removeMarked:(id)sender
|
- (IBAction)removeMarked:(id)sender
|
||||||
{
|
{
|
||||||
int mark_count = [[py getMarkCount] intValue];
|
int mark_count = [[py getMarkCount] intValue];
|
||||||
@@ -390,19 +259,11 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
|
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
|
||||||
return;
|
return;
|
||||||
[py removeMarked];
|
[py removeMarked];
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)removeSelected:(id)sender
|
- (IBAction)removeSelected:(id)sender
|
||||||
{
|
{
|
||||||
NSArray *nodeList = [self getSelected:YES];
|
[outline removeSelected];
|
||||||
if (![nodeList count])
|
|
||||||
return;
|
|
||||||
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",[nodeList count]]] == NSAlertSecondButtonReturn) // NO
|
|
||||||
return;
|
|
||||||
[self performPySelection:[self getSelectedPaths:YES]];
|
|
||||||
[py removeSelected];
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)renameSelected:(id)sender
|
- (IBAction)renameSelected:(id)sender
|
||||||
@@ -419,7 +280,6 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
- (IBAction)revealSelected:(id)sender
|
- (IBAction)revealSelected:(id)sender
|
||||||
{
|
{
|
||||||
[self performPySelection:[self getSelectedPaths:NO]];
|
|
||||||
[py revealSelected];
|
[py revealSelected];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,21 +288,14 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[preferencesPanel showWindow:sender];
|
[preferencesPanel showWindow:sender];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (IBAction)startDuplicateScan:(id)sender
|
||||||
|
{
|
||||||
|
// Virtual
|
||||||
|
}
|
||||||
|
|
||||||
- (IBAction)switchSelected:(id)sender
|
- (IBAction)switchSelected:(id)sender
|
||||||
{
|
{
|
||||||
// It might look like a complicated way to get the length of the current dupe list on the py side
|
|
||||||
// but after a lot of fussing around, believe it or not, it actually is.
|
|
||||||
NSInteger matchesTag = _powerMode ? 2 : 0;
|
|
||||||
NSInteger startLen = [[py getOutlineView:matchesTag childCountsForPath:[NSArray array]] count];
|
|
||||||
[py makeSelectedReference];
|
[py makeSelectedReference];
|
||||||
[self performPySelection:[self getSelectedPaths:NO]];
|
|
||||||
// In some cases (when in a filtered view in Power Marker mode, it's possible that the demoted
|
|
||||||
// ref is not a part of the filter, making the table smaller. In those cases, we want to do a
|
|
||||||
// complete reload of the table to avoid a crash.
|
|
||||||
if ([[py getOutlineView:matchesTag childCountsForPath:[NSArray array]] count] == startLen)
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsUpdatedNotification object:self];
|
|
||||||
else
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleColumn:(id)sender
|
- (IBAction)toggleColumn:(id)sender
|
||||||
@@ -488,43 +341,6 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[self changePowerMarker:sender];
|
[self changePowerMarker:sender];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delegate */
|
|
||||||
- (void)outlineView:(NSOutlineView *)outlineView didClickTableColumn:(NSTableColumn *)tableColumn
|
|
||||||
{
|
|
||||||
if ([[outlineView sortDescriptors] count] < 1)
|
|
||||||
return;
|
|
||||||
NSSortDescriptor *sd = [[outlineView sortDescriptors] objectAtIndex:0];
|
|
||||||
if (_powerMode)
|
|
||||||
[py sortDupesBy:i2n([[sd key] intValue]) ascending:b2n([sd ascending])];
|
|
||||||
else
|
|
||||||
[py sortGroupsBy:i2n([[sd key] intValue]) ascending:b2n([sd ascending])];
|
|
||||||
[self reloadMatches];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
|
|
||||||
{
|
|
||||||
OVNode *node = item;
|
|
||||||
if ([[tableColumn identifier] isEqual:@"mark"]) {
|
|
||||||
[cell setEnabled: [node isMarkable]];
|
|
||||||
}
|
|
||||||
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
|
|
||||||
// Determine if the text color will be blue due to directory being reference.
|
|
||||||
NSTextFieldCell *textCell = cell;
|
|
||||||
if ([node isMarkable]) {
|
|
||||||
[textCell setTextColor:[NSColor blackColor]];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[textCell setTextColor:[NSColor blueColor]];
|
|
||||||
}
|
|
||||||
if ((_displayDelta) && (_powerMode || ([node level] > 1))) {
|
|
||||||
NSInteger i = [[tableColumn identifier] integerValue];
|
|
||||||
if ([_deltaColumns containsIndex:i]) {
|
|
||||||
[textCell setTextColor:[NSColor orangeColor]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Notifications */
|
/* Notifications */
|
||||||
- (void)windowWillClose:(NSNotification *)aNotification
|
- (void)windowWillClose:(NSNotification *)aNotification
|
||||||
{
|
{
|
||||||
@@ -533,7 +349,6 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
- (void)jobCompleted:(NSNotification *)aNotification
|
- (void)jobCompleted:(NSNotification *)aNotification
|
||||||
{
|
{
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
|
|
||||||
NSInteger r = n2i([py getOperationalErrorCount]);
|
NSInteger r = n2i([py getOperationalErrorCount]);
|
||||||
id lastAction = [[ProgressController mainProgressController] jobId];
|
id lastAction = [[ProgressController mainProgressController] jobId];
|
||||||
if ([lastAction isEqualTo:jobCopy]) {
|
if ([lastAction isEqualTo:jobCopy]) {
|
||||||
@@ -558,7 +373,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[Dialogs showMessage:@"All marked files were sucessfully sent to Trash."];
|
[Dialogs showMessage:@"All marked files were sucessfully sent to Trash."];
|
||||||
}
|
}
|
||||||
else if ([lastAction isEqualTo:jobScan]) {
|
else if ([lastAction isEqualTo:jobScan]) {
|
||||||
NSInteger groupCount = [[py getOutlineView:0 childCountsForPath:[NSArray array]] count];
|
NSInteger groupCount = [outline intProperty:@"children_count" valueAtPath:nil];
|
||||||
if (groupCount == 0)
|
if (groupCount == 0)
|
||||||
[Dialogs showMessage:@"No duplicates found."];
|
[Dialogs showMessage:@"No duplicates found."];
|
||||||
}
|
}
|
||||||
@@ -589,29 +404,6 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[Dialogs showMessage:msg];
|
[Dialogs showMessage:msg];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
[self performPySelection:[self getSelectedPaths:NO]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)resultsChanged:(NSNotification *)aNotification
|
|
||||||
{
|
|
||||||
[self reloadMatches];
|
|
||||||
[self refreshStats];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)resultsMarkingChanged:(NSNotification *)aNotification
|
|
||||||
{
|
|
||||||
[matches invalidateMarkings];
|
|
||||||
[self refreshStats];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)resultsUpdated:(NSNotification *)aNotification
|
|
||||||
{
|
|
||||||
[matches invalidateBuffers];
|
|
||||||
[matches invalidateMarkings];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
|
- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
|
||||||
{
|
{
|
||||||
return ![[ProgressController mainProgressController] isShown];
|
return ![[ProgressController mainProgressController] isShown];
|
||||||
|
|||||||
19
cocoa/base/StatsLabel.h
Normal file
19
cocoa/base/StatsLabel.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "HSGUIController.h"
|
||||||
|
#import "PyStatsLabel.h"
|
||||||
|
|
||||||
|
@interface StatsLabel : HSGUIController
|
||||||
|
{
|
||||||
|
NSTextField *labelView;
|
||||||
|
}
|
||||||
|
- (id)initWithPyParent:(id)aPyParent labelView:(NSTextField *)aLabelView;
|
||||||
|
- (PyStatsLabel *)py;
|
||||||
|
@end
|
||||||
38
cocoa/base/StatsLabel.m
Normal file
38
cocoa/base/StatsLabel.m
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "StatsLabel.h"
|
||||||
|
#import "Utils.h"
|
||||||
|
|
||||||
|
@implementation StatsLabel
|
||||||
|
- (id)initWithPyParent:(id)aPyParent labelView:(NSTextField *)aLabelView
|
||||||
|
{
|
||||||
|
self = [super initWithPyClassName:@"PyStatsLabel" pyParent:aPyParent];
|
||||||
|
labelView = [aLabelView retain];
|
||||||
|
[self connect];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[self disconnect];
|
||||||
|
[labelView release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (PyStatsLabel *)py
|
||||||
|
{
|
||||||
|
return (PyStatsLabel *)py;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Python --> Cocoa */
|
||||||
|
- (void)refresh
|
||||||
|
{
|
||||||
|
[labelView setStringValue:[[self py] display]];
|
||||||
|
}
|
||||||
|
@end
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
</object>
|
</object>
|
||||||
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<integer value="219"/>
|
||||||
<integer value="29"/>
|
<integer value="29"/>
|
||||||
<integer value="21"/>
|
|
||||||
</object>
|
</object>
|
||||||
<object class="NSArray" key="IBDocument.PluginDependencies">
|
<object class="NSArray" key="IBDocument.PluginDependencies">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
@@ -710,7 +710,7 @@
|
|||||||
<object class="NSMutableArray" key="NSTableColumns">
|
<object class="NSMutableArray" key="NSTableColumns">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
<object class="NSTableColumn" id="430098394">
|
<object class="NSTableColumn" id="430098394">
|
||||||
<string key="NSIdentifier">mark</string>
|
<string key="NSIdentifier">marked</string>
|
||||||
<double key="NSWidth">47</double>
|
<double key="NSWidth">47</double>
|
||||||
<double key="NSMinWidth">16</double>
|
<double key="NSMinWidth">16</double>
|
||||||
<double key="NSMaxWidth">1000</double>
|
<double key="NSMaxWidth">1000</double>
|
||||||
@@ -752,6 +752,7 @@
|
|||||||
<int key="NSPeriodicDelay">400</int>
|
<int key="NSPeriodicDelay">400</int>
|
||||||
<int key="NSPeriodicInterval">75</int>
|
<int key="NSPeriodicInterval">75</int>
|
||||||
</object>
|
</object>
|
||||||
|
<bool key="NSIsEditable">YES</bool>
|
||||||
<reference key="NSTableView" ref="40047569"/>
|
<reference key="NSTableView" ref="40047569"/>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSTableColumn" id="932540235">
|
<object class="NSTableColumn" id="932540235">
|
||||||
@@ -1667,14 +1668,6 @@
|
|||||||
</object>
|
</object>
|
||||||
<int key="connectionID">212</int>
|
<int key="connectionID">212</int>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBConnectionRecord">
|
|
||||||
<object class="IBOutletConnection" key="connection">
|
|
||||||
<string key="label">matches</string>
|
|
||||||
<reference key="source" ref="339936126"/>
|
|
||||||
<reference key="destination" ref="40047569"/>
|
|
||||||
</object>
|
|
||||||
<int key="connectionID">245</int>
|
|
||||||
</object>
|
|
||||||
<object class="IBConnectionRecord">
|
<object class="IBConnectionRecord">
|
||||||
<object class="IBOutletConnection" key="connection">
|
<object class="IBOutletConnection" key="connection">
|
||||||
<string key="label">initialFirstResponder</string>
|
<string key="label">initialFirstResponder</string>
|
||||||
@@ -1683,22 +1676,6 @@
|
|||||||
</object>
|
</object>
|
||||||
<int key="connectionID">279</int>
|
<int key="connectionID">279</int>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBConnectionRecord">
|
|
||||||
<object class="IBOutletConnection" key="connection">
|
|
||||||
<string key="label">delegate</string>
|
|
||||||
<reference key="source" ref="40047569"/>
|
|
||||||
<reference key="destination" ref="339936126"/>
|
|
||||||
</object>
|
|
||||||
<int key="connectionID">410</int>
|
|
||||||
</object>
|
|
||||||
<object class="IBConnectionRecord">
|
|
||||||
<object class="IBActionConnection" key="connection">
|
|
||||||
<string key="label">markToggle:</string>
|
|
||||||
<reference key="source" ref="339936126"/>
|
|
||||||
<reference key="destination" ref="705360835"/>
|
|
||||||
</object>
|
|
||||||
<int key="connectionID">414</int>
|
|
||||||
</object>
|
|
||||||
<object class="IBConnectionRecord">
|
<object class="IBConnectionRecord">
|
||||||
<object class="IBOutletConnection" key="connection">
|
<object class="IBOutletConnection" key="connection">
|
||||||
<string key="label">stats</string>
|
<string key="label">stats</string>
|
||||||
@@ -1939,14 +1916,6 @@
|
|||||||
</object>
|
</object>
|
||||||
<int key="connectionID">758</int>
|
<int key="connectionID">758</int>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBConnectionRecord">
|
|
||||||
<object class="IBOutletConnection" key="connection">
|
|
||||||
<string key="label">py</string>
|
|
||||||
<reference key="source" ref="40047569"/>
|
|
||||||
<reference key="destination" ref="875360857"/>
|
|
||||||
</object>
|
|
||||||
<int key="connectionID">764</int>
|
|
||||||
</object>
|
|
||||||
<object class="IBConnectionRecord">
|
<object class="IBConnectionRecord">
|
||||||
<object class="IBActionConnection" key="connection">
|
<object class="IBActionConnection" key="connection">
|
||||||
<string key="label">removeSelected:</string>
|
<string key="label">removeSelected:</string>
|
||||||
@@ -2227,6 +2196,14 @@
|
|||||||
</object>
|
</object>
|
||||||
<int key="connectionID">1175</int>
|
<int key="connectionID">1175</int>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBConnectionRecord">
|
||||||
|
<object class="IBOutletConnection" key="connection">
|
||||||
|
<string key="label">matches</string>
|
||||||
|
<reference key="source" ref="339936126"/>
|
||||||
|
<reference key="destination" ref="40047569"/>
|
||||||
|
</object>
|
||||||
|
<int key="connectionID">1176</int>
|
||||||
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||||
<object class="NSArray" key="orderedObjects">
|
<object class="NSArray" key="orderedObjects">
|
||||||
@@ -3406,7 +3383,7 @@
|
|||||||
<string>{340, 340}</string>
|
<string>{340, 340}</string>
|
||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<string>MatchesView</string>
|
<string>HSOutlineView</string>
|
||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
@@ -3584,7 +3561,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<nil key="sourceID"/>
|
<nil key="sourceID"/>
|
||||||
<int key="maxID">1175</int>
|
<int key="maxID">1176</int>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
<object class="IBClassDescriber" key="IBDocument.Classes">
|
||||||
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
||||||
@@ -3663,7 +3640,7 @@
|
|||||||
</object>
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBProjectSource</string>
|
<string key="majorKey">IBProjectSource</string>
|
||||||
<string key="minorKey">dgbase/AppDelegate.h</string>
|
<string key="minorKey">../base/AppDelegate.h</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
@@ -3675,19 +3652,22 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
<string key="className">MatchesView</string>
|
<string key="className">HSOutlineView</string>
|
||||||
<string key="superclassName">OutlineView</string>
|
<string key="superclassName">NSOutlineView</string>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="417275989">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="384069338">
|
||||||
<string key="majorKey">IBProjectSource</string>
|
<string key="majorKey">IBProjectSource</string>
|
||||||
<string key="minorKey">dgbase/ResultWindow.h</string>
|
<string key="minorKey">../views/HSOutlineView.h</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
<string key="className">MatchesView</string>
|
<string key="className">NSObject</string>
|
||||||
<string key="superclassName">OutlineView</string>
|
<reference key="sourceIdentifier" ref="384069338"/>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
</object>
|
||||||
<string key="majorKey">IBUserSource</string>
|
<object class="IBPartialClassDescription">
|
||||||
<string key="minorKey"/>
|
<string key="className">NSObject</string>
|
||||||
|
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="653924221">
|
||||||
|
<string key="majorKey">IBProjectSource</string>
|
||||||
|
<string key="minorKey">../views/NSTableViewAdditions.h</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
@@ -3699,31 +3679,15 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
<string key="className">OutlineView</string>
|
<string key="className">NSTableView</string>
|
||||||
<string key="superclassName">NSOutlineView</string>
|
<reference key="sourceIdentifier" ref="653924221"/>
|
||||||
<object class="NSMutableDictionary" key="outlets">
|
|
||||||
<string key="NS.key.0">py</string>
|
|
||||||
<string key="NS.object.0">PyApp</string>
|
|
||||||
</object>
|
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
|
||||||
<string key="majorKey">IBProjectSource</string>
|
|
||||||
<string key="minorKey">cocoalib/Outline.h</string>
|
|
||||||
</object>
|
|
||||||
</object>
|
|
||||||
<object class="IBPartialClassDescription">
|
|
||||||
<string key="className">OutlineView</string>
|
|
||||||
<string key="superclassName">NSOutlineView</string>
|
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
|
||||||
<string key="majorKey">IBUserSource</string>
|
|
||||||
<string key="minorKey"/>
|
|
||||||
</object>
|
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
<string key="className">PyApp</string>
|
<string key="className">PyApp</string>
|
||||||
<string key="superclassName">PyRegistrable</string>
|
<string key="superclassName">PyRegistrable</string>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBProjectSource</string>
|
<string key="majorKey">IBProjectSource</string>
|
||||||
<string key="minorKey">cocoalib/PyApp.h</string>
|
<string key="minorKey">../PyApp.h</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
@@ -3755,7 +3719,7 @@
|
|||||||
<string key="superclassName">PyApp</string>
|
<string key="superclassName">PyApp</string>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBProjectSource</string>
|
<string key="majorKey">IBProjectSource</string>
|
||||||
<string key="minorKey">dgbase/PyDupeGuru.h</string>
|
<string key="minorKey">../base/PyDupeGuru.h</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
@@ -3763,7 +3727,7 @@
|
|||||||
<string key="superclassName">NSObject</string>
|
<string key="superclassName">NSObject</string>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBProjectSource</string>
|
<string key="majorKey">IBProjectSource</string>
|
||||||
<string key="minorKey">cocoalib/PyRegistrable.h</string>
|
<string key="minorKey">../PyRegistrable.h</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
@@ -3797,7 +3761,7 @@
|
|||||||
</object>
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBProjectSource</string>
|
<string key="majorKey">IBProjectSource</string>
|
||||||
<string key="minorKey">cocoalib/RecentDirectories.h</string>
|
<string key="minorKey">../RecentDirectories.h</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
@@ -3812,123 +3776,14 @@
|
|||||||
<string key="className">ResultWindow</string>
|
<string key="className">ResultWindow</string>
|
||||||
<string key="superclassName">ResultWindowBase</string>
|
<string key="superclassName">ResultWindowBase</string>
|
||||||
<object class="NSMutableDictionary" key="actions">
|
<object class="NSMutableDictionary" key="actions">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<string key="NS.key.0">removeDeadTracks:</string>
|
||||||
<object class="NSArray" key="dict.sortedKeys">
|
<string key="NS.object.0">id</string>
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
|
||||||
<string>clearIgnoreList:</string>
|
|
||||||
<string>filter:</string>
|
|
||||||
<string>ignoreSelected:</string>
|
|
||||||
<string>markAll:</string>
|
|
||||||
<string>markInvert:</string>
|
|
||||||
<string>markNone:</string>
|
|
||||||
<string>markSelected:</string>
|
|
||||||
<string>markToggle:</string>
|
|
||||||
<string>openSelected:</string>
|
|
||||||
<string>refresh:</string>
|
|
||||||
<string>removeMarked:</string>
|
|
||||||
<string>removeSelected:</string>
|
|
||||||
<string>renameSelected:</string>
|
|
||||||
<string>resetColumnsToDefault:</string>
|
|
||||||
<string>revealSelected:</string>
|
|
||||||
<string>startDuplicateScan:</string>
|
|
||||||
<string>toggleDelta:</string>
|
|
||||||
</object>
|
|
||||||
<object class="NSMutableArray" key="dict.values">
|
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
</object>
|
|
||||||
</object>
|
|
||||||
<object class="NSMutableDictionary" key="outlets">
|
|
||||||
<string key="NS.key.0">filterField</string>
|
|
||||||
<string key="NS.object.0">NSSearchField</string>
|
|
||||||
</object>
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBProjectSource</string>
|
<string key="majorKey">IBProjectSource</string>
|
||||||
<string key="minorKey">ResultWindow.h</string>
|
<string key="minorKey">ResultWindow.h</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
|
||||||
<string key="className">ResultWindow</string>
|
|
||||||
<string key="superclassName">ResultWindowBase</string>
|
|
||||||
<object class="NSMutableDictionary" key="actions">
|
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
|
||||||
<object class="NSArray" key="dict.sortedKeys">
|
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
|
||||||
<string>changeDelta:</string>
|
|
||||||
<string>changePowerMarker:</string>
|
|
||||||
<string>collapseAll:</string>
|
|
||||||
<string>copyMarked:</string>
|
|
||||||
<string>deleteMarked:</string>
|
|
||||||
<string>expandAll:</string>
|
|
||||||
<string>exportToXHTML:</string>
|
|
||||||
<string>moveMarked:</string>
|
|
||||||
<string>switchSelected:</string>
|
|
||||||
<string>togglePowerMarker:</string>
|
|
||||||
</object>
|
|
||||||
<object class="NSMutableArray" key="dict.values">
|
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>id</string>
|
|
||||||
</object>
|
|
||||||
</object>
|
|
||||||
<object class="NSMutableDictionary" key="outlets">
|
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
|
||||||
<object class="NSArray" key="dict.sortedKeys">
|
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
|
||||||
<string>actionMenuView</string>
|
|
||||||
<string>app</string>
|
|
||||||
<string>deltaSwitch</string>
|
|
||||||
<string>deltaSwitchView</string>
|
|
||||||
<string>filterFieldView</string>
|
|
||||||
<string>matches</string>
|
|
||||||
<string>pmSwitch</string>
|
|
||||||
<string>pmSwitchView</string>
|
|
||||||
<string>py</string>
|
|
||||||
<string>stats</string>
|
|
||||||
</object>
|
|
||||||
<object class="NSMutableArray" key="dict.values">
|
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
|
||||||
<string>NSView</string>
|
|
||||||
<string>id</string>
|
|
||||||
<string>NSSegmentedControl</string>
|
|
||||||
<string>NSView</string>
|
|
||||||
<string>NSView</string>
|
|
||||||
<string>MatchesView</string>
|
|
||||||
<string>NSSegmentedControl</string>
|
|
||||||
<string>NSView</string>
|
|
||||||
<string>PyDupeGuru</string>
|
|
||||||
<string>NSTextField</string>
|
|
||||||
</object>
|
|
||||||
</object>
|
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
|
||||||
<string key="majorKey">IBUserSource</string>
|
|
||||||
<string key="minorKey"/>
|
|
||||||
</object>
|
|
||||||
</object>
|
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
<string key="className">ResultWindowBase</string>
|
<string key="className">ResultWindowBase</string>
|
||||||
<string key="superclassName">NSWindowController</string>
|
<string key="superclassName">NSWindowController</string>
|
||||||
@@ -3938,15 +3793,29 @@
|
|||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
<string>changeDelta:</string>
|
<string>changeDelta:</string>
|
||||||
<string>changePowerMarker:</string>
|
<string>changePowerMarker:</string>
|
||||||
|
<string>clearIgnoreList:</string>
|
||||||
<string>copyMarked:</string>
|
<string>copyMarked:</string>
|
||||||
<string>deleteMarked:</string>
|
<string>deleteMarked:</string>
|
||||||
<string>expandAll:</string>
|
|
||||||
<string>exportToXHTML:</string>
|
<string>exportToXHTML:</string>
|
||||||
|
<string>filter:</string>
|
||||||
|
<string>ignoreSelected:</string>
|
||||||
|
<string>markAll:</string>
|
||||||
|
<string>markInvert:</string>
|
||||||
|
<string>markNone:</string>
|
||||||
|
<string>markSelected:</string>
|
||||||
<string>moveMarked:</string>
|
<string>moveMarked:</string>
|
||||||
|
<string>openClicked:</string>
|
||||||
|
<string>openSelected:</string>
|
||||||
|
<string>removeMarked:</string>
|
||||||
|
<string>removeSelected:</string>
|
||||||
|
<string>renameSelected:</string>
|
||||||
<string>resetColumnsToDefault:</string>
|
<string>resetColumnsToDefault:</string>
|
||||||
|
<string>revealSelected:</string>
|
||||||
<string>showPreferencesPanel:</string>
|
<string>showPreferencesPanel:</string>
|
||||||
|
<string>startDuplicateScan:</string>
|
||||||
<string>switchSelected:</string>
|
<string>switchSelected:</string>
|
||||||
<string>toggleColumn:</string>
|
<string>toggleColumn:</string>
|
||||||
|
<string>toggleDelta:</string>
|
||||||
<string>toggleDetailsPanel:</string>
|
<string>toggleDetailsPanel:</string>
|
||||||
<string>togglePowerMarker:</string>
|
<string>togglePowerMarker:</string>
|
||||||
</object>
|
</object>
|
||||||
@@ -3965,6 +3834,20 @@
|
|||||||
<string>id</string>
|
<string>id</string>
|
||||||
<string>id</string>
|
<string>id</string>
|
||||||
<string>id</string>
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
|
<string>id</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSMutableDictionary" key="outlets">
|
<object class="NSMutableDictionary" key="outlets">
|
||||||
@@ -3974,6 +3857,7 @@
|
|||||||
<string>app</string>
|
<string>app</string>
|
||||||
<string>columnsMenu</string>
|
<string>columnsMenu</string>
|
||||||
<string>deltaSwitch</string>
|
<string>deltaSwitch</string>
|
||||||
|
<string>filterField</string>
|
||||||
<string>matches</string>
|
<string>matches</string>
|
||||||
<string>pmSwitch</string>
|
<string>pmSwitch</string>
|
||||||
<string>py</string>
|
<string>py</string>
|
||||||
@@ -3984,13 +3868,17 @@
|
|||||||
<string>id</string>
|
<string>id</string>
|
||||||
<string>NSMenu</string>
|
<string>NSMenu</string>
|
||||||
<string>NSSegmentedControl</string>
|
<string>NSSegmentedControl</string>
|
||||||
<string>MatchesView</string>
|
<string>NSSearchField</string>
|
||||||
|
<string>HSOutlineView</string>
|
||||||
<string>NSSegmentedControl</string>
|
<string>NSSegmentedControl</string>
|
||||||
<string>PyDupeGuruBase</string>
|
<string>PyDupeGuruBase</string>
|
||||||
<string>NSTextField</string>
|
<string>NSTextField</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<reference key="sourceIdentifier" ref="417275989"/>
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
|
<string key="majorKey">IBProjectSource</string>
|
||||||
|
<string key="minorKey">../base/ResultWindow.h</string>
|
||||||
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
<string key="className">SUUpdater</string>
|
<string key="className">SUUpdater</string>
|
||||||
@@ -4612,7 +4500,7 @@
|
|||||||
<integer value="3000" key="NS.object.0"/>
|
<integer value="3000" key="NS.object.0"/>
|
||||||
</object>
|
</object>
|
||||||
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
||||||
<string key="IBDocument.LastKnownRelativeProjectPath">../../dupeguru.xcodeproj</string>
|
<string key="IBDocument.LastKnownRelativeProjectPath">../../me/dupeguru.xcodeproj</string>
|
||||||
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
||||||
</data>
|
</data>
|
||||||
</archive>
|
</archive>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>hsft</string>
|
<string>hsft</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>5.7.1</string>
|
<string>5.7.2</string>
|
||||||
<key>NSMainNibFile</key>
|
<key>NSMainNibFile</key>
|
||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
|
|||||||
@@ -7,14 +7,9 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "../../cocoalib/Outline.h"
|
|
||||||
#import "../base/ResultWindow.h"
|
#import "../base/ResultWindow.h"
|
||||||
#import "DirectoryPanel.h"
|
#import "DirectoryPanel.h"
|
||||||
|
|
||||||
@interface ResultWindow : ResultWindowBase
|
@interface ResultWindow : ResultWindowBase {}
|
||||||
{
|
|
||||||
NSString *_lastAction;
|
|
||||||
}
|
|
||||||
- (IBAction)removeDeadTracks:(id)sender;
|
- (IBAction)removeDeadTracks:(id)sender;
|
||||||
- (IBAction)startDuplicateScan:(id)sender;
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
[super awakeFromNib];
|
[super awakeFromNib];
|
||||||
[[self window] setTitle:@"dupeGuru Music Edition"];
|
[[self window] setTitle:@"dupeGuru Music Edition"];
|
||||||
_deltaColumns = [[NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,7)] retain];
|
NSMutableIndexSet *deltaColumns = [[NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,7)] retain];
|
||||||
[_deltaColumns removeIndex:6];
|
[deltaColumns removeIndex:6];
|
||||||
|
[outline setDeltaColumns:deltaColumns];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
@@ -68,8 +69,6 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[_py setMixFileKind:[ud objectForKey:@"mixFileKind"]];
|
[_py setMixFileKind:[ud objectForKey:@"mixFileKind"]];
|
||||||
[_py setMatchSimilarWords:[ud objectForKey:@"matchSimilarWords"]];
|
[_py setMatchSimilarWords:[ud objectForKey:@"matchSimilarWords"]];
|
||||||
NSInteger r = n2i([py doScan]);
|
NSInteger r = n2i([py doScan]);
|
||||||
[matches reloadData];
|
|
||||||
[self refreshStats];
|
|
||||||
if (r == 1)
|
if (r == 1)
|
||||||
[Dialogs showMessage:@"You cannot make a duplicate scan with only reference directories."];
|
[Dialogs showMessage:@"You cannot make a duplicate scan with only reference directories."];
|
||||||
if (r == 3)
|
if (r == 3)
|
||||||
|
|||||||
@@ -21,7 +21,16 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
|
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
|
||||||
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
|
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
|
||||||
|
CE003CC611242D00004B0AA7 /* HSGUIController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CB411242D00004B0AA7 /* HSGUIController.m */; };
|
||||||
|
CE003CC711242D00004B0AA7 /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CB611242D00004B0AA7 /* HSOutline.m */; };
|
||||||
|
CE003CC811242D00004B0AA7 /* HSWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CB811242D00004B0AA7 /* HSWindowController.m */; };
|
||||||
|
CE003CC911242D00004B0AA7 /* NSEventAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CBA11242D00004B0AA7 /* NSEventAdditions.m */; };
|
||||||
|
CE003CCA11242D00004B0AA7 /* HSOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CC111242D00004B0AA7 /* HSOutlineView.m */; };
|
||||||
|
CE003CCB11242D00004B0AA7 /* NSIndexPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CC311242D00004B0AA7 /* NSIndexPathAdditions.m */; };
|
||||||
|
CE003CCC11242D00004B0AA7 /* NSTableViewAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CC511242D00004B0AA7 /* NSTableViewAdditions.m */; };
|
||||||
|
CE003CD011242D2C004B0AA7 /* DirectoryOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CCE11242D2C004B0AA7 /* DirectoryOutline.m */; };
|
||||||
CE073F6309CAE1A3005C1D2F /* dupeguru_me_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_me_help */; };
|
CE073F6309CAE1A3005C1D2F /* dupeguru_me_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_me_help */; };
|
||||||
|
CE0B3D6711243F83009A7A30 /* ResultOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0B3D6611243F83009A7A30 /* ResultOutline.m */; };
|
||||||
CE1425890AFB718500BD5167 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
|
CE1425890AFB718500BD5167 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
|
||||||
CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
|
CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
|
||||||
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
|
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
|
||||||
@@ -35,7 +44,6 @@
|
|||||||
CE4B59CA1119919700C06C9E /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C71119919700C06C9E /* registration.xib */; };
|
CE4B59CA1119919700C06C9E /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C71119919700C06C9E /* registration.xib */; };
|
||||||
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE10FC6C12E00EC695D /* Dialogs.m */; };
|
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE10FC6C12E00EC695D /* Dialogs.m */; };
|
||||||
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */; };
|
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */; };
|
||||||
CE515DF50FC6C12E00EC695D /* Outline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE50FC6C12E00EC695D /* Outline.m */; };
|
|
||||||
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE70FC6C12E00EC695D /* ProgressController.m */; };
|
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE70FC6C12E00EC695D /* ProgressController.m */; };
|
||||||
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */; };
|
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */; };
|
||||||
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */; };
|
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */; };
|
||||||
@@ -50,6 +58,7 @@
|
|||||||
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
|
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
|
||||||
CE900AD2109B238600754048 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD1109B238600754048 /* Preferences.xib */; };
|
CE900AD2109B238600754048 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD1109B238600754048 /* Preferences.xib */; };
|
||||||
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD6109B2A9B00754048 /* MainMenu.xib */; };
|
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD6109B2A9B00754048 /* MainMenu.xib */; };
|
||||||
|
CEDF07A3112493B200EE5BC0 /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDF07A2112493B200EE5BC0 /* StatsLabel.m */; };
|
||||||
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
|
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
|
||||||
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; };
|
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; };
|
||||||
CEFC295509C89FF200D9F998 /* details32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295309C89FF200D9F998 /* details32.png */; };
|
CEFC295509C89FF200D9F998 /* details32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295309C89FF200D9F998 /* details32.png */; };
|
||||||
@@ -78,7 +87,30 @@
|
|||||||
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||||
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; };
|
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; };
|
||||||
8D1107320486CEB800E47090 /* dupeGuru ME.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "dupeGuru ME.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
8D1107320486CEB800E47090 /* dupeGuru ME.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "dupeGuru ME.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
CE003CB311242D00004B0AA7 /* HSGUIController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSGUIController.h; sourceTree = "<group>"; };
|
||||||
|
CE003CB411242D00004B0AA7 /* HSGUIController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSGUIController.m; sourceTree = "<group>"; };
|
||||||
|
CE003CB511242D00004B0AA7 /* HSOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutline.h; sourceTree = "<group>"; };
|
||||||
|
CE003CB611242D00004B0AA7 /* HSOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutline.m; sourceTree = "<group>"; };
|
||||||
|
CE003CB711242D00004B0AA7 /* HSWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSWindowController.h; sourceTree = "<group>"; };
|
||||||
|
CE003CB811242D00004B0AA7 /* HSWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSWindowController.m; sourceTree = "<group>"; };
|
||||||
|
CE003CB911242D00004B0AA7 /* NSEventAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSEventAdditions.h; path = ../../cocoalib/NSEventAdditions.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE003CBA11242D00004B0AA7 /* NSEventAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSEventAdditions.m; path = ../../cocoalib/NSEventAdditions.m; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE003CBC11242D00004B0AA7 /* PyGUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyGUI.h; sourceTree = "<group>"; };
|
||||||
|
CE003CBD11242D00004B0AA7 /* PyOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyOutline.h; sourceTree = "<group>"; };
|
||||||
|
CE003CBE11242D00004B0AA7 /* PyRegistrable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyRegistrable.h; path = ../../cocoalib/PyRegistrable.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE003CC011242D00004B0AA7 /* HSOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutlineView.h; sourceTree = "<group>"; };
|
||||||
|
CE003CC111242D00004B0AA7 /* HSOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutlineView.m; sourceTree = "<group>"; };
|
||||||
|
CE003CC211242D00004B0AA7 /* NSIndexPathAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSIndexPathAdditions.h; sourceTree = "<group>"; };
|
||||||
|
CE003CC311242D00004B0AA7 /* NSIndexPathAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSIndexPathAdditions.m; sourceTree = "<group>"; };
|
||||||
|
CE003CC411242D00004B0AA7 /* NSTableViewAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSTableViewAdditions.h; sourceTree = "<group>"; };
|
||||||
|
CE003CC511242D00004B0AA7 /* NSTableViewAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSTableViewAdditions.m; sourceTree = "<group>"; };
|
||||||
|
CE003CCD11242D2C004B0AA7 /* DirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DirectoryOutline.h; path = ../base/DirectoryOutline.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE003CCE11242D2C004B0AA7 /* DirectoryOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DirectoryOutline.m; path = ../base/DirectoryOutline.m; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE003CCF11242D2C004B0AA7 /* PyDirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDirectoryOutline.h; path = ../base/PyDirectoryOutline.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE073F5409CAE1A3005C1D2F /* dupeguru_me_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_me_help; path = ../../help_me/dupeguru_me_help; sourceTree = "<group>"; };
|
CE073F5409CAE1A3005C1D2F /* dupeguru_me_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_me_help; path = ../../help_me/dupeguru_me_help; sourceTree = "<group>"; };
|
||||||
|
CE0B3D6411243F83009A7A30 /* PyResultTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTree.h; path = ../base/PyResultTree.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE0B3D6511243F83009A7A30 /* ResultOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultOutline.h; path = ../base/ResultOutline.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE0B3D6611243F83009A7A30 /* ResultOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultOutline.m; path = ../base/ResultOutline.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE1425880AFB718500BD5167 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
|
CE1425880AFB718500BD5167 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
|
||||||
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
|
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -96,8 +128,6 @@
|
|||||||
CE515DE10FC6C12E00EC695D /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; };
|
CE515DE10FC6C12E00EC695D /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
|
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSErrorReportWindow.m; path = ../../cocoalib/HSErrorReportWindow.m; sourceTree = SOURCE_ROOT; };
|
CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSErrorReportWindow.m; path = ../../cocoalib/HSErrorReportWindow.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE515DE40FC6C12E00EC695D /* Outline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Outline.h; path = ../../cocoalib/Outline.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DE50FC6C12E00EC695D /* Outline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Outline.m; path = ../../cocoalib/Outline.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DE60FC6C12E00EC695D /* ProgressController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressController.h; path = ../../cocoalib/ProgressController.h; sourceTree = SOURCE_ROOT; };
|
CE515DE60FC6C12E00EC695D /* ProgressController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressController.h; path = ../../cocoalib/ProgressController.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE515DE70FC6C12E00EC695D /* ProgressController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProgressController.m; path = ../../cocoalib/ProgressController.m; sourceTree = SOURCE_ROOT; };
|
CE515DE70FC6C12E00EC695D /* ProgressController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProgressController.m; path = ../../cocoalib/ProgressController.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE515DE80FC6C12E00EC695D /* PyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyApp.h; path = ../../cocoalib/PyApp.h; sourceTree = SOURCE_ROOT; };
|
CE515DE80FC6C12E00EC695D /* PyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyApp.h; path = ../../cocoalib/PyApp.h; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -128,6 +158,9 @@
|
|||||||
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; };
|
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; };
|
||||||
CECA899B09DB132E00A3D774 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DetailsPanel.m; sourceTree = "<group>"; };
|
CECA899B09DB132E00A3D774 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DetailsPanel.m; sourceTree = "<group>"; };
|
||||||
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CEDF07A0112493B200EE5BC0 /* PyStatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyStatsLabel.h; path = ../base/PyStatsLabel.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CEDF07A1112493B200EE5BC0 /* StatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StatsLabel.h; path = ../base/StatsLabel.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CEDF07A2112493B200EE5BC0 /* StatsLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StatsLabel.m; path = ../base/StatsLabel.m; sourceTree = SOURCE_ROOT; };
|
||||||
CEEB135109C837A2004D2330 /* dupeguru.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = dupeguru.icns; sourceTree = "<group>"; };
|
CEEB135109C837A2004D2330 /* dupeguru.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = dupeguru.icns; sourceTree = "<group>"; };
|
||||||
CEFC294509C89E3D00D9F998 /* folder32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = folder32.png; path = ../../images/folder32.png; sourceTree = SOURCE_ROOT; };
|
CEFC294509C89E3D00D9F998 /* folder32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = folder32.png; path = ../../images/folder32.png; sourceTree = SOURCE_ROOT; };
|
||||||
CEFC295309C89FF200D9F998 /* details32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = details32.png; path = ../../images/details32.png; sourceTree = SOURCE_ROOT; };
|
CEFC295309C89FF200D9F998 /* details32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = details32.png; path = ../../images/details32.png; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -237,6 +270,44 @@
|
|||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
CE003CB211242D00004B0AA7 /* controllers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
CE003CB311242D00004B0AA7 /* HSGUIController.h */,
|
||||||
|
CE003CB411242D00004B0AA7 /* HSGUIController.m */,
|
||||||
|
CE003CB511242D00004B0AA7 /* HSOutline.h */,
|
||||||
|
CE003CB611242D00004B0AA7 /* HSOutline.m */,
|
||||||
|
CE003CB711242D00004B0AA7 /* HSWindowController.h */,
|
||||||
|
CE003CB811242D00004B0AA7 /* HSWindowController.m */,
|
||||||
|
);
|
||||||
|
name = controllers;
|
||||||
|
path = ../../cocoalib/controllers;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
CE003CBB11242D00004B0AA7 /* proxies */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
CE003CBC11242D00004B0AA7 /* PyGUI.h */,
|
||||||
|
CE003CBD11242D00004B0AA7 /* PyOutline.h */,
|
||||||
|
);
|
||||||
|
name = proxies;
|
||||||
|
path = ../../cocoalib/proxies;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
CE003CBF11242D00004B0AA7 /* views */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
CE003CC011242D00004B0AA7 /* HSOutlineView.h */,
|
||||||
|
CE003CC111242D00004B0AA7 /* HSOutlineView.m */,
|
||||||
|
CE003CC211242D00004B0AA7 /* NSIndexPathAdditions.h */,
|
||||||
|
CE003CC311242D00004B0AA7 /* NSIndexPathAdditions.m */,
|
||||||
|
CE003CC411242D00004B0AA7 /* NSTableViewAdditions.h */,
|
||||||
|
CE003CC511242D00004B0AA7 /* NSTableViewAdditions.m */,
|
||||||
|
);
|
||||||
|
name = views;
|
||||||
|
path = ../../cocoalib/views;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
CE3FBDD01094637800B72D77 /* xib */ = {
|
CE3FBDD01094637800B72D77 /* xib */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -272,17 +343,21 @@
|
|||||||
CE515DDD0FC6C09400EC695D /* cocoalib */ = {
|
CE515DDD0FC6C09400EC695D /* cocoalib */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CE003CB211242D00004B0AA7 /* controllers */,
|
||||||
|
CE003CBB11242D00004B0AA7 /* proxies */,
|
||||||
|
CE003CBF11242D00004B0AA7 /* views */,
|
||||||
CE4B59C41119919700C06C9E /* xib */,
|
CE4B59C41119919700C06C9E /* xib */,
|
||||||
CE49DEF10FDFEB810098617B /* brsinglelineformatter */,
|
CE49DEF10FDFEB810098617B /* brsinglelineformatter */,
|
||||||
CE515DE00FC6C12E00EC695D /* Dialogs.h */,
|
CE515DE00FC6C12E00EC695D /* Dialogs.h */,
|
||||||
CE515DE10FC6C12E00EC695D /* Dialogs.m */,
|
CE515DE10FC6C12E00EC695D /* Dialogs.m */,
|
||||||
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */,
|
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */,
|
||||||
CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */,
|
CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */,
|
||||||
CE515DE40FC6C12E00EC695D /* Outline.h */,
|
CE003CB911242D00004B0AA7 /* NSEventAdditions.h */,
|
||||||
CE515DE50FC6C12E00EC695D /* Outline.m */,
|
CE003CBA11242D00004B0AA7 /* NSEventAdditions.m */,
|
||||||
CE515DE60FC6C12E00EC695D /* ProgressController.h */,
|
CE515DE60FC6C12E00EC695D /* ProgressController.h */,
|
||||||
CE515DE70FC6C12E00EC695D /* ProgressController.m */,
|
CE515DE70FC6C12E00EC695D /* ProgressController.m */,
|
||||||
CE515DE80FC6C12E00EC695D /* PyApp.h */,
|
CE515DE80FC6C12E00EC695D /* PyApp.h */,
|
||||||
|
CE003CBE11242D00004B0AA7 /* PyRegistrable.h */,
|
||||||
CE515DE90FC6C12E00EC695D /* RecentDirectories.h */,
|
CE515DE90FC6C12E00EC695D /* RecentDirectories.h */,
|
||||||
CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */,
|
CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */,
|
||||||
CE515DEB0FC6C12E00EC695D /* RegistrationInterface.h */,
|
CE515DEB0FC6C12E00EC695D /* RegistrationInterface.h */,
|
||||||
@@ -298,6 +373,9 @@
|
|||||||
CE515E140FC6C17900EC695D /* dgbase */ = {
|
CE515E140FC6C17900EC695D /* dgbase */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CE003CCD11242D2C004B0AA7 /* DirectoryOutline.h */,
|
||||||
|
CE003CCE11242D2C004B0AA7 /* DirectoryOutline.m */,
|
||||||
|
CE003CCF11242D2C004B0AA7 /* PyDirectoryOutline.h */,
|
||||||
CE515E150FC6C19300EC695D /* AppDelegate.h */,
|
CE515E150FC6C19300EC695D /* AppDelegate.h */,
|
||||||
CE515E160FC6C19300EC695D /* AppDelegate.m */,
|
CE515E160FC6C19300EC695D /* AppDelegate.m */,
|
||||||
CE515E170FC6C19300EC695D /* Consts.h */,
|
CE515E170FC6C19300EC695D /* Consts.h */,
|
||||||
@@ -305,10 +383,16 @@
|
|||||||
CE6032BF0FE6784C007E33FF /* DetailsPanel.m */,
|
CE6032BF0FE6784C007E33FF /* DetailsPanel.m */,
|
||||||
CE515E180FC6C19300EC695D /* DirectoryPanel.h */,
|
CE515E180FC6C19300EC695D /* DirectoryPanel.h */,
|
||||||
CE515E190FC6C19300EC695D /* DirectoryPanel.m */,
|
CE515E190FC6C19300EC695D /* DirectoryPanel.m */,
|
||||||
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */,
|
|
||||||
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */,
|
|
||||||
CE515E1B0FC6C19300EC695D /* ResultWindow.h */,
|
CE515E1B0FC6C19300EC695D /* ResultWindow.h */,
|
||||||
CE515E1C0FC6C19300EC695D /* ResultWindow.m */,
|
CE515E1C0FC6C19300EC695D /* ResultWindow.m */,
|
||||||
|
CE0B3D6511243F83009A7A30 /* ResultOutline.h */,
|
||||||
|
CE0B3D6611243F83009A7A30 /* ResultOutline.m */,
|
||||||
|
CEDF07A1112493B200EE5BC0 /* StatsLabel.h */,
|
||||||
|
CEDF07A2112493B200EE5BC0 /* StatsLabel.m */,
|
||||||
|
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */,
|
||||||
|
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */,
|
||||||
|
CE0B3D6411243F83009A7A30 /* PyResultTree.h */,
|
||||||
|
CEDF07A0112493B200EE5BC0 /* PyStatsLabel.h */,
|
||||||
);
|
);
|
||||||
name = dgbase;
|
name = dgbase;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -398,7 +482,6 @@
|
|||||||
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */,
|
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */,
|
||||||
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */,
|
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */,
|
||||||
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */,
|
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */,
|
||||||
CE515DF50FC6C12E00EC695D /* Outline.m in Sources */,
|
|
||||||
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */,
|
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */,
|
||||||
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */,
|
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */,
|
||||||
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */,
|
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */,
|
||||||
@@ -409,6 +492,16 @@
|
|||||||
CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */,
|
CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */,
|
||||||
CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.m in Sources */,
|
CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.m in Sources */,
|
||||||
CE6032C00FE6784C007E33FF /* DetailsPanel.m in Sources */,
|
CE6032C00FE6784C007E33FF /* DetailsPanel.m in Sources */,
|
||||||
|
CE003CC611242D00004B0AA7 /* HSGUIController.m in Sources */,
|
||||||
|
CE003CC711242D00004B0AA7 /* HSOutline.m in Sources */,
|
||||||
|
CE003CC811242D00004B0AA7 /* HSWindowController.m in Sources */,
|
||||||
|
CE003CC911242D00004B0AA7 /* NSEventAdditions.m in Sources */,
|
||||||
|
CE003CCA11242D00004B0AA7 /* HSOutlineView.m in Sources */,
|
||||||
|
CE003CCB11242D00004B0AA7 /* NSIndexPathAdditions.m in Sources */,
|
||||||
|
CE003CCC11242D00004B0AA7 /* NSTableViewAdditions.m in Sources */,
|
||||||
|
CE003CD011242D2C004B0AA7 /* DirectoryOutline.m in Sources */,
|
||||||
|
CE0B3D6711243F83009A7A30 /* ResultOutline.m in Sources */,
|
||||||
|
CEDF07A3112493B200EE5BC0 /* StatsLabel.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,9 +44,8 @@ static NSString* jobAddIPhoto = @"jobAddIPhoto";
|
|||||||
|
|
||||||
- (void)jobCompleted:(NSNotification *)aNotification
|
- (void)jobCompleted:(NSNotification *)aNotification
|
||||||
{
|
{
|
||||||
if ([[ProgressController mainProgressController] jobId] == jobAddIPhoto)
|
if ([[ProgressController mainProgressController] jobId] == jobAddIPhoto) {
|
||||||
{
|
[outlineView reloadData];
|
||||||
[directories reloadData];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>hsft</string>
|
<string>hsft</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.8.2</string>
|
<string>1.8.3</string>
|
||||||
<key>NSMainNibFile</key>
|
<key>NSMainNibFile</key>
|
||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
|
|||||||
@@ -39,6 +39,14 @@
|
|||||||
CE80DB8B0FC1951C0086DCA6 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */; };
|
CE80DB8B0FC1951C0086DCA6 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */; };
|
||||||
CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB890FC1951C0086DCA6 /* ResultWindow.m */; };
|
CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB890FC1951C0086DCA6 /* ResultWindow.m */; };
|
||||||
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
|
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
|
||||||
|
CE9EA7561122C96C008CD2BC /* HSGUIController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7441122C96C008CD2BC /* HSGUIController.m */; };
|
||||||
|
CE9EA7571122C96C008CD2BC /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7461122C96C008CD2BC /* HSOutline.m */; };
|
||||||
|
CE9EA7581122C96C008CD2BC /* HSWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7481122C96C008CD2BC /* HSWindowController.m */; };
|
||||||
|
CE9EA7591122C96C008CD2BC /* NSEventAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA74A1122C96C008CD2BC /* NSEventAdditions.m */; };
|
||||||
|
CE9EA75A1122C96C008CD2BC /* HSOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7511122C96C008CD2BC /* HSOutlineView.m */; };
|
||||||
|
CE9EA75B1122C96C008CD2BC /* NSIndexPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7531122C96C008CD2BC /* NSIndexPathAdditions.m */; };
|
||||||
|
CE9EA75C1122C96C008CD2BC /* NSTableViewAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7551122C96C008CD2BC /* NSTableViewAdditions.m */; };
|
||||||
|
CE9EA7721122CA0B008CD2BC /* DirectoryOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7701122CA0B008CD2BC /* DirectoryOutline.m */; };
|
||||||
CEBAE4270FDA97E000B7887D /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CEBAE4240FDA97E000B7887D /* BRSingleLineFormatter.m */; };
|
CEBAE4270FDA97E000B7887D /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CEBAE4240FDA97E000B7887D /* BRSingleLineFormatter.m */; };
|
||||||
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECA899A09DB132E00A3D774 /* DetailsPanel.h */; };
|
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECA899A09DB132E00A3D774 /* DetailsPanel.h */; };
|
||||||
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CECA899B09DB132E00A3D774 /* DetailsPanel.m */; };
|
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CECA899B09DB132E00A3D774 /* DetailsPanel.m */; };
|
||||||
@@ -122,6 +130,26 @@
|
|||||||
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultWindow.h; path = ../base/ResultWindow.h; sourceTree = SOURCE_ROOT; };
|
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultWindow.h; path = ../base/ResultWindow.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE80DB890FC1951C0086DCA6 /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultWindow.m; path = ../base/ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
CE80DB890FC1951C0086DCA6 /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultWindow.m; path = ../base/ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE848A1809DD85810004CB44 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
|
CE848A1809DD85810004CB44 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
|
||||||
|
CE9EA7431122C96C008CD2BC /* HSGUIController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSGUIController.h; sourceTree = "<group>"; };
|
||||||
|
CE9EA7441122C96C008CD2BC /* HSGUIController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSGUIController.m; sourceTree = "<group>"; };
|
||||||
|
CE9EA7451122C96C008CD2BC /* HSOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutline.h; sourceTree = "<group>"; };
|
||||||
|
CE9EA7461122C96C008CD2BC /* HSOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutline.m; sourceTree = "<group>"; };
|
||||||
|
CE9EA7471122C96C008CD2BC /* HSWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSWindowController.h; sourceTree = "<group>"; };
|
||||||
|
CE9EA7481122C96C008CD2BC /* HSWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSWindowController.m; sourceTree = "<group>"; };
|
||||||
|
CE9EA7491122C96C008CD2BC /* NSEventAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSEventAdditions.h; path = ../../cocoalib/NSEventAdditions.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE9EA74A1122C96C008CD2BC /* NSEventAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSEventAdditions.m; path = ../../cocoalib/NSEventAdditions.m; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE9EA74C1122C96C008CD2BC /* PyGUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyGUI.h; sourceTree = "<group>"; };
|
||||||
|
CE9EA74D1122C96C008CD2BC /* PyOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyOutline.h; sourceTree = "<group>"; };
|
||||||
|
CE9EA74E1122C96C008CD2BC /* PyRegistrable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyRegistrable.h; path = ../../cocoalib/PyRegistrable.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE9EA7501122C96C008CD2BC /* HSOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutlineView.h; sourceTree = "<group>"; };
|
||||||
|
CE9EA7511122C96C008CD2BC /* HSOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutlineView.m; sourceTree = "<group>"; };
|
||||||
|
CE9EA7521122C96C008CD2BC /* NSIndexPathAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSIndexPathAdditions.h; sourceTree = "<group>"; };
|
||||||
|
CE9EA7531122C96C008CD2BC /* NSIndexPathAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSIndexPathAdditions.m; sourceTree = "<group>"; };
|
||||||
|
CE9EA7541122C96C008CD2BC /* NSTableViewAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSTableViewAdditions.h; sourceTree = "<group>"; };
|
||||||
|
CE9EA7551122C96C008CD2BC /* NSTableViewAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSTableViewAdditions.m; sourceTree = "<group>"; };
|
||||||
|
CE9EA76F1122CA0B008CD2BC /* DirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DirectoryOutline.h; path = ../base/DirectoryOutline.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE9EA7701122CA0B008CD2BC /* DirectoryOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DirectoryOutline.m; path = ../base/DirectoryOutline.m; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE9EA7711122CA0B008CD2BC /* PyDirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDirectoryOutline.h; path = ../base/PyDirectoryOutline.h; sourceTree = SOURCE_ROOT; };
|
||||||
CEBAE4230FDA97E000B7887D /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; };
|
CEBAE4230FDA97E000B7887D /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; };
|
||||||
CEBAE4240FDA97E000B7887D /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; };
|
CEBAE4240FDA97E000B7887D /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; };
|
||||||
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; };
|
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; };
|
||||||
@@ -261,12 +289,17 @@
|
|||||||
CE80DB1A0FC192AB0086DCA6 /* cocoalib */ = {
|
CE80DB1A0FC192AB0086DCA6 /* cocoalib */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CE9EA7421122C96C008CD2BC /* controllers */,
|
||||||
|
CE9EA74B1122C96C008CD2BC /* proxies */,
|
||||||
|
CE9EA74F1122C96C008CD2BC /* views */,
|
||||||
CE7AC9141119911200D02F6C /* xib */,
|
CE7AC9141119911200D02F6C /* xib */,
|
||||||
CEBAE4220FDA97E000B7887D /* brsinglelineformatter */,
|
CEBAE4220FDA97E000B7887D /* brsinglelineformatter */,
|
||||||
CE80DB480FC193770086DCA6 /* NSImageAdditions.h */,
|
CE80DB480FC193770086DCA6 /* NSImageAdditions.h */,
|
||||||
CE80DB490FC193770086DCA6 /* NSImageAdditions.m */,
|
CE80DB490FC193770086DCA6 /* NSImageAdditions.m */,
|
||||||
CE80DB450FC193650086DCA6 /* NSNotificationAdditions.h */,
|
CE80DB450FC193650086DCA6 /* NSNotificationAdditions.h */,
|
||||||
CE80DB460FC193650086DCA6 /* NSNotificationAdditions.m */,
|
CE80DB460FC193650086DCA6 /* NSNotificationAdditions.m */,
|
||||||
|
CE9EA7491122C96C008CD2BC /* NSEventAdditions.h */,
|
||||||
|
CE9EA74A1122C96C008CD2BC /* NSEventAdditions.m */,
|
||||||
CE80DB1B0FC192D60086DCA6 /* Dialogs.h */,
|
CE80DB1B0FC192D60086DCA6 /* Dialogs.h */,
|
||||||
CE80DB1C0FC192D60086DCA6 /* Dialogs.m */,
|
CE80DB1C0FC192D60086DCA6 /* Dialogs.m */,
|
||||||
CE80DB1D0FC192D60086DCA6 /* HSErrorReportWindow.h */,
|
CE80DB1D0FC192D60086DCA6 /* HSErrorReportWindow.h */,
|
||||||
@@ -276,6 +309,7 @@
|
|||||||
CE80DB210FC192D60086DCA6 /* ProgressController.h */,
|
CE80DB210FC192D60086DCA6 /* ProgressController.h */,
|
||||||
CE80DB220FC192D60086DCA6 /* ProgressController.m */,
|
CE80DB220FC192D60086DCA6 /* ProgressController.m */,
|
||||||
CE80DB230FC192D60086DCA6 /* PyApp.h */,
|
CE80DB230FC192D60086DCA6 /* PyApp.h */,
|
||||||
|
CE9EA74E1122C96C008CD2BC /* PyRegistrable.h */,
|
||||||
CE80DB240FC192D60086DCA6 /* RecentDirectories.h */,
|
CE80DB240FC192D60086DCA6 /* RecentDirectories.h */,
|
||||||
CE80DB250FC192D60086DCA6 /* RecentDirectories.m */,
|
CE80DB250FC192D60086DCA6 /* RecentDirectories.m */,
|
||||||
CE80DB260FC192D60086DCA6 /* RegistrationInterface.h */,
|
CE80DB260FC192D60086DCA6 /* RegistrationInterface.h */,
|
||||||
@@ -291,6 +325,7 @@
|
|||||||
CE80DB810FC194BD0086DCA6 /* dgbase */ = {
|
CE80DB810FC194BD0086DCA6 /* dgbase */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CE9EA7711122CA0B008CD2BC /* PyDirectoryOutline.h */,
|
||||||
CE80DB820FC1951C0086DCA6 /* AppDelegate.h */,
|
CE80DB820FC1951C0086DCA6 /* AppDelegate.h */,
|
||||||
CE80DB830FC1951C0086DCA6 /* AppDelegate.m */,
|
CE80DB830FC1951C0086DCA6 /* AppDelegate.m */,
|
||||||
CE80DB840FC1951C0086DCA6 /* Consts.h */,
|
CE80DB840FC1951C0086DCA6 /* Consts.h */,
|
||||||
@@ -298,6 +333,8 @@
|
|||||||
CE6044EB0FE6796200B71262 /* DetailsPanel.m */,
|
CE6044EB0FE6796200B71262 /* DetailsPanel.m */,
|
||||||
CE80DB850FC1951C0086DCA6 /* DirectoryPanel.h */,
|
CE80DB850FC1951C0086DCA6 /* DirectoryPanel.h */,
|
||||||
CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */,
|
CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */,
|
||||||
|
CE9EA76F1122CA0B008CD2BC /* DirectoryOutline.h */,
|
||||||
|
CE9EA7701122CA0B008CD2BC /* DirectoryOutline.m */,
|
||||||
CE80DB870FC1951C0086DCA6 /* PyDupeGuru.h */,
|
CE80DB870FC1951C0086DCA6 /* PyDupeGuru.h */,
|
||||||
CE18126F111C9D5100E49FCE /* PyDetailsPanel.h */,
|
CE18126F111C9D5100E49FCE /* PyDetailsPanel.h */,
|
||||||
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */,
|
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */,
|
||||||
@@ -306,6 +343,44 @@
|
|||||||
name = dgbase;
|
name = dgbase;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
CE9EA7421122C96C008CD2BC /* controllers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
CE9EA7431122C96C008CD2BC /* HSGUIController.h */,
|
||||||
|
CE9EA7441122C96C008CD2BC /* HSGUIController.m */,
|
||||||
|
CE9EA7451122C96C008CD2BC /* HSOutline.h */,
|
||||||
|
CE9EA7461122C96C008CD2BC /* HSOutline.m */,
|
||||||
|
CE9EA7471122C96C008CD2BC /* HSWindowController.h */,
|
||||||
|
CE9EA7481122C96C008CD2BC /* HSWindowController.m */,
|
||||||
|
);
|
||||||
|
name = controllers;
|
||||||
|
path = ../../cocoalib/controllers;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
CE9EA74B1122C96C008CD2BC /* proxies */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
CE9EA74C1122C96C008CD2BC /* PyGUI.h */,
|
||||||
|
CE9EA74D1122C96C008CD2BC /* PyOutline.h */,
|
||||||
|
);
|
||||||
|
name = proxies;
|
||||||
|
path = ../../cocoalib/proxies;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
CE9EA74F1122C96C008CD2BC /* views */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
CE9EA7501122C96C008CD2BC /* HSOutlineView.h */,
|
||||||
|
CE9EA7511122C96C008CD2BC /* HSOutlineView.m */,
|
||||||
|
CE9EA7521122C96C008CD2BC /* NSIndexPathAdditions.h */,
|
||||||
|
CE9EA7531122C96C008CD2BC /* NSIndexPathAdditions.m */,
|
||||||
|
CE9EA7541122C96C008CD2BC /* NSTableViewAdditions.h */,
|
||||||
|
CE9EA7551122C96C008CD2BC /* NSTableViewAdditions.m */,
|
||||||
|
);
|
||||||
|
name = views;
|
||||||
|
path = ../../cocoalib/views;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
CEBAE4220FDA97E000B7887D /* brsinglelineformatter */ = {
|
CEBAE4220FDA97E000B7887D /* brsinglelineformatter */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -416,6 +491,14 @@
|
|||||||
CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */,
|
CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */,
|
||||||
CEBAE4270FDA97E000B7887D /* BRSingleLineFormatter.m in Sources */,
|
CEBAE4270FDA97E000B7887D /* BRSingleLineFormatter.m in Sources */,
|
||||||
CE6044EC0FE6796200B71262 /* DetailsPanel.m in Sources */,
|
CE6044EC0FE6796200B71262 /* DetailsPanel.m in Sources */,
|
||||||
|
CE9EA7561122C96C008CD2BC /* HSGUIController.m in Sources */,
|
||||||
|
CE9EA7571122C96C008CD2BC /* HSOutline.m in Sources */,
|
||||||
|
CE9EA7581122C96C008CD2BC /* HSWindowController.m in Sources */,
|
||||||
|
CE9EA7591122C96C008CD2BC /* NSEventAdditions.m in Sources */,
|
||||||
|
CE9EA75A1122C96C008CD2BC /* HSOutlineView.m in Sources */,
|
||||||
|
CE9EA75B1122C96C008CD2BC /* NSIndexPathAdditions.m in Sources */,
|
||||||
|
CE9EA75C1122C96C008CD2BC /* NSTableViewAdditions.m in Sources */,
|
||||||
|
CE9EA7721122CA0B008CD2BC /* DirectoryOutline.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
50
core/app.py
50
core/app.py
@@ -104,6 +104,11 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
path = Path(str_path)
|
path = Path(str_path)
|
||||||
return fs.get_file(path, self.directories.fileclasses)
|
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
|
@staticmethod
|
||||||
def _open_path(path):
|
def _open_path(path):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@@ -151,6 +156,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
filter = escape(filter, '()[]\\.|+?^')
|
filter = escape(filter, '()[]\\.|+?^')
|
||||||
filter = escape(filter, '*', '.')
|
filter = escape(filter, '*', '.')
|
||||||
self.results.apply_filter(filter)
|
self.results.apply_filter(filter)
|
||||||
|
self.notify('results_changed')
|
||||||
|
|
||||||
def clean_empty_dirs(self, path):
|
def clean_empty_dirs(self, path):
|
||||||
if self.options['clean_empty_dirs']:
|
if self.options['clean_empty_dirs']:
|
||||||
@@ -233,11 +239,34 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
if g not in changed_groups:
|
if g not in changed_groups:
|
||||||
self.results.make_ref(dupe)
|
self.results.make_ref(dupe)
|
||||||
changed_groups.add(g)
|
changed_groups.add(g)
|
||||||
|
self.notify('results_switched')
|
||||||
|
|
||||||
|
def mark_all(self):
|
||||||
|
self.results.mark_all()
|
||||||
|
self.notify('marking_changed')
|
||||||
|
|
||||||
|
def mark_none(self):
|
||||||
|
self.results.mark_none()
|
||||||
|
self.notify('marking_changed')
|
||||||
|
|
||||||
|
def mark_invert(self):
|
||||||
|
self.results.mark_invert()
|
||||||
|
self.notify('marking_changed')
|
||||||
|
|
||||||
|
def mark_dupe(self, dupe, marked):
|
||||||
|
if marked:
|
||||||
|
self.results.mark(dupe)
|
||||||
|
else:
|
||||||
|
self.results.unmark(dupe)
|
||||||
|
self.notify('marking_changed')
|
||||||
|
|
||||||
def open_selected(self):
|
def open_selected(self):
|
||||||
if self.selected_dupes:
|
if self.selected_dupes:
|
||||||
self._open_path(self.selected_dupes[0].path)
|
self._open_path(self.selected_dupes[0].path)
|
||||||
|
|
||||||
|
def purge_ignore_list(self):
|
||||||
|
self.scanner.ignore_list.Filter(lambda f,s:op.exists(f) and op.exists(s))
|
||||||
|
|
||||||
def remove_directory(self,index):
|
def remove_directory(self,index):
|
||||||
try:
|
try:
|
||||||
del self.directories[index]
|
del self.directories[index]
|
||||||
@@ -246,11 +275,25 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def remove_duplicates(self, duplicates):
|
def remove_duplicates(self, duplicates):
|
||||||
self.results.remove_duplicates(duplicates)
|
self.results.remove_duplicates(self.without_ref(duplicates))
|
||||||
|
self.notify('results_changed')
|
||||||
|
|
||||||
|
def remove_marked(self):
|
||||||
|
self.results.perform_on_marked(lambda x:True, True)
|
||||||
|
self.notify('results_changed')
|
||||||
|
|
||||||
def remove_selected(self):
|
def remove_selected(self):
|
||||||
self.remove_duplicates(self.selected_dupes)
|
self.remove_duplicates(self.selected_dupes)
|
||||||
|
|
||||||
|
def rename_selected(self, newname):
|
||||||
|
try:
|
||||||
|
d = self.selected_dupes[0]
|
||||||
|
d.rename(newname)
|
||||||
|
return True
|
||||||
|
except (IndexError, fs.FSError) as e:
|
||||||
|
logging.warning("dupeGuru Warning: %s" % unicode(e))
|
||||||
|
return False
|
||||||
|
|
||||||
def reveal_selected(self):
|
def reveal_selected(self):
|
||||||
if self.selected_dupes:
|
if self.selected_dupes:
|
||||||
self._reveal_path(self.selected_dupes[0].path)
|
self._reveal_path(self.selected_dupes[0].path)
|
||||||
@@ -283,6 +326,11 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
self.results.groups = []
|
self.results.groups = []
|
||||||
self._start_job(JOB_SCAN, do)
|
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('marking_changed')
|
||||||
|
|
||||||
def without_ref(self, dupes):
|
def without_ref(self, dupes):
|
||||||
return [dupe for dupe in dupes if self.results.get_group_of_duplicate(dupe).ref is not dupe]
|
return [dupe for dupe in dupes if self.results.get_group_of_duplicate(dupe).ref is not dupe]
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ from hsutil.cocoa import install_exception_hook
|
|||||||
from hsutil.cocoa.objcmin import (NSNotificationCenter, NSUserDefaults,
|
from hsutil.cocoa.objcmin import (NSNotificationCenter, NSUserDefaults,
|
||||||
NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSUserDomainMask,
|
NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSUserDomainMask,
|
||||||
NSWorkspace, NSWorkspaceRecycleOperation)
|
NSWorkspace, NSWorkspaceRecycleOperation)
|
||||||
from hsutil.misc import stripnone
|
|
||||||
from hsutil.reg import RegistrationRequired
|
from hsutil.reg import RegistrationRequired
|
||||||
|
|
||||||
from . import app, fs
|
from . import app, fs
|
||||||
@@ -46,7 +45,6 @@ class DupeGuru(app.DupeGuru):
|
|||||||
appdata = op.join(appsupport, appdata_subdir)
|
appdata = op.join(appsupport, appdata_subdir)
|
||||||
app.DupeGuru.__init__(self, data_module, appdata, appid)
|
app.DupeGuru.__init__(self, data_module, appdata, appid)
|
||||||
self.progress = cocoa.ThreadedJobPerformer()
|
self.progress = cocoa.ThreadedJobPerformer()
|
||||||
self.display_delta_values = False
|
|
||||||
|
|
||||||
#--- Override
|
#--- Override
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -75,34 +73,10 @@ class DupeGuru(app.DupeGuru):
|
|||||||
ud = {'desc': JOBID2TITLE[jobid], 'jobid':jobid}
|
ud = {'desc': JOBID2TITLE[jobid], 'jobid':jobid}
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_('JobStarted', self, ud)
|
NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_('JobStarted', self, ud)
|
||||||
|
|
||||||
#---Helpers
|
|
||||||
def GetObjects(self,node_path):
|
|
||||||
#returns a tuple g,d
|
|
||||||
try:
|
|
||||||
g = self.results.groups[node_path[0]]
|
|
||||||
if len(node_path) == 2:
|
|
||||||
return (g,self.results.groups[node_path[0]].dupes[node_path[1]])
|
|
||||||
else:
|
|
||||||
return (g,None)
|
|
||||||
except IndexError:
|
|
||||||
return (None,None)
|
|
||||||
|
|
||||||
#---Public
|
#---Public
|
||||||
copy_or_move_marked = demo_method(app.DupeGuru.copy_or_move_marked)
|
copy_or_move_marked = demo_method(app.DupeGuru.copy_or_move_marked)
|
||||||
delete_marked = demo_method(app.DupeGuru.delete_marked)
|
delete_marked = demo_method(app.DupeGuru.delete_marked)
|
||||||
|
|
||||||
def PurgeIgnoreList(self):
|
|
||||||
self.scanner.ignore_list.Filter(lambda f,s:op.exists(f) and op.exists(s))
|
|
||||||
|
|
||||||
def RenameSelected(self, newname):
|
|
||||||
try:
|
|
||||||
d = self.selected_dupes[0]
|
|
||||||
d.rename(newname)
|
|
||||||
return True
|
|
||||||
except (IndexError, fs.FSError) as e:
|
|
||||||
logging.warning("dupeGuru Warning: %s" % unicode(e))
|
|
||||||
return False
|
|
||||||
|
|
||||||
def start_scanning(self):
|
def start_scanning(self):
|
||||||
self._select_dupes([])
|
self._select_dupes([])
|
||||||
try:
|
try:
|
||||||
@@ -113,106 +87,3 @@ class DupeGuru(app.DupeGuru):
|
|||||||
except app.AllFilesAreRefError:
|
except app.AllFilesAreRefError:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def selected_result_node_paths(self):
|
|
||||||
def get_path(dupe):
|
|
||||||
try:
|
|
||||||
group = self.results.get_group_of_duplicate(dupe)
|
|
||||||
groupindex = self.results.groups.index(group)
|
|
||||||
if dupe is group.ref:
|
|
||||||
return [groupindex]
|
|
||||||
dupeindex = group.dupes.index(dupe)
|
|
||||||
return [groupindex, dupeindex]
|
|
||||||
except ValueError: # dupe not in there
|
|
||||||
return None
|
|
||||||
|
|
||||||
dupes = self.selected_dupes
|
|
||||||
return stripnone(get_path(dupe) for dupe in dupes)
|
|
||||||
|
|
||||||
def selected_powermarker_node_paths(self):
|
|
||||||
def get_path(dupe):
|
|
||||||
try:
|
|
||||||
dupeindex = self.results.dupes.index(dupe)
|
|
||||||
return [dupeindex]
|
|
||||||
except ValueError: # dupe not in there
|
|
||||||
return None
|
|
||||||
|
|
||||||
dupes = self.selected_dupes
|
|
||||||
return stripnone(get_path(dupe) for dupe in dupes)
|
|
||||||
|
|
||||||
def SelectResultNodePaths(self,node_paths):
|
|
||||||
def extract_dupe(t):
|
|
||||||
g,d = t
|
|
||||||
if d is not None:
|
|
||||||
return d
|
|
||||||
else:
|
|
||||||
if g is not None:
|
|
||||||
return g.ref
|
|
||||||
|
|
||||||
selected = [extract_dupe(self.GetObjects(p)) for p in node_paths]
|
|
||||||
self._select_dupes([dupe for dupe in selected if dupe is not None])
|
|
||||||
|
|
||||||
def SelectPowerMarkerNodePaths(self,node_paths):
|
|
||||||
rows = [p[0] for p in node_paths]
|
|
||||||
dupes = [self.results.dupes[row] for row in rows if row in xrange(len(self.results.dupes))]
|
|
||||||
self._select_dupes(dupes)
|
|
||||||
|
|
||||||
def sort_dupes(self,key,asc):
|
|
||||||
self.results.sort_dupes(key,asc,self.display_delta_values)
|
|
||||||
|
|
||||||
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,8 @@ from hsutil.cocoa.inter import signature, PyOutline, PyGUIObject, PyRegistrable
|
|||||||
|
|
||||||
from .gui.details_panel import DetailsPanel
|
from .gui.details_panel import DetailsPanel
|
||||||
from .gui.directory_tree import DirectoryTree
|
from .gui.directory_tree import DirectoryTree
|
||||||
|
from .gui.result_tree import ResultTree
|
||||||
|
from .gui.stats_label import StatsLabel
|
||||||
|
|
||||||
# Fix py2app's problems on relative imports
|
# Fix py2app's problems on relative imports
|
||||||
from core import app, app_cocoa, data, directories, engine, export, ignore, results, fs, scanner
|
from core import app, app_cocoa, data, directories, engine, export, ignore, results, fs, scanner
|
||||||
@@ -43,19 +45,19 @@ class PyDupeGuruBase(PyRegistrable):
|
|||||||
self.py.load()
|
self.py.load()
|
||||||
|
|
||||||
def markAll(self):
|
def markAll(self):
|
||||||
self.py.results.mark_all()
|
self.py.mark_all()
|
||||||
|
|
||||||
def markNone(self):
|
def markNone(self):
|
||||||
self.py.results.mark_none()
|
self.py.mark_none()
|
||||||
|
|
||||||
def markInvert(self):
|
def markInvert(self):
|
||||||
self.py.results.mark_invert()
|
self.py.mark_invert()
|
||||||
|
|
||||||
def purgeIgnoreList(self):
|
def purgeIgnoreList(self):
|
||||||
self.py.PurgeIgnoreList()
|
self.py.purge_ignore_list()
|
||||||
|
|
||||||
def toggleSelectedMark(self):
|
def toggleSelectedMark(self):
|
||||||
self.py.ToggleSelectedMarkState()
|
self.py.toggle_selected_mark_state()
|
||||||
|
|
||||||
def saveIgnoreList(self):
|
def saveIgnoreList(self):
|
||||||
self.py.save_ignore_list()
|
self.py.save_ignore_list()
|
||||||
@@ -63,18 +65,6 @@ class PyDupeGuruBase(PyRegistrable):
|
|||||||
def saveResults(self):
|
def saveResults(self):
|
||||||
self.py.save()
|
self.py.save()
|
||||||
|
|
||||||
def selectedResultNodePaths(self):
|
|
||||||
return self.py.selected_result_node_paths()
|
|
||||||
|
|
||||||
def selectResultNodePaths_(self,node_paths):
|
|
||||||
self.py.SelectResultNodePaths(node_paths)
|
|
||||||
|
|
||||||
def selectedPowerMarkerNodePaths(self):
|
|
||||||
return self.py.selected_powermarker_node_paths()
|
|
||||||
|
|
||||||
def selectPowerMarkerNodePaths_(self,node_paths):
|
|
||||||
self.py.SelectPowerMarkerNodePaths(node_paths)
|
|
||||||
|
|
||||||
#---Actions
|
#---Actions
|
||||||
def addSelectedToIgnoreList(self):
|
def addSelectedToIgnoreList(self):
|
||||||
self.py.add_selected_to_ignore_list()
|
self.py.add_selected_to_ignore_list()
|
||||||
@@ -95,24 +85,14 @@ class PyDupeGuruBase(PyRegistrable):
|
|||||||
self.py.open_selected()
|
self.py.open_selected()
|
||||||
|
|
||||||
def removeMarked(self):
|
def removeMarked(self):
|
||||||
self.py.results.perform_on_marked(lambda x:True, True)
|
self.py.remove_marked()
|
||||||
|
|
||||||
def removeSelected(self):
|
|
||||||
self.py.remove_selected()
|
|
||||||
|
|
||||||
def renameSelected_(self,newname):
|
def renameSelected_(self,newname):
|
||||||
return self.py.RenameSelected(newname)
|
return self.py.rename_selected(newname)
|
||||||
|
|
||||||
def revealSelected(self):
|
def revealSelected(self):
|
||||||
self.py.reveal_selected()
|
self.py.reveal_selected()
|
||||||
|
|
||||||
#---Misc
|
|
||||||
def sortDupesBy_ascending_(self, key, asc):
|
|
||||||
self.py.sort_dupes(key, asc)
|
|
||||||
|
|
||||||
def sortGroupsBy_ascending_(self, key, asc):
|
|
||||||
self.py.sort_groups(key, asc)
|
|
||||||
|
|
||||||
#---Information
|
#---Information
|
||||||
def getIgnoreListCount(self):
|
def getIgnoreListCount(self):
|
||||||
return len(self.py.scanner.ignore_list)
|
return len(self.py.scanner.ignore_list)
|
||||||
@@ -120,34 +100,13 @@ class PyDupeGuruBase(PyRegistrable):
|
|||||||
def getMarkCount(self):
|
def getMarkCount(self):
|
||||||
return self.py.results.mark_count
|
return self.py.results.mark_count
|
||||||
|
|
||||||
def getStatLine(self):
|
|
||||||
return self.py.stat_line
|
|
||||||
|
|
||||||
def getOperationalErrorCount(self):
|
def getOperationalErrorCount(self):
|
||||||
return self.py.last_op_error_count
|
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
|
#---Properties
|
||||||
def setMixFileKind_(self, mix_file_kind):
|
def setMixFileKind_(self, mix_file_kind):
|
||||||
self.py.scanner.mix_file_kind = mix_file_kind
|
self.py.scanner.mix_file_kind = mix_file_kind
|
||||||
|
|
||||||
def setDisplayDeltaValues_(self, display_delta_values):
|
|
||||||
self.py.display_delta_values= display_delta_values
|
|
||||||
|
|
||||||
def setEscapeFilterRegexp_(self, escape_filter_regexp):
|
def setEscapeFilterRegexp_(self, escape_filter_regexp):
|
||||||
self.py.options['escape_filter_regexp'] = escape_filter_regexp
|
self.py.options['escape_filter_regexp'] = escape_filter_regexp
|
||||||
|
|
||||||
@@ -164,6 +123,9 @@ class PyDupeGuruBase(PyRegistrable):
|
|||||||
def cancelJob(self):
|
def cancelJob(self):
|
||||||
self.py.progress.job_cancelled = True
|
self.py.progress.job_cancelled = True
|
||||||
|
|
||||||
|
def jobCompleted_(self, jobid):
|
||||||
|
self.py._job_completed(jobid)
|
||||||
|
|
||||||
|
|
||||||
class PyDetailsPanel(PyGUIObject):
|
class PyDetailsPanel(PyGUIObject):
|
||||||
py_class = DetailsPanel
|
py_class = DetailsPanel
|
||||||
@@ -182,3 +144,55 @@ class PyDirectoryOutline(PyOutline):
|
|||||||
def addDirectory_(self, path):
|
def addDirectory_(self, path):
|
||||||
self.py.add_directory(path)
|
self.py.add_directory(path)
|
||||||
|
|
||||||
|
|
||||||
|
class PyResultOutline(PyOutline):
|
||||||
|
py_class = ResultTree
|
||||||
|
|
||||||
|
@signature('c@:')
|
||||||
|
def powerMarkerMode(self):
|
||||||
|
return self.py.power_marker
|
||||||
|
|
||||||
|
@signature('v@:c')
|
||||||
|
def setPowerMarkerMode_(self, value):
|
||||||
|
self.py.power_marker = value
|
||||||
|
|
||||||
|
@signature('c@:')
|
||||||
|
def deltaValuesMode(self):
|
||||||
|
return self.py.delta_values
|
||||||
|
|
||||||
|
@signature('v@:c')
|
||||||
|
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('c@:@')
|
||||||
|
def renameSelected_(self, newname):
|
||||||
|
return self.py.rename_selected(newname)
|
||||||
|
|
||||||
|
@signature('v@:ic')
|
||||||
|
def sortBy_ascending_(self, key, asc):
|
||||||
|
self.py.sort(key, asc)
|
||||||
|
|
||||||
|
def markSelected(self):
|
||||||
|
self.py.app.toggle_selected_mark_state()
|
||||||
|
|
||||||
|
def removeSelected(self):
|
||||||
|
self.py.app.remove_selected()
|
||||||
|
|
||||||
|
def rootChildrenCounts(self):
|
||||||
|
return self.py.root_children_counts()
|
||||||
|
|
||||||
|
# python --> cocoa
|
||||||
|
def invalidate_markings(self):
|
||||||
|
self.cocoa.invalidateMarkings()
|
||||||
|
|
||||||
|
|
||||||
|
class PyStatsLabel(PyGUIObject):
|
||||||
|
py_class = StatsLabel
|
||||||
|
|
||||||
|
def display(self):
|
||||||
|
return self.py.display
|
||||||
|
|
||||||
|
|||||||
@@ -21,3 +21,12 @@ class GUIObject(Listener):
|
|||||||
def dupes_selected(self):
|
def dupes_selected(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def marking_changed(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def results_changed(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def results_switched(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,11 @@ class DetailsPanel(GUIObject):
|
|||||||
def __init__(self, view, app):
|
def __init__(self, view, app):
|
||||||
GUIObject.__init__(self, view, app)
|
GUIObject.__init__(self, view, app)
|
||||||
self._table = []
|
self._table = []
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
GUIObject.connect(self)
|
||||||
self._refresh()
|
self._refresh()
|
||||||
self.connect()
|
self.view.refresh()
|
||||||
|
|
||||||
#--- Private
|
#--- Private
|
||||||
def _refresh(self):
|
def _refresh(self):
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ class DirectoryTree(GUIObject, Tree):
|
|||||||
def __init__(self, view, app):
|
def __init__(self, view, app):
|
||||||
GUIObject.__init__(self, view, app)
|
GUIObject.__init__(self, view, app)
|
||||||
Tree.__init__(self)
|
Tree.__init__(self)
|
||||||
self.connect()
|
|
||||||
|
def connect(self):
|
||||||
|
GUIObject.connect(self)
|
||||||
self._refresh()
|
self._refresh()
|
||||||
self.view.refresh()
|
self.view.refresh()
|
||||||
|
|
||||||
|
|||||||
159
core/gui/result_tree.py
Normal file
159
core/gui/result_tree.py
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
# -*- 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 operator import attrgetter
|
||||||
|
|
||||||
|
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 = None
|
||||||
|
self._data_delta = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self):
|
||||||
|
if self._data is None:
|
||||||
|
self._data = self._app._get_display_info(self._dupe, self._group, False)
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_delta(self):
|
||||||
|
if self._data_delta is None:
|
||||||
|
self._data_delta = self._app._get_display_info(self._dupe, self._group, True)
|
||||||
|
return self._data_delta
|
||||||
|
|
||||||
|
@property
|
||||||
|
def markable(self):
|
||||||
|
return self._app.results.is_markable(self._dupe)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def marked(self):
|
||||||
|
return self._app.results.is_marked(self._dupe)
|
||||||
|
|
||||||
|
@marked.setter
|
||||||
|
def marked(self, value):
|
||||||
|
self._app.mark_dupe(self._dupe, value)
|
||||||
|
|
||||||
|
|
||||||
|
class ResultTree(GUIObject, Tree):
|
||||||
|
def __init__(self, view, app):
|
||||||
|
GUIObject.__init__(self, view, app)
|
||||||
|
Tree.__init__(self)
|
||||||
|
self._power_marker = False
|
||||||
|
self._delta_values = False
|
||||||
|
self._sort_descriptors = (0, True)
|
||||||
|
|
||||||
|
#--- Override
|
||||||
|
def connect(self):
|
||||||
|
GUIObject.connect(self)
|
||||||
|
self._refresh()
|
||||||
|
self.view.refresh()
|
||||||
|
|
||||||
|
def _select_nodes(self, nodes):
|
||||||
|
Tree._select_nodes(self, nodes)
|
||||||
|
self.app._select_dupes(map(attrgetter('_dupe'), nodes))
|
||||||
|
|
||||||
|
#--- Private
|
||||||
|
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 = 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
|
||||||
|
|
||||||
|
#--- Public
|
||||||
|
def get_node_value(self, path, column):
|
||||||
|
try:
|
||||||
|
node = self.get_node(path)
|
||||||
|
except IndexError:
|
||||||
|
return '---'
|
||||||
|
if self.delta_values:
|
||||||
|
return node.data_delta[column]
|
||||||
|
else:
|
||||||
|
return node.data[column]
|
||||||
|
|
||||||
|
def rename_selected(self, newname):
|
||||||
|
node = self.selected_node
|
||||||
|
node._data = None
|
||||||
|
node._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.view.refresh()
|
||||||
|
|
||||||
|
#--- Properties
|
||||||
|
@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
|
||||||
|
key, asc = self._sort_descriptors
|
||||||
|
self.sort(key, asc)
|
||||||
|
self._refresh()
|
||||||
|
self.view.refresh()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def delta_values(self):
|
||||||
|
return self._delta_values
|
||||||
|
|
||||||
|
@delta_values.setter
|
||||||
|
def delta_values(self, value):
|
||||||
|
if value == self._delta_values:
|
||||||
|
return
|
||||||
|
self._delta_values = value
|
||||||
|
self._refresh()
|
||||||
|
self.view.refresh()
|
||||||
|
|
||||||
|
#--- Event Handlers
|
||||||
|
def marking_changed(self):
|
||||||
|
self.view.invalidate_markings()
|
||||||
|
|
||||||
|
def results_changed(self):
|
||||||
|
self._refresh()
|
||||||
|
self.view.refresh()
|
||||||
|
|
||||||
|
def results_switched(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
|
||||||
|
self.view.refresh()
|
||||||
|
|
||||||
23
core/gui/stats_label.py
Normal file
23
core/gui/stats_label.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# -*- 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 .base import GUIObject
|
||||||
|
|
||||||
|
class StatsLabel(GUIObject):
|
||||||
|
def connect(self):
|
||||||
|
GUIObject.connect(self)
|
||||||
|
self.view.refresh()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display(self):
|
||||||
|
return self.app.stat_line
|
||||||
|
|
||||||
|
def results_changed(self):
|
||||||
|
self.view.refresh()
|
||||||
|
marking_changed = results_changed
|
||||||
@@ -1,402 +0,0 @@
|
|||||||
# Created By: Virgil Dupras
|
|
||||||
# Created On: 2006/11/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
|
|
||||||
|
|
||||||
import tempfile
|
|
||||||
import shutil
|
|
||||||
import logging
|
|
||||||
import os.path as op
|
|
||||||
|
|
||||||
from nose.tools import eq_
|
|
||||||
|
|
||||||
from hsutil.path import Path
|
|
||||||
from hsutil.testcase import TestCase
|
|
||||||
from hsutil.decorators import log_calls
|
|
||||||
from hsutil import io
|
|
||||||
|
|
||||||
from . import data
|
|
||||||
from .results_test import GetTestGroups
|
|
||||||
from .. import engine, fs
|
|
||||||
try:
|
|
||||||
from ..app_cocoa import DupeGuru as DupeGuruBase
|
|
||||||
except ImportError:
|
|
||||||
from nose.plugins.skip import SkipTest
|
|
||||||
raise SkipTest("These tests can only be run on OS X")
|
|
||||||
from ..gui.details_panel import DetailsPanel
|
|
||||||
from ..gui.directory_tree import DirectoryTree
|
|
||||||
|
|
||||||
class DupeGuru(DupeGuruBase):
|
|
||||||
def __init__(self):
|
|
||||||
DupeGuruBase.__init__(self, data, '/tmp', appid=4)
|
|
||||||
|
|
||||||
def _start_job(self, jobid, func):
|
|
||||||
func(nulljob)
|
|
||||||
|
|
||||||
def r2np(rows):
|
|
||||||
#Transforms a list of rows [1,2,3] into a list of node paths [[1],[2],[3]]
|
|
||||||
return [[i] for i in rows]
|
|
||||||
|
|
||||||
class CallLogger(object):
|
|
||||||
"""This is a dummy object that logs all calls made to it.
|
|
||||||
|
|
||||||
It is used to simulate the GUI layer.
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
self.calls = []
|
|
||||||
|
|
||||||
def __getattr__(self, func_name):
|
|
||||||
def func(*args, **kw):
|
|
||||||
self.calls.append(func_name)
|
|
||||||
return func
|
|
||||||
|
|
||||||
def clear_calls(self):
|
|
||||||
del self.calls[:]
|
|
||||||
|
|
||||||
|
|
||||||
class TCDupeGuru(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.app = DupeGuru()
|
|
||||||
self.dpanel_gui = CallLogger()
|
|
||||||
self.dpanel = DetailsPanel(self.dpanel_gui, self.app)
|
|
||||||
self.dtree_gui = CallLogger()
|
|
||||||
self.dtree = DirectoryTree(self.dtree_gui, self.app)
|
|
||||||
self.objects,self.matches,self.groups = GetTestGroups()
|
|
||||||
self.app.results.groups = self.groups
|
|
||||||
tmppath = self.tmppath()
|
|
||||||
io.mkdir(tmppath + 'foo')
|
|
||||||
io.mkdir(tmppath + 'bar')
|
|
||||||
self.app.directories.add_path(tmppath)
|
|
||||||
|
|
||||||
def check_gui_calls(self, gui, expected, verify_order=False):
|
|
||||||
"""Checks that the expected calls have been made to 'gui', then clears the log.
|
|
||||||
|
|
||||||
`expected` is an iterable of strings representing method names.
|
|
||||||
If `verify_order` is True, the order of the calls matters.
|
|
||||||
"""
|
|
||||||
if verify_order:
|
|
||||||
eq_(gui.calls, expected)
|
|
||||||
else:
|
|
||||||
eq_(set(gui.calls), set(expected))
|
|
||||||
gui.clear_calls()
|
|
||||||
|
|
||||||
def check_gui_calls_partial(self, gui, expected=None, not_expected=None):
|
|
||||||
"""Checks that the expected calls have been made to 'gui', then clears the log.
|
|
||||||
|
|
||||||
`expected` is an iterable of strings representing method names. Order doesn't matter.
|
|
||||||
Moreover, if calls have been made that are not in expected, no failure occur.
|
|
||||||
`not_expected` can be used for a more explicit check (rather than calling `check_gui_calls`
|
|
||||||
with an empty `expected`) to assert that calls have *not* been made.
|
|
||||||
"""
|
|
||||||
calls = set(gui.calls)
|
|
||||||
if expected is not None:
|
|
||||||
expected = set(expected)
|
|
||||||
not_called = expected - calls
|
|
||||||
assert not not_called, u"These calls haven't been made: {0}".format(not_called)
|
|
||||||
if not_expected is not None:
|
|
||||||
not_expected = set(not_expected)
|
|
||||||
called = not_expected & calls
|
|
||||||
assert not called, u"These calls shouldn't have been made: {0}".format(called)
|
|
||||||
gui.clear_calls()
|
|
||||||
|
|
||||||
def clear_gui_calls(self):
|
|
||||||
for attr in dir(self):
|
|
||||||
if attr.endswith('_gui'):
|
|
||||||
gui = getattr(self, attr)
|
|
||||||
if hasattr(gui, 'calls'): # We might have test methods ending with '_gui'
|
|
||||||
gui.clear_calls()
|
|
||||||
|
|
||||||
def test_GetObjects(self):
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
groups = self.groups
|
|
||||||
g,d = app.GetObjects([0])
|
|
||||||
self.assert_(g is groups[0])
|
|
||||||
self.assert_(d is None)
|
|
||||||
g,d = app.GetObjects([0,0])
|
|
||||||
self.assert_(g is groups[0])
|
|
||||||
self.assert_(d is objects[1])
|
|
||||||
g,d = app.GetObjects([1,0])
|
|
||||||
self.assert_(g is groups[1])
|
|
||||||
self.assert_(d is objects[4])
|
|
||||||
|
|
||||||
def test_GetObjects_after_sort(self):
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
groups = self.groups[:] #To keep the old order in memory
|
|
||||||
app.sort_groups(0,False) #0 = Filename
|
|
||||||
#Now, the group order is supposed to be reversed
|
|
||||||
g,d = app.GetObjects([0,0])
|
|
||||||
self.assert_(g is groups[1])
|
|
||||||
self.assert_(d is objects[4])
|
|
||||||
|
|
||||||
def test_GetObjects_out_of_range(self):
|
|
||||||
app = self.app
|
|
||||||
self.assertEqual((None,None),app.GetObjects([2]))
|
|
||||||
self.assertEqual((None,None),app.GetObjects([]))
|
|
||||||
self.assertEqual((None,None),app.GetObjects([1,2]))
|
|
||||||
|
|
||||||
def test_selected_result_node_paths(self):
|
|
||||||
# app.selected_dupes is correctly converted into node paths
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
paths = [[0, 0], [0, 1], [1]]
|
|
||||||
app.SelectResultNodePaths(paths)
|
|
||||||
eq_(app.selected_result_node_paths(), paths)
|
|
||||||
|
|
||||||
def test_selected_result_node_paths_after_deletion(self):
|
|
||||||
# cases where the selected dupes aren't there are correctly handled
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
paths = [[0, 0], [0, 1], [1]]
|
|
||||||
app.SelectResultNodePaths(paths)
|
|
||||||
app.remove_selected()
|
|
||||||
# The first 2 dupes have been removed. The 3rd one is a ref. it stays there, in first pos.
|
|
||||||
eq_(app.selected_result_node_paths(), [[0]]) # no exception
|
|
||||||
|
|
||||||
def test_selectResultNodePaths(self):
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
app.SelectResultNodePaths([[0,0],[0,1]])
|
|
||||||
self.assertEqual(2,len(app.selected_dupes))
|
|
||||||
self.assert_(app.selected_dupes[0] is objects[1])
|
|
||||||
self.assert_(app.selected_dupes[1] is objects[2])
|
|
||||||
|
|
||||||
def test_selectResultNodePaths_with_ref(self):
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
app.SelectResultNodePaths([[0,0],[0,1],[1]])
|
|
||||||
self.assertEqual(3,len(app.selected_dupes))
|
|
||||||
self.assert_(app.selected_dupes[0] is objects[1])
|
|
||||||
self.assert_(app.selected_dupes[1] is objects[2])
|
|
||||||
self.assert_(app.selected_dupes[2] is self.groups[1].ref)
|
|
||||||
|
|
||||||
def test_selectResultNodePaths_empty(self):
|
|
||||||
self.app.SelectResultNodePaths([])
|
|
||||||
self.assertEqual(0,len(self.app.selected_dupes))
|
|
||||||
|
|
||||||
def test_selectResultNodePaths_after_sort(self):
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
groups = self.groups[:] #To keep the old order in memory
|
|
||||||
app.sort_groups(0,False) #0 = Filename
|
|
||||||
#Now, the group order is supposed to be reversed
|
|
||||||
app.SelectResultNodePaths([[0,0],[1],[1,0]])
|
|
||||||
self.assertEqual(3,len(app.selected_dupes))
|
|
||||||
self.assert_(app.selected_dupes[0] is objects[4])
|
|
||||||
self.assert_(app.selected_dupes[1] is groups[0].ref)
|
|
||||||
self.assert_(app.selected_dupes[2] is objects[1])
|
|
||||||
|
|
||||||
def test_selectResultNodePaths_out_of_range(self):
|
|
||||||
app = self.app
|
|
||||||
app.SelectResultNodePaths([[0,0],[0,1],[1],[1,1],[2]])
|
|
||||||
self.assertEqual(3,len(app.selected_dupes))
|
|
||||||
|
|
||||||
def test_selected_powermarker_node_paths(self):
|
|
||||||
# app.selected_dupes is correctly converted into paths
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
paths = r2np([0, 1, 2])
|
|
||||||
app.SelectPowerMarkerNodePaths(paths)
|
|
||||||
eq_(app.selected_powermarker_node_paths(), paths)
|
|
||||||
|
|
||||||
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
|
|
||||||
paths = r2np([0, 1, 2])
|
|
||||||
app.SelectPowerMarkerNodePaths(paths)
|
|
||||||
app.remove_selected()
|
|
||||||
eq_(app.selected_powermarker_node_paths(), []) # no exception
|
|
||||||
|
|
||||||
def test_selectPowerMarkerRows(self):
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([0,1,2]))
|
|
||||||
self.assertEqual(3,len(app.selected_dupes))
|
|
||||||
self.assert_(app.selected_dupes[0] is objects[1])
|
|
||||||
self.assert_(app.selected_dupes[1] is objects[2])
|
|
||||||
self.assert_(app.selected_dupes[2] is objects[4])
|
|
||||||
|
|
||||||
def test_selectPowerMarkerRows_empty(self):
|
|
||||||
self.app.SelectPowerMarkerNodePaths([])
|
|
||||||
self.assertEqual(0,len(self.app.selected_dupes))
|
|
||||||
|
|
||||||
def test_selectPowerMarkerRows_after_sort(self):
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
app.sort_dupes(0,False) #0 = Filename
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([0,1,2]))
|
|
||||||
self.assertEqual(3,len(app.selected_dupes))
|
|
||||||
self.assert_(app.selected_dupes[0] is objects[4])
|
|
||||||
self.assert_(app.selected_dupes[1] is objects[2])
|
|
||||||
self.assert_(app.selected_dupes[2] is objects[1])
|
|
||||||
|
|
||||||
def test_selectPowerMarkerRows_out_of_range(self):
|
|
||||||
app = self.app
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([0,1,2,3]))
|
|
||||||
self.assertEqual(3,len(app.selected_dupes))
|
|
||||||
|
|
||||||
def test_toggleSelectedMark(self):
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
app.ToggleSelectedMarkState()
|
|
||||||
self.assertEqual(0,app.results.mark_count)
|
|
||||||
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]))
|
|
||||||
|
|
||||||
def test_refreshDetailsWithSelected(self):
|
|
||||||
self.app.SelectPowerMarkerNodePaths(r2np([0,2]))
|
|
||||||
eq_(self.dpanel.row(0), ('Filename', 'bar bleh', 'foo bar'))
|
|
||||||
self.check_gui_calls(self.dpanel_gui, ['refresh'])
|
|
||||||
self.app.SelectPowerMarkerNodePaths([])
|
|
||||||
eq_(self.dpanel.row(0), ('Filename', '---', '---'))
|
|
||||||
self.check_gui_calls(self.dpanel_gui, ['refresh'])
|
|
||||||
|
|
||||||
def test_makeSelectedReference(self):
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
groups = self.groups
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([0,2]))
|
|
||||||
app.make_selected_reference()
|
|
||||||
assert groups[0].ref is objects[1]
|
|
||||||
assert groups[1].ref is objects[4]
|
|
||||||
|
|
||||||
def test_makeSelectedReference_by_selecting_two_dupes_in_the_same_group(self):
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
groups = self.groups
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([0,1,2]))
|
|
||||||
#Only 0 and 2 must go ref, not 1 because it is a part of the same group
|
|
||||||
app.make_selected_reference()
|
|
||||||
assert groups[0].ref is objects[1]
|
|
||||||
assert groups[1].ref is objects[4]
|
|
||||||
|
|
||||||
def test_removeSelected(self):
|
|
||||||
app = self.app
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([0,2]))
|
|
||||||
app.remove_selected()
|
|
||||||
eq_(len(app.results.dupes), 1)
|
|
||||||
app.remove_selected()
|
|
||||||
eq_(len(app.results.dupes), 1)
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([0,2]))
|
|
||||||
app.remove_selected()
|
|
||||||
eq_(len(app.results.dupes), 0)
|
|
||||||
|
|
||||||
def test_addDirectory_simple(self):
|
|
||||||
# There's already a directory in self.app, so adding another once makes 2 of em
|
|
||||||
app = self.app
|
|
||||||
eq_(app.add_directory(self.datadirpath()), 0)
|
|
||||||
eq_(len(app.directories), 2)
|
|
||||||
|
|
||||||
def test_addDirectory_already_there(self):
|
|
||||||
app = self.app
|
|
||||||
self.assertEqual(0,app.add_directory(self.datadirpath()))
|
|
||||||
self.assertEqual(1,app.add_directory(self.datadirpath()))
|
|
||||||
|
|
||||||
def test_addDirectory_does_not_exist(self):
|
|
||||||
app = self.app
|
|
||||||
self.assertEqual(2,app.add_directory('/does_not_exist'))
|
|
||||||
|
|
||||||
def test_ignore(self):
|
|
||||||
app = self.app
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([2])) #The dupe of the second, 2 sized group
|
|
||||||
app.add_selected_to_ignore_list()
|
|
||||||
self.assertEqual(1,len(app.scanner.ignore_list))
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([0])) #first dupe of the 3 dupes group
|
|
||||||
app.add_selected_to_ignore_list()
|
|
||||||
#BOTH the ref and the other dupe should have been added
|
|
||||||
self.assertEqual(3,len(app.scanner.ignore_list))
|
|
||||||
|
|
||||||
def test_purgeIgnoreList(self):
|
|
||||||
app = self.app
|
|
||||||
p1 = self.filepath('zerofile')
|
|
||||||
p2 = self.filepath('zerofill')
|
|
||||||
dne = '/does_not_exist'
|
|
||||||
app.scanner.ignore_list.Ignore(dne,p1)
|
|
||||||
app.scanner.ignore_list.Ignore(p2,dne)
|
|
||||||
app.scanner.ignore_list.Ignore(p1,p2)
|
|
||||||
app.PurgeIgnoreList()
|
|
||||||
self.assertEqual(1,len(app.scanner.ignore_list))
|
|
||||||
self.assert_(app.scanner.ignore_list.AreIgnored(p1,p2))
|
|
||||||
self.assert_(not app.scanner.ignore_list.AreIgnored(dne,p1))
|
|
||||||
|
|
||||||
def test_only_unicode_is_added_to_ignore_list(self):
|
|
||||||
def FakeIgnore(first,second):
|
|
||||||
if not isinstance(first,unicode):
|
|
||||||
self.fail()
|
|
||||||
if not isinstance(second,unicode):
|
|
||||||
self.fail()
|
|
||||||
|
|
||||||
app = self.app
|
|
||||||
app.scanner.ignore_list.Ignore = FakeIgnore
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([2])) #The dupe of the second, 2 sized group
|
|
||||||
app.add_selected_to_ignore_list()
|
|
||||||
|
|
||||||
|
|
||||||
class TCDupeGuru_renameSelected(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
p = self.tmppath()
|
|
||||||
fp = open(unicode(p + 'foo bar 1'),mode='w')
|
|
||||||
fp.close()
|
|
||||||
fp = open(unicode(p + 'foo bar 2'),mode='w')
|
|
||||||
fp.close()
|
|
||||||
fp = open(unicode(p + 'foo bar 3'),mode='w')
|
|
||||||
fp.close()
|
|
||||||
files = fs.get_files(p)
|
|
||||||
matches = engine.getmatches(files)
|
|
||||||
groups = engine.get_groups(matches)
|
|
||||||
g = groups[0]
|
|
||||||
g.prioritize(lambda x:x.name)
|
|
||||||
app = DupeGuru()
|
|
||||||
app.results.groups = groups
|
|
||||||
self.app = app
|
|
||||||
self.groups = groups
|
|
||||||
self.p = p
|
|
||||||
self.files = files
|
|
||||||
|
|
||||||
def test_simple(self):
|
|
||||||
app = self.app
|
|
||||||
g = self.groups[0]
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([0]))
|
|
||||||
assert app.RenameSelected('renamed')
|
|
||||||
names = io.listdir(self.p)
|
|
||||||
assert 'renamed' in names
|
|
||||||
assert 'foo bar 2' not in names
|
|
||||||
eq_(g.dupes[0].name, 'renamed')
|
|
||||||
|
|
||||||
def test_none_selected(self):
|
|
||||||
app = self.app
|
|
||||||
g = self.groups[0]
|
|
||||||
app.SelectPowerMarkerNodePaths([])
|
|
||||||
self.mock(logging, 'warning', log_calls(lambda msg: None))
|
|
||||||
assert not app.RenameSelected('renamed')
|
|
||||||
msg = logging.warning.calls[0]['msg']
|
|
||||||
eq_('dupeGuru Warning: list index out of range', msg)
|
|
||||||
names = io.listdir(self.p)
|
|
||||||
assert 'renamed' not in names
|
|
||||||
assert 'foo bar 2' in names
|
|
||||||
eq_(g.dupes[0].name, 'foo bar 2')
|
|
||||||
|
|
||||||
def test_name_already_exists(self):
|
|
||||||
app = self.app
|
|
||||||
g = self.groups[0]
|
|
||||||
app.SelectPowerMarkerNodePaths(r2np([0]))
|
|
||||||
self.mock(logging, 'warning', log_calls(lambda msg: None))
|
|
||||||
assert not app.RenameSelected('foo bar 1')
|
|
||||||
msg = logging.warning.calls[0]['msg']
|
|
||||||
assert msg.startswith('dupeGuru Warning: \'foo bar 1\' already exists in')
|
|
||||||
names = io.listdir(self.p)
|
|
||||||
assert 'foo bar 1' in names
|
|
||||||
assert 'foo bar 2' in names
|
|
||||||
eq_(g.dupes[0].name, 'foo bar 2')
|
|
||||||
|
|
||||||
@@ -7,6 +7,9 @@
|
|||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from nose.tools import eq_
|
||||||
|
|
||||||
from hsutil.testcase import TestCase
|
from hsutil.testcase import TestCase
|
||||||
from hsutil import io
|
from hsutil import io
|
||||||
@@ -16,8 +19,12 @@ import hsutil.files
|
|||||||
from hsutil.job import nulljob
|
from hsutil.job import nulljob
|
||||||
|
|
||||||
from . import data
|
from . import data
|
||||||
from .. import app, fs
|
from .results_test import GetTestGroups
|
||||||
|
from .. import app, fs, engine
|
||||||
from ..app import DupeGuru as DupeGuruBase
|
from ..app import DupeGuru as DupeGuruBase
|
||||||
|
from ..gui.details_panel import DetailsPanel
|
||||||
|
from ..gui.directory_tree import DirectoryTree
|
||||||
|
from ..gui.result_tree import ResultTree
|
||||||
|
|
||||||
class DupeGuru(DupeGuruBase):
|
class DupeGuru(DupeGuruBase):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -27,6 +34,23 @@ class DupeGuru(DupeGuruBase):
|
|||||||
func(nulljob)
|
func(nulljob)
|
||||||
|
|
||||||
|
|
||||||
|
class CallLogger(object):
|
||||||
|
"""This is a dummy object that logs all calls made to it.
|
||||||
|
|
||||||
|
It is used to simulate the GUI layer.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.calls = []
|
||||||
|
|
||||||
|
def __getattr__(self, func_name):
|
||||||
|
def func(*args, **kw):
|
||||||
|
self.calls.append(func_name)
|
||||||
|
return func
|
||||||
|
|
||||||
|
def clear_calls(self):
|
||||||
|
del self.calls[:]
|
||||||
|
|
||||||
|
|
||||||
class TCDupeGuru(TestCase):
|
class TCDupeGuru(TestCase):
|
||||||
cls_tested_module = app
|
cls_tested_module = app
|
||||||
def test_apply_filter_calls_results_apply_filter(self):
|
def test_apply_filter_calls_results_apply_filter(self):
|
||||||
@@ -133,3 +157,330 @@ class TCDupeGuru_clean_empty_dirs(TestCase):
|
|||||||
self.assertEqual(Path('not-empty/empty'), calls[1]['path'])
|
self.assertEqual(Path('not-empty/empty'), calls[1]['path'])
|
||||||
self.assertEqual(Path('not-empty'), calls[2]['path'])
|
self.assertEqual(Path('not-empty'), calls[2]['path'])
|
||||||
|
|
||||||
|
|
||||||
|
class TCDupeGuruWithResults(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.app = DupeGuru()
|
||||||
|
self.objects,self.matches,self.groups = GetTestGroups()
|
||||||
|
self.app.results.groups = self.groups
|
||||||
|
self.dpanel_gui = CallLogger()
|
||||||
|
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.dpanel.connect()
|
||||||
|
self.dtree.connect()
|
||||||
|
self.rtree.connect()
|
||||||
|
tmppath = self.tmppath()
|
||||||
|
io.mkdir(tmppath + 'foo')
|
||||||
|
io.mkdir(tmppath + 'bar')
|
||||||
|
self.app.directories.add_path(tmppath)
|
||||||
|
|
||||||
|
def check_gui_calls(self, gui, expected, verify_order=False):
|
||||||
|
"""Checks that the expected calls have been made to 'gui', then clears the log.
|
||||||
|
|
||||||
|
`expected` is an iterable of strings representing method names.
|
||||||
|
If `verify_order` is True, the order of the calls matters.
|
||||||
|
"""
|
||||||
|
if verify_order:
|
||||||
|
eq_(gui.calls, expected)
|
||||||
|
else:
|
||||||
|
eq_(set(gui.calls), set(expected))
|
||||||
|
gui.clear_calls()
|
||||||
|
|
||||||
|
def check_gui_calls_partial(self, gui, expected=None, not_expected=None):
|
||||||
|
"""Checks that the expected calls have been made to 'gui', then clears the log.
|
||||||
|
|
||||||
|
`expected` is an iterable of strings representing method names. Order doesn't matter.
|
||||||
|
Moreover, if calls have been made that are not in expected, no failure occur.
|
||||||
|
`not_expected` can be used for a more explicit check (rather than calling `check_gui_calls`
|
||||||
|
with an empty `expected`) to assert that calls have *not* been made.
|
||||||
|
"""
|
||||||
|
calls = set(gui.calls)
|
||||||
|
if expected is not None:
|
||||||
|
expected = set(expected)
|
||||||
|
not_called = expected - calls
|
||||||
|
assert not not_called, u"These calls haven't been made: {0}".format(not_called)
|
||||||
|
if not_expected is not None:
|
||||||
|
not_expected = set(not_expected)
|
||||||
|
called = not_expected & calls
|
||||||
|
assert not called, u"These calls shouldn't have been made: {0}".format(called)
|
||||||
|
gui.clear_calls()
|
||||||
|
|
||||||
|
def clear_gui_calls(self):
|
||||||
|
for attr in dir(self):
|
||||||
|
if attr.endswith('_gui'):
|
||||||
|
gui = getattr(self, attr)
|
||||||
|
if hasattr(gui, 'calls'): # We might have test methods ending with '_gui'
|
||||||
|
gui.clear_calls()
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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.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]]) # no exception
|
||||||
|
|
||||||
|
def test_selectResultNodePaths(self):
|
||||||
|
app = self.app
|
||||||
|
objects = self.objects
|
||||||
|
self.rtree.selected_paths = [[0, 0], [0, 1]]
|
||||||
|
eq_(len(app.selected_dupes), 2)
|
||||||
|
assert app.selected_dupes[0] is objects[1]
|
||||||
|
assert app.selected_dupes[1] is objects[2]
|
||||||
|
|
||||||
|
def test_selectResultNodePaths_with_ref(self):
|
||||||
|
app = self.app
|
||||||
|
objects = self.objects
|
||||||
|
self.rtree.selected_paths = [[0, 0], [0, 1], [1]]
|
||||||
|
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 self.groups[1].ref
|
||||||
|
|
||||||
|
def test_selectResultNodePaths_after_sort(self):
|
||||||
|
app = self.app
|
||||||
|
objects = self.objects
|
||||||
|
groups = self.groups[:] #To keep the old order in memory
|
||||||
|
self.rtree.sort(0, False) #0 = Filename
|
||||||
|
#Now, the group order is supposed to be reversed
|
||||||
|
self.rtree.selected_paths = [[0, 0], [1], [1, 0]]
|
||||||
|
eq_(len(app.selected_dupes), 3)
|
||||||
|
assert app.selected_dupes[0] is objects[4]
|
||||||
|
assert app.selected_dupes[1] is groups[0].ref
|
||||||
|
assert app.selected_dupes[2] is objects[1]
|
||||||
|
|
||||||
|
def test_selected_powermarker_node_paths(self):
|
||||||
|
# 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]])
|
||||||
|
|
||||||
|
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]]
|
||||||
|
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)
|
||||||
|
|
||||||
|
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]]
|
||||||
|
eq_(len(app.selected_dupes), 3)
|
||||||
|
assert app.selected_dupes[0] is objects[4]
|
||||||
|
assert app.selected_dupes[1] is objects[2]
|
||||||
|
assert app.selected_dupes[2] is objects[1]
|
||||||
|
|
||||||
|
def test_toggleSelectedMark(self):
|
||||||
|
app = self.app
|
||||||
|
objects = self.objects
|
||||||
|
app.toggle_selected_mark_state()
|
||||||
|
eq_(app.results.mark_count, 0)
|
||||||
|
self.rtree.selected_paths = [[0, 0], [1, 0]]
|
||||||
|
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.rtree.selected_paths = [[0, 0], [1, 0]]
|
||||||
|
eq_(self.dpanel.row(0), ('Filename', 'bar bleh', 'foo bar'))
|
||||||
|
self.check_gui_calls(self.dpanel_gui, ['refresh'])
|
||||||
|
self.rtree.selected_paths = []
|
||||||
|
eq_(self.dpanel.row(0), ('Filename', '---', '---'))
|
||||||
|
self.check_gui_calls(self.dpanel_gui, ['refresh'])
|
||||||
|
|
||||||
|
def test_makeSelectedReference(self):
|
||||||
|
app = self.app
|
||||||
|
objects = self.objects
|
||||||
|
groups = self.groups
|
||||||
|
self.rtree.selected_paths = [[0, 0], [1, 0]]
|
||||||
|
app.make_selected_reference()
|
||||||
|
assert groups[0].ref is objects[1]
|
||||||
|
assert groups[1].ref is objects[4]
|
||||||
|
|
||||||
|
def test_makeSelectedReference_by_selecting_two_dupes_in_the_same_group(self):
|
||||||
|
app = self.app
|
||||||
|
objects = self.objects
|
||||||
|
groups = self.groups
|
||||||
|
self.rtree.selected_paths = [[0, 0], [0, 1], [1, 0]]
|
||||||
|
#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]
|
||||||
|
assert groups[1].ref is objects[4]
|
||||||
|
|
||||||
|
def test_removeSelected(self):
|
||||||
|
app = self.app
|
||||||
|
self.rtree.selected_paths = [[0, 0], [1, 0]]
|
||||||
|
app.remove_selected()
|
||||||
|
eq_(len(app.results.dupes), 1)
|
||||||
|
app.remove_selected()
|
||||||
|
eq_(len(app.results.dupes), 1)
|
||||||
|
self.rtree.selected_path = [0, 0]
|
||||||
|
app.remove_selected()
|
||||||
|
eq_(len(app.results.dupes), 0)
|
||||||
|
|
||||||
|
def test_addDirectory_simple(self):
|
||||||
|
# There's already a directory in self.app, so adding another once makes 2 of em
|
||||||
|
app = self.app
|
||||||
|
eq_(app.add_directory(self.datadirpath()), 0)
|
||||||
|
eq_(len(app.directories), 2)
|
||||||
|
|
||||||
|
def test_addDirectory_already_there(self):
|
||||||
|
app = self.app
|
||||||
|
self.assertEqual(0,app.add_directory(self.datadirpath()))
|
||||||
|
self.assertEqual(1,app.add_directory(self.datadirpath()))
|
||||||
|
|
||||||
|
def test_addDirectory_does_not_exist(self):
|
||||||
|
app = self.app
|
||||||
|
self.assertEqual(2,app.add_directory('/does_not_exist'))
|
||||||
|
|
||||||
|
def test_ignore(self):
|
||||||
|
app = self.app
|
||||||
|
self.rtree.selected_path = [1, 0] #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
|
||||||
|
app.add_selected_to_ignore_list()
|
||||||
|
#BOTH the ref and the other dupe should have been added
|
||||||
|
eq_(len(app.scanner.ignore_list), 3)
|
||||||
|
|
||||||
|
def test_purgeIgnoreList(self):
|
||||||
|
app = self.app
|
||||||
|
p1 = self.filepath('zerofile')
|
||||||
|
p2 = self.filepath('zerofill')
|
||||||
|
dne = '/does_not_exist'
|
||||||
|
app.scanner.ignore_list.Ignore(dne,p1)
|
||||||
|
app.scanner.ignore_list.Ignore(p2,dne)
|
||||||
|
app.scanner.ignore_list.Ignore(p1,p2)
|
||||||
|
app.purge_ignore_list()
|
||||||
|
self.assertEqual(1,len(app.scanner.ignore_list))
|
||||||
|
self.assert_(app.scanner.ignore_list.AreIgnored(p1,p2))
|
||||||
|
self.assert_(not app.scanner.ignore_list.AreIgnored(dne,p1))
|
||||||
|
|
||||||
|
def test_only_unicode_is_added_to_ignore_list(self):
|
||||||
|
def FakeIgnore(first,second):
|
||||||
|
if not isinstance(first,unicode):
|
||||||
|
self.fail()
|
||||||
|
if not isinstance(second,unicode):
|
||||||
|
self.fail()
|
||||||
|
|
||||||
|
app = self.app
|
||||||
|
app.scanner.ignore_list.Ignore = FakeIgnore
|
||||||
|
self.rtree.selected_path = [1, 0]
|
||||||
|
app.add_selected_to_ignore_list()
|
||||||
|
|
||||||
|
|
||||||
|
class TCDupeGuru_renameSelected(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
p = self.tmppath()
|
||||||
|
fp = open(unicode(p + 'foo bar 1'),mode='w')
|
||||||
|
fp.close()
|
||||||
|
fp = open(unicode(p + 'foo bar 2'),mode='w')
|
||||||
|
fp.close()
|
||||||
|
fp = open(unicode(p + 'foo bar 3'),mode='w')
|
||||||
|
fp.close()
|
||||||
|
files = fs.get_files(p)
|
||||||
|
matches = engine.getmatches(files)
|
||||||
|
groups = engine.get_groups(matches)
|
||||||
|
g = groups[0]
|
||||||
|
g.prioritize(lambda x:x.name)
|
||||||
|
app = DupeGuru()
|
||||||
|
app.results.groups = groups
|
||||||
|
self.app = app
|
||||||
|
self.groups = groups
|
||||||
|
self.p = p
|
||||||
|
self.files = files
|
||||||
|
self.rtree_gui = CallLogger()
|
||||||
|
self.rtree = ResultTree(self.rtree_gui, self.app)
|
||||||
|
self.rtree.connect()
|
||||||
|
|
||||||
|
def test_simple(self):
|
||||||
|
app = self.app
|
||||||
|
g = self.groups[0]
|
||||||
|
self.rtree.selected_path = [0, 0]
|
||||||
|
assert app.rename_selected('renamed')
|
||||||
|
names = io.listdir(self.p)
|
||||||
|
assert 'renamed' in names
|
||||||
|
assert 'foo bar 2' not in names
|
||||||
|
eq_(g.dupes[0].name, 'renamed')
|
||||||
|
|
||||||
|
def test_none_selected(self):
|
||||||
|
app = self.app
|
||||||
|
g = self.groups[0]
|
||||||
|
self.rtree.selected_paths = []
|
||||||
|
self.mock(logging, 'warning', log_calls(lambda msg: None))
|
||||||
|
assert not app.rename_selected('renamed')
|
||||||
|
msg = logging.warning.calls[0]['msg']
|
||||||
|
eq_('dupeGuru Warning: list index out of range', msg)
|
||||||
|
names = io.listdir(self.p)
|
||||||
|
assert 'renamed' not in names
|
||||||
|
assert 'foo bar 2' in names
|
||||||
|
eq_(g.dupes[0].name, 'foo bar 2')
|
||||||
|
|
||||||
|
def test_name_already_exists(self):
|
||||||
|
app = self.app
|
||||||
|
g = self.groups[0]
|
||||||
|
self.rtree.selected_path = [0, 0]
|
||||||
|
self.mock(logging, 'warning', log_calls(lambda msg: None))
|
||||||
|
assert not app.rename_selected('foo bar 1')
|
||||||
|
msg = logging.warning.calls[0]['msg']
|
||||||
|
assert msg.startswith('dupeGuru Warning: \'foo bar 1\' already exists in')
|
||||||
|
names = io.listdir(self.p)
|
||||||
|
assert 'foo bar 1' in names
|
||||||
|
assert 'foo bar 2' in names
|
||||||
|
eq_(g.dupes[0].name, 'foo bar 2')
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ static PyObject* block_osx_getblocks(PyObject *self, PyObject *args)
|
|||||||
CGImageSourceRef source;
|
CGImageSourceRef source;
|
||||||
CGImageRef image;
|
CGImageRef image;
|
||||||
size_t width, height;
|
size_t width, height;
|
||||||
int block_count, block_width, block_height, block_xcount, block_ycount, i;
|
int block_count, block_width, block_height, i;
|
||||||
|
|
||||||
width = 0;
|
width = 0;
|
||||||
height = 0;
|
height = 0;
|
||||||
@@ -192,23 +192,24 @@ static PyObject* block_osx_getblocks(PyObject *self, PyObject *args)
|
|||||||
|
|
||||||
block_width = max(width/block_count, 1);
|
block_width = max(width/block_count, 1);
|
||||||
block_height = max(height/block_count, 1);
|
block_height = max(height/block_count, 1);
|
||||||
/* block_count might have changed */
|
|
||||||
block_xcount = (width / block_width);
|
|
||||||
block_ycount = (height / block_height);
|
|
||||||
|
|
||||||
result = PyList_New(block_xcount * block_ycount);
|
result = PyList_New(block_count * block_count);
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i=0; i<block_ycount; i++)
|
for(i=0; i<block_count; i++) {
|
||||||
{
|
int j, top;
|
||||||
int j;
|
top = min(i*block_height, height-block_height);
|
||||||
for(j=0; j<block_xcount; j++)
|
for(j=0; j<block_count; j++) {
|
||||||
{
|
int left;
|
||||||
PyObject *block = getblock(bitmapData, width, height, j*block_width, i*block_height,
|
left = min(j*block_width, width-block_width);
|
||||||
block_width, block_height);
|
PyObject *block = getblock(bitmapData, width, height, left, top, block_width, block_height);
|
||||||
PyList_SET_ITEM(result, i*block_ycount+j, block);
|
if (block == NULL) {
|
||||||
|
Py_DECREF(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyList_SET_ITEM(result, i*block_count+j, block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
- date: 2010-02-13
|
||||||
|
version: 5.7.2
|
||||||
|
description: |
|
||||||
|
* Fixed a crash upon quitting when support folder is not present. (#83)
|
||||||
|
* Fixed a crash during sorting. (#85)
|
||||||
|
* Fixed selection glitches, especially while renaming. (#93)
|
||||||
- date: 2010-01-19
|
- date: 2010-01-19
|
||||||
version: 5.7.1
|
version: 5.7.1
|
||||||
description: |
|
description: |
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
- date: 2010-02-11
|
||||||
|
version: 1.8.3
|
||||||
|
description: |
|
||||||
|
* Fixed a crash when reading certain pictures. [Mac OS X] (#94)
|
||||||
|
* Fixed selection glitches, especially while renaming. (#93)
|
||||||
- date: 2010-02-06
|
- date: 2010-02-06
|
||||||
version: 1.8.2
|
version: 1.8.2
|
||||||
description: |
|
description: |
|
||||||
@@ -79,159 +84,145 @@
|
|||||||
* Converted the Windows GUI to Qt, thus enabling multiprocessing and making the scanning process
|
* Converted the Windows GUI to Qt, thus enabling multiprocessing and making the scanning process
|
||||||
faster.
|
faster.
|
||||||
- date: 2009-03-24
|
- date: 2009-03-24
|
||||||
description: "* **Improved** scanning speed, mainly on OS X where all cores of the\
|
|
||||||
\ CPU are now used.\r\n* **Fixed** an occasional crash caused by permission issues.\r\
|
|
||||||
\n* **Fixed** a bug where the \"X discarded\" notice would show a too large number\
|
|
||||||
\ of discarded duplicates."
|
|
||||||
version: 1.6.0
|
version: 1.6.0
|
||||||
|
description: |
|
||||||
|
* **Improved** scanning speed, mainly on OS X where all cores of the CPU are now used.
|
||||||
|
* **Fixed** an occasional crash caused by permission issues.
|
||||||
|
* **Fixed** a bug where the "X discarded" notice would show a too large number of discarded duplicates.
|
||||||
- date: 2008-09-10
|
- date: 2008-09-10
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Added</b> a notice in the status bar when\
|
|
||||||
\ matches were discarded during the scan.</li>\n\t\t\t\t\t\t<li><b>Improved</b>\
|
|
||||||
\ duplicate prioritization (smartly chooses which file you will keep).</li>\n\t\
|
|
||||||
\t\t\t\t\t<li><b>Improved</b> scan progress feedback.</li>\n\t\t\t\t\t\t<li><b>Improved</b>\
|
|
||||||
\ responsiveness of the user interface for certain actions.</li>\n\t\t \
|
|
||||||
\ </ul>"
|
|
||||||
version: 1.5.0
|
version: 1.5.0
|
||||||
|
description: |
|
||||||
|
* **Added** a notice in the status bar when matches were discarded during the scan.
|
||||||
|
* **Improved** duplicate prioritization (smartly chooses which file you will keep).
|
||||||
|
* **Improved** scan progress feedback.
|
||||||
|
* **Improved** responsiveness of the user interface for certain actions.
|
||||||
- date: 2008-07-28
|
- date: 2008-07-28
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Improved</b> iPhoto compatibility on Mac\
|
|
||||||
\ OS X.</li>\n\t\t\t\t\t\t<li><b>Improved</b> the speed of results loading and\
|
|
||||||
\ saving.</li>\n\t\t\t\t\t\t<li><b>Fixed</b> a crash sometimes occurring during\
|
|
||||||
\ duplicate deletion.</li>\n\t\t </ul>"
|
|
||||||
version: 1.4.2
|
version: 1.4.2
|
||||||
|
description: |
|
||||||
|
* **Improved** iPhoto compatibility on Mac OS X.
|
||||||
|
* **Improved** the speed of results loading and saving.
|
||||||
|
* **Fixed** a crash sometimes occurring during duplicate deletion.
|
||||||
- date: 2008-04-12
|
- date: 2008-04-12
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Improved</b> iPhoto Library loading feedback\
|
|
||||||
\ on Mac OS X.</li>\n\t\t\t\t\t\t<li><b>Fixed</b> the directory selection dialog.\
|
|
||||||
\ Bundles can be selected again on Mac OS X.</li>\n\t\t\t\t\t\t<li><b>Fixed</b>\
|
|
||||||
\ \"Clear Ignore List\" crash in Windows.</li>\n\t\t </ul>"
|
|
||||||
version: 1.4.1
|
version: 1.4.1
|
||||||
|
description: |
|
||||||
|
* **Improved** iPhoto Library loading feedback on Mac OS X.
|
||||||
|
* **Fixed** the directory selection dialog. Bundles can be selected again on Mac OS X.
|
||||||
|
* **Fixed** "Clear Ignore List" crash in Windows.
|
||||||
- date: 2008-02-20
|
- date: 2008-02-20
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Added</b> iPhoto Library support on Mac OS\
|
|
||||||
\ X.</li>\n\t\t\t\t\t\t<li><b>Fixed</b> occasional crashes when scanning corrupted\
|
|
||||||
\ pictures.</li>\n\t\t </ul>"
|
|
||||||
version: 1.4.0
|
|
||||||
- date: 2008-02-20
|
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Added</b> iPhoto Library support on Mac OS\
|
|
||||||
\ X.</li>\n\t\t\t\t\t\t<li><b>Fixed</b> occasional crashes when scanning corrupted\
|
|
||||||
\ pictures.</li>\n\t\t </ul>"
|
|
||||||
version: 1.4.0
|
version: 1.4.0
|
||||||
|
description: |
|
||||||
|
* **Added** iPhoto Library support on Mac OS X.
|
||||||
|
* **Fixed** occasional crashes when scanning corrupted pictures.
|
||||||
- date: 2008-01-12
|
- date: 2008-01-12
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Improved</b> scan, delete and move speed\
|
|
||||||
\ in situations where there were a lot of duplicates.</li>\n\t\t\t\t\t\t<li><b>Fixed</b>\
|
|
||||||
\ occasional crashes when moving a lot of files at once.</li>\n\t\t\t\t\t\t<li><b>Fixed</b>\
|
|
||||||
\ an issue sometimes preventing the application from starting at all.</li>\n\t\
|
|
||||||
\t </ul>"
|
|
||||||
version: 1.3.4
|
version: 1.3.4
|
||||||
|
description: |
|
||||||
|
* **Improved** scan, delete and move speed in situations where there were a lot of duplicates.
|
||||||
|
* **Fixed** occasional crashes when moving a lot of files at once.
|
||||||
|
* **Fixed** an issue sometimes preventing the application from starting at all.
|
||||||
- date: 2007-12-03
|
- date: 2007-12-03
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Improved</b> the handling of low memory situations.</li>\n\
|
|
||||||
\t\t\t\t\t\t<li><b>Improved</b> the directory panel. The \"Remove\" button changes\
|
|
||||||
\ to \"Put Back\" when an excluded directory is selected.</li>\n\t\t\t\t\t\t<li><b>Fixed</b>\
|
|
||||||
\ the directory selection dialog. iPhoto '08 library files can now be selected.</li>\n\
|
|
||||||
\t\t </ul>"
|
|
||||||
version: 1.3.3
|
version: 1.3.3
|
||||||
|
description: |
|
||||||
|
* **Improved** the handling of low memory situations.
|
||||||
|
* **Improved** the directory panel. The "Remove" button changes to "Put Back" when an excluded directory is selected.
|
||||||
|
* **Fixed** the directory selection dialog. iPhoto '08 library files can now be selected.
|
||||||
- date: 2007-11-24
|
- date: 2007-11-24
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Added</b> the \"Remove empty folders\" option.</li>\n\
|
|
||||||
\t\t\t\t\t\t<li><b>Fixed</b> results load/save issues.</li>\n\t\t\t\t\t\t<li><b>Fixed</b>\
|
|
||||||
\ occasional status bar inaccuracies when the results are filtered.</li>\n\t\t\
|
|
||||||
\ </ul>"
|
|
||||||
version: 1.3.2
|
version: 1.3.2
|
||||||
|
description: |
|
||||||
|
* **Added** the "Remove empty folders" option.
|
||||||
|
* **Fixed** results load/save issues.
|
||||||
|
* **Fixed** occasional status bar inaccuracies when the results are filtered.
|
||||||
- date: 2007-10-21
|
- date: 2007-10-21
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Improved</b> results loading speed.</li>\n\
|
|
||||||
\t\t\t\t\t\t<li><b>Improved</b> details panel's picture loading (made it asynchronous).</li>\n\
|
|
||||||
\t\t\t\t\t\t<li><b>Fixed</b> a bug where the stats line at the bottom would sometimes\
|
|
||||||
\ go confused while having a filter active.</li>\n\t\t\t\t\t\t<li><b>Fixed</b>\
|
|
||||||
\ a bug under Windows where some duplicate markings would be lost.</li>\n\t\t\
|
|
||||||
\ </ul>"
|
|
||||||
version: 1.3.1
|
version: 1.3.1
|
||||||
|
description: |
|
||||||
|
* **Improved** results loading speed.
|
||||||
|
* **Improved** details panel's picture loading (made it asynchronous).
|
||||||
|
* **Fixed** a bug where the stats line at the bottom would sometimes go confused while having a filter active.
|
||||||
|
* **Fixed** a bug under Windows where some duplicate markings would be lost.
|
||||||
- date: 2007-09-22
|
- date: 2007-09-22
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Added</b> post scan filtering.</li>\n\t\t\
|
|
||||||
\t\t\t\t<li><b>Fixed</b> issues with the rename feature under Windows</li>\n\t\
|
|
||||||
\t\t\t\t\t<li><b>Fixed</b> some user interface annoyances under Windows</li>\n\
|
|
||||||
\t\t </ul>"
|
|
||||||
version: 1.3.0
|
version: 1.3.0
|
||||||
|
description: |
|
||||||
|
* **Added** post scan filtering.
|
||||||
|
* **Fixed** issues with the rename feature under Windows.
|
||||||
|
* **Fixed** some user interface annoyances under Windows.
|
||||||
- date: 2007-05-19
|
- date: 2007-05-19
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Improved</b> UI responsiveness (using threads)\
|
|
||||||
\ under Mac OS X.</li>\n\t\t\t\t\t\t<li><b>Improved</b> result load/save speed\
|
|
||||||
\ and memory usage.</li>\n\t\t </ul>"
|
|
||||||
version: 1.2.1
|
version: 1.2.1
|
||||||
|
description: |
|
||||||
|
* **Improved** UI responsiveness (using threads) under Mac OS X.
|
||||||
|
* **Improved** result load/save speed and memory usage.
|
||||||
- date: 2007-03-17
|
- date: 2007-03-17
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Changed</b> the picture decoding libraries\
|
|
||||||
\ for both Mac OS X and Windows. The Mac OS X version uses the Core Graphics library\
|
|
||||||
\ and the Windows version uses the .NET framework imaging capabilities. This results\
|
|
||||||
\ in much faster scans. As a bonus, the Mac OS X version of dupeGuru PE now supports\
|
|
||||||
\ RAW images.</li>\n\t\t </ul>"
|
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
|
description: |
|
||||||
|
* **Changed** the picture decoding libraries for both Mac OS X and Windows. The Mac OS X version
|
||||||
|
uses the Core Graphics library and the Windows version uses the .NET framework imaging
|
||||||
|
capabilities. This results in much faster scans. As a bonus, the Mac OS X version of dupeGuru
|
||||||
|
PE now supports RAW images.
|
||||||
- date: 2007-02-11
|
- date: 2007-02-11
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Added</b> Re-orderable columns. In fact,\
|
|
||||||
\ I re-added the feature which was lost in the C# conversion in 2.4.0 (Windows).</li>\n\
|
|
||||||
\t\t\t\t\t\t<li><b>Fixed</b> a bug with all the Delete/Move/Copy actions with\
|
|
||||||
\ certain kinds of files.</li>\n\t\t </ul>"
|
|
||||||
version: 1.1.6
|
version: 1.1.6
|
||||||
|
description: |
|
||||||
|
* **Added** Re-orderable columns. In fact, I re-added the feature which was lost in the C#
|
||||||
|
conversion in 1.1.0 (Windows).
|
||||||
|
* **Fixed** a bug with all the Delete/Move/Copy actions with certain kinds of files.
|
||||||
- date: 2007-01-11
|
- date: 2007-01-11
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Fixed</b> a bug with the Move action.</li>\n\
|
|
||||||
\t\t </ul>"
|
|
||||||
version: 1.1.5
|
version: 1.1.5
|
||||||
|
description: |
|
||||||
|
* **Fixed** a bug with the Move action.
|
||||||
- date: 2007-01-09
|
- date: 2007-01-09
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Fixed</b> a \"ghosting\" bug. Dupes deleted\
|
|
||||||
\ by dupeGuru would sometimes come back in subsequent scans (Windows).</li>\n\t\
|
|
||||||
\t\t\t\t\t<li><b>Fixed</b> bugs sometimes making dupeGuru crash when marking a\
|
|
||||||
\ dupe (Windows).</li>\n\t\t\t\t\t\t<li><b>Fixed</b> some minor visual glitches\
|
|
||||||
\ (Windows).</li>\n\t\t </ul>"
|
|
||||||
version: 1.1.4
|
version: 1.1.4
|
||||||
|
description: |
|
||||||
|
* **Fixed** a "ghosting" bug. Dupes deleted by dupeGuru would sometimes come back in subsequent scans (Windows).
|
||||||
|
* **Fixed** bugs sometimes making dupeGuru crash when marking a dupe (Windows).
|
||||||
|
* **Fixed** some minor visual glitches (Windows).
|
||||||
- date: 2006-12-23
|
- date: 2006-12-23
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Improved</b> the caching system. This makes\
|
|
||||||
\ duplicate scans significantly faster.</li>\n\t\t\t\t\t\t<li><b>Improved</b>\
|
|
||||||
\ the rename file dialog to exclude the extension from the original selection\
|
|
||||||
\ (so when you start typing your new filename, it doesn't overwrite it) (Windows).</li>\n\
|
|
||||||
\t\t\t\t\t\t<li><b>Changed</b> some menu key shortcuts that created conflicts\
|
|
||||||
\ (Windows).</li>\n\t\t\t\t\t\t<li><b>Fixed</b> a bug preventing files from \"\
|
|
||||||
reference\" directories to be displayed in blue in the results (Windows).</li>\n\
|
|
||||||
\t\t\t\t\t\t<li><b>Fixed</b> a bug preventing some files to be sent to the recycle\
|
|
||||||
\ bin (Windows).</li>\n\t\t\t\t\t\t<li><b>Fixed</b> a bug with the \"Remove\"\
|
|
||||||
\ button of the directories panel (Windows).</li>\n\t\t\t\t\t\t<li><b>Fixed</b>\
|
|
||||||
\ a bug in the packaging preventing certain Windows configurations to start dupeGuru\
|
|
||||||
\ at all.</li>\n\t\t </ul>"
|
|
||||||
version: 1.1.3
|
version: 1.1.3
|
||||||
|
description: |
|
||||||
|
* **Improved** the caching system. This makes duplicate scans significantly faster.
|
||||||
|
* **Improved** the rename file dialog to exclude the extension from the original selection (so
|
||||||
|
when you start typing your new filename, it doesn't overwrite it) (Windows).
|
||||||
|
* **Changed** some menu key shortcuts that created conflicts (Windows).
|
||||||
|
* **Fixed** a bug preventing files from "reference" directories to be displayed in blue in the results (Windows).
|
||||||
|
* **Fixed** a bug preventing some files to be sent to the recycle bin (Windows).
|
||||||
|
* **Fixed** a bug with the "Remove" button of the directories panel (Windows).
|
||||||
|
* **Fixed** a bug in the packaging preventing certain Windows configurations to start dupeGuru at all.
|
||||||
- date: 2006-11-18
|
- date: 2006-11-18
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Fixed</b> a bug with directory states.</li>\n\
|
|
||||||
\t\t </ul>"
|
|
||||||
version: 1.1.2
|
version: 1.1.2
|
||||||
|
description: |
|
||||||
|
* **Fixed** a bug with directory states.
|
||||||
- date: 2006-11-17
|
- date: 2006-11-17
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Fixed</b> a bug causing the ignore list not\
|
|
||||||
\ to be saved.</li>\n\t\t\t\t\t\t<li><b>Fixed</b> a bug with selection under Power\
|
|
||||||
\ Marker mode.</li>\n\t\t </ul>"
|
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
|
description: |
|
||||||
|
* **Fixed** a bug causing the ignore list not to be saved.
|
||||||
|
* **Fixed** a bug with selection under Power Marker mode.
|
||||||
- date: 2006-11-15
|
- date: 2006-11-15
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Changed</b> the Windows interface. It is\
|
|
||||||
\ now .NET based.</li>\n\t\t\t\t\t\t<li><b>Added</b> an auto-update feature to\
|
|
||||||
\ the windows version.</li>\n\t\t\t\t\t\t<li><b>Changed</b> the way power marking\
|
|
||||||
\ works. It is now a mode instead of a separate window.</li>\n\t\t\t\t\t\t<li><b>Changed</b>\
|
|
||||||
\ the \"Size (MB)\" column for a \"Size (KB)\" column. The values are now \"ceiled\"\
|
|
||||||
\ instead of rounded. Therefore, a size \"0\" is now really 0 bytes, not just\
|
|
||||||
\ a value too small to be rounded up. It is also the case for delta values.</li>\n\
|
|
||||||
\t\t\t\t\t\t<li><b>Fixed</b> a bug sometimes making delete and move operations\
|
|
||||||
\ stall.</li>\n\t\t </ul>"
|
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
|
description: |
|
||||||
|
* **Changed** the Windows interface. It is now .NET based.
|
||||||
|
* **Added** an auto-update feature to the windows version.
|
||||||
|
* **Changed** the way power marking works. It is now a mode instead of a separate window.
|
||||||
|
* **Changed** the "Size (MB)" column for a "Size (KB)" column. The values are now "ceiled"
|
||||||
|
instead of rounded. Therefore, a size "0" is now really 0 bytes, not just a value too small to
|
||||||
|
be rounded up. It is also the case for delta values.
|
||||||
|
* **Fixed** a bug sometimes making delete and move operations stall.
|
||||||
- date: 2006-10-12
|
- date: 2006-10-12
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Added</b> an auto-update feature in the Mac\
|
|
||||||
\ OS X version (with Sparkle).</li>\n\t\t \t<li><b>Fixed</b> a bug\
|
|
||||||
\ sometimes causing inaccuracies of the Match %.</li>\n\t\t </ul>"
|
|
||||||
version: 1.0.5
|
version: 1.0.5
|
||||||
|
description: |
|
||||||
|
* **Added** an auto-update feature in the Mac OS X version (with Sparkle).
|
||||||
|
* **Fixed** a bug sometimes causing inaccuracies of the Match %.
|
||||||
- date: 2006-09-21
|
- date: 2006-09-21
|
||||||
description: "<ul>\n\t\t \t<li><b>Fixed</b> a bug with the cache system.</li>\n\
|
|
||||||
\t\t </ul>"
|
|
||||||
version: 1.0.4
|
version: 1.0.4
|
||||||
|
description: |
|
||||||
|
* **Fixed** a bug with the cache system.
|
||||||
- date: 2006-09-15
|
- date: 2006-09-15
|
||||||
description: "<ul>\n\t\t\t\t\t\t<li><b>Added</b> the ability to search for scaled\
|
|
||||||
\ duplicates.</li>\n\t\t\t\t\t\t<li><b>Added</b> a cache system for faster scans.</li>\n\
|
|
||||||
\t\t \t<li><b>Improved</b> speed of the scanning engine.</li>\n\t\t\
|
|
||||||
\ </ul>"
|
|
||||||
version: 1.0.3
|
version: 1.0.3
|
||||||
|
description: |
|
||||||
|
* **Added** the ability to search for scaled duplicates.
|
||||||
|
* **Added** a cache system for faster scans.
|
||||||
|
* **Improved** speed of the scanning engine.
|
||||||
- date: 2006-09-11
|
- date: 2006-09-11
|
||||||
description: "<ul>\n\t\t \t<li><b>Improved</b> speed of the scanning\
|
|
||||||
\ engine.</li>\n\t\t\t\t\t\t<li><b>Improved</b> the display of pictures in the\
|
|
||||||
\ details panel (Windows).</li>\n\t\t </ul>"
|
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
|
description: |
|
||||||
|
* **Improved** speed of the scanning engine.
|
||||||
|
* **Improved** the display of pictures in the details panel (Windows).
|
||||||
- date: 2006-09-08
|
- date: 2006-09-08
|
||||||
description: "<ul>\n\t\t \t<li>Initial release.</li>\n\t\t \
|
|
||||||
\ </ul>"
|
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
description: |
|
||||||
|
* Initial release.
|
||||||
|
|||||||
@@ -12,13 +12,12 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import os.path as op
|
import os.path as op
|
||||||
|
|
||||||
from PyQt4.QtCore import Qt, QTimer, QObject, QCoreApplication, QUrl, SIGNAL
|
from PyQt4.QtCore import QTimer, QObject, QCoreApplication, QUrl, SIGNAL
|
||||||
from PyQt4.QtGui import QProgressDialog, QDesktopServices, QFileDialog, QDialog, QMessageBox
|
from PyQt4.QtGui import QDesktopServices, QFileDialog, QDialog, QMessageBox
|
||||||
|
|
||||||
from hsutil import job
|
from hsutil import job
|
||||||
from hsutil.reg import RegistrationRequired
|
from hsutil.reg import RegistrationRequired
|
||||||
|
|
||||||
from core import fs
|
|
||||||
from core.app import DupeGuru as DupeGuruBase, JOB_SCAN, JOB_LOAD, JOB_MOVE, JOB_COPY, JOB_DELETE
|
from core.app import DupeGuru as DupeGuruBase, JOB_SCAN, JOB_LOAD, JOB_MOVE, JOB_COPY, JOB_DELETE
|
||||||
|
|
||||||
from qtlib.about_box import AboutBox
|
from qtlib.about_box import AboutBox
|
||||||
@@ -148,10 +147,6 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
if self.main_window._confirm(title, msg):
|
if self.main_window._confirm(title, msg):
|
||||||
DupeGuruBase.add_selected_to_ignore_list(self)
|
DupeGuruBase.add_selected_to_ignore_list(self)
|
||||||
|
|
||||||
def apply_filter(self, filter):
|
|
||||||
DupeGuruBase.apply_filter(self, filter)
|
|
||||||
self.emit(SIGNAL('resultsChanged()'))
|
|
||||||
|
|
||||||
@demo_method
|
@demo_method
|
||||||
def copy_or_move_marked(self, copy):
|
def copy_or_move_marked(self, copy):
|
||||||
opname = 'copy' if copy else 'move'
|
opname = 'copy' if copy else 'move'
|
||||||
@@ -165,14 +160,6 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
|
|
||||||
delete_marked = demo_method(DupeGuruBase.delete_marked)
|
delete_marked = demo_method(DupeGuruBase.delete_marked)
|
||||||
|
|
||||||
def make_selected_reference(self):
|
|
||||||
DupeGuruBase.make_selected_reference(self)
|
|
||||||
self.emit(SIGNAL('resultsChanged()'))
|
|
||||||
|
|
||||||
def remove_duplicates(self, duplicates):
|
|
||||||
DupeGuruBase.remove_duplicates(self, duplicates)
|
|
||||||
self.emit(SIGNAL('resultsChanged()'))
|
|
||||||
|
|
||||||
def remove_selected(self):
|
def remove_selected(self):
|
||||||
dupes = self.without_ref(self.selected_dupes)
|
dupes = self.without_ref(self.selected_dupes)
|
||||||
if not dupes:
|
if not dupes:
|
||||||
@@ -186,37 +173,10 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
def askForRegCode(self):
|
def askForRegCode(self):
|
||||||
self.reg.ask_for_code()
|
self.reg.ask_for_code()
|
||||||
|
|
||||||
def mark_all(self):
|
|
||||||
self.results.mark_all()
|
|
||||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
|
||||||
|
|
||||||
def mark_invert(self):
|
|
||||||
self.results.mark_invert()
|
|
||||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
|
||||||
|
|
||||||
def mark_none(self):
|
|
||||||
self.results.mark_none()
|
|
||||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
|
||||||
|
|
||||||
def openDebugLog(self):
|
def openDebugLog(self):
|
||||||
debugLogPath = op.join(self.appdata, 'debug.log')
|
debugLogPath = op.join(self.appdata, 'debug.log')
|
||||||
self._open_path(debugLogPath)
|
self._open_path(debugLogPath)
|
||||||
|
|
||||||
def remove_marked_duplicates(self):
|
|
||||||
marked = [d for d in self.results.dupes if self.results.is_marked(d)]
|
|
||||||
self.remove_duplicates(marked)
|
|
||||||
|
|
||||||
def rename_dupe(self, dupe, newname):
|
|
||||||
try:
|
|
||||||
dupe.rename(newname)
|
|
||||||
return True
|
|
||||||
except (IndexError, fs.FSError) as e:
|
|
||||||
logging.warning("dupeGuru Warning: %s" % unicode(e))
|
|
||||||
return False
|
|
||||||
|
|
||||||
def select_dupes(self, dupes):
|
|
||||||
self._select_dupes(dupes)
|
|
||||||
|
|
||||||
def show_about_box(self):
|
def show_about_box(self):
|
||||||
self.about_box.show()
|
self.about_box.show()
|
||||||
|
|
||||||
@@ -238,11 +198,6 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
self.prefs.save()
|
self.prefs.save()
|
||||||
self._update_options()
|
self._update_options()
|
||||||
|
|
||||||
def toggle_marking_for_dupes(self, dupes):
|
|
||||||
for dupe in dupes:
|
|
||||||
self.results.mark_toggle(dupe)
|
|
||||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
|
||||||
|
|
||||||
#--- Events
|
#--- Events
|
||||||
def application_will_terminate(self):
|
def application_will_terminate(self):
|
||||||
self.save()
|
self.save()
|
||||||
@@ -253,7 +208,7 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
self.reg.show_nag()
|
self.reg.show_nag()
|
||||||
|
|
||||||
def job_finished(self, jobid):
|
def job_finished(self, jobid):
|
||||||
self.emit(SIGNAL('resultsChanged()'))
|
self._job_completed(jobid)
|
||||||
if jobid in (JOB_MOVE, JOB_COPY, JOB_DELETE) and self.last_op_error_count > 0:
|
if jobid in (JOB_MOVE, JOB_COPY, JOB_DELETE) and self.last_op_error_count > 0:
|
||||||
msg = "{0} files could not be processed.".format(self.results.mark_count)
|
msg = "{0} files could not be processed.".format(self.results.mark_count)
|
||||||
QMessageBox.warning(self.main_window, 'Warning', msg)
|
QMessageBox.warning(self.main_window, 'Warning', msg)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class DetailsDialog(QDialog):
|
|||||||
self.tableModel = DetailsModel(self.model)
|
self.tableModel = DetailsModel(self.model)
|
||||||
# tableView is defined in subclasses
|
# tableView is defined in subclasses
|
||||||
self.tableView.setModel(self.tableModel)
|
self.tableView.setModel(self.tableModel)
|
||||||
|
self.model.connect()
|
||||||
|
|
||||||
def _setupUi(self): # Virtual
|
def _setupUi(self): # Virtual
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class DirectoriesModel(TreeModel):
|
|||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
TreeModel.__init__(self)
|
TreeModel.__init__(self)
|
||||||
self.model = DirectoryTree(self, app)
|
self.model = DirectoryTree(self, app)
|
||||||
|
self.model.connect()
|
||||||
|
|
||||||
def _createNode(self, ref, row):
|
def _createNode(self, ref, row):
|
||||||
return RefNode(self, None, ref, row)
|
return RefNode(self, None, ref, row)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
from PyQt4.QtCore import Qt, QCoreApplication, QProcess, SIGNAL, QUrl
|
from PyQt4.QtCore import Qt, QCoreApplication, QProcess, SIGNAL, QUrl
|
||||||
from PyQt4.QtGui import (QMainWindow, QMenu, QPixmap, QIcon, QToolButton, QLabel, QHeaderView,
|
from PyQt4.QtGui import (QMainWindow, QMenu, QPixmap, QIcon, QToolButton, QLabel, QHeaderView,
|
||||||
QMessageBox, QInputDialog, QLineEdit, QItemSelectionModel, QDesktopServices)
|
QMessageBox, QInputDialog, QLineEdit, QDesktopServices)
|
||||||
|
|
||||||
from hsutil.misc import nonone
|
from hsutil.misc import nonone
|
||||||
|
|
||||||
@@ -16,7 +16,8 @@ from core.app import NoScannableFileError, AllFilesAreRefError
|
|||||||
|
|
||||||
import dg_rc
|
import dg_rc
|
||||||
from main_window_ui import Ui_MainWindow
|
from main_window_ui import Ui_MainWindow
|
||||||
from results_model import ResultsDelegate, ResultsModel
|
from results_model import ResultsModel
|
||||||
|
from stats_label import StatsLabel
|
||||||
|
|
||||||
class MainWindow(QMainWindow, Ui_MainWindow):
|
class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
@@ -24,23 +25,16 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.app = app
|
self.app = app
|
||||||
self._last_filter = None
|
self._last_filter = None
|
||||||
self._setupUi()
|
self._setupUi()
|
||||||
self.resultsDelegate = ResultsDelegate()
|
self.resultsModel = ResultsModel(self.app, self.resultsView)
|
||||||
self.resultsModel = ResultsModel(self.app)
|
self.stats = StatsLabel(app, self.statusLabel)
|
||||||
self.resultsView.setModel(self.resultsModel)
|
|
||||||
self.resultsView.setItemDelegate(self.resultsDelegate)
|
|
||||||
self._load_columns()
|
self._load_columns()
|
||||||
self._update_column_actions_status()
|
self._update_column_actions_status()
|
||||||
self.resultsView.expandAll()
|
|
||||||
self._update_status_line()
|
|
||||||
|
|
||||||
self.connect(self.app, SIGNAL('resultsChanged()'), self.resultsChanged)
|
|
||||||
self.connect(self.app, SIGNAL('dupeMarkingChanged()'), self.dupeMarkingChanged)
|
|
||||||
self.connect(self.actionQuit, SIGNAL('triggered()'), QCoreApplication.instance().quit)
|
self.connect(self.actionQuit, SIGNAL('triggered()'), QCoreApplication.instance().quit)
|
||||||
self.connect(self.resultsView.selectionModel(), SIGNAL('selectionChanged(QItemSelection,QItemSelection)'), self.selectionChanged)
|
|
||||||
self.connect(self.menuColumns, SIGNAL('triggered(QAction*)'), self.columnToggled)
|
self.connect(self.menuColumns, SIGNAL('triggered(QAction*)'), self.columnToggled)
|
||||||
self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate)
|
self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate)
|
||||||
self.connect(self.resultsModel, SIGNAL('modelReset()'), self.resultsReset)
|
|
||||||
self.connect(self.resultsView, SIGNAL('doubleClicked()'), self.resultsDoubleClicked)
|
self.connect(self.resultsView, SIGNAL('doubleClicked()'), self.resultsDoubleClicked)
|
||||||
|
self.connect(self.resultsView, SIGNAL('spacePressed()'), self.resultsSpacePressed)
|
||||||
|
|
||||||
def _setupUi(self):
|
def _setupUi(self):
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
@@ -108,11 +102,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
h.setSectionHidden(index, not visible)
|
h.setSectionHidden(index, not visible)
|
||||||
h.setResizeMode(0, QHeaderView.Stretch)
|
h.setResizeMode(0, QHeaderView.Stretch)
|
||||||
|
|
||||||
def _redraw_results(self):
|
|
||||||
# HACK. this is the only way I found to update the widget without reseting everything
|
|
||||||
self.resultsView.scroll(0, 1)
|
|
||||||
self.resultsView.scroll(0, -1)
|
|
||||||
|
|
||||||
def _save_columns(self):
|
def _save_columns(self):
|
||||||
h = self.resultsView.header()
|
h = self.resultsView.header()
|
||||||
widths = []
|
widths = []
|
||||||
@@ -131,9 +120,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
colid = action.column_index
|
colid = action.column_index
|
||||||
action.setChecked(not h.isSectionHidden(colid))
|
action.setChecked(not h.isSectionHidden(colid))
|
||||||
|
|
||||||
def _update_status_line(self):
|
|
||||||
self.statusLabel.setText(self.app.stat_line)
|
|
||||||
|
|
||||||
#--- Actions
|
#--- Actions
|
||||||
def aboutTriggered(self):
|
def aboutTriggered(self):
|
||||||
self.app.show_about_box()
|
self.app.show_about_box()
|
||||||
@@ -185,8 +171,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.app.delete_marked()
|
self.app.delete_marked()
|
||||||
|
|
||||||
def deltaTriggered(self):
|
def deltaTriggered(self):
|
||||||
self.resultsModel.delta = self.actionDelta.isChecked()
|
self.resultsModel.delta_values = self.actionDelta.isChecked()
|
||||||
self._redraw_results()
|
|
||||||
|
|
||||||
def detailsTriggered(self):
|
def detailsTriggered(self):
|
||||||
self.app.show_details()
|
self.app.show_details()
|
||||||
@@ -217,8 +202,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.app.mark_none()
|
self.app.mark_none()
|
||||||
|
|
||||||
def markSelectedTriggered(self):
|
def markSelectedTriggered(self):
|
||||||
dupes = self.resultsView.selectedDupes()
|
self.app.toggle_selected_mark_state()
|
||||||
self.app.toggle_marking_for_dupes(dupes)
|
|
||||||
|
|
||||||
def moveTriggered(self):
|
def moveTriggered(self):
|
||||||
self.app.copy_or_move_marked(False)
|
self.app.copy_or_move_marked(False)
|
||||||
@@ -245,7 +229,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
title = "Remove duplicates"
|
title = "Remove duplicates"
|
||||||
msg = "You are about to remove {0} files from results. Continue?".format(count)
|
msg = "You are about to remove {0} files from results. Continue?".format(count)
|
||||||
if self._confirm(title, msg):
|
if self._confirm(title, msg):
|
||||||
self.app.remove_marked_duplicates()
|
self.app.remove_marked()
|
||||||
|
|
||||||
def removeSelectedTriggered(self):
|
def removeSelectedTriggered(self):
|
||||||
self.app.remove_selected()
|
self.app.remove_selected()
|
||||||
@@ -292,25 +276,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
def contextMenuEvent(self, event):
|
def contextMenuEvent(self, event):
|
||||||
self.actionActions.menu().exec_(event.globalPos())
|
self.actionActions.menu().exec_(event.globalPos())
|
||||||
|
|
||||||
def dupeMarkingChanged(self):
|
|
||||||
self._redraw_results()
|
|
||||||
self._update_status_line()
|
|
||||||
|
|
||||||
def resultsChanged(self):
|
|
||||||
self.resultsView.model().reset()
|
|
||||||
|
|
||||||
def resultsDoubleClicked(self):
|
def resultsDoubleClicked(self):
|
||||||
self.app.open_selected()
|
self.app.open_selected()
|
||||||
|
|
||||||
def resultsReset(self):
|
def resultsSpacePressed(self):
|
||||||
self.resultsView.expandAll()
|
self.app.toggle_selected_mark_state()
|
||||||
if self.app.selected_dupes:
|
|
||||||
[modelIndex] = self.resultsModel.indexesForDupes(self.app.selected_dupes[:1])
|
|
||||||
if modelIndex.isValid():
|
|
||||||
flags = QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows
|
|
||||||
self.resultsView.selectionModel().setCurrentIndex(modelIndex, flags)
|
|
||||||
self._update_status_line()
|
|
||||||
|
|
||||||
def selectionChanged(self, selected, deselected):
|
|
||||||
self.app.select_dupes(self.resultsView.selectedDupes())
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,74 +6,63 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
from PyQt4.QtCore import SIGNAL, Qt, QAbstractItemModel, QModelIndex, QRect
|
from PyQt4.QtCore import SIGNAL, Qt
|
||||||
from PyQt4.QtGui import QBrush, QStyledItemDelegate, QFont, QTreeView, QColor
|
from PyQt4.QtGui import (QBrush, QStyledItemDelegate, QFont, QTreeView, QColor, QItemSelectionModel,
|
||||||
|
QItemSelection)
|
||||||
|
|
||||||
from qtlib.tree_model import TreeNode, TreeModel
|
from qtlib.tree_model import TreeModel, RefNode
|
||||||
|
|
||||||
class ResultNode(TreeNode):
|
|
||||||
def __init__(self, model, parent, row, dupe, group):
|
|
||||||
TreeNode.__init__(self, model, parent, row)
|
|
||||||
self.dupe = dupe
|
|
||||||
self.group = group
|
|
||||||
self._normalData = None
|
|
||||||
self._deltaData = None
|
|
||||||
|
|
||||||
def _createNode(self, ref, row):
|
|
||||||
return ResultNode(self.model, self, row, ref, self.group)
|
|
||||||
|
|
||||||
def _getChildren(self):
|
|
||||||
return self.group.dupes if self.dupe is self.group.ref else []
|
|
||||||
|
|
||||||
def invalidate(self):
|
|
||||||
self._normalData = None
|
|
||||||
self._deltaData = None
|
|
||||||
TreeNode.invalidate(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def normalData(self):
|
|
||||||
if self._normalData is None:
|
|
||||||
self._normalData = self.model._app._get_display_info(self.dupe, self.group, delta=False)
|
|
||||||
return self._normalData
|
|
||||||
|
|
||||||
@property
|
|
||||||
def deltaData(self):
|
|
||||||
if self._deltaData is None:
|
|
||||||
self._deltaData = self.model._app._get_display_info(self.dupe, self.group, delta=True)
|
|
||||||
return self._deltaData
|
|
||||||
|
|
||||||
|
from core.gui.result_tree import ResultTree as ResultTreeModel
|
||||||
|
|
||||||
class ResultsDelegate(QStyledItemDelegate):
|
class ResultsDelegate(QStyledItemDelegate):
|
||||||
def initStyleOption(self, option, index):
|
def initStyleOption(self, option, index):
|
||||||
QStyledItemDelegate.initStyleOption(self, option, index)
|
QStyledItemDelegate.initStyleOption(self, option, index)
|
||||||
node = index.internalPointer()
|
node = index.internalPointer()
|
||||||
if node.group.ref is node.dupe:
|
ref = node.ref
|
||||||
|
if ref._group.ref is ref._dupe:
|
||||||
newfont = QFont(option.font)
|
newfont = QFont(option.font)
|
||||||
newfont.setBold(True)
|
newfont.setBold(True)
|
||||||
option.font = newfont
|
option.font = newfont
|
||||||
|
|
||||||
|
|
||||||
class ResultsModel(TreeModel):
|
class ResultsModel(TreeModel):
|
||||||
def __init__(self, app):
|
def __init__(self, app, view):
|
||||||
|
TreeModel.__init__(self)
|
||||||
|
self.view = view
|
||||||
self._app = app
|
self._app = app
|
||||||
self._results = app.results
|
|
||||||
self._data = app.data
|
self._data = app.data
|
||||||
self._delta_columns = app.DELTA_COLUMNS
|
self._delta_columns = app.DELTA_COLUMNS
|
||||||
self.delta = False
|
self.resultsDelegate = ResultsDelegate()
|
||||||
self._power_marker = False
|
self.model = ResultTreeModel(self, app)
|
||||||
TreeModel.__init__(self)
|
self.view.setItemDelegate(self.resultsDelegate)
|
||||||
|
self.view.setModel(self)
|
||||||
|
self.model.connect()
|
||||||
|
|
||||||
|
self.connect(self.view.selectionModel(), SIGNAL('selectionChanged(QItemSelection,QItemSelection)'), self.selectionChanged)
|
||||||
|
|
||||||
def _createNode(self, ref, row):
|
def _createNode(self, ref, row):
|
||||||
if self.power_marker:
|
return RefNode(self, None, ref, row)
|
||||||
# ref is a dupe
|
|
||||||
group = self._results.get_group_of_duplicate(ref)
|
|
||||||
return ResultNode(self, None, row, ref, group)
|
|
||||||
else:
|
|
||||||
# ref is a group
|
|
||||||
return ResultNode(self, None, row, ref.ref, ref)
|
|
||||||
|
|
||||||
def _getChildren(self):
|
def _getChildren(self):
|
||||||
return self._results.dupes if self.power_marker else self._results.groups
|
return list(self.model)
|
||||||
|
|
||||||
|
def _updateSelection(self):
|
||||||
|
selectedIndexes = []
|
||||||
|
for path in self.model.selected_paths:
|
||||||
|
modelIndex = self.findIndex(path)
|
||||||
|
if modelIndex.isValid():
|
||||||
|
selectedIndexes.append(modelIndex)
|
||||||
|
if selectedIndexes:
|
||||||
|
selection = QItemSelection()
|
||||||
|
for modelIndex in selectedIndexes:
|
||||||
|
selection.select(modelIndex, modelIndex)
|
||||||
|
flags = QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows
|
||||||
|
self.view.selectionModel().select(selection, flags)
|
||||||
|
flags = QItemSelectionModel.Rows
|
||||||
|
self.view.selectionModel().setCurrentIndex(selectedIndexes[0], flags)
|
||||||
|
self.view.scrollTo(selectedIndexes[0])
|
||||||
|
else:
|
||||||
|
self.view.selectionModel().clear()
|
||||||
|
|
||||||
def columnCount(self, parent):
|
def columnCount(self, parent):
|
||||||
return len(self._data.COLUMNS)
|
return len(self._data.COLUMNS)
|
||||||
@@ -82,51 +71,23 @@ class ResultsModel(TreeModel):
|
|||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return None
|
return None
|
||||||
node = index.internalPointer()
|
node = index.internalPointer()
|
||||||
|
ref = node.ref
|
||||||
if role == Qt.DisplayRole:
|
if role == Qt.DisplayRole:
|
||||||
data = node.deltaData if self.delta else node.normalData
|
data = ref.data_delta if self.model.delta_values else ref.data
|
||||||
return data[index.column()]
|
return data[index.column()]
|
||||||
elif role == Qt.CheckStateRole:
|
elif role == Qt.CheckStateRole:
|
||||||
if index.column() == 0 and node.dupe is not node.group.ref:
|
if index.column() == 0 and ref.markable:
|
||||||
state = Qt.Checked if self._results.is_marked(node.dupe) else Qt.Unchecked
|
return Qt.Checked if ref.marked else Qt.Unchecked
|
||||||
return state
|
|
||||||
elif role == Qt.ForegroundRole:
|
elif role == Qt.ForegroundRole:
|
||||||
if node.dupe is node.group.ref or node.dupe.is_ref:
|
if ref._dupe is ref._group.ref or ref._dupe.is_ref:
|
||||||
return QBrush(Qt.blue)
|
return QBrush(Qt.blue)
|
||||||
elif self.delta and index.column() in self._delta_columns:
|
elif self.model.delta_values and index.column() in self._delta_columns:
|
||||||
return QBrush(QColor(255, 142, 40)) # orange
|
return QBrush(QColor(255, 142, 40)) # orange
|
||||||
elif role == Qt.EditRole:
|
elif role == Qt.EditRole:
|
||||||
if index.column() == 0:
|
if index.column() == 0:
|
||||||
return node.normalData[index.column()]
|
return ref.data[index.column()]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def dupesForIndexes(self, indexes):
|
|
||||||
nodes = [index.internalPointer() for index in indexes]
|
|
||||||
return [node.dupe for node in nodes]
|
|
||||||
|
|
||||||
def indexesForDupes(self, dupes):
|
|
||||||
def index(dupe):
|
|
||||||
try:
|
|
||||||
if self.power_marker:
|
|
||||||
row = self._results.dupes.index(dupe)
|
|
||||||
node = self.subnodes[row]
|
|
||||||
assert node.dupe is dupe
|
|
||||||
return self.createIndex(row, 0, node)
|
|
||||||
else:
|
|
||||||
group = self._results.get_group_of_duplicate(dupe)
|
|
||||||
row = self._results.groups.index(group)
|
|
||||||
node = self.subnodes[row]
|
|
||||||
if dupe is group.ref:
|
|
||||||
assert node.dupe is dupe
|
|
||||||
return self.createIndex(row, 0, node)
|
|
||||||
subrow = group.dupes.index(dupe)
|
|
||||||
subnode = node.subnodes[subrow]
|
|
||||||
assert subnode.dupe is dupe
|
|
||||||
return self.createIndex(subrow, 0, subnode)
|
|
||||||
except ValueError: # the dupe is not there anymore
|
|
||||||
return QModelIndex()
|
|
||||||
|
|
||||||
return map(index, dupes)
|
|
||||||
|
|
||||||
def flags(self, index):
|
def flags(self, index):
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return Qt.ItemIsEnabled
|
return Qt.ItemIsEnabled
|
||||||
@@ -144,48 +105,61 @@ class ResultsModel(TreeModel):
|
|||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return False
|
return False
|
||||||
node = index.internalPointer()
|
node = index.internalPointer()
|
||||||
|
ref = node.ref
|
||||||
if role == Qt.CheckStateRole:
|
if role == Qt.CheckStateRole:
|
||||||
if index.column() == 0:
|
if index.column() == 0:
|
||||||
self._app.toggle_marking_for_dupes([node.dupe])
|
self._app.mark_dupe(ref._dupe, value.toBool())
|
||||||
return True
|
return True
|
||||||
if role == Qt.EditRole:
|
if role == Qt.EditRole:
|
||||||
if index.column() == 0:
|
if index.column() == 0:
|
||||||
value = unicode(value.toString())
|
value = unicode(value.toString())
|
||||||
if self._app.rename_dupe(node.dupe, value):
|
return self.model.rename_selected(value)
|
||||||
node.invalidate()
|
|
||||||
return True
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def sort(self, column, order):
|
def sort(self, column, order):
|
||||||
if self.power_marker:
|
self.model.sort(column, order == Qt.AscendingOrder)
|
||||||
self._results.sort_dupes(column, order == Qt.AscendingOrder, self.delta)
|
|
||||||
else:
|
|
||||||
self._results.sort_groups(column, order == Qt.AscendingOrder)
|
|
||||||
self.reset()
|
|
||||||
|
|
||||||
def toggleMarked(self, indexes):
|
|
||||||
assert indexes
|
|
||||||
dupes = self.dupesForIndexes(indexes)
|
|
||||||
self._app.toggle_marking_for_dupes(dupes)
|
|
||||||
|
|
||||||
#--- Properties
|
#--- Properties
|
||||||
@property
|
@property
|
||||||
def power_marker(self):
|
def power_marker(self):
|
||||||
return self._power_marker
|
return self.model.power_marker
|
||||||
|
|
||||||
@power_marker.setter
|
@power_marker.setter
|
||||||
def power_marker(self, value):
|
def power_marker(self, value):
|
||||||
if value == self._power_marker:
|
self.model.power_marker = value
|
||||||
return
|
|
||||||
self._power_marker = value
|
@property
|
||||||
|
def delta_values(self):
|
||||||
|
return self.model.delta_values
|
||||||
|
|
||||||
|
@delta_values.setter
|
||||||
|
def delta_values(self, value):
|
||||||
|
self.model.delta_values = value
|
||||||
|
|
||||||
|
#--- Events
|
||||||
|
def selectionChanged(self, selected, deselected):
|
||||||
|
indexes = self.view.selectionModel().selectedRows()
|
||||||
|
nodes = [index.internalPointer() for index in indexes]
|
||||||
|
self.model.selected_nodes = [node.ref for node in nodes]
|
||||||
|
|
||||||
|
#--- model --> view
|
||||||
|
def refresh(self):
|
||||||
self.reset()
|
self.reset()
|
||||||
|
self.view.expandAll()
|
||||||
|
self._updateSelection()
|
||||||
|
|
||||||
|
def invalidate_markings(self):
|
||||||
|
# redraw view
|
||||||
|
# HACK. this is the only way I found to update the widget without reseting everything
|
||||||
|
self.view.scroll(0, 1)
|
||||||
|
self.view.scroll(0, -1)
|
||||||
|
|
||||||
|
|
||||||
class ResultsView(QTreeView):
|
class ResultsView(QTreeView):
|
||||||
#--- Override
|
#--- Override
|
||||||
def keyPressEvent(self, event):
|
def keyPressEvent(self, event):
|
||||||
if event.text() == ' ':
|
if event.text() == ' ':
|
||||||
self.model().toggleMarked(self.selectionModel().selectedRows())
|
self.emit(SIGNAL('spacePressed()'))
|
||||||
return
|
return
|
||||||
QTreeView.keyPressEvent(self, event)
|
QTreeView.keyPressEvent(self, event)
|
||||||
|
|
||||||
@@ -193,11 +167,3 @@ class ResultsView(QTreeView):
|
|||||||
self.emit(SIGNAL('doubleClicked()'))
|
self.emit(SIGNAL('doubleClicked()'))
|
||||||
# We don't call the superclass' method because the default behavior is to rename the cell.
|
# We don't call the superclass' method because the default behavior is to rename the cell.
|
||||||
|
|
||||||
def setModel(self, model):
|
|
||||||
assert isinstance(model, ResultsModel)
|
|
||||||
QTreeView.setModel(self, model)
|
|
||||||
|
|
||||||
#--- Public
|
|
||||||
def selectedDupes(self):
|
|
||||||
return self.model().dupesForIndexes(self.selectionModel().selectedRows())
|
|
||||||
|
|
||||||
|
|||||||
20
qt/base/stats_label.py
Normal file
20
qt/base/stats_label.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Created By: Virgil Dupras
|
||||||
|
# Created On: 2010-02-12
|
||||||
|
# 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 core.gui.stats_label import StatsLabel as StatsLabelModel
|
||||||
|
|
||||||
|
class StatsLabel(object):
|
||||||
|
def __init__(self, app, view):
|
||||||
|
self.view = view
|
||||||
|
self.model = StatsLabelModel(self, app)
|
||||||
|
self.model.connect()
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
self.view.setText(self.model.display)
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ from preferences_dialog import PreferencesDialog
|
|||||||
class DupeGuru(DupeGuruBase):
|
class DupeGuru(DupeGuruBase):
|
||||||
LOGO_NAME = 'logo_me'
|
LOGO_NAME = 'logo_me'
|
||||||
NAME = 'dupeGuru Music Edition'
|
NAME = 'dupeGuru Music Edition'
|
||||||
VERSION = '5.7.1'
|
VERSION = '5.7.2'
|
||||||
DELTA_COLUMNS = frozenset([2, 3, 4, 5, 7, 8])
|
DELTA_COLUMNS = frozenset([2, 3, 4, 5, 7, 8])
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class File(fs.File):
|
|||||||
class DupeGuru(DupeGuruBase):
|
class DupeGuru(DupeGuruBase):
|
||||||
LOGO_NAME = 'logo_pe'
|
LOGO_NAME = 'logo_pe'
|
||||||
NAME = 'dupeGuru Picture Edition'
|
NAME = 'dupeGuru Picture Edition'
|
||||||
VERSION = '1.8.2'
|
VERSION = '1.8.3'
|
||||||
DELTA_COLUMNS = frozenset([2, 5, 6])
|
DELTA_COLUMNS = frozenset([2, 5, 6])
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user