diff --git a/cocoa/base/DetailsPanel.h b/cocoa/base/DetailsPanel.h index 9e54ed8a..5d4ac15c 100644 --- a/cocoa/base/DetailsPanel.h +++ b/cocoa/base/DetailsPanel.h @@ -8,18 +8,18 @@ http://www.hardcoded.net/licenses/hs_license #import #import "PyApp.h" -#import "Table.h" - +#import "PyDetailsPanel.h" @interface DetailsPanel : NSWindowController { - IBOutlet TableView *detailsTable; + IBOutlet NSTableView *detailsTable; + + PyDetailsPanel *py; } - (id)initWithPy:(PyApp *)aPy; -- (void)refresh; - (void)toggleVisibility; -/* Notifications */ -- (void)duplicateSelectionChanged:(NSNotification *)aNotification; +/* Python --> Cocoa */ +- (void)refresh; @end \ No newline at end of file diff --git a/cocoa/base/DetailsPanel.m b/cocoa/base/DetailsPanel.m index cef08865..3d4a08aa 100644 --- a/cocoa/base/DetailsPanel.m +++ b/cocoa/base/DetailsPanel.m @@ -7,38 +7,56 @@ http://www.hardcoded.net/licenses/hs_license */ #import "DetailsPanel.h" -#import "Consts.h" +#import "Utils.h" @implementation DetailsPanel - (id)initWithPy:(PyApp *)aPy { self = [super initWithWindowNibName:@"DetailsPanel"]; [self window]; //So the detailsTable is initialized. - [detailsTable setPy:aPy]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(duplicateSelectionChanged:) name:DuplicateSelectionChangedNotification object:nil]; + Class pyClass = [Utils classNamed:@"PyDetailsPanel"]; + py = [[pyClass alloc] initWithCocoa:self pyParent:aPy]; return self; } -- (void)refresh +- (void)dealloc +{ + [py release]; + [super dealloc]; +} + +- (void)refreshDetails { [detailsTable reloadData]; } - (void)toggleVisibility { - if ([[self window] isVisible]) + if ([[self window] isVisible]) { [[self window] close]; - else - { - [self refresh]; // selection might have changed since last time + } + else { + [self refreshDetails]; // selection might have changed since last time [[self window] orderFront:nil]; } } -/* Notifications */ -- (void)duplicateSelectionChanged:(NSNotification *)aNotification +/* NSTableView Delegate */ +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { - if ([[self window] isVisible]) - [self refresh]; + return [py numberOfRows]; +} + +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)row +{ + return [py valueForColumn:[column identifier] row:row]; +} + +/* Python --> Cocoa */ +- (void)refresh +{ + if ([[self window] isVisible]) { + [self refreshDetails]; + } } @end diff --git a/cocoa/base/PyDetailsPanel.h b/cocoa/base/PyDetailsPanel.h new file mode 100644 index 00000000..a906d57e --- /dev/null +++ b/cocoa/base/PyDetailsPanel.h @@ -0,0 +1,15 @@ +/* +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 + +@interface PyDetailsPanel : NSObject +- (id)initWithCocoa:(id)cocoa pyParent:(id)pyParent; +- (NSInteger)numberOfRows; +- (id)valueForColumn:(NSString *)column row:(NSInteger)row; +@end \ No newline at end of file diff --git a/cocoa/base/xib/DetailsPanel.xib b/cocoa/base/xib/DetailsPanel.xib index ee489ddb..d4990d5f 100644 --- a/cocoa/base/xib/DetailsPanel.xib +++ b/cocoa/base/xib/DetailsPanel.xib @@ -2,17 +2,17 @@ 1050 - 10B504 + 10C540 740 - 1038.2 - 437.00 + 1038.25 + 458.00 com.apple.InterfaceBuilder.CocoaPlugin 740 YES - + YES @@ -297,13 +297,21 @@ 12 + + + dataSource + + + + 21 + detailsTable - 13 + 22 @@ -438,15 +446,22 @@ YES YES + -3.IBPluginDependency 10.IBPluginDependency 10.ImportedFromIB2 11.IBPluginDependency 11.ImportedFromIB2 + 15.IBPluginDependency 15.IBShouldRemoveOnLegacySave + 16.IBPluginDependency 16.IBShouldRemoveOnLegacySave + 17.IBPluginDependency 17.IBShouldRemoveOnLegacySave + 18.IBPluginDependency 18.IBShouldRemoveOnLegacySave + 19.IBPluginDependency 19.IBShouldRemoveOnLegacySave + 20.IBPluginDependency 20.IBShouldRemoveOnLegacySave 5.IBEditorWindowLastContentRect 5.IBPluginDependency @@ -458,7 +473,6 @@ 6.ImportedFromIB2 7.IBPluginDependency 7.ImportedFromIB2 - 8.CustomClassName 8.IBPluginDependency 8.ImportedFromIB2 9.IBPluginDependency @@ -467,14 +481,21 @@ YES com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin {{109, 656}, {451, 161}} com.apple.InterfaceBuilder.CocoaPlugin @@ -486,7 +507,6 @@ com.apple.InterfaceBuilder.CocoaPlugin - TableView com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -509,19 +529,11 @@ - 20 + 22 YES - - DetailsPanel - NSWindowController - - IBProjectSource - DetailsPanel.h - - DetailsPanel NSWindowController @@ -530,60 +542,21 @@ NSTableView - IBUserSource - + IBProjectSource + ../base/DetailsPanel.h DetailsPanel NSWindowController - - detailsTable - TableView - - - IBProjectSource - dgbase/DetailsPanel.h - - - - FirstResponder IBUserSource - PyApp - PyRegistrable - - IBProjectSource - cocoalib/PyApp.h - - - - PyRegistrable + FirstResponder NSObject - - IBProjectSource - cocoalib/PyRegistrable.h - - - - TableView - NSTableView - - py - PyApp - - - IBProjectSource - cocoalib/Table.h - - - - TableView - NSTableView IBUserSource diff --git a/cocoa/me/dg_cocoa.py b/cocoa/me/dg_cocoa.py index 32ba3689..7aee6df6 100644 --- a/cocoa/me/dg_cocoa.py +++ b/cocoa/me/dg_cocoa.py @@ -6,7 +6,7 @@ from hsutil.cocoa import signature -from core.app_cocoa_inter import PyDupeGuruBase +from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel from core_me.app_cocoa import DupeGuruME from core.scanner import (SCAN_TYPE_FILENAME, SCAN_TYPE_FIELDS, SCAN_TYPE_FIELDS_NO_ORDER, SCAN_TYPE_TAG, SCAN_TYPE_CONTENT, SCAN_TYPE_CONTENT_AUDIO) diff --git a/cocoa/me/dupeguru.xcodeproj/project.pbxproj b/cocoa/me/dupeguru.xcodeproj/project.pbxproj index 366eac2d..e1c250a7 100644 --- a/cocoa/me/dupeguru.xcodeproj/project.pbxproj +++ b/cocoa/me/dupeguru.xcodeproj/project.pbxproj @@ -130,6 +130,7 @@ CE900AD6109B2A9B00754048 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = ""; }; CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = ""; }; CECA899B09DB132E00A3D774 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DetailsPanel.m; sourceTree = ""; }; + CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; }; CEEB135109C837A2004D2330 /* dupeguru.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = dupeguru.icns; sourceTree = ""; }; 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; }; @@ -310,6 +311,7 @@ CE515E180FC6C19300EC695D /* DirectoryPanel.h */, CE515E190FC6C19300EC695D /* DirectoryPanel.m */, CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */, + CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */, CE515E1B0FC6C19300EC695D /* ResultWindow.h */, CE515E1C0FC6C19300EC695D /* ResultWindow.m */, ); diff --git a/cocoa/me/main.m b/cocoa/me/main.m index 95d93cd0..6fbc417e 100644 --- a/cocoa/me/main.m +++ b/cocoa/me/main.m @@ -7,10 +7,12 @@ http://www.hardcoded.net/licenses/hs_license */ #import +#import "Utils.h" int main(int argc, char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [Utils setPluginName:@"dg_cocoa"]; NSString *pluginPath = [[NSBundle mainBundle] pathForResource:@"dg_cocoa" ofType:@"plugin"]; diff --git a/cocoa/pe/DetailsPanel.h b/cocoa/pe/DetailsPanel.h index 9061cadf..9974627f 100644 --- a/cocoa/pe/DetailsPanel.h +++ b/cocoa/pe/DetailsPanel.h @@ -16,7 +16,7 @@ http://www.hardcoded.net/licenses/hs_license IBOutlet NSImageView *refImage; IBOutlet NSProgressIndicator *refProgressIndicator; - PyApp *py; + PyApp *pyApp; BOOL _needsRefresh; NSString *_dupePath; NSString *_refPath; diff --git a/cocoa/pe/DetailsPanel.m b/cocoa/pe/DetailsPanel.m index 4e9fc89e..dce36c12 100644 --- a/cocoa/pe/DetailsPanel.m +++ b/cocoa/pe/DetailsPanel.m @@ -17,7 +17,7 @@ http://www.hardcoded.net/licenses/hs_license - (id)initWithPy:(PyApp *)aPy { self = [super initWithPy:aPy]; - py = aPy; + pyApp = aPy; _needsRefresh = YES; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imageLoaded:) name:ImageLoadedNotification object:self]; return self; @@ -36,18 +36,18 @@ http://www.hardcoded.net/licenses/hs_license [pool release]; } -- (void)refresh +- (void)refreshDetails { if (!_needsRefresh) return; [detailsTable reloadData]; - NSString *refPath = [(PyDupeGuru *)py getSelectedDupeRefPath]; + NSString *refPath = [(PyDupeGuru *)pyApp getSelectedDupeRefPath]; if (_refPath != nil) [_refPath autorelease]; _refPath = [refPath retain]; [NSThread detachNewThreadSelector:@selector(loadImageAsync:) toTarget:self withObject:refPath]; - NSString *dupePath = [(PyDupeGuru *)py getSelectedDupePath]; + NSString *dupePath = [(PyDupeGuru *)pyApp getSelectedDupePath]; if (_dupePath != nil) [_dupePath autorelease]; _dupePath = [dupePath retain]; @@ -59,12 +59,6 @@ http://www.hardcoded.net/licenses/hs_license } /* Notifications */ -- (void)duplicateSelectionChanged:(NSNotification *)aNotification -{ - _needsRefresh = YES; - [super duplicateSelectionChanged:aNotification]; -} - - (void)imageLoaded:(NSNotification *)aNotification { NSString *imagePath = [[aNotification userInfo] valueForKey:@"imagePath"]; @@ -80,4 +74,11 @@ http://www.hardcoded.net/licenses/hs_license [dupeProgressIndicator stopAnimation:nil]; } } + +/* Python --> Cocoa */ +- (void)refresh +{ + _needsRefresh = YES; + [super refresh]; +} @end diff --git a/cocoa/pe/dg_cocoa.py b/cocoa/pe/dg_cocoa.py index 70f9f449..ef489bbf 100644 --- a/cocoa/pe/dg_cocoa.py +++ b/cocoa/pe/dg_cocoa.py @@ -4,7 +4,7 @@ # which should be included with this package. The terms are also available at # http://www.hardcoded.net/licenses/hs_license -from core.app_cocoa_inter import PyDupeGuruBase +from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel from core_pe import app_cocoa as app_pe_cocoa # Fix py2app imports which chokes on relative imports diff --git a/cocoa/pe/dupeguru.xcodeproj/project.pbxproj b/cocoa/pe/dupeguru.xcodeproj/project.pbxproj index f2adf4d4..e1fed2c3 100644 --- a/cocoa/pe/dupeguru.xcodeproj/project.pbxproj +++ b/cocoa/pe/dupeguru.xcodeproj/project.pbxproj @@ -77,6 +77,7 @@ CE031753109B345200517EE6 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = ""; }; CE073F5409CAE1A3005C1D2F /* dupeguru_pe_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_pe_help; path = ../../help_pe/dupeguru_pe_help; sourceTree = SOURCE_ROOT; }; CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = ""; }; + CE18126F111C9D5100E49FCE /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; 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; }; CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; }; @@ -303,6 +304,7 @@ CE80DB850FC1951C0086DCA6 /* DirectoryPanel.h */, CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */, CE80DB870FC1951C0086DCA6 /* PyDupeGuru.h */, + CE18126F111C9D5100E49FCE /* PyDetailsPanel.h */, CE80DB880FC1951C0086DCA6 /* ResultWindow.h */, CE80DB890FC1951C0086DCA6 /* ResultWindow.m */, ); diff --git a/cocoa/pe/main.m b/cocoa/pe/main.m index 95d93cd0..6fbc417e 100644 --- a/cocoa/pe/main.m +++ b/cocoa/pe/main.m @@ -7,10 +7,12 @@ http://www.hardcoded.net/licenses/hs_license */ #import +#import "Utils.h" int main(int argc, char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [Utils setPluginName:@"dg_cocoa"]; NSString *pluginPath = [[NSBundle mainBundle] pathForResource:@"dg_cocoa" ofType:@"plugin"]; diff --git a/cocoa/pe/xib/DetailsPanel.xib b/cocoa/pe/xib/DetailsPanel.xib index 75d522e9..7c36b052 100644 --- a/cocoa/pe/xib/DetailsPanel.xib +++ b/cocoa/pe/xib/DetailsPanel.xib @@ -2,17 +2,17 @@ 1050 - 10B504 + 10C540 740 - 1038.2 - 437.00 + 1038.25 + 458.00 com.apple.InterfaceBuilder.CocoaPlugin 740 YES - + YES @@ -487,6 +487,14 @@ 31 + + + dataSource + + + + 43 + @@ -768,7 +776,6 @@ 6.ImportedFromIB2 7.IBPluginDependency 7.ImportedFromIB2 - 8.CustomClassName 8.IBPluginDependency 8.ImportedFromIB2 9.IBPluginDependency @@ -825,7 +832,6 @@ com.apple.InterfaceBuilder.CocoaPlugin - TableView com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -848,11 +854,23 @@ - 42 + 43 YES + + DetailsPanel + NSWindowController + + detailsTable + NSTableView + + + IBProjectSource + ../base/DetailsPanel.h + + DetailsPanelPE DetailsPanel @@ -890,18 +908,6 @@ - - DetailsPanel - NSWindowController - - detailsTable - TableView - - - IBProjectSource - dgbase/DetailsPanel.h - - FirstResponder NSObject @@ -910,34 +916,6 @@ - - PyApp - PyRegistrable - - IBProjectSource - cocoalib/PyApp.h - - - - TableView - NSTableView - - py - PyApp - - - IBProjectSource - cocoalib/Table.h - - - - TableView - NSTableView - - IBUserSource - - - YES diff --git a/cocoa/se/dg_cocoa.py b/cocoa/se/dg_cocoa.py index bf19d2dc..64caccb8 100644 --- a/cocoa/se/dg_cocoa.py +++ b/cocoa/se/dg_cocoa.py @@ -7,7 +7,7 @@ from hsutil.cocoa import signature from core import scanner -from core.app_cocoa_inter import PyDupeGuruBase +from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel from core_se.app_cocoa import DupeGuru # Fix py2app imports with chokes on relative imports diff --git a/cocoa/se/dupeguru.xcodeproj/project.pbxproj b/cocoa/se/dupeguru.xcodeproj/project.pbxproj index 6da2c61a..321ba7dc 100644 --- a/cocoa/se/dupeguru.xcodeproj/project.pbxproj +++ b/cocoa/se/dupeguru.xcodeproj/project.pbxproj @@ -76,6 +76,7 @@ CE3A46F9109B212E002ABFD5 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../base/xib/MainMenu.xib; sourceTree = ""; }; 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; }; 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; }; @@ -289,6 +290,7 @@ CEFC7FB40FC951A700CD5728 /* DirectoryPanel.h */, CEFC7FB50FC951A700CD5728 /* DirectoryPanel.m */, CEFC7FB60FC951A700CD5728 /* PyDupeGuru.h */, + CE6E7407111C997500C350E3 /* PyDetailsPanel.h */, CEFC7FB70FC951A700CD5728 /* ResultWindow.h */, CEFC7FB80FC951A700CD5728 /* ResultWindow.m */, ); diff --git a/cocoa/se/main.m b/cocoa/se/main.m index 95d93cd0..6fbc417e 100644 --- a/cocoa/se/main.m +++ b/cocoa/se/main.m @@ -7,10 +7,12 @@ http://www.hardcoded.net/licenses/hs_license */ #import +#import "Utils.h" int main(int argc, char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [Utils setPluginName:@"dg_cocoa"]; NSString *pluginPath = [[NSBundle mainBundle] pathForResource:@"dg_cocoa" ofType:@"plugin"]; diff --git a/core/app_cocoa.py b/core/app_cocoa.py index 1beb3a1e..bbe43878 100644 --- a/core/app_cocoa.py +++ b/core/app_cocoa.py @@ -15,6 +15,7 @@ from hsutil.cocoa.objcmin import (NSNotificationCenter, NSUserDefaults, NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSUserDomainMask, NSWorkspace, NSWorkspaceRecycleOperation) from hsutil.misc import stripnone +from hsutil.notify import Broadcaster from hsutil.reg import RegistrationRequired from . import app, fs @@ -36,8 +37,9 @@ def demo_method(method): return wrapper -class DupeGuru(app.DupeGuru): +class DupeGuru(app.DupeGuru, Broadcaster): def __init__(self, data_module, appdata_subdir, appid): + Broadcaster.__init__(self) LOGGING_LEVEL = logging.DEBUG if NSUserDefaults.standardUserDefaults().boolForKey_('debug') else logging.WARNING logging.basicConfig(level=LOGGING_LEVEL, format='%(levelname)s %(message)s') logging.debug('started in debug mode') @@ -48,7 +50,7 @@ class DupeGuru(app.DupeGuru): self.progress = cocoa.ThreadedJobPerformer() self.display_delta_values = False self.selected_dupes = [] - self.RefreshDetailsTable(None,None) + self.RefreshDetailsWithSelected() #--- Override @staticmethod @@ -91,14 +93,6 @@ class DupeGuru(app.DupeGuru): curr_path = self.directories.get_subfolders(curr_path)[current_index] return self.get_folder_path(node_path[1:], curr_path) - def RefreshDetailsTable(self,dupe,group): - l1 = self._get_display_info(dupe, group, False) - # we don't want the two sides of the table to display the stats for the same file - ref = group.ref if group is not None and group.ref is not dupe else None - l2 = self._get_display_info(ref, group, False) - names = [c['display'] for c in self.data.COLUMNS] - self.details_table = zip(names,l1,l2) - #---Public def AddSelectedToIgnoreList(self): for dupe in self.selected_dupes: @@ -120,13 +114,7 @@ class DupeGuru(app.DupeGuru): self.scanner.ignore_list.Filter(lambda f,s:op.exists(f) and op.exists(s)) def RefreshDetailsWithSelected(self): - if self.selected_dupes: - self.RefreshDetailsTable( - self.selected_dupes[0], - self.results.get_group_of_duplicate(self.selected_dupes[0]) - ) - else: - self.RefreshDetailsTable(None,None) + self.notify('details_table_changed') def RemoveDirectory(self,index): try: @@ -153,7 +141,8 @@ class DupeGuru(app.DupeGuru): NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(path,'') def start_scanning(self): - self.RefreshDetailsTable(None, None) + self.selected_dupes = [] + self.RefreshDetailsWithSelected() try: app.DupeGuru.start_scanning(self) return 0 @@ -291,15 +280,3 @@ class DupeGuru(app.DupeGuru): else: return 0 - def GetTableViewCount(self, tag): - if self.progress._job_running: - return 0 - return len(self.details_table) - - def GetTableViewMarkedIndexes(self,tag): - return [] - - def GetTableViewValues(self,tag,row): - return self.details_table[row] - - diff --git a/core/app_cocoa_inter.py b/core/app_cocoa_inter.py index 21bf04c9..6337696f 100644 --- a/core/app_cocoa_inter.py +++ b/core/app_cocoa_inter.py @@ -14,6 +14,8 @@ from hsutil.cocoa.objcmin import NSObject from hsutil.cocoa import signature from hsutil.reg import InvalidCodeError +from .gui.details_panel import DetailsPanel + # Fix py2app's problems on relative imports from core import app, app_cocoa, data, directories, engine, export, ignore, results, fs, scanner from hsutil import conflict @@ -200,3 +202,23 @@ class PyDupeGuruBase(PyApp): def setRegisteredCode_andEmail_(self, code, email): self.app.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) + return self + + @signature('i@:') + def numberOfRows(self): + return self.py.row_count() + + @signature('@@:@i') + def valueForColumn_row_(self, column, row): + return self.py.row(row)[int(column)] + + # python --> cocoa + def refresh(self): + self.cocoa.refresh() + diff --git a/core/gui/__init__.py b/core/gui/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/core/gui/details_panel.py b/core/gui/details_panel.py new file mode 100644 index 00000000..e724b4fd --- /dev/null +++ b/core/gui/details_panel.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Created By: Virgil Dupras +# Created On: 2010-02-05 +# 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 DetailsPanel(Listener): + def __init__(self, view, app): + Listener.__init__(self, app) + self.app = app + self.view = view + self._table = [] + self._refresh() + self.connect() + + #--- Private + def _refresh(self): + if self.app.selected_dupes: + dupe = self.app.selected_dupes[0] + group = self.app.results.get_group_of_duplicate(dupe) + else: + dupe = None + group = None + l1 = self.app._get_display_info(dupe, group, False) + # we don't want the two sides of the table to display the stats for the same file + ref = group.ref if group is not None and group.ref is not dupe else None + l2 = self.app._get_display_info(ref, group, False) + names = [c['display'] for c in self.app.data.COLUMNS] + self._table = zip(names, l1, l2) + + #--- Public + def row_count(self): + return len(self._table) + + def row(self, row_index): + return self._table[row_index] + + #--- Event Handlers + def details_table_changed(self): + self._refresh() + self.view.refresh() + diff --git a/core/tests/app_cocoa_test.py b/core/tests/app_cocoa_test.py index 6e0af5b8..6d15e958 100644 --- a/core/tests/app_cocoa_test.py +++ b/core/tests/app_cocoa_test.py @@ -26,6 +26,7 @@ try: 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 class DupeGuru(DupeGuruBase): def __init__(self): @@ -38,9 +39,28 @@ 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.objects,self.matches,self.groups = GetTestGroups() self.app.results.groups = self.groups tmppath = self.tmppath() @@ -48,6 +68,44 @@ class TCDupeGuru(TestCase): 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 @@ -194,24 +252,14 @@ class TCDupeGuru(TestCase): self.assert_(app.results.is_marked(objects[4])) def test_refreshDetailsWithSelected(self): - def mock_refresh(dupe,group): - self.called = True - if self.app.selected_dupes: - self.assert_(dupe is self.app.selected_dupes[0]) - self.assert_(group is self.app.results.get_group_of_duplicate(dupe)) - else: - self.assert_(dupe is None) - self.assert_(group is None) - - self.app.RefreshDetailsTable = mock_refresh - self.called = False self.app.SelectPowerMarkerNodePaths(r2np([0,2])) self.app.RefreshDetailsWithSelected() - self.assert_(self.called) - self.called = False + eq_(self.dpanel.row(0), ('Filename', 'bar bleh', 'foo bar')) + self.check_gui_calls(self.dpanel_gui, ['refresh']) self.app.SelectPowerMarkerNodePaths([]) self.app.RefreshDetailsWithSelected() - self.assert_(self.called) + eq_(self.dpanel.row(0), ('Filename', '---', '---')) + self.check_gui_calls(self.dpanel_gui, ['refresh']) def test_makeSelectedReference(self): app = self.app diff --git a/core/tests/data.py b/core/tests/data.py index 8a856451..824ea4f7 100644 --- a/core/tests/data.py +++ b/core/tests/data.py @@ -31,7 +31,7 @@ def GetDisplayInfo(dupe, group, delta): dupe.name, format_path(dupe.path), format_size(size, 0, 1, False), - dupe.extension, + dupe.extension if hasattr(dupe, 'extension') else '---', ] def GetDupeSortKey(dupe, get_group, key, delta):