diff --git a/cocoa/base/DirectoryOutline.h b/cocoa/base/DirectoryOutline.h new file mode 100644 index 00000000..997fa6dc --- /dev/null +++ b/cocoa/base/DirectoryOutline.h @@ -0,0 +1,16 @@ +/* +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 +#import "HSOutline.h" +#import "PyDirectoryOutline.h" + +@interface DirectoryOutline : HSOutline {} +- (id)initWithPyParent:(id)aPyParent view:(HSOutlineView *)aOutlineView; +- (PyDirectoryOutline *)py; +@end; \ No newline at end of file diff --git a/cocoa/base/DirectoryOutline.m b/cocoa/base/DirectoryOutline.m new file mode 100644 index 00000000..092f5f1c --- /dev/null +++ b/cocoa/base/DirectoryOutline.m @@ -0,0 +1,77 @@ +/* +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 "DirectoryOutline.h" + +@implementation DirectoryOutline +- (id)initWithPyParent:(id)aPyParent view:(HSOutlineView *)aOutlineView +{ + self = [super initWithPyClassName:@"PyDirectoryOutline" pyParent:aPyParent view:aOutlineView]; + [outlineView registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; + return self; +} + +- (PyDirectoryOutline *)py +{ + return (PyDirectoryOutline *)py; +} + +/* Delegate */ +- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index +{ + NSPasteboard *pboard; + NSDragOperation sourceDragMask; + sourceDragMask = [info draggingSourceOperationMask]; + pboard = [info draggingPasteboard]; + if ([[pboard types] containsObject:NSFilenamesPboardType]) { + if (sourceDragMask & NSDragOperationLink) + return NSDragOperationLink; + } + return NSDragOperationNone; +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index +{ + NSPasteboard *pboard; + NSDragOperation sourceDragMask; + sourceDragMask = [info draggingSourceOperationMask]; + pboard = [info draggingPasteboard]; + if ([[pboard types] containsObject:NSFilenamesPboardType]) { + NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType]; + if (!(sourceDragMask & NSDragOperationLink)) + return NO; + for (NSString *filename in filenames) { + [[self py] addDirectory:filename]; + } + } + return YES; +} + +- (void)outlineView:(NSOutlineView *)aOutlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + if ([cell isKindOfClass:[NSTextFieldCell class]]) { + NSTextFieldCell *textCell = cell; + NSIndexPath *path = item; + BOOL selected = [path isEqualTo:[outlineView selectedPath]]; + if (selected) { + [textCell setTextColor:[NSColor blackColor]]; + return; + } + NSInteger state = [self intProperty:@"state" valueAtPath:path]; + if (state == 1) { + [textCell setTextColor:[NSColor blueColor]]; + } + else if (state == 2) { + [textCell setTextColor:[NSColor redColor]]; + } + else { + [textCell setTextColor:[NSColor blackColor]]; + } + } +} +@end \ No newline at end of file diff --git a/cocoa/base/DirectoryPanel.h b/cocoa/base/DirectoryPanel.h index f6eccf6c..612899f7 100644 --- a/cocoa/base/DirectoryPanel.h +++ b/cocoa/base/DirectoryPanel.h @@ -8,31 +8,23 @@ http://www.hardcoded.net/licenses/hs_license #import #import "RecentDirectories.h" -#import "Outline.h" +#import "HSOutlineView.h" +#import "DirectoryOutline.h" #import "PyDupeGuru.h" -@interface DirectoryOutline : OutlineView -{ -} -@end - -@protocol DirectoryOutlineDelegate -- (void)outlineView:(NSOutlineView *)outlineView addDirectory:(NSString *)directory; -@end - @interface DirectoryPanel : NSWindowController { IBOutlet NSPopUpButton *addButtonPopUp; - IBOutlet DirectoryOutline *directories; + IBOutlet HSOutlineView *outlineView; IBOutlet NSButton *removeButton; PyDupeGuruBase *_py; RecentDirectories *_recentDirectories; + DirectoryOutline *outline; } - (id)initWithParentApp:(id)aParentApp; - (IBAction)askForDirectory:(id)sender; -- (IBAction)changeDirectoryState:(id)sender; - (IBAction)popupAddDirectoryMenu:(id)sender; - (IBAction)removeSelectedDirectory:(id)sender; - (IBAction)toggleVisible:(id)sender; diff --git a/cocoa/base/DirectoryPanel.m b/cocoa/base/DirectoryPanel.m index 4e71ff80..d2c2d855 100644 --- a/cocoa/base/DirectoryPanel.m +++ b/cocoa/base/DirectoryPanel.m @@ -11,48 +11,6 @@ http://www.hardcoded.net/licenses/hs_license #import "Utils.h" #import "AppDelegate.h" -@implementation DirectoryOutline -- (void)doInit -{ - [super doInit]; - [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; -} - -- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index -{ - NSPasteboard *pboard; - NSDragOperation sourceDragMask; - sourceDragMask = [info draggingSourceOperationMask]; - pboard = [info draggingPasteboard]; - if ([[pboard types] containsObject:NSFilenamesPboardType]) - { - if (sourceDragMask & NSDragOperationLink) - return NSDragOperationLink; - } - return NSDragOperationNone; -} - -- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index -{ - NSPasteboard *pboard; - NSDragOperation sourceDragMask; - sourceDragMask = [info draggingSourceOperationMask]; - pboard = [info draggingPasteboard]; - if ( [[pboard types] containsObject:NSFilenamesPboardType] ) - { - NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType]; - if (!(sourceDragMask & NSDragOperationLink)) - return NO; - if (([self delegate] == nil) || (![[self delegate] respondsToSelector:@selector(outlineView:addDirectory:)])) - return NO; - for (NSString *filename in filenames) - [[self delegate] outlineView:self addDirectory:filename]; - } - return YES; -} - -@end - @implementation DirectoryPanel - (id)initWithParentApp:(id)aParentApp { @@ -61,23 +19,19 @@ http://www.hardcoded.net/licenses/hs_license AppDelegateBase *app = aParentApp; _py = [app py]; _recentDirectories = [app recentDirectories]; - [directories setPy:_py]; - NSPopUpButtonCell *cell = [[directories tableColumnWithIdentifier:@"1"] dataCell]; - [cell addItemWithTitle:@"Normal"]; - [cell addItemWithTitle:@"Reference"]; - [cell addItemWithTitle:@"Excluded"]; - for (NSInteger i=0;i<[[cell itemArray] count];i++) - { - NSMenuItem *mi = [[cell itemArray] objectAtIndex:i]; - [mi setTarget:self]; - [mi setAction:@selector(changeDirectoryState:)]; - [mi setTag:i]; - } + outline = [[DirectoryOutline alloc] initWithPyParent:_py view:outlineView]; [self refreshRemoveButtonText]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(directorySelectionChanged:) name:NSOutlineViewSelectionDidChangeNotification object:directories]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(directorySelectionChanged:) + name:NSOutlineViewSelectionDidChangeNotification object:outlineView]; return self; } +- (void)dealloc +{ + [outline release]; + [super dealloc]; +} + /* Actions */ - (IBAction)askForDirectory:(id)sender @@ -95,15 +49,6 @@ http://www.hardcoded.net/licenses/hs_license } } -- (IBAction)changeDirectoryState:(id)sender -{ - OVNode *node = [directories itemAtRow:[directories clickedRow]]; - [_py setDirectory:p2a([node indexPath]) state:i2n([sender tag])]; - [node resetAllBuffers]; - [directories reloadItem:node reloadChildren:YES]; - [directories display]; -} - - (IBAction)popupAddDirectoryMenu:(id)sender { if ([[_recentDirectories directories] count] == 0) @@ -125,21 +70,17 @@ http://www.hardcoded.net/licenses/hs_license - (IBAction)removeSelectedDirectory:(id)sender { [[self window] makeKeyAndOrderFront:nil]; - if ([directories selectedRow] < 0) + if ([outlineView selectedRow] < 0) return; - OVNode *node = [directories itemAtRow:[directories selectedRow]]; - if ([node level] == 1) - { - [_py removeDirectory:i2n([node index])]; - [directories reloadData]; + NSIndexPath *path = [outline selectedIndexPath]; + NSInteger state = [outline intProperty:@"state" valueAtPath:path]; + if (([path length] == 1) && (state != 2)) { + [_py removeDirectory:i2n([path indexAtPosition:0])]; } - else - { - NSInteger state = n2i([[node buffer] objectAtIndex:1]); + else { NSInteger newState = state == 2 ? 0 : 2; // If excluded, put it back - [_py setDirectory:p2a([node indexPath]) state:i2n(newState)]; - [node resetAllBuffers]; - [directories display]; + [outline setIntProperty:@"state" value:newState atPath:path]; + [outlineView display]; } [self refreshRemoveButtonText]; } @@ -150,70 +91,40 @@ http://www.hardcoded.net/licenses/hs_license } /* Public */ - - (void)addDirectory:(NSString *)directory { NSInteger r = [[_py addDirectory:directory] intValue]; - if (r) - { + if (r) { NSString *m; - switch (r) - { - case 1: - { + switch (r) { + case 1: { m = @"This directory already is in the list."; break; } - case 2: - { + case 2: { m = @"This directory does not exist."; break; } } [Dialogs showMessage:m]; } - [directories reloadData]; [_recentDirectories addDirectory:directory]; [[self window] makeKeyAndOrderFront:nil]; } - (void)refreshRemoveButtonText { - if ([directories selectedRow] < 0) - { + if ([outlineView selectedRow] < 0) { [removeButton setEnabled:NO]; return; } [removeButton setEnabled:YES]; - OVNode *node = [directories itemAtRow:[directories selectedRow]]; - NSInteger state = n2i([[node buffer] objectAtIndex:1]); + NSInteger state = [outline intProperty:@"state" valueAtPath:[outline selectedIndexPath]]; NSString *buttonText = state == 2 ? @"Put Back" : @"Remove"; [removeButton setTitle:buttonText]; } /* Delegate */ - -- (void)outlineView:(NSOutlineView *)outlineView addDirectory:(NSString *)directory -{ - [self addDirectory:directory]; -} - -- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item -{ - OVNode *node = item; - NSInteger state = n2i([[node buffer] objectAtIndex:1]); - if ([cell isKindOfClass:[NSTextFieldCell class]]) - { - NSTextFieldCell *textCell = cell; - if (state == 1) - [textCell setTextColor:[NSColor blueColor]]; - else if (state == 2) - [textCell setTextColor:[NSColor redColor]]; - else - [textCell setTextColor:[NSColor blackColor]]; - } -} - - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)path { BOOL isdir; diff --git a/cocoa/base/PyDirectoryOutline.h b/cocoa/base/PyDirectoryOutline.h new file mode 100644 index 00000000..bc58acfa --- /dev/null +++ b/cocoa/base/PyDirectoryOutline.h @@ -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 +#import "PyOutline.h" + +@interface PyDirectoryOutline : PyOutline +- (void)addDirectory:(NSString *)directoryPath; +@end \ No newline at end of file diff --git a/cocoa/base/PyDupeGuru.h b/cocoa/base/PyDupeGuru.h index 022a296c..9a495e4d 100644 --- a/cocoa/base/PyDupeGuru.h +++ b/cocoa/base/PyDupeGuru.h @@ -13,7 +13,6 @@ http://www.hardcoded.net/licenses/hs_license //Actions - (NSNumber *)addDirectory:(NSString *)name; - (void)removeDirectory:(NSNumber *)index; -- (void)setDirectory:(NSArray *)indexPath state:(NSNumber *)state; - (void)loadResults; - (void)saveResults; - (void)loadIgnoreList; diff --git a/cocoa/base/xib/DirectoryPanel.xib b/cocoa/base/xib/DirectoryPanel.xib index 6a79a718..fdbc980e 100644 --- a/cocoa/base/xib/DirectoryPanel.xib +++ b/cocoa/base/xib/DirectoryPanel.xib @@ -2,17 +2,17 @@ 1050 - 10B504 + 10C540 740 - 1038.2 - 437.00 + 1038.25 + 458.00 com.apple.InterfaceBuilder.CocoaPlugin 740 YES - + YES @@ -70,7 +70,6 @@ 256 {327, 165} - 1 YES @@ -88,7 +87,7 @@ YES - 0 + name 236 16 1000 @@ -142,7 +141,7 @@ - 1 + state 85.35595703125 30.35595703125 1000 @@ -173,15 +172,41 @@ 400 75 - + + + Normal + + 2147483647 + 1 + _popUpItemAction: + + YES - + Normal YES + + + + Reference + + 2147483647 + _popUpItemAction: + + + + + Excluded + + 2147483647 + _popUpItemAction: + + + YES + YES - -1 3 YES YES @@ -189,6 +214,7 @@ 2 YES + YES @@ -415,30 +441,6 @@ 19 - - - nextKeyView - - - - 20 - - - - nextKeyView - - - - 21 - - - - nextKeyView - - - - 22 - popupAddDirectoryMenu: @@ -471,22 +473,6 @@ 26 - - - directories - - - - 27 - - - - delegate - - - - 29 - performClose: @@ -503,6 +489,14 @@ 43 + + + outlineView + + + + 54 + @@ -685,6 +679,12 @@ 50 + + YES + + + + @@ -702,6 +702,21 @@ + + 55 + + + + + 56 + + + + + 57 + + + @@ -739,6 +754,7 @@ 5.ImportedFromIB2 5.windowTemplate.hasMinSize 5.windowTemplate.minSize + 50.IBEditorWindowLastContentRect 50.IBPluginDependency 51.IBPluginDependency 51.IBShouldRemoveOnLegacySave @@ -746,6 +762,9 @@ 52.IBShouldRemoveOnLegacySave 53.IBPluginDependency 53.IBShouldRemoveOnLegacySave + 55.IBPluginDependency + 56.IBPluginDependency + 57.IBPluginDependency 6.IBPluginDependency 6.ImportedFromIB2 7.IBPluginDependency @@ -761,7 +780,7 @@ com.apple.InterfaceBuilder.CocoaPlugin - DirectoryOutline + HSOutlineView com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -788,6 +807,7 @@ {369, 269} + {{98, 740}, {327, 63}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -796,6 +816,9 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -821,27 +844,11 @@ - 53 + 57 YES - - DirectoryOutline - OutlineView - - IBProjectSource - dgbase/DirectoryPanel.h - - - - DirectoryPanel - NSWindowController - - IBProjectSource - DirectoryPanel.h - - DirectoryPanel NSWindowController @@ -850,7 +857,6 @@ YES askForDirectory: - changeDirectoryState: popupAddDirectoryMenu: removeSelectedDirectory: toggleVisible: @@ -861,7 +867,6 @@ id id id - id @@ -869,60 +874,29 @@ YES addButtonPopUp - directories + outlineView removeButton YES NSPopUpButton - NSOutlineView + HSOutlineView NSButton + + IBProjectSource + ../base/DirectoryPanel.h + + + + DirectoryPanel + NSWindowController IBUserSource - - DirectoryPanel - NSWindowController - - YES - - YES - askForDirectory: - changeDirectoryState: - popupAddDirectoryMenu: - removeSelectedDirectory: - toggleVisible: - - - YES - id - id - id - id - id - - - - YES - - YES - addButtonPopUp - directories - removeButton - - - YES - NSPopUpButton - DirectoryOutline - NSButton - - - - FirstResponder NSObject @@ -932,40 +906,27 @@ - OutlineView + HSOutlineView NSOutlineView - - py - PyApp - - + IBProjectSource - cocoalib/Outline.h + ../views/HSOutlineView.h - OutlineView - NSOutlineView - - IBUserSource - + NSObject + + + + NSObject + + IBProjectSource + ../views/NSTableViewAdditions.h - PyApp - PyRegistrable - - IBProjectSource - cocoalib/PyApp.h - - - - PyRegistrable - NSObject - - IBProjectSource - cocoalib/PyRegistrable.h - + NSTableView + @@ -1497,7 +1458,7 @@ YES - ../../dupeguru.xcodeproj + ../../se/dupeguru.xcodeproj 3 diff --git a/cocoa/me/dg_cocoa.py b/cocoa/me/dg_cocoa.py index 7aee6df6..1a837336 100644 --- a/cocoa/me/dg_cocoa.py +++ b/cocoa/me/dg_cocoa.py @@ -18,27 +18,27 @@ from hsmedia import aiff, flac, genres, id3v1, id3v2, mp4, mpeg, ogg, wma class PyDupeGuru(PyDupeGuruBase): def init(self): self = super(PyDupeGuru,self).init() - self.app = DupeGuruME() + self.py = DupeGuruME() return self def removeDeadTracks(self): - self.app.remove_dead_tracks() + self.py.remove_dead_tracks() def scanDeadTracks(self): - self.app.scan_dead_tracks() + self.py.scan_dead_tracks() #---Information @signature('i@:') def deadTrackCount(self): - return len(self.app.dead_tracks) + return len(self.py.dead_tracks) #---Properties def setMinMatchPercentage_(self, percentage): - self.app.scanner.min_match_percentage = int(percentage) + self.py.scanner.min_match_percentage = int(percentage) def setScanType_(self, scan_type): try: - self.app.scanner.scan_type = [ + self.py.scanner.scan_type = [ SCAN_TYPE_FILENAME, SCAN_TYPE_FIELDS, SCAN_TYPE_FIELDS_NO_ORDER, @@ -50,16 +50,16 @@ class PyDupeGuru(PyDupeGuruBase): pass def setWordWeighting_(self, words_are_weighted): - self.app.scanner.word_weighting = words_are_weighted + self.py.scanner.word_weighting = words_are_weighted def setMatchSimilarWords_(self, match_similar_words): - self.app.scanner.match_similar_words = match_similar_words + self.py.scanner.match_similar_words = match_similar_words def enable_scanForTag_(self, enable, scan_tag): if enable: - self.app.scanner.scanned_tags.add(scan_tag) + self.py.scanner.scanned_tags.add(scan_tag) else: - self.app.scanner.scanned_tags.discard(scan_tag) + self.py.scanner.scanned_tags.discard(scan_tag) #---Registration def appName(self): diff --git a/cocoa/pe/dg_cocoa.py b/cocoa/pe/dg_cocoa.py index ef489bbf..6a705864 100644 --- a/cocoa/pe/dg_cocoa.py +++ b/cocoa/pe/dg_cocoa.py @@ -13,25 +13,25 @@ from core_pe import block, cache, matchbase, data, _block_osx class PyDupeGuru(PyDupeGuruBase): def init(self): self = super(PyDupeGuru, self).init() - self.app = app_pe_cocoa.DupeGuruPE() + self.py = app_pe_cocoa.DupeGuruPE() return self def clearPictureCache(self): - self.app.scanner.clear_picture_cache() + self.py.scanner.clear_picture_cache() #---Information def getSelectedDupePath(self): - return unicode(self.app.selected_dupe_path()) + return unicode(self.py.selected_dupe_path()) def getSelectedDupeRefPath(self): - return unicode(self.app.selected_dupe_ref_path()) + return unicode(self.py.selected_dupe_ref_path()) #---Properties def setMatchScaled_(self,match_scaled): - self.app.scanner.match_scaled = match_scaled + self.py.scanner.match_scaled = match_scaled def setMinMatchPercentage_(self,percentage): - self.app.scanner.threshold = int(percentage) + self.py.scanner.threshold = int(percentage) #---Registration def appName(self): diff --git a/cocoa/se/dg_cocoa.py b/cocoa/se/dg_cocoa.py index 64caccb8..c3626f30 100644 --- a/cocoa/se/dg_cocoa.py +++ b/cocoa/se/dg_cocoa.py @@ -16,16 +16,16 @@ from core_se import fs, data class PyDupeGuru(PyDupeGuruBase): def init(self): self = super(PyDupeGuru,self).init() - self.app = DupeGuru() + self.py = DupeGuru() return self #---Properties def setMinMatchPercentage_(self,percentage): - self.app.scanner.min_match_percentage = int(percentage) + self.py.scanner.min_match_percentage = int(percentage) def setScanType_(self,scan_type): try: - self.app.scanner.scan_type = [ + self.py.scanner.scan_type = [ scanner.SCAN_TYPE_FILENAME, scanner.SCAN_TYPE_CONTENT ][scan_type] @@ -33,14 +33,14 @@ class PyDupeGuru(PyDupeGuruBase): pass def setWordWeighting_(self,words_are_weighted): - self.app.scanner.word_weighting = words_are_weighted + self.py.scanner.word_weighting = words_are_weighted def setMatchSimilarWords_(self,match_similar_words): - self.app.scanner.match_similar_words = match_similar_words + self.py.scanner.match_similar_words = match_similar_words @signature('v@:i') def setSizeThreshold_(self, size_threshold): - self.app.scanner.size_threshold = size_threshold + self.py.scanner.size_threshold = size_threshold #---Registration def appName(self): diff --git a/cocoa/se/dupeguru.xcodeproj/project.pbxproj b/cocoa/se/dupeguru.xcodeproj/project.pbxproj index 1ab3b0f8..25029686 100644 --- a/cocoa/se/dupeguru.xcodeproj/project.pbxproj +++ b/cocoa/se/dupeguru.xcodeproj/project.pbxproj @@ -20,6 +20,13 @@ CE45579B0AE3BC2B005A9546 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; }; CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; }; CE6E0DFE1054E9EF008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */; }; + CE76FDC4111EE37C006618EA /* HSOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDBF111EE37C006618EA /* HSOutlineView.m */; }; + CE76FDC5111EE37C006618EA /* NSIndexPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */; }; + CE76FDC6111EE37C006618EA /* NSTableViewAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC3111EE37C006618EA /* NSTableViewAdditions.m */; }; + CE76FDCF111EE38E006618EA /* HSGUIController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC9111EE38E006618EA /* HSGUIController.m */; }; + CE76FDD4111EE3A7006618EA /* DirectoryOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDD2111EE3A7006618EA /* DirectoryOutline.m */; }; + CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDDE111EE42F006618EA /* HSOutline.m */; }; + CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDF6111EE561006618EA /* NSEventAdditions.m */; }; CEAC6811109B0B7E00B43C85 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEAC6810109B0B7E00B43C85 /* Preferences.xib */; }; CEDD92DA0FDD01640031C7B7 /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDD92D70FDD01640031C7B7 /* BRSingleLineFormatter.m */; }; CEE7EA130FE675C80004E467 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEE7EA120FE675C80004E467 /* DetailsPanel.m */; }; @@ -76,6 +83,23 @@ CE45579A0AE3BC2B005A9546 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = ""; }; CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = ""; }; CE6E7407111C997500C350E3 /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; }; + CE76FDBE111EE37C006618EA /* HSOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutlineView.h; sourceTree = ""; }; + CE76FDBF111EE37C006618EA /* HSOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutlineView.m; sourceTree = ""; }; + CE76FDC0111EE37C006618EA /* NSIndexPathAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSIndexPathAdditions.h; sourceTree = ""; }; + CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSIndexPathAdditions.m; sourceTree = ""; }; + CE76FDC2111EE37C006618EA /* NSTableViewAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSTableViewAdditions.h; sourceTree = ""; }; + CE76FDC3111EE37C006618EA /* NSTableViewAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSTableViewAdditions.m; sourceTree = ""; }; + CE76FDC8111EE38E006618EA /* HSGUIController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSGUIController.h; sourceTree = ""; }; + CE76FDC9111EE38E006618EA /* HSGUIController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSGUIController.m; sourceTree = ""; }; + CE76FDCD111EE38E006618EA /* PyGUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyGUI.h; sourceTree = ""; }; + CE76FDCE111EE38E006618EA /* PyOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyOutline.h; sourceTree = ""; }; + CE76FDD1111EE3A7006618EA /* DirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DirectoryOutline.h; path = ../base/DirectoryOutline.h; sourceTree = SOURCE_ROOT; }; + CE76FDD2111EE3A7006618EA /* DirectoryOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DirectoryOutline.m; path = ../base/DirectoryOutline.m; sourceTree = SOURCE_ROOT; }; + CE76FDD3111EE3A7006618EA /* PyDirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDirectoryOutline.h; path = ../base/PyDirectoryOutline.h; sourceTree = SOURCE_ROOT; }; + CE76FDDD111EE42F006618EA /* HSOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutline.h; sourceTree = ""; }; + CE76FDDE111EE42F006618EA /* HSOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutline.m; sourceTree = ""; }; + CE76FDF5111EE561006618EA /* NSEventAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSEventAdditions.h; path = ../../cocoalib/NSEventAdditions.h; sourceTree = SOURCE_ROOT; }; + CE76FDF6111EE561006618EA /* NSEventAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSEventAdditions.m; path = ../../cocoalib/NSEventAdditions.m; sourceTree = SOURCE_ROOT; }; CEAC6810109B0B7E00B43C85 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Preferences.xib; path = xib/Preferences.xib; sourceTree = ""; }; CEDD92D60FDD01640031C7B7 /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; }; CEDD92D70FDD01640031C7B7 /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; }; @@ -216,6 +240,42 @@ path = ../../cocoalib/xib; sourceTree = SOURCE_ROOT; }; + CE76FDBD111EE37C006618EA /* views */ = { + isa = PBXGroup; + children = ( + CE76FDBE111EE37C006618EA /* HSOutlineView.h */, + CE76FDBF111EE37C006618EA /* HSOutlineView.m */, + CE76FDC0111EE37C006618EA /* NSIndexPathAdditions.h */, + CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */, + CE76FDC2111EE37C006618EA /* NSTableViewAdditions.h */, + CE76FDC3111EE37C006618EA /* NSTableViewAdditions.m */, + ); + name = views; + path = ../../cocoalib/views; + sourceTree = SOURCE_ROOT; + }; + CE76FDC7111EE38E006618EA /* controllers */ = { + isa = PBXGroup; + children = ( + CE76FDDD111EE42F006618EA /* HSOutline.h */, + CE76FDDE111EE42F006618EA /* HSOutline.m */, + CE76FDC8111EE38E006618EA /* HSGUIController.h */, + CE76FDC9111EE38E006618EA /* HSGUIController.m */, + ); + name = controllers; + path = ../../cocoalib/controllers; + sourceTree = SOURCE_ROOT; + }; + CE76FDCC111EE38E006618EA /* proxies */ = { + isa = PBXGroup; + children = ( + CE76FDCD111EE38E006618EA /* PyGUI.h */, + CE76FDCE111EE38E006618EA /* PyOutline.h */, + ); + name = proxies; + path = ../../cocoalib/proxies; + sourceTree = SOURCE_ROOT; + }; CEDD92D50FDD01640031C7B7 /* brsinglelineformatter */ = { isa = PBXGroup; children = ( @@ -250,6 +310,11 @@ CEFC7F890FC9513600CD5728 /* cocoalib */ = { isa = PBXGroup; children = ( + CE76FDF5111EE561006618EA /* NSEventAdditions.h */, + CE76FDF6111EE561006618EA /* NSEventAdditions.m */, + CE76FDC7111EE38E006618EA /* controllers */, + CE76FDCC111EE38E006618EA /* proxies */, + CE76FDBD111EE37C006618EA /* views */, CE19BC5F11199231007CCEB0 /* xib */, CEDD92D50FDD01640031C7B7 /* brsinglelineformatter */, CEFC7F8A0FC9517500CD5728 /* Dialogs.h */, @@ -277,6 +342,9 @@ CEFC7FB00FC9518F00CD5728 /* dgbase */ = { isa = PBXGroup; children = ( + CE76FDD1111EE3A7006618EA /* DirectoryOutline.h */, + CE76FDD2111EE3A7006618EA /* DirectoryOutline.m */, + CE76FDD3111EE3A7006618EA /* PyDirectoryOutline.h */, CEFC7FB10FC951A700CD5728 /* AppDelegate.h */, CEFC7FB20FC951A700CD5728 /* AppDelegate.m */, CEFC7FB30FC951A700CD5728 /* Consts.h */, @@ -379,6 +447,13 @@ CEFC7FBB0FC951A700CD5728 /* ResultWindow.m in Sources */, CEDD92DA0FDD01640031C7B7 /* BRSingleLineFormatter.m in Sources */, CEE7EA130FE675C80004E467 /* DetailsPanel.m in Sources */, + CE76FDC4111EE37C006618EA /* HSOutlineView.m in Sources */, + CE76FDC5111EE37C006618EA /* NSIndexPathAdditions.m in Sources */, + CE76FDC6111EE37C006618EA /* NSTableViewAdditions.m in Sources */, + CE76FDCF111EE38E006618EA /* HSGUIController.m in Sources */, + CE76FDD4111EE3A7006618EA /* DirectoryOutline.m in Sources */, + CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */, + CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/core/app.py b/core/app.py index 6d07d63d..00bee391 100644 --- a/core/app.py +++ b/core/app.py @@ -84,6 +84,7 @@ class DupeGuru(RegistrableApplication, Broadcaster): def _do_load(self, j): self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml')) + self.notify('directories_changed') j = j.start_subjob([1, 9]) self.results.load_from_xml(op.join(self.appdata, 'last_results.xml'), self._get_file, j) files = flatten(g[:] for g in self.results.groups) @@ -128,6 +129,7 @@ class DupeGuru(RegistrableApplication, Broadcaster): def add_directory(self, d): try: self.directories.add_path(Path(d)) + self.notify('directories_changed') return 0 except directories.AlreadyThereError: return 1 @@ -236,6 +238,13 @@ class DupeGuru(RegistrableApplication, Broadcaster): if self.selected_dupes: self._open_path(self.selected_dupes[0].path) + def remove_directory(self,index): + try: + del self.directories[index] + self.notify('directories_changed') + except IndexError: + pass + def remove_duplicates(self, duplicates): self.results.remove_duplicates(duplicates) diff --git a/core/app_cocoa.py b/core/app_cocoa.py index e4cffa20..69525ab3 100644 --- a/core/app_cocoa.py +++ b/core/app_cocoa.py @@ -87,16 +87,6 @@ class DupeGuru(app.DupeGuru): except IndexError: return (None,None) - def get_folder_path(self, node_path, curr_path=None): - if not node_path: - return curr_path - current_index = node_path[0] - if curr_path is None: - curr_path = self.directories[current_index] - else: - curr_path = self.directories.get_subfolders(curr_path)[current_index] - return self.get_folder_path(node_path[1:], curr_path) - #---Public copy_or_move_marked = demo_method(app.DupeGuru.copy_or_move_marked) delete_marked = demo_method(app.DupeGuru.delete_marked) @@ -104,12 +94,6 @@ class DupeGuru(app.DupeGuru): def PurgeIgnoreList(self): self.scanner.ignore_list.Filter(lambda f,s:op.exists(f) and op.exists(s)) - def RemoveDirectory(self,index): - try: - del self.directories[index] - except IndexError: - pass - def RenameSelected(self, newname): try: d = self.selected_dupes[0] @@ -172,10 +156,6 @@ class DupeGuru(app.DupeGuru): dupes = [self.results.dupes[row] for row in rows if row in xrange(len(self.results.dupes))] self._select_dupes(dupes) - def SetDirectoryState(self, node_path, state): - p = self.get_folder_path(node_path) - self.directories.set_state(p, state) - def sort_dupes(self,key,asc): self.results.sort_dupes(key,asc,self.display_delta_values) @@ -190,8 +170,6 @@ class DupeGuru(app.DupeGuru): def GetOutlineViewMaxLevel(self, tag): if tag == 0: return 2 - elif tag == 1: - return 0 elif tag == 2: return 1 @@ -201,16 +179,6 @@ class DupeGuru(app.DupeGuru): if tag == 0: #Normal results assert not node_path # no other value is possible return [len(g.dupes) for g in self.results.groups] - elif tag == 1: #Directories - try: - if node_path: - path = self.get_folder_path(node_path) - subfolders = self.directories.get_subfolders(path) - else: - subfolders = self.directories - return [len(self.directories.get_subfolders(path)) for path in subfolders] - except IndexError: # node_path out of range - return [] else: #Power Marker assert not node_path # no other value is possible return [0 for d in self.results.dupes] @@ -230,13 +198,6 @@ class DupeGuru(app.DupeGuru): g = self.results.get_group_of_duplicate(d) result = self._get_display_info(d, g, self.display_delta_values) return result - elif tag == 1: #Directories - try: - path = self.get_folder_path(node_path) - name = unicode(path) if len(node_path) == 1 else path[-1] - return [name, self.directories.get_state(path)] - except IndexError: # node_path out of range - return [] def GetOutlineViewMarked(self, tag, node_path): # 0=unmarked 1=marked 2=unmarkable @@ -244,8 +205,6 @@ class DupeGuru(app.DupeGuru): return if not node_path: return 2 - if tag == 1: #Directories - return 2 if tag == 0: #Normal results g, d = self.GetObjects(node_path) else: #Power Marker diff --git a/core/app_cocoa_inter.py b/core/app_cocoa_inter.py index 7c02aa4b..415597d7 100644 --- a/core/app_cocoa_inter.py +++ b/core/app_cocoa_inter.py @@ -11,10 +11,11 @@ from hsutil.cocoa.objcmin import NSObject -from hsutil.cocoa import signature +from hsutil.cocoa.inter import signature, PyOutline from hsutil.reg import InvalidCodeError from .gui.details_panel import DetailsPanel +from .gui.directory_tree import DirectoryTree # Fix py2app's problems on relative imports from core import app, app_cocoa, data, directories, engine, export, ignore, results, fs, scanner @@ -26,185 +27,182 @@ class PyApp(NSObject): class PyDupeGuruBase(PyApp): #---Directories def addDirectory_(self, directory): - return self.app.add_directory(directory) + return self.py.add_directory(directory) def removeDirectory_(self, index): - self.app.RemoveDirectory(index) - - def setDirectory_state_(self, node_path, state): - self.app.SetDirectoryState(node_path, state) + self.py.remove_directory(index) #---Results def clearIgnoreList(self): - self.app.scanner.ignore_list.Clear() + self.py.scanner.ignore_list.Clear() def doScan(self): - return self.app.start_scanning() + return self.py.start_scanning() def exportToXHTMLwithColumns_(self, column_ids): - return self.app.export_to_xhtml(column_ids) + return self.py.export_to_xhtml(column_ids) def loadIgnoreList(self): - self.app.load_ignore_list() + self.py.load_ignore_list() def loadResults(self): - self.app.load() + self.py.load() def markAll(self): - self.app.results.mark_all() + self.py.results.mark_all() def markNone(self): - self.app.results.mark_none() + self.py.results.mark_none() def markInvert(self): - self.app.results.mark_invert() + self.py.results.mark_invert() def purgeIgnoreList(self): - self.app.PurgeIgnoreList() + self.py.PurgeIgnoreList() def toggleSelectedMark(self): - self.app.ToggleSelectedMarkState() + self.py.ToggleSelectedMarkState() def saveIgnoreList(self): - self.app.save_ignore_list() + self.py.save_ignore_list() def saveResults(self): - self.app.save() + self.py.save() def selectedResultNodePaths(self): - return self.app.selected_result_node_paths() + return self.py.selected_result_node_paths() def selectResultNodePaths_(self,node_paths): - self.app.SelectResultNodePaths(node_paths) + self.py.SelectResultNodePaths(node_paths) def selectedPowerMarkerNodePaths(self): - return self.app.selected_powermarker_node_paths() + return self.py.selected_powermarker_node_paths() def selectPowerMarkerNodePaths_(self,node_paths): - self.app.SelectPowerMarkerNodePaths(node_paths) + self.py.SelectPowerMarkerNodePaths(node_paths) #---Actions def addSelectedToIgnoreList(self): - self.app.add_selected_to_ignore_list() + self.py.add_selected_to_ignore_list() def deleteMarked(self): - self.app.delete_marked() + self.py.delete_marked() def applyFilter_(self, filter): - self.app.apply_filter(filter) + self.py.apply_filter(filter) def makeSelectedReference(self): - self.app.make_selected_reference() + self.py.make_selected_reference() def copyOrMove_markedTo_recreatePath_(self, copy, destination, recreate_path): - self.app.copy_or_move_marked(copy, destination, recreate_path) + self.py.copy_or_move_marked(copy, destination, recreate_path) def openSelected(self): - self.app.open_selected() + self.py.open_selected() def removeMarked(self): - self.app.results.perform_on_marked(lambda x:True, True) + self.py.results.perform_on_marked(lambda x:True, True) def removeSelected(self): - self.app.remove_selected() + self.py.remove_selected() def renameSelected_(self,newname): - return self.app.RenameSelected(newname) + return self.py.RenameSelected(newname) def revealSelected(self): - self.app.reveal_selected() + self.py.reveal_selected() #---Misc def sortDupesBy_ascending_(self, key, asc): - self.app.sort_dupes(key, asc) + self.py.sort_dupes(key, asc) def sortGroupsBy_ascending_(self, key, asc): - self.app.sort_groups(key, asc) + self.py.sort_groups(key, asc) #---Information def getIgnoreListCount(self): - return len(self.app.scanner.ignore_list) + return len(self.py.scanner.ignore_list) def getMarkCount(self): - return self.app.results.mark_count + return self.py.results.mark_count def getStatLine(self): - return self.app.stat_line + return self.py.stat_line def getOperationalErrorCount(self): - return self.app.last_op_error_count + return self.py.last_op_error_count #---Data @signature('i@:i') def getOutlineViewMaxLevel_(self, tag): - return self.app.GetOutlineViewMaxLevel(tag) + return self.py.GetOutlineViewMaxLevel(tag) @signature('@@:i@') def getOutlineView_childCountsForPath_(self, tag, node_path): - return self.app.GetOutlineViewChildCounts(tag, node_path) + return self.py.GetOutlineViewChildCounts(tag, node_path) def getOutlineView_valuesForIndexes_(self, tag, node_path): - return self.app.GetOutlineViewValues(tag, node_path) + return self.py.GetOutlineViewValues(tag, node_path) def getOutlineView_markedAtIndexes_(self, tag, node_path): - return self.app.GetOutlineViewMarked(tag, node_path) + return self.py.GetOutlineViewMarked(tag, node_path) def getTableViewCount_(self, tag): - return self.app.GetTableViewCount(tag) + return self.py.GetTableViewCount(tag) def getTableViewMarkedIndexes_(self, tag): - return self.app.GetTableViewMarkedIndexes(tag) + return self.py.GetTableViewMarkedIndexes(tag) def getTableView_valuesForRow_(self, tag, row): - return self.app.GetTableViewValues(tag, row) + return self.py.GetTableViewValues(tag, row) #---Properties def setMixFileKind_(self, mix_file_kind): - self.app.scanner.mix_file_kind = mix_file_kind + self.py.scanner.mix_file_kind = mix_file_kind def setDisplayDeltaValues_(self, display_delta_values): - self.app.display_delta_values= display_delta_values + self.py.display_delta_values= display_delta_values def setEscapeFilterRegexp_(self, escape_filter_regexp): - self.app.options['escape_filter_regexp'] = escape_filter_regexp + self.py.options['escape_filter_regexp'] = escape_filter_regexp def setRemoveEmptyFolders_(self, remove_empty_folders): - self.app.options['clean_empty_dirs'] = remove_empty_folders + self.py.options['clean_empty_dirs'] = remove_empty_folders #---Worker def getJobProgress(self): - return self.app.progress.last_progress + return self.py.progress.last_progress def getJobDesc(self): - return self.app.progress.last_desc + return self.py.progress.last_desc def cancelJob(self): - self.app.progress.job_cancelled = True + self.py.progress.job_cancelled = True #---Registration def demoLimitDescription(self): - return self.app.DEMO_LIMIT_DESC + return self.py.DEMO_LIMIT_DESC @signature('i@:') def isRegistered(self): - return self.app.registered + return self.py.registered def isCodeValid_withEmail_(self, code, email): try: - self.app.validate_code(code, email) + self.py.validate_code(code, email) return None except InvalidCodeError as e: return unicode(e) def setRegisteredCode_andEmail_(self, code, email): - self.app.set_registration(code, email) + self.py.set_registration(code, email) class PyDetailsPanel(NSObject): def initWithCocoa_pyParent_(self, cocoa, pyparent): super(PyDetailsPanel, self).init() self.cocoa = cocoa - self.py = DetailsPanel(self, pyparent.app) + self.py = DetailsPanel(self, pyparent.py) return self @signature('i@:') @@ -219,3 +217,10 @@ class PyDetailsPanel(NSObject): def refresh(self): self.cocoa.refresh() + +class PyDirectoryOutline(PyOutline): + py_class = DirectoryTree + + def addDirectory_(self, path): + self.py.add_directory(path) + diff --git a/core/gui/base.py b/core/gui/base.py new file mode 100644 index 00000000..cfd993b6 --- /dev/null +++ b/core/gui/base.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Created By: Virgil Dupras +# Created On: 2010-02-06 +# 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 hsutil.notify import Listener + +class GUIObject(Listener): + def __init__(self, view, app): + Listener.__init__(self, app) + self.view = view + self.app = app + + def directories_changed(self): + pass + + def dupes_selected(self): + pass + diff --git a/core/gui/details_panel.py b/core/gui/details_panel.py index 0111e1b1..ca9f83b0 100644 --- a/core/gui/details_panel.py +++ b/core/gui/details_panel.py @@ -7,13 +7,11 @@ # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/hs_license -from hsutil.notify import Listener +from .base import GUIObject -class DetailsPanel(Listener): +class DetailsPanel(GUIObject): def __init__(self, view, app): - Listener.__init__(self, app) - self.app = app - self.view = view + GUIObject.__init__(self, view, app) self._table = [] self._refresh() self.connect() diff --git a/core/gui/directory_tree.py b/core/gui/directory_tree.py new file mode 100644 index 00000000..a705e9c1 --- /dev/null +++ b/core/gui/directory_tree.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# Created By: Virgil Dupras +# Created On: 2010-02-06 +# Copyright 2010 Hardcoded Software (http://www.hardcoded.net) +# +# This software is licensed under the "HS" License as described in the "LICENSE" file, +# which should be included with this package. The terms are also available at +# http://www.hardcoded.net/licenses/hs_license + +from hsgui.tree import Tree, Node + +from ..directories import STATE_NORMAL, STATE_REFERENCE, STATE_EXCLUDED +from .base import GUIObject + +STATE_ORDER = [STATE_NORMAL, STATE_REFERENCE, STATE_EXCLUDED] + +# Lazily loads children +class DirectoryNode(Node): + def __init__(self, app, path, name): + Node.__init__(self, name) + self._app = app + self._directory_path = path + self._loaded = False + self._state = STATE_ORDER.index(self._app.directories.get_state(path)) + + def _load(self): + self.clear() + subpaths = self._app.directories.get_subfolders(self._directory_path) + for path in subpaths: + self.append(DirectoryNode(self._app, path, path[-1])) + self._loaded = True + + @property + def children_count(self): + if not self._loaded: + self._load() + return len(self) + + # The state propery is an index to the combobox + @property + def state(self): + return self._state + + @state.setter + def state(self, value): + if value == self._state: + return + self._state = value + state = STATE_ORDER[value] + self._app.directories.set_state(self._directory_path, state) + + +class DirectoryTree(GUIObject, Tree): + def __init__(self, view, app): + GUIObject.__init__(self, view, app) + Tree.__init__(self) + self.connect() + self._refresh() + self.view.refresh() + + def _refresh(self): + self.clear() + for path in self.app.directories: + self.append(DirectoryNode(self.app, path, unicode(path))) + + def add_directory(self, path): + self.app.add_directory(path) + + #--- Event Handlers + def directories_changed(self): + self._refresh() + self.view.refresh() + diff --git a/core/tests/app_cocoa_test.py b/core/tests/app_cocoa_test.py index 5af6ae92..42b4af8b 100644 --- a/core/tests/app_cocoa_test.py +++ b/core/tests/app_cocoa_test.py @@ -27,6 +27,7 @@ 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): @@ -61,6 +62,8 @@ class TCDupeGuru(TestCase): 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() @@ -339,21 +342,6 @@ class TCDupeGuru(TestCase): app.SelectPowerMarkerNodePaths(r2np([2])) #The dupe of the second, 2 sized group app.add_selected_to_ignore_list() - def test_GetOutlineViewChildCounts_out_of_range(self): - # Out of range requests don't crash and return an empty value - app = self.app - # [0, 2] is out of range - eq_(app.GetOutlineViewChildCounts(1, [0, 2]), []) # no crash - - def test_GetOutlineViewValues_out_of_range(self): - # Out of range requests don't crash and return an empty value - app = self.app - # [0, 2] is out of range - # Directories - eq_(app.GetOutlineViewValues(1, [0, 2]), []) # no crash - # Normal results - app.GetOutlineViewValues(0, [42, 0]) # no crash - class TCDupeGuru_renameSelected(TestCase): def setUp(self):