diff --git a/cocoa/base/PyDupeGuru.h b/cocoa/base/PyDupeGuru.h index 7879caa9..ebe1a325 100644 --- a/cocoa/base/PyDupeGuru.h +++ b/cocoa/base/PyDupeGuru.h @@ -14,7 +14,9 @@ http://www.hardcoded.net/licenses/hs_license - (NSNumber *)addDirectory:(NSString *)name; - (void)removeDirectory:(NSNumber *)index; - (void)loadResults; +- (void)loadResultsFrom:(NSString *)filename; - (void)saveResults; +- (void)saveResultsAs:(NSString *)filename; - (void)loadIgnoreList; - (void)saveIgnoreList; - (void)clearIgnoreList; diff --git a/cocoa/base/ResultWindow.h b/cocoa/base/ResultWindow.h index 3df5601c..7eee7ce9 100644 --- a/cocoa/base/ResultWindow.h +++ b/cocoa/base/ResultWindow.h @@ -49,6 +49,7 @@ http://www.hardcoded.net/licenses/hs_license - (IBAction)filter:(id)sender; - (IBAction)ignoreSelected:(id)sender; - (IBAction)invokeCustomCommand:(id)sender; +- (IBAction)loadResults:(id)sender; - (IBAction)markAll:(id)sender; - (IBAction)markInvert:(id)sender; - (IBAction)markNone:(id)sender; @@ -61,6 +62,7 @@ http://www.hardcoded.net/licenses/hs_license - (IBAction)renameSelected:(id)sender; - (IBAction)resetColumnsToDefault:(id)sender; - (IBAction)revealSelected:(id)sender; +- (IBAction)saveResults:(id)sender; - (IBAction)showPreferencesPanel:(id)sender; - (IBAction)startDuplicateScan:(id)sender; - (IBAction)switchSelected:(id)sender; diff --git a/cocoa/base/ResultWindow.m b/cocoa/base/ResultWindow.m index 1d7dc9b9..0312d852 100644 --- a/cocoa/base/ResultWindow.m +++ b/cocoa/base/ResultWindow.m @@ -212,6 +212,21 @@ http://www.hardcoded.net/licenses/hs_license } } +- (IBAction)loadResults:(id)sender +{ + NSOpenPanel *op = [NSOpenPanel openPanel]; + [op setCanChooseFiles:YES]; + [op setCanChooseDirectories:NO]; + [op setCanCreateDirectories:NO]; + [op setAllowsMultipleSelection:NO]; + [op setAllowedFileTypes:[NSArray arrayWithObject:@"dupeguru"]]; + [op setTitle:@"Select a results file to load"]; + if ([op runModal] == NSOKButton) { + NSString *filename = [[op filenames] objectAtIndex:0]; + [py loadResultsFrom:filename]; + } +} + - (IBAction)markAll:(id)sender { [py markAll]; @@ -303,6 +318,17 @@ http://www.hardcoded.net/licenses/hs_license [preferencesPanel showWindow:sender]; } +- (IBAction)saveResults:(id)sender +{ + NSSavePanel *sp = [NSSavePanel savePanel]; + [sp setCanCreateDirectories:YES]; + [sp setAllowedFileTypes:[NSArray arrayWithObject:@"dupeguru"]]; + [sp setTitle:@"Select a file to save your results to"]; + if ([sp runModal] == NSOKButton) { + [py saveResultsAs:[sp filename]]; + } +} + - (IBAction)startDuplicateScan:(id)sender { // Virtual diff --git a/cocoa/base/xib/MainMenu.xib b/cocoa/base/xib/MainMenu.xib index ea7f76be..26a70c66 100644 --- a/cocoa/base/xib/MainMenu.xib +++ b/cocoa/base/xib/MainMenu.xib @@ -2,18 +2,17 @@ 1050 - 10D573 - 740 + 10F569 + 788 1038.29 - 460.00 + 461.00 com.apple.InterfaceBuilder.CocoaPlugin - 740 + 788 YES - - + YES @@ -83,9 +82,11 @@ Power Marker - + 256 {{7, 14}, {67, 24}} + + YES 67239424 @@ -176,9 +177,11 @@ Filter - + 258 {{0, 14}, {81, 22}} + + YES 343014976 @@ -320,9 +323,11 @@ Action - + 256 {{0, 14}, {58, 26}} + + YES -2076049856 @@ -525,9 +530,11 @@ Delta Values - + 256 {{4, 14}, {67, 24}} + + YES 67239424 @@ -674,7 +681,7 @@ {1.79769e+308, 1.79769e+308} {340, 340} - + 256 YES @@ -823,7 +830,6 @@ {{1, 17}, {515, 317}} - 4 @@ -856,7 +862,6 @@ {{1, 0}, {515, 17}} - 4 @@ -865,7 +870,6 @@ {{20, 45}, {517, 335}} - 562 @@ -897,7 +901,6 @@ {557, 400} - {{0, 0}, {1440, 878}} {340, 418} @@ -1029,6 +1032,48 @@ _NSAppleMenu + + + File + + 2147483647 + + + submenuAction: + + File + + YES + + + Load Results... + o + 1048576 + 2147483647 + + + + + + Save Results... + s + 1048576 + 2147483647 + + + + + + Export Results to XHTML + E + 1048576 + 2147483647 + + + + + + Edit @@ -1137,7 +1182,7 @@ Start Duplicate Scan - s + d 1048576 2147483647 @@ -1152,15 +1197,6 @@ - - - Export Results to XHTML - E - 1048576 - 2147483647 - - - YES @@ -2221,6 +2257,22 @@ 1178 + + + saveResults: + + + + 1207 + + + + loadResults: + + + + 1208 + @@ -2336,6 +2388,7 @@ + MainMenu @@ -2545,7 +2598,6 @@ - @@ -2621,11 +2673,6 @@ - - 947 - - - 618 @@ -3089,6 +3136,41 @@ + + 1203 + + + YES + + + + + + 1204 + + + YES + + + + + + + + 1205 + + + + + 1206 + + + + + 947 + + + @@ -3113,6 +3195,7 @@ 1028.ImportedFromIB2 103.IBPluginDependency 103.ImportedFromIB2 + 106.IBEditorWindowLastContentRect 106.IBPluginDependency 106.ImportedFromIB2 111.IBPluginDependency @@ -3141,6 +3224,11 @@ 1172.IBPluginDependency 1173.IBPluginDependency 1177.IBPluginDependency + 1203.IBPluginDependency + 1204.IBEditorWindowLastContentRect + 1204.IBPluginDependency + 1205.IBPluginDependency + 1206.IBPluginDependency 134.IBPluginDependency 134.ImportedFromIB2 136.IBPluginDependency @@ -3177,6 +3265,7 @@ 222.ImportedFromIB2 23.IBPluginDependency 23.ImportedFromIB2 + 24.IBEditorWindowLastContentRect 24.IBPluginDependency 24.ImportedFromIB2 29.IBEditorWindowLastContentRect @@ -3314,6 +3403,7 @@ 955.ImportedFromIB2 959.IBPluginDependency 959.ImportedFromIB2 + 960.IBEditorWindowLastContentRect 960.IBPluginDependency 960.ImportedFromIB2 961.IBPluginDependency @@ -3322,6 +3412,7 @@ 962.ImportedFromIB2 965.IBPluginDependency 965.ImportedFromIB2 + 966.IBEditorWindowLastContentRect 966.IBPluginDependency 966.ImportedFromIB2 985.IBPluginDependency @@ -3351,6 +3442,7 @@ com.apple.InterfaceBuilder.CocoaPlugin + {{602, 725}, {196, 43}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -3368,7 +3460,7 @@ com.apple.InterfaceBuilder.CocoaPlugin - {{79, 766}, {617, 0}} + {{409, 745}, {617, 0}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -3380,6 +3472,11 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + {{242, 705}, {258, 63}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -3415,9 +3512,10 @@ com.apple.InterfaceBuilder.CocoaPlugin + {{531, 625}, {193, 143}} com.apple.InterfaceBuilder.CocoaPlugin - {{140, 768}, {481, 20}} + {{140, 768}, {523, 20}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -3448,7 +3546,7 @@ com.apple.InterfaceBuilder.CocoaPlugin - {{286, 455}, {361, 313}} + {{328, 475}, {361, 293}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -3468,7 +3566,7 @@ com.apple.InterfaceBuilder.CocoaPlugin - {{355, 762}, {64, 6}} + {{397, 762}, {64, 6}} com.apple.InterfaceBuilder.CocoaPlugin {{182, 609}, {331, 133}} @@ -3552,6 +3650,7 @@ com.apple.InterfaceBuilder.CocoaPlugin + {{475, 725}, {167, 43}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -3560,6 +3659,7 @@ com.apple.InterfaceBuilder.CocoaPlugin + {{284, 615}, {188, 153}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -3586,7 +3686,7 @@ - 1178 + 1208 @@ -3607,6 +3707,25 @@ id + + YES + + YES + openWebsite: + toggleDirectories: + + + YES + + openWebsite: + id + + + toggleDirectories: + id + + + IBProjectSource AppDelegate.h @@ -3619,6 +3738,13 @@ unlockApp: id + + unlockApp: + + unlockApp: + id + + YES @@ -3634,6 +3760,30 @@ NSMenuItem + + YES + + YES + py + recentDirectories + unlockMenuItem + + + YES + + py + PyDupeGuru + + + recentDirectories + RecentDirectories + + + unlockMenuItem + NSMenuItem + + + IBUserSource @@ -3646,6 +3796,13 @@ unlockApp: id + + unlockApp: + + unlockApp: + id + + YES @@ -3663,6 +3820,35 @@ NSMenuItem + + YES + + YES + py + recentDirectories + result + unlockMenuItem + + + YES + + py + PyDupeGuruBase + + + recentDirectories + RecentDirectories + + + result + ResultWindowBase + + + unlockMenuItem + NSMenuItem + + + IBProjectSource ../base/AppDelegate.h @@ -3771,6 +3957,25 @@ id + + YES + + YES + clearMenu: + menuClick: + + + YES + + clearMenu: + id + + + menuClick: + id + + + YES @@ -3784,6 +3989,25 @@ NSMenu + + YES + + YES + delegate + menu + + + YES + + delegate + id + + + menu + NSMenu + + + IBProjectSource ../RecentDirectories.h @@ -3813,6 +4037,25 @@ id + + YES + + YES + resetColumnsToDefault: + startDuplicateScan: + + + YES + + resetColumnsToDefault: + id + + + startDuplicateScan: + id + + + IBProjectSource ResultWindow.h @@ -3834,6 +4077,7 @@ filter: ignoreSelected: invokeCustomCommand: + loadResults: markAll: markInvert: markNone: @@ -3846,6 +4090,7 @@ renameSelected: resetColumnsToDefault: revealSelected: + saveResults: showPreferencesPanel: startDuplicateScan: switchSelected: @@ -3884,6 +4129,167 @@ id id id + id + id + + + + YES + + YES + changeDelta: + changePowerMarker: + clearIgnoreList: + copyMarked: + deleteMarked: + exportToXHTML: + filter: + ignoreSelected: + invokeCustomCommand: + loadResults: + markAll: + markInvert: + markNone: + markSelected: + moveMarked: + openClicked: + openSelected: + removeMarked: + removeSelected: + renameSelected: + resetColumnsToDefault: + revealSelected: + saveResults: + showPreferencesPanel: + startDuplicateScan: + switchSelected: + toggleColumn: + toggleDelta: + toggleDetailsPanel: + togglePowerMarker: + + + YES + + changeDelta: + id + + + changePowerMarker: + id + + + clearIgnoreList: + id + + + copyMarked: + id + + + deleteMarked: + id + + + exportToXHTML: + id + + + filter: + id + + + ignoreSelected: + id + + + invokeCustomCommand: + id + + + loadResults: + id + + + markAll: + id + + + markInvert: + id + + + markNone: + id + + + markSelected: + id + + + moveMarked: + id + + + openClicked: + id + + + openSelected: + id + + + removeMarked: + id + + + removeSelected: + id + + + renameSelected: + id + + + resetColumnsToDefault: + id + + + revealSelected: + id + + + saveResults: + id + + + showPreferencesPanel: + id + + + startDuplicateScan: + id + + + switchSelected: + id + + + toggleColumn: + id + + + toggleDelta: + id + + + toggleDetailsPanel: + id + + + togglePowerMarker: + id + @@ -3911,6 +4317,55 @@ NSTextField + + YES + + YES + app + columnsMenu + deltaSwitch + filterField + matches + pmSwitch + py + stats + + + YES + + app + id + + + columnsMenu + NSMenu + + + deltaSwitch + NSSegmentedControl + + + filterField + NSSearchField + + + matches + HSOutlineView + + + pmSwitch + NSSegmentedControl + + + py + PyDupeGuruBase + + + stats + NSTextField + + + IBProjectSource ../base/ResultWindow.h @@ -4502,6 +4957,13 @@ showWindow: id + + showWindow: + + showWindow: + id + + IBFrameworkSource AppKit.framework/Headers/NSWindowController.h @@ -4514,15 +4976,30 @@ checkForUpdates: id + + checkForUpdates: + + checkForUpdates: + id + + delegate id + + delegate + + delegate + id + + 0 + IBCocoaFramework com.apple.InterfaceBuilder.CocoaPlugin.macosx @@ -4538,5 +5015,28 @@ YES ../../se/dupeguru.xcodeproj 3 + + YES + + YES + NSActionTemplate + NSApplicationIcon + NSMenuCheckmark + NSMenuMixedState + details32 + folder32 + preferences32 + + + YES + {15, 15} + {128, 128} + {9, 8} + {7, 2} + {48, 48} + {32, 32} + {32, 32} + + diff --git a/core/app.py b/core/app.py index 75d36273..b46e6c9a 100644 --- a/core/app.py +++ b/core/app.py @@ -244,6 +244,11 @@ class DupeGuru(RegistrableApplication, Broadcaster): self._start_job(JOB_LOAD, self._do_load) self.load_ignore_list() + def load_from(self, filename): + def do(j): + self.results.load_from_xml(filename, self._get_file, j) + self._start_job(JOB_LOAD, do) + def load_ignore_list(self): p = op.join(self.appdata, 'ignore_list.xml') self.scanner.ignore_list.load_from_xml(p) @@ -322,6 +327,11 @@ class DupeGuru(RegistrableApplication, Broadcaster): if self.results.is_modified: self.results.save_to_xml(op.join(self.appdata, 'last_results.xml')) + def save_as(self, filename): + self.results.save_to_xml(filename) + # It's not because we saved it here that we don't want to save it in appdata when we quit + self.results.is_modified = True + def save_ignore_list(self): if not op.exists(self.appdata): os.makedirs(self.appdata) diff --git a/core/app_cocoa_inter.py b/core/app_cocoa_inter.py index 75ba44c6..29b535f6 100644 --- a/core/app_cocoa_inter.py +++ b/core/app_cocoa_inter.py @@ -46,6 +46,9 @@ class PyDupeGuruBase(PyRegistrable): def loadResults(self): self.py.load() + def loadResultsFrom_(self, filename): + self.py.load_from(filename) + def markAll(self): self.py.mark_all() @@ -67,6 +70,9 @@ class PyDupeGuruBase(PyRegistrable): def saveResults(self): self.py.save() + def saveResultsAs_(self, filename): + self.py.save_as(filename) + #---Actions def addSelectedToIgnoreList(self): self.py.add_selected_to_ignore_list() diff --git a/core_pe/app_cocoa.py b/core_pe/app_cocoa.py index f56a6773..10dd2c30 100644 --- a/core_pe/app_cocoa.py +++ b/core_pe/app_cocoa.py @@ -164,10 +164,6 @@ class DupeGuruPE(app_cocoa.DupeGuru): else: app_cocoa.DupeGuru._do_delete_dupe(self, dupe) - def _do_load(self, j): - self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml')) - self.results.load_from_xml(op.join(self.appdata, 'last_results.xml'), self._get_file, j) - def _get_file(self, str_path): p = Path(str_path) if (self.directories.iphoto_libpath is not None) and (p in self.directories.iphoto_libpath[:-1]): diff --git a/qt/base/main_window.py b/qt/base/main_window.py index 7ee74f88..8382a857 100644 --- a/qt/base/main_window.py +++ b/qt/base/main_window.py @@ -10,7 +10,7 @@ import sys from PyQt4.QtCore import Qt, QCoreApplication, QProcess, SIGNAL, QUrl from PyQt4.QtGui import (QMainWindow, QMenu, QPixmap, QIcon, QToolButton, QLabel, QHeaderView, - QMessageBox, QInputDialog, QLineEdit, QDesktopServices) + QMessageBox, QInputDialog, QLineEdit, QDesktopServices, QFileDialog) from hsutil.misc import nonone @@ -37,7 +37,13 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate) self.connect(self.resultsView, SIGNAL('doubleClicked()'), self.resultsDoubleClicked) self.connect(self.resultsView, SIGNAL('spacePressed()'), self.resultsSpacePressed) + + # Actions (the vast majority of them are connected in the UI file, but I'm trying to + # phase away from those, and these connections are harder to maintain than through simple + # code self.actionInvokeCustomCommand.triggered.connect(self.app.invokeCustomCommand) + self.actionLoadResults.triggered.connect(self.loadResultsTriggered) + self.actionSaveResults.triggered.connect(self.saveResultsTriggered) def _setupUi(self): self.setupUi(self) @@ -196,7 +202,14 @@ class MainWindow(QMainWindow, Ui_MainWindow): exported_path = self.app.export_to_xhtml(column_ids) url = QUrl.fromLocalFile(exported_path) QDesktopServices.openUrl(url) - + + def loadResultsTriggered(self): + title = "Select a results file to load" + files = "dupeGuru Results (*.dupeguru)" + destination = QFileDialog.getOpenFileName(self, title, '', files) + if destination: + self.app.load_from(destination) + def makeReferenceTriggered(self): self.app.make_selected_reference() @@ -248,6 +261,13 @@ class MainWindow(QMainWindow, Ui_MainWindow): def revealTriggered(self): self.app.reveal_selected() + def saveResultsTriggered(self): + title = "Select a file to save your results to" + files = "dupeGuru Results (*.dupeguru)" + destination = QFileDialog.getSaveFileName(self, title, '', files) + if destination: + self.app.save_as(destination) + def scanTriggered(self): title = "Start a new scan" if len(self.app.results.groups) > 0: diff --git a/qt/base/main_window.ui b/qt/base/main_window.ui index 7d443b43..971425d3 100644 --- a/qt/base/main_window.ui +++ b/qt/base/main_window.ui @@ -126,6 +126,8 @@ + + @@ -183,7 +185,7 @@ Start scanning for duplicates - Ctrl+S + Ctrl+T @@ -427,6 +429,22 @@ Export To XHTML + + + Load Results... + + + Ctrl+L + + + + + Save Results... + + + Ctrl+S + + Open Debug Log