1
0
mirror of https://github.com/arsenetar/dupeguru.git synced 2026-01-25 08:01:39 +00:00

Compare commits

...

14 Commits

Author SHA1 Message Date
Virgil Dupras
ee96d5f88c Fixed Windows packaging for dgse. 2010-04-13 14:04:15 +01:00
Virgil Dupras
e96a917bef Fixed the problem dialog under cocoa, which was visible at launch. 2010-04-13 14:22:24 +02:00
Virgil Dupras
769b816998 se v2.10.0 2010-04-13 11:58:53 +02:00
Virgil Dupras
ff891c210c [#4 state:fixed] Filters are now applied on the whole file path. 2010-04-13 11:40:20 +02:00
Virgil Dupras
3ed5e1bf95 [#12 state:fixed] Added custom command help. 2010-04-13 11:05:42 +02:00
Virgil Dupras
5bc8581389 [#12] Tweaked the custom command feature under Cocoa. 2010-04-13 10:52:44 +02:00
Virgil Dupras
7346b422d5 [#12] Added the Custom Command preference on the Qt side. 2010-04-13 09:02:09 +01:00
Virgil Dupras
5c80ac1c74 [#12] dgse cocoa: Added custom command invocation. 2010-04-12 17:43:24 +02:00
Virgil Dupras
699023992c Added the problem dialog to the Qt side. 2010-04-12 15:29:56 +02:00
Virgil Dupras
454ce604ad Merged hsgui heads. 2010-04-12 12:22:18 +02:00
Virgil Dupras
1e0f6bfecb Added a dialog giving more information about the causes of problems during operations. 2010-04-12 12:21:01 +02:00
Virgil Dupras
7f10aa3de2 Merged heads. 2010-04-08 07:07:45 -07:00
Virgil Dupras
f8764ab85e dgme qt: Fixed visual glitch in preference panel under Linux. 2010-04-08 07:06:32 -07:00
Virgil Dupras
aa8544308e Added tag pe1.8.6 for changeset 556baf4a4107 2010-04-08 15:01:27 +02:00
37 changed files with 2892 additions and 1063 deletions

View File

@@ -14,3 +14,4 @@ cbcf9c80fee4c908ef2efbf1c143c9e47676c9b2 pe1.8.0
90ed56ee602666db2f267f73eac6f824347039b5 me5.7.2
4c3cb1e671a333eabde1151c7c6ffb3609cab025 pe1.8.4
0a71306434bca51bea9a5d5ae54fe1bf0e4900d8 pe1.8.5
556baf4a410779e9bbf43129de133e4c4b26d679 pe1.8.6

View File

@@ -0,0 +1,25 @@
/*
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 "HSWindowController.h"
#import "PyApp.h"
#import "PyProblemDialog.h"
#import "HSTable.h"
@interface ProblemDialog : HSWindowController
{
IBOutlet NSTableView *problemTableView;
HSTable *problemTable;
}
- (id)initWithPy:(PyApp *)aPy;
- (PyProblemDialog *)py;
- (IBAction)revealSelected:(id)sender;
@end

View File

@@ -0,0 +1,40 @@
/*
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 "ProblemDialog.h"
#import "Utils.h"
@implementation ProblemDialog
- (id)initWithPy:(PyApp *)aPy
{
self = [super initWithNibName:@"ProblemDialog" pyClassName:@"PyProblemDialog" pyParent:aPy];
[self window]; //So the detailsTable is initialized.
problemTable = [[HSTable alloc] initWithPyClassName:@"PyProblemTable" pyParent:[self py] view:problemTableView];
[self connect];
[problemTable connect];
return self;
}
- (void)dealloc
{
[problemTable disconnect];
[self disconnect];
[problemTable release];
[super dealloc];
}
- (PyProblemDialog *)py
{
return (PyProblemDialog *)py;
}
- (IBAction)revealSelected:(id)sender
{
[[self py] revealSelected];
}
@end

View File

@@ -20,6 +20,7 @@ http://www.hardcoded.net/licenses/hs_license
- (void)clearIgnoreList;
- (void)purgeIgnoreList;
- (NSString *)exportToXHTMLwithColumns:(NSArray *)aColIds;
- (void)invokeCommand:(NSString *)cmd;
- (NSNumber *)doScan;
@@ -41,7 +42,7 @@ http://www.hardcoded.net/licenses/hs_license
//Data
- (NSNumber *)getIgnoreListCount;
- (NSNumber *)getMarkCount;
- (NSNumber *)getOperationalErrorCount;
- (BOOL)scanWasProblematic;
//Scanning options
- (void)setMinMatchPercentage:(NSNumber *)percentage;

View 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 PyProblemDialog : PyGUI
- (void)revealSelected;
@end

View File

@@ -10,6 +10,7 @@ http://www.hardcoded.net/licenses/hs_license
#import "HSOutlineView.h"
#import "StatsLabel.h"
#import "ResultOutline.h"
#import "ProblemDialog.h"
#import "PyDupeGuru.h"
@interface ResultWindowBase : NSWindowController
@@ -28,6 +29,7 @@ http://www.hardcoded.net/licenses/hs_license
NSWindowController *preferencesPanel;
ResultOutline *outline;
StatsLabel *statsLabel;
ProblemDialog *problemDialog;
}
/* Helpers */
- (void)fillColumnsMenu;
@@ -46,6 +48,7 @@ http://www.hardcoded.net/licenses/hs_license
- (IBAction)exportToXHTML:(id)sender;
- (IBAction)filter:(id)sender;
- (IBAction)ignoreSelected:(id)sender;
- (IBAction)invokeCustomCommand:(id)sender;
- (IBAction)markAll:(id)sender;
- (IBAction)markInvert:(id)sender;
- (IBAction)markNone:(id)sender;

View File

@@ -21,6 +21,7 @@ http://www.hardcoded.net/licenses/hs_license
preferencesPanel = [[NSWindowController alloc] initWithWindowNibName:@"Preferences"];
outline = [[ResultOutline alloc] initWithPyParent:py view:matches];
statsLabel = [[StatsLabel alloc] initWithPyParent:py labelView:stats];
problemDialog = [[ProblemDialog alloc] initWithPy:py];
[self initResultColumns];
[self fillColumnsMenu];
[deltaSwitch setSelectedSegment:0];
@@ -38,6 +39,8 @@ http://www.hardcoded.net/licenses/hs_license
{
[outline release];
[preferencesPanel release];
[statsLabel release];
[problemDialog release];
[super dealloc];
}
@@ -197,6 +200,18 @@ http://www.hardcoded.net/licenses/hs_license
[py addSelectedToIgnoreList];
}
- (IBAction)invokeCustomCommand:(id)sender
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *cmd = [ud stringForKey:@"CustomCommand"];
if ((cmd != nil) && ([cmd length] > 0)) {
[py invokeCommand:cmd];
}
else {
[Dialogs showMessage:@"You have no custom command set up. Set it up in your preferences."];
}
}
- (IBAction)markAll:(id)sender
{
[py markAll];
@@ -349,29 +364,31 @@ http://www.hardcoded.net/licenses/hs_license
- (void)jobCompleted:(NSNotification *)aNotification
{
NSInteger r = n2i([py getOperationalErrorCount]);
id lastAction = [[ProgressController mainProgressController] jobId];
if ([lastAction isEqualTo:jobCopy]) {
if (r > 0)
[Dialogs showMessage:[NSString stringWithFormat:@"%d file(s) couldn't be copied.",r]];
else
if ([py scanWasProblematic]) {
[problemDialog showWindow:self];
}
else {
[Dialogs showMessage:@"All marked files were copied sucessfully."];
}
}
else if ([lastAction isEqualTo:jobMove]) {
if (r > 0)
[Dialogs showMessage:[NSString stringWithFormat:@"%d file(s) couldn't be moved. They were kept in the results, and still are marked.",r]];
else
if ([py scanWasProblematic]) {
[problemDialog showWindow:self];
}
else {
[Dialogs showMessage:@"All marked files were moved sucessfully."];
}
else if ([lastAction isEqualTo:jobDelete]) {
if (r > 0) {
NSString *msg = @"%d file(s) couldn't be sent to Trash. They were kept in the results, "\
"and still are marked. See the F.A.Q. section in the help file for details.";
[Dialogs showMessage:[NSString stringWithFormat:msg,r]];
}
else
else if ([lastAction isEqualTo:jobDelete]) {
if ([py scanWasProblematic]) {
[problemDialog showWindow:self];
}
else {
[Dialogs showMessage:@"All marked files were sucessfully sent to Trash."];
}
}
else if ([lastAction isEqualTo:jobScan]) {
NSInteger groupCount = [outline intProperty:@"children_count" valueAtPath:nil];
if (groupCount == 0)

View File

@@ -2,18 +2,18 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">10C540</string>
<string key="IBDocument.SystemVersion">10D573</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.AppKitVersion">1038.25</string>
<string key="IBDocument.HIToolboxVersion">458.00</string>
<string key="IBDocument.AppKitVersion">1038.29</string>
<string key="IBDocument.HIToolboxVersion">460.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">740</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="598"/>
<integer value="219"/>
<integer value="29"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -1146,7 +1146,7 @@
<object class="NSMenuItem" id="578499792">
<reference key="NSMenu" ref="600111647"/>
<string key="NSTitle">Clear Ignore List</string>
<string key="NSKeyEquiv">I</string>
<string key="NSKeyEquiv">G</string>
<int key="NSKeyEquivModMask">1048576</int>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="852972005"/>
@@ -1231,7 +1231,7 @@
<object class="NSMenuItem" id="904423169">
<reference key="NSMenu" ref="600111647"/>
<string key="NSTitle">Add Selected to Ignore List</string>
<string key="NSKeyEquiv">i</string>
<string key="NSKeyEquiv">g</string>
<int key="NSKeyEquivModMask">1048576</int>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="852972005"/>
@@ -1275,6 +1275,15 @@
<reference key="NSOnImage" ref="852972005"/>
<reference key="NSMixedImage" ref="218295580"/>
</object>
<object class="NSMenuItem" id="517397504">
<reference key="NSMenu" ref="600111647"/>
<string key="NSTitle">Invoke Custom Command</string>
<string key="NSKeyEquiv">i</string>
<int key="NSKeyEquivModMask">1048576</int>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="852972005"/>
<reference key="NSMixedImage" ref="218295580"/>
</object>
<object class="NSMenuItem" id="564101661">
<reference key="NSMenu" ref="600111647"/>
<string key="NSTitle">Rename Selected</string>
@@ -2204,6 +2213,14 @@
</object>
<int key="connectionID">1176</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">invokeCustomCommand:</string>
<reference key="source" ref="339936126"/>
<reference key="destination" ref="517397504"/>
</object>
<int key="connectionID">1178</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -2530,6 +2547,7 @@
<reference ref="564101661"/>
<reference ref="630362403"/>
<reference ref="747820446"/>
<reference ref="517397504"/>
</object>
<reference key="parent" ref="528113253"/>
</object>
@@ -3066,6 +3084,11 @@
<reference key="object" ref="747820446"/>
<reference key="parent" ref="600111647"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">1177</int>
<reference key="object" ref="517397504"/>
<reference key="parent" ref="600111647"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
@@ -3117,6 +3140,7 @@
<string>1171.IBPluginDependency</string>
<string>1172.IBPluginDependency</string>
<string>1173.IBPluginDependency</string>
<string>1177.IBPluginDependency</string>
<string>134.IBPluginDependency</string>
<string>134.ImportedFromIB2</string>
<string>136.IBPluginDependency</string>
@@ -3355,6 +3379,7 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
@@ -3374,9 +3399,9 @@
<boolean value="YES"/>
<boolean value="YES"/>
<boolean value="YES"/>
<string>{{109, 366}, {557, 400}}</string>
<string>{{439, 345}, {557, 400}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{109, 366}, {557, 400}}</string>
<string>{{439, 345}, {557, 400}}</string>
<boolean value="YES"/>
<boolean value="YES"/>
<boolean value="YES"/>
@@ -3423,7 +3448,7 @@
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>{{286, 475}, {361, 293}}</string>
<string>{{286, 455}, {361, 313}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -3561,7 +3586,7 @@
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">1176</int>
<int key="maxID">1178</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -3776,8 +3801,17 @@
<string key="className">ResultWindow</string>
<string key="superclassName">ResultWindowBase</string>
<object class="NSMutableDictionary" key="actions">
<string key="NS.key.0">removeDeadTracks:</string>
<string key="NS.object.0">id</string>
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>resetColumnsToDefault:</string>
<string>startDuplicateScan:</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>id</string>
<string>id</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
@@ -3799,6 +3833,7 @@
<string>exportToXHTML:</string>
<string>filter:</string>
<string>ignoreSelected:</string>
<string>invokeCustomCommand:</string>
<string>markAll:</string>
<string>markInvert:</string>
<string>markNone:</string>
@@ -3848,6 +3883,7 @@
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
@@ -4500,7 +4536,7 @@
<integer value="3000" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">../../me/dupeguru.xcodeproj</string>
<string key="IBDocument.LastKnownRelativeProjectPath">../../se/dupeguru.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
</data>
</archive>

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,7 @@
<key>CFBundleSignature</key>
<string>hsft</string>
<key>CFBundleVersion</key>
<string>2.9.2</string>
<string>2.10.0</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>

View File

@@ -19,6 +19,8 @@
CE3A46FA109B212E002ABFD5 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3A46F9109B212E002ABFD5 /* MainMenu.xib */; };
CE45579B0AE3BC2B005A9546 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; };
CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; };
CE647E571173024A006D28BA /* ProblemDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = CE647E551173024A006D28BA /* ProblemDialog.m */; };
CE647E591173026F006D28BA /* ProblemDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE647E581173026F006D28BA /* ProblemDialog.xib */; };
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 */; };
@@ -27,6 +29,7 @@
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 */; };
CE8C53BC117324CE0011B41F /* HSTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CE8C53BB117324CE0011B41F /* HSTable.m */; };
CE91F215113BC22D0010360B /* ResultOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE91F212113BC22D0010360B /* ResultOutline.m */; };
CE91F216113BC22D0010360B /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE91F214113BC22D0010360B /* StatsLabel.m */; };
CEAC6811109B0B7E00B43C85 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEAC6810109B0B7E00B43C85 /* Preferences.xib */; };
@@ -83,6 +86,10 @@
CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; };
CE3A46F9109B212E002ABFD5 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../base/xib/MainMenu.xib; sourceTree = "<group>"; };
CE45579A0AE3BC2B005A9546 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
CE647E541173024A006D28BA /* ProblemDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProblemDialog.h; path = ../base/ProblemDialog.h; sourceTree = SOURCE_ROOT; };
CE647E551173024A006D28BA /* ProblemDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProblemDialog.m; path = ../base/ProblemDialog.m; sourceTree = SOURCE_ROOT; };
CE647E561173024A006D28BA /* PyProblemDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyProblemDialog.h; path = ../base/PyProblemDialog.h; sourceTree = SOURCE_ROOT; };
CE647E581173026F006D28BA /* ProblemDialog.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ProblemDialog.xib; path = ../base/xib/ProblemDialog.xib; sourceTree = SOURCE_ROOT; };
CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = "<group>"; };
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 = "<group>"; };
@@ -102,6 +109,8 @@
CE76FDDE111EE42F006618EA /* HSOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutline.m; sourceTree = "<group>"; };
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; };
CE8C53B61173248F0011B41F /* PyTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyTable.h; sourceTree = "<group>"; };
CE8C53BB117324CE0011B41F /* HSTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSTable.m; sourceTree = "<group>"; };
CE91F20F113BC22D0010360B /* PyResultTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTree.h; path = ../base/PyResultTree.h; sourceTree = SOURCE_ROOT; };
CE91F210113BC22D0010360B /* PyStatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyStatsLabel.h; path = ../base/PyStatsLabel.h; sourceTree = SOURCE_ROOT; };
CE91F211113BC22D0010360B /* ResultOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultOutline.h; path = ../base/ResultOutline.h; sourceTree = SOURCE_ROOT; };
@@ -271,6 +280,7 @@
CE76FDDE111EE42F006618EA /* HSOutline.m */,
CE76FDC8111EE38E006618EA /* HSGUIController.h */,
CE76FDC9111EE38E006618EA /* HSGUIController.m */,
CE8C53BB117324CE0011B41F /* HSTable.m */,
);
name = controllers;
path = ../../cocoalib/controllers;
@@ -281,6 +291,7 @@
children = (
CE76FDCD111EE38E006618EA /* PyGUI.h */,
CE76FDCE111EE38E006618EA /* PyOutline.h */,
CE8C53B61173248F0011B41F /* PyTable.h */,
);
name = proxies;
path = ../../cocoalib/proxies;
@@ -299,6 +310,7 @@
CEEFC0CA10943849001F3A39 /* xib */ = {
isa = PBXGroup;
children = (
CE647E581173026F006D28BA /* ProblemDialog.xib */,
CE3A46F9109B212E002ABFD5 /* MainMenu.xib */,
CEAC6810109B0B7E00B43C85 /* Preferences.xib */,
CEEFC0F710945D9F001F3A39 /* DirectoryPanel.xib */,
@@ -370,6 +382,9 @@
CE6E7407111C997500C350E3 /* PyDetailsPanel.h */,
CEFC7FB70FC951A700CD5728 /* ResultWindow.h */,
CEFC7FB80FC951A700CD5728 /* ResultWindow.m */,
CE647E541173024A006D28BA /* ProblemDialog.h */,
CE647E551173024A006D28BA /* ProblemDialog.m */,
CE647E561173024A006D28BA /* PyProblemDialog.h */,
);
name = dgbase;
sourceTree = "<group>";
@@ -435,6 +450,7 @@
CE19BC6311199231007CCEB0 /* ErrorReportWindow.xib in Resources */,
CE19BC6411199231007CCEB0 /* progress.xib in Resources */,
CE19BC6511199231007CCEB0 /* registration.xib in Resources */,
CE647E591173026F006D28BA /* ProblemDialog.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -470,6 +486,8 @@
CEBE4D74111F0EE1009AAC6D /* HSWindowController.m in Sources */,
CE91F215113BC22D0010360B /* ResultOutline.m in Sources */,
CE91F216113BC22D0010360B /* StatsLabel.m in Sources */,
CE647E571173024A006D28BA /* ProblemDialog.m in Sources */,
CE8C53BC117324CE0011B41F /* HSTable.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,8 @@ from __future__ import unicode_literals
import os
import os.path as op
import logging
import subprocess
import re
from send2trash import send2trash
from hsutil import io, files
@@ -48,7 +50,6 @@ class DupeGuru(RegistrableApplication, Broadcaster):
self.results = results.Results(data_module)
self.scanner = scanner.Scanner()
self.action_count = 0
self.last_op_error_count = 0
self.options = {
'escape_filter_regexp': True,
'clean_empty_dirs': False,
@@ -70,19 +71,13 @@ class DupeGuru(RegistrableApplication, Broadcaster):
return self._do_delete_dupe(dupe)
j.start_job(self.results.mark_count)
self.last_op_error_count = self.results.perform_on_marked(op, True)
self.results.perform_on_marked(op, True)
def _do_delete_dupe(self, dupe):
if not io.exists(dupe.path):
return True
try:
send2trash(unicode(dupe.path))
except OSError as e:
msg = "Could not send {0} to trash: {1}"
logging.warning(msg.format(unicode(dupe.path), unicode(e)))
return False
return
send2trash(unicode(dupe.path)) # Raises OSError when there's a problem
self.clean_empty_dirs(dupe.path[:-1])
return True
def _do_load(self, j):
self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml'))
@@ -114,8 +109,11 @@ class DupeGuru(RegistrableApplication, Broadcaster):
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):
if jobid == JOB_SCAN:
self.notify('results_changed')
elif jobid in (JOB_LOAD, JOB_MOVE, JOB_DELETE):
self.notify('results_changed')
self.notify('problems_changed')
@staticmethod
def _open_path(path):
@@ -182,28 +180,23 @@ class DupeGuru(RegistrableApplication, Broadcaster):
dest_path = dest_path + source_path[1:-1] #Remove drive letter and filename
elif dest_type == 1:
dest_path = dest_path + source_path[location_path:-1]
try:
if not io.exists(dest_path):
io.makedirs(dest_path)
# Raises an EnvironmentError if there's a problem
if copy:
files.copy(source_path, dest_path)
else:
files.move(source_path, dest_path)
self.clean_empty_dirs(source_path[:-1])
except EnvironmentError as e:
operation = 'Copy' if copy else 'Move'
logging.warning('%s operation failed on %s. Error: %s' % (operation, unicode(dupe.path), unicode(e)))
return False
return True
def copy_or_move_marked(self, copy, destination, recreate_path):
def do(j):
def op(dupe):
j.add_progress()
return self.copy_or_move(dupe, copy, destination, recreate_path)
self.copy_or_move(dupe, copy, destination, recreate_path)
j.start_job(self.results.mark_count)
self.last_op_error_count = self.results.perform_on_marked(op, not copy)
self.results.perform_on_marked(op, not copy)
self._demo_check()
jobid = JOB_COPY if copy else JOB_MOVE
@@ -227,6 +220,31 @@ class DupeGuru(RegistrableApplication, Broadcaster):
rows.append(row)
return export.export_to_xhtml(colnames, rows)
def invoke_command(self, cmd):
"""Calls command `cmd` with %d and %r placeholders replaced.
Using the current selection, %d is replaced with the currently selected dupe and %r is
replaced with that dupe's ref file. If there's no selection, the command is not invoked.
If the dupe is a ref, %d and %r will be the same.
"""
if not self.selected_dupes:
return
dupe = self.selected_dupes[0]
group = self.results.get_group_of_duplicate(dupe)
ref = group.ref
cmd = cmd.replace('%d', unicode(dupe.path))
cmd = cmd.replace('%r', unicode(ref.path))
match = re.match(r'"([^"]+)"(.*)', cmd)
if match is not None:
# This code here is because subprocess. Popen doesn't seem to accept, under Windows,
# executable paths with spaces in it, *even* when they're enclosed in "". So this is
# a workaround to make the damn thing work.
exepath, args = match.groups()
path, exename = op.split(exepath)
subprocess.Popen(exename + args, shell=True, cwd=path)
else:
subprocess.Popen(cmd, shell=True)
def load(self):
self._start_job(JOB_LOAD, self._do_load)
self.load_ignore_list()
@@ -283,7 +301,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
self.notify('results_changed_but_keep_selection')
def remove_marked(self):
self.results.perform_on_marked(lambda x:True, True)
self.results.perform_on_marked(lambda x:None, True)
self.notify('results_changed')
def remove_selected(self):

View File

@@ -9,10 +9,12 @@
# Common interface for all editions' dg_cocoa unit.
from hsutil.cocoa.inter import signature, PyOutline, PyGUIObject, PyRegistrable
from hsutil.cocoa.inter import signature, PyTable, PyOutline, PyGUIObject, PyRegistrable
from .gui.details_panel import DetailsPanel
from .gui.directory_tree import DirectoryTree
from .gui.problem_dialog import ProblemDialog
from .gui.problem_table import ProblemTable
from .gui.result_tree import ResultTree
from .gui.stats_label import StatsLabel
@@ -93,6 +95,9 @@ class PyDupeGuruBase(PyRegistrable):
def revealSelected(self):
self.py.reveal_selected()
def invokeCommand_(self, cmd):
self.py.invoke_command(cmd)
#---Information
def getIgnoreListCount(self):
return len(self.py.scanner.ignore_list)
@@ -100,8 +105,9 @@ class PyDupeGuruBase(PyRegistrable):
def getMarkCount(self):
return self.py.results.mark_count
def getOperationalErrorCount(self):
return self.py.last_op_error_count
@signature('i@:')
def scanWasProblematic(self):
return bool(self.py.results.problems)
#---Properties
def setMixFileKind_(self, mix_file_kind):
@@ -196,3 +202,13 @@ class PyStatsLabel(PyGUIObject):
def display(self):
return self.py.display
class PyProblemDialog(PyGUIObject):
py_class = ProblemDialog
def revealSelected(self):
self.py.reveal_selected_dupe()
class PyProblemTable(PyTable):
py_class = ProblemTable

View File

@@ -24,6 +24,9 @@ class GUIObject(Listener):
def marking_changed(self):
pass
def problems_changed(self):
pass
def results_changed(self):
pass

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2010-04-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 hsutil.notify import Broadcaster
from .base import GUIObject
class ProblemDialog(GUIObject, Broadcaster):
def __init__(self, view, app):
GUIObject.__init__(self, view, app)
Broadcaster.__init__(self)
self._selected_dupe = None
def reveal_selected_dupe(self):
if self._selected_dupe is not None:
self.app._reveal_path(self._selected_dupe.path)
def select_dupe(self, dupe):
self._selected_dupe = dupe
#--- Event Handlers
def problems_changed(self):
self._selected_dupe = None
self.notify('problems_changed')

43
core/gui/problem_table.py Normal file
View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2010-04-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 hsutil.notify import Listener
from hsgui.table import GUITable, Row
class ProblemTable(GUITable, Listener):
def __init__(self, view, problem_dialog):
GUITable.__init__(self)
Listener.__init__(self, problem_dialog)
self.view = view
self.dialog = problem_dialog
#--- Override
def _update_selection(self):
row = self.selected_row
dupe = row.dupe if row is not None else None
self.dialog.select_dupe(dupe)
def _fill(self):
problems = self.dialog.app.results.problems
for dupe, msg in problems:
self.append(ProblemRow(self, dupe, msg))
#--- Event handlers
def problems_changed(self):
self.refresh()
self.view.refresh()
class ProblemRow(Row):
def __init__(self, table, dupe, msg):
Row.__init__(self, table)
self.dupe = dupe
self.msg = msg
self.path = unicode(dupe.path)

View File

@@ -32,6 +32,7 @@ class Results(Markable):
self.__recalculate_stats()
self.__marked_size = 0
self.data = data_module
self.problems = [] # (dupe, error_msg)
def _did_mark(self, dupe):
self.__marked_size += dupe.size
@@ -146,7 +147,7 @@ class Results(Markable):
self.__filters.append(filter_str)
if self.__filtered_dupes is None:
self.__filtered_dupes = flatten(g[:] for g in self.groups)
self.__filtered_dupes = set(dupe for dupe in self.__filtered_dupes if filter_re.search(dupe.name))
self.__filtered_dupes = set(dupe for dupe in self.__filtered_dupes if filter_re.search(unicode(dupe.path)))
filtered_groups = set()
for dupe in self.__filtered_dupes:
filtered_groups.add(self.get_group_of_duplicate(dupe))
@@ -230,17 +231,22 @@ class Results(Markable):
self.__dupes = None
def perform_on_marked(self, func, remove_from_results):
problems = []
for d in self.dupes:
if self.is_marked(d) and (not func(d)):
problems.append(d)
# Performs `func` on all marked dupes. If an EnvironmentError is raised during the call,
# the problematic dupe is added to self.problems.
self.problems = []
to_remove = []
marked = (dupe for dupe in self.dupes if self.is_marked(dupe))
for dupe in marked:
try:
func(dupe)
to_remove.append(dupe)
except EnvironmentError as e:
self.problems.append((dupe, unicode(e)))
if remove_from_results:
to_remove = [d for d in self.dupes if self.is_marked(d) and (d not in problems)]
self.remove_duplicates(to_remove)
self.mark_none()
for d in problems:
self.mark(d)
return len(problems)
for dupe, _ in self.problems:
self.mark(dupe)
def remove_duplicates(self, dupes):
'''Remove 'dupes' from their respective group, and remove the group is it ends up empty.

View File

@@ -9,7 +9,9 @@
import StringIO
import os.path as op
from lxml import etree
from nose.tools import eq_
from hsutil.path import Path
from hsutil.testcase import TestCase
@@ -252,18 +254,23 @@ class TCResultsMarkings(TestCase):
def test_perform_on_marked_with_problems(self):
def log_object(o):
log.append(o)
return o is not self.objects[1]
if o is self.objects[1]:
raise EnvironmentError('foobar')
log = []
self.results.mark_all()
self.assert_(self.results.is_marked(self.objects[1]))
self.assertEqual(1,self.results.perform_on_marked(log_object, True))
self.assertEqual(3,len(log))
self.assertEqual(1,len(self.results.groups))
self.assertEqual(2,len(self.results.groups[0]))
self.assert_(self.objects[1] in self.results.groups[0])
self.assert_(not self.results.is_marked(self.objects[2]))
self.assert_(self.results.is_marked(self.objects[1]))
assert self.results.is_marked(self.objects[1])
self.results.perform_on_marked(log_object, True)
eq_(len(log), 3)
eq_(len(self.results.groups), 1)
eq_(len(self.results.groups[0]), 2)
assert self.objects[1] in self.results.groups[0]
assert not self.results.is_marked(self.objects[2])
assert self.results.is_marked(self.objects[1])
eq_(len(self.results.problems), 1)
dupe, msg = self.results.problems[0]
assert dupe is self.objects[1]
eq_(msg, 'foobar')
def test_perform_on_marked_with_ref(self):
def log_object(o):
@@ -563,6 +570,10 @@ class TCResultsXML(TestCase):
self.results.load_from_xml(f, self.get_file)
first(self.results.groups[0].matches).percentage
def test_apply_filter_works_on_paths(self):
# apply_filter() searches on the whole path, not just on the filename.
self.results.apply_filter(u'basepath')
eq_(len(self.results.groups), 2)
class TCResultsFilter(TestCase):
def setUp(self):

View File

@@ -7,9 +7,7 @@
# http://www.hardcoded.net/licenses/hs_license
import os.path as op
import logging
import plistlib
import re
from lxml import etree
from appscript import app, k, CommandError
@@ -148,7 +146,7 @@ class DupeGuruPE(app_cocoa.DupeGuru):
except (CommandError, RuntimeError):
pass
j.start_job(self.results.mark_count, "Sending dupes to the Trash")
self.last_op_error_count = self.results.perform_on_marked(op, True)
self.results.perform_on_marked(op, True)
del self.path2iphoto
def _do_delete_dupe(self, dupe):
@@ -158,14 +156,13 @@ class DupeGuruPE(app_cocoa.DupeGuru):
try:
a = app('iPhoto')
a.remove(photo, timeout=0)
return True
except (CommandError, RuntimeError):
return False
except (CommandError, RuntimeError) as e:
raise EnvironmentError(unicode(e))
else:
logging.warning(u"Could not find photo %s in iPhoto Library", unicode(dupe.path))
return False
msg = u"Could not find photo %s in iPhoto Library" % unicode(dupe.path)
raise EnvironmentError(msg)
else:
return app_cocoa.DupeGuru._do_delete_dupe(self, dupe)
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'))

View File

@@ -1,3 +1,9 @@
- date: 2010-04-13
version: 2.10.0
description: |
* Improved error messages when files can't be sent to trash, moved or copied.
* Added a custom command invocation action. (#12)
* Filters are now applied on whole paths. (#4)
- date: 2010-02-10
version: 2.9.2
description: |

View File

@@ -25,3 +25,11 @@
* **Recreate absolute path:** The source file's path will be re-created in the destination directory in it's entirety. For example, if you move "/Users/foobar/Music/Artist/Album/the_song.mp3" to the destination "/Users/foobar/MyDestination", the final destination for the file will be "/Users/foobar/MyDestination/Users/foobar/Music/Artist/Album".
In all cases, dupeGuru nicely handles naming conflicts by prepending a number to the destination filename if the filename already exists in the destination.
**Custom Command:** This preference determines the command that will be invoked by the "Invoke Custom Command" action. You can invoke any external application through this action. This can be useful if, for example, you have a nice diffing application installed.
The format of the command is the same as what you would write in the command line, except that there are 2 placeholders: **%d** and **%r**. These placeholders will be replaced by the path of the selected dupe (%d) and the path of the selected dupe's reference file (%r).
If the path to your executable contains space characters, you should enclose it in "" quotes. You should also enclose placeholders in quotes because it's very possible that paths to dupes and refs will contain spaces. Here's an example custom command:
"C:\Program Files\SuperDiffProg\SuperDiffProg.exe" "%d" "%r"

View File

@@ -70,4 +70,5 @@ Action Menu
* **Add Selected to Ignore List:** This first removes all selected duplicates from results, and then add the match of that duplicate and the current reference in the ignore list. This match will not come up again in further scan. The duplicate itself might come back, but it will be matched with another reference file. You can clear the ignore list with the Clear Ignore List command.
* **Open Selected with Default Application:** Open the file with the application associated with selected file's type.
* **Reveal Selected in Finder:** Open the folder containing selected file.
* **Invoke Custom Command:** Invokes the external application you've set up in your preferences using the current selection as arguments in the invocation.
* **Rename Selected:** Prompts you for a new name, and then rename the selected file.

View File

@@ -57,7 +57,11 @@ def package_windows(edition, dev):
for lib in libs:
print_and_do("upx --best \"dist\\{0}\"".format(lib))
print_and_do("xcopy /Y /S /I ..\\..\\help_{0}\\dupeguru_{0}_help dist\\help".format(edition))
help_basedir = '..\\..\\help_{0}'.format(edition)
help_dir = 'dupeguru_{0}_help'.format(edition) if edition != 'se' else 'dupeguru_help'
help_path = op.join(help_basedir, help_dir)
print "Copying {0} to dist\\help".format(help_path)
shutil.copytree(help_path, 'dist\\help')
# AdvancedInstaller.com has to be in your PATH
# this is so we don'a have to re-commit installer.aip at every version change

View File

@@ -28,6 +28,7 @@ from . import platform
from .main_window import MainWindow
from .directories_dialog import DirectoriesDialog
from .problem_dialog import ProblemDialog
JOBID2TITLE = {
JOB_SCAN: "Scanning for duplicates",
@@ -71,6 +72,7 @@ class DupeGuru(DupeGuruBase, QObject):
self._progress = Progress(self.main_window)
self.directories_dialog = DirectoriesDialog(self.main_window, self)
self.details_dialog = self._create_details_dialog(self.main_window)
self.problemDialog = ProblemDialog(parent=self.main_window, app=self)
self.preferences_dialog = self._create_preferences_dialog(self.main_window)
self.about_box = AboutBox(self.main_window, self)
@@ -169,6 +171,14 @@ class DupeGuru(DupeGuruBase, QObject):
def askForRegCode(self):
self.reg.ask_for_code()
def invokeCustomCommand(self):
cmd = self.prefs.custom_command
if cmd:
self.invoke_command(cmd)
else:
msg = "You have no custom command set up. Please, set it up in your preferences."
QMessageBox.warning(self.main_window, 'Custom Command', msg)
def openDebugLog(self):
debugLogPath = op.join(self.appdata, 'debug.log')
self._open_path(debugLogPath)
@@ -206,9 +216,12 @@ class DupeGuru(DupeGuruBase, QObject):
def job_finished(self, jobid):
self._job_completed(jobid)
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)
QMessageBox.warning(self.main_window, 'Warning', msg)
if jobid in (JOB_MOVE, JOB_COPY, JOB_DELETE):
if self.results.problems:
self.problemDialog.show()
else:
msg = "All files were processed successfully."
QMessageBox.information(self.main_window, 'Operation Complete', msg)
elif jobid == JOB_SCAN:
if not self.results.groups:
title = "Scanning complete"

View File

@@ -37,6 +37,7 @@ 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)
self.actionInvokeCustomCommand.triggered.connect(self.app.invokeCustomCommand)
def _setupUi(self):
self.setupUi(self)
@@ -76,6 +77,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
actionMenu.addSeparator()
actionMenu.addAction(self.actionOpenSelected)
actionMenu.addAction(self.actionRevealSelected)
actionMenu.addAction(self.actionInvokeCustomCommand)
actionMenu.addAction(self.actionRenameSelected)
self.actionActions.setMenu(actionMenu)
button = QToolButton(self.toolBar)

View File

@@ -44,6 +44,9 @@
<attribute name="headerStretchLastSection">
<bool>false</bool>
</attribute>
<attribute name="headerStretchLastSection">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
@@ -54,7 +57,7 @@
<x>0</x>
<y>0</y>
<width>630</width>
<height>22</height>
<height>20</height>
</rect>
</property>
<widget class="QMenu" name="menuColumns">
@@ -77,6 +80,7 @@
<addaction name="separator"/>
<addaction name="actionOpenSelected"/>
<addaction name="actionRevealSelected"/>
<addaction name="actionInvokeCustomCommand"/>
<addaction name="actionRenameSelected"/>
<addaction name="separator"/>
<addaction name="actionApplyFilter"/>
@@ -428,6 +432,14 @@
<string>Open Debug Log</string>
</property>
</action>
<action name="actionInvokeCustomCommand">
<property name="text">
<string>Invoke Custom Command</string>
</property>
<property name="shortcut">
<string>Ctrl+I</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@@ -26,6 +26,7 @@ class Preferences(PreferencesBase):
self.use_regexp = get('UseRegexp', self.use_regexp)
self.remove_empty_folders = get('RemoveEmptyFolders', self.remove_empty_folders)
self.destination_type = get('DestinationType', self.destination_type)
self.custom_command = get('CustomCommand', self.custom_command)
widths = get('ColumnsWidth', self.columns_width)
# only set nonzero values
for index, width in enumerate(widths[:len(self.columns_width)]):
@@ -46,6 +47,7 @@ class Preferences(PreferencesBase):
self.use_regexp = False
self.remove_empty_folders = False
self.destination_type = 1
self.custom_command = ''
self.registration_code = ''
self.registration_email = ''
self._reset_specific()
@@ -64,6 +66,7 @@ class Preferences(PreferencesBase):
set_('UseRegexp', self.use_regexp)
set_('RemoveEmptyFolders', self.remove_empty_folders)
set_('DestinationType', self.destination_type)
set_('CustomCommand', self.custom_command)
set_('ColumnsWidth', self.columns_width)
set_('ColumnsVisible', self.columns_visible)
set_('RegistrationCode', self.registration_code)

32
qt/base/problem_dialog.py Normal file
View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2010-04-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 PyQt4.QtCore import Qt
from PyQt4.QtGui import QDialog
from core.gui.problem_dialog import ProblemDialog as ProblemDialogModel
from .problem_table import ProblemTable
from .problem_dialog_ui import Ui_ProblemDialog
class ProblemDialog(QDialog, Ui_ProblemDialog):
def __init__(self, parent, app):
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
QDialog.__init__(self, parent, flags)
self.app = app
self._setupUi()
self.model = ProblemDialogModel(view=self, app=app)
self.table = ProblemTable(problem_dialog=self, view=self.tableView)
self.model.connect()
self.table.model.connect()
self.revealButton.clicked.connect(self.model.reveal_selected_dupe)
def _setupUi(self):
self.setupUi(self)

116
qt/base/problem_dialog.ui Normal file
View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProblemDialog</class>
<widget class="QDialog" name="ProblemDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>413</width>
<height>323</height>
</rect>
</property>
<property name="windowTitle">
<string>Problems!</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>There were problems processing some (or all) of the files. The cause of these problems are described in the table below. Those files were not removed from your results.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tableView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>18</number>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>18</number>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="revealButton">
<property name="text">
<string>Reveal Selected</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="text">
<string>Close</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>closeButton</sender>
<signal>clicked()</signal>
<receiver>ProblemDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>367</x>
<y>301</y>
</hint>
<hint type="destinationlabel">
<x>272</x>
<y>299</y>
</hint>
</hints>
</connection>
</connections>
</ui>

25
qt/base/problem_table.py Normal file
View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2010-04-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 qtlib.column import Column
from qtlib.table import Table
from core.gui.problem_table import ProblemTable as ProblemTableModel
class ProblemTable(Table):
COLUMNS = [
Column('path', 'File Path', 150),
Column('msg', 'Error Message', 150),
]
def __init__(self, problem_dialog, view):
model = ProblemTableModel(view=self, problem_dialog=problem_dialog.model)
Table.__init__(self, model, view)
# we have to prevent Return from initiating editing.
# self.view.editSelected = lambda: None

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>322</height>
<width>449</width>
<height>361</height>
</rect>
</property>
<property name="windowTitle">
@@ -193,97 +193,86 @@
<height>40</height>
</size>
</property>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>91</width>
<height>17</height>
</rect>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Tags to scan:</string>
</property>
</widget>
<widget class="QCheckBox" name="tagTrackBox">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>51</width>
<height>21</height>
</rect>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>15</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="tagTrackBox">
<property name="text">
<string>Track</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="tagArtistBox">
<property name="geometry">
<rect>
<x>60</x>
<y>20</y>
<width>51</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Artist</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="tagAlbumBox">
<property name="geometry">
<rect>
<x>110</x>
<y>20</y>
<width>61</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Album</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="tagTitleBox">
<property name="geometry">
<rect>
<x>170</x>
<y>20</y>
<width>51</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Title</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="tagGenreBox">
<property name="geometry">
<rect>
<x>220</x>
<y>20</y>
<width>61</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Genre</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="tagYearBox">
<property name="geometry">
<rect>
<x>280</x>
<y>20</y>
<width>51</width>
<height>21</height>
</rect>
</property>
<property name="text">
<string>Year</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>

View File

@@ -27,7 +27,7 @@ class DupeGuru(DupeGuruBase):
EDITION = 'se'
LOGO_NAME = 'logo_se'
NAME = 'dupeGuru'
VERSION = '2.9.2'
VERSION = '2.10.0'
DELTA_COLUMNS = frozenset([2, 4, 5])
def __init__(self):

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DOCUMENT type="Advanced Installer" CreateVersion="4.4.1" version="7.5" modules="professional" RootPath="." Language="en">
<DOCUMENT type="Advanced Installer" CreateVersion="4.4.1" version="7.5.2" modules="professional" RootPath="." Language="en">
<COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
<ROW Property="AI_FINDEXE_TITLE" Value="Select the installation package for [|ProductName]" ValueLocId="AI.Property.FindExeTitle"/>
<ROW Property="AI_SHORTCUTSREG" Value="0|0|0|"/>
@@ -30,23 +30,19 @@
<ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
<ROW Directory="accessible_DIR" Directory_Parent="qt4_plugins_DIR" DefaultDir="access~1|accessible"/>
<ROW Directory="codecs_DIR" Directory_Parent="qt4_plugins_DIR" DefaultDir="codecs"/>
<ROW Directory="gen_py_DIR" Directory_Parent="support_DIR" DefaultDir="gen_py"/>
<ROW Directory="iconengines_DIR" Directory_Parent="qt4_plugins_DIR" DefaultDir="iconen~1|iconengines"/>
<ROW Directory="imageformats_DIR" Directory_Parent="qt4_plugins_DIR" DefaultDir="imagef~1|imageformats"/>
<ROW Directory="qt4_plugins_DIR" Directory_Parent="APPDIR" DefaultDir="qt4_pl~1|qt4_plugins"/>
<ROW Directory="support_DIR" Directory_Parent="APPDIR" DefaultDir="support"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
<ROW Component="AIShRegAnswer" ComponentId="{775090B3-2E56-40F5-9DD8-24A2F82DA601}" Directory_="APPDIR" Attributes="4" KeyPath="AIShRegAnswer"/>
<ROW Component="AI_ExePath" ComponentId="{56EC90C1-F906-4F6E-95B2-1876CAA651C2}" Directory_="APPDIR" Attributes="4" KeyPath="AI_ExePath"/>
<ROW Component="PyWinTypes26.dll" ComponentId="{B664FF8C-C60F-423C-9AC4-26144896E583}" Directory_="APPDIR" Attributes="0" KeyPath="PyWinTypes26.dll"/>
<ROW Component="CurrentVersion" ComponentId="{398CD484-F093-4CEC-BE84-1BD0DD461942}" Directory_="APPDIR" Attributes="4" KeyPath="CurrentVersion"/>
<ROW Component="QtCore4.dll" ComponentId="{F517476C-BC6D-40B6-A063-5A10680ECA05}" Directory_="APPDIR" Attributes="0" KeyPath="QtCore4.dll"/>
<ROW Component="QtGui4.dll" ComponentId="{4915BAC4-AFB0-42E1-BF2E-D4C3E58D4BEE}" Directory_="APPDIR" Attributes="0" KeyPath="QtGui4.dll"/>
<ROW Component="bz2.pyd" ComponentId="{E03E8F51-0E8D-40A2-9ED0-A8EA0ED4CD19}" Directory_="APPDIR" Attributes="0" KeyPath="bz2.pyd" Type="0"/>
<ROW Component="dupeGuru.exe" ComponentId="{A8FFC84F-B54B-4883-B9FD-5C545AF0E51C}" Directory_="APPDIR" Attributes="0" KeyPath="dupeGuru.exe"/>
<ROW Component="init_.py" ComponentId="{7B86D715-9C99-4021-8A3C-437BEA4986BC}" Directory_="gen_py_DIR" Attributes="0" KeyPath="init_.py" Type="0"/>
<ROW Component="python26.dll" ComponentId="{C47E3AEB-FCE1-4A7D-90AF-26D52100756F}" Directory_="APPDIR" Attributes="0" KeyPath="python26.dll"/>
<ROW Component="pythoncom26.dll" ComponentId="{474C48BA-8C13-428C-B932-49C65A1619FC}" Directory_="APPDIR" Attributes="0" KeyPath="pythoncom26.dll"/>
<ROW Component="qcncodecs4.dll" ComponentId="{1FA15E05-79B4-490E-8BE7-2915DAFECDA0}" Directory_="codecs_DIR" Attributes="0" KeyPath="qcncodecs4.dll"/>
<ROW Component="qgif4.dll" ComponentId="{12390BD7-63E5-4BAE-A760-84D6E47387F3}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qgif4.dll"/>
<ROW Component="qico4.dll" ComponentId="{7EC94828-5141-4383-BB9C-89C6AE543237}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qico4.dll"/>
@@ -56,53 +52,45 @@
<ROW Component="qmng4.dll" ComponentId="{11B243B6-A6E5-4282-A58B-5A4F5A2CB253}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qmng4.dll"/>
<ROW Component="qsvg4.dll" ComponentId="{D689DDEB-D4E9-4DE0-B32B-85FD25C40726}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qsvg4.dll"/>
<ROW Component="qsvgicon4.dll" ComponentId="{6EF94FDD-3E92-4886-925F-B264C962E7EF}" Directory_="iconengines_DIR" Attributes="0" KeyPath="qsvgicon4.dll"/>
<ROW Component="qtaccessiblecompatwidgets4.dll" ComponentId="{D0CC18F8-1B1D-40E6-BFB3-14F2DE96B6ED}" Directory_="accessible_DIR" Attributes="0" KeyPath="qtaccessiblecompatwidgets4.dll"/>
<ROW Component="qtaccessiblewidgets4.dll" ComponentId="{0DB9EE4C-922F-42AC-80CC-4EA3CBBB1629}" Directory_="accessible_DIR" Attributes="0" KeyPath="qtaccessiblewidgets4.dll"/>
<ROW Component="qtiff4.dll" ComponentId="{660F482B-9508-4A26-BC1A-610E84829CA4}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qtiff4.dll"/>
<ROW Component="qtwcodecs4.dll" ComponentId="{68610953-B652-4340-BBB9-B1EEB3A6AF7A}" Directory_="codecs_DIR" Attributes="0" KeyPath="qtwcodecs4.dll"/>
<ROW Component="updater.exe" ComponentId="{CB63C33D-EB1B-420A-8BAA-CD380923F12B}" Directory_="APPDIR" Attributes="0" KeyPath="updater.exe"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
<ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="updater.exe dupeGuru.exe AIShRegAnswer python26.dll pythoncom26.dll PyWinTypes26.dll QtCore4.dll QtGui4.dll bz2.pyd qtaccessiblewidgets4.dll qcncodecs4.dll qjpcodecs4.dll qkrcodecs4.dll qtwcodecs4.dll qsvgicon4.dll qgif4.dll qico4.dll qjpeg4.dll qmng4.dll qsvg4.dll qtiff4.dll AI_ExePath qtaccessiblecompatwidgets4.dll init_.py"/>
<ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="updater.exe dupeGuru.exe AIShRegAnswer python26.dll QtCore4.dll QtGui4.dll bz2.pyd qtaccessiblewidgets4.dll qcncodecs4.dll qjpcodecs4.dll qkrcodecs4.dll qtwcodecs4.dll qsvgicon4.dll qgif4.dll qico4.dll qjpeg4.dll qmng4.dll qsvg4.dll qtiff4.dll AI_ExePath CurrentVersion"/>
<ATTRIBUTE name="CurrentFeature" value="MainFeature"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
<ROW File="PyQt4.QtCore.pyd" Component_="bz2.pyd" FileName="PyQt4Q~1.pyd|PyQt4.QtCore.pyd" Attributes="0" SourcePath="dist\PyQt4.QtCore.pyd" SelfReg="false" Sequence="10"/>
<ROW File="PyQt4.QtGui.pyd" Component_="bz2.pyd" FileName="PyQt4Q~2.pyd|PyQt4.QtGui.pyd" Attributes="0" SourcePath="dist\PyQt4.QtGui.pyd" SelfReg="false" Sequence="11"/>
<ROW File="PyWinTypes26.dll" Component_="PyWinTypes26.dll" FileName="PyWinT~1.dll|PyWinTypes26.dll" Attributes="0" SourcePath="dist\PyWinTypes26.dll" SelfReg="false" Sequence="5"/>
<ROW File="QtCore4.dll" Component_="QtCore4.dll" FileName="QtCore4.dll" Attributes="0" SourcePath="dist\QtCore4.dll" SelfReg="false" Sequence="6"/>
<ROW File="QtGui4.dll" Component_="QtGui4.dll" FileName="QtGui4.dll" Attributes="0" SourcePath="dist\QtGui4.dll" SelfReg="false" Sequence="7"/>
<ROW File="bz2.pyd" Component_="bz2.pyd" FileName="bz2.pyd" Attributes="0" SourcePath="dist\bz2.pyd" SelfReg="false" Sequence="8"/>
<ROW File="dupeGuru.exe" Component_="dupeGuru.exe" FileName="dupeGuru.exe" Attributes="0" SourcePath="dist\dupeGuru.exe" SelfReg="false" Sequence="2"/>
<ROW File="hashlib.pyd" Component_="bz2.pyd" FileName="_hashlib.pyd" Attributes="0" SourcePath="dist\_hashlib.pyd" SelfReg="false" Sequence="31"/>
<ROW File="init_.py" Component_="init_.py" FileName="__init__.py" Attributes="0" SourcePath="dist\support\gen_py\__init__.py" SelfReg="false" Sequence="36"/>
<ROW File="pyexpat.pyd" Component_="bz2.pyd" FileName="pyexpat.pyd" Attributes="0" SourcePath="dist\pyexpat.pyd" SelfReg="false" Sequence="9"/>
<ROW File="PyQt4.QtCore.pyd" Component_="bz2.pyd" FileName="PyQt4Q~1.pyd|PyQt4.QtCore.pyd" Attributes="0" SourcePath="dist\PyQt4.QtCore.pyd" SelfReg="false" Sequence="7"/>
<ROW File="PyQt4.QtGui.pyd" Component_="bz2.pyd" FileName="PyQt4Q~2.pyd|PyQt4.QtGui.pyd" Attributes="0" SourcePath="dist\PyQt4.QtGui.pyd" SelfReg="false" Sequence="8"/>
<ROW File="QtCore4.dll" Component_="QtCore4.dll" FileName="QtCore4.dll" Attributes="0" SourcePath="dist\QtCore4.dll" SelfReg="false" Sequence="4"/>
<ROW File="QtGui4.dll" Component_="QtGui4.dll" FileName="QtGui4.dll" Attributes="0" SourcePath="dist\QtGui4.dll" SelfReg="false" Sequence="5"/>
<ROW File="bz2.pyd" Component_="bz2.pyd" FileName="bz2.pyd" Attributes="0" SourcePath="dist\bz2.pyd" SelfReg="false" Sequence="6"/>
<ROW File="dupeGuru.exe" Component_="dupeGuru.exe" FileName="dupeGuru.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="dist\dupeGuru.exe" SelfReg="false" Sequence="2"/>
<ROW File="dupeGuru.upx" Component_="bz2.pyd" FileName="dupeGuru.upx" Attributes="0" SourcePath="dist\dupeGuru.upx" SelfReg="false" Sequence="27"/>
<ROW File="hashlib.pyd" Component_="bz2.pyd" FileName="_hashlib.pyd" Attributes="0" SourcePath="dist\_hashlib.pyd" SelfReg="false" Sequence="24"/>
<ROW File="lxml.etree.pyd" Component_="bz2.pyd" FileName="lxmlet~1.pyd|lxml.etree.pyd" Attributes="0" SourcePath="dist\lxml.etree.pyd" SelfReg="false" Sequence="28"/>
<ROW File="python26.dll" Component_="python26.dll" FileName="python26.dll" Attributes="0" SourcePath="dist\python26.dll" SelfReg="false" Sequence="3"/>
<ROW File="pythoncom26.dll" Component_="pythoncom26.dll" FileName="python~1.dll|pythoncom26.dll" Attributes="0" SourcePath="dist\pythoncom26.dll" SelfReg="false" Sequence="4"/>
<ROW File="qcncodecs4.dll" Component_="qcncodecs4.dll" FileName="qcncod~1.dll|qcncodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qcncodecs4.dll" SelfReg="false" Sequence="13"/>
<ROW File="qgif4.dll" Component_="qgif4.dll" FileName="qgif4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qgif4.dll" SelfReg="false" Sequence="18"/>
<ROW File="qico4.dll" Component_="qico4.dll" FileName="qico4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qico4.dll" SelfReg="false" Sequence="19"/>
<ROW File="qjpcodecs4.dll" Component_="qjpcodecs4.dll" FileName="qjpcod~1.dll|qjpcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qjpcodecs4.dll" SelfReg="false" Sequence="14"/>
<ROW File="qjpeg4.dll" Component_="qjpeg4.dll" FileName="qjpeg4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qjpeg4.dll" SelfReg="false" Sequence="20"/>
<ROW File="qkrcodecs4.dll" Component_="qkrcodecs4.dll" FileName="qkrcod~1.dll|qkrcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qkrcodecs4.dll" SelfReg="false" Sequence="15"/>
<ROW File="qmng4.dll" Component_="qmng4.dll" FileName="qmng4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qmng4.dll" SelfReg="false" Sequence="21"/>
<ROW File="qsvg4.dll" Component_="qsvg4.dll" FileName="qsvg4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qsvg4.dll" SelfReg="false" Sequence="22"/>
<ROW File="qsvgicon4.dll" Component_="qsvgicon4.dll" FileName="qsvgic~1.dll|qsvgicon4.dll" Attributes="0" SourcePath="dist\qt4_plugins\iconengines\qsvgicon4.dll" SelfReg="false" Sequence="17"/>
<ROW File="qtaccessiblecompatwidgets4.dll" Component_="qtaccessiblecompatwidgets4.dll" FileName="qtacce~1.dll|qtaccessiblecompatwidgets4.dll" Attributes="0" SourcePath="dist\qt4_plugins\accessible\qtaccessiblecompatwidgets4.dll" SelfReg="false" Sequence="35"/>
<ROW File="qtaccessiblewidgets4.dll" Component_="qtaccessiblewidgets4.dll" FileName="qtacce~2.dll|qtaccessiblewidgets4.dll" Attributes="0" SourcePath="dist\qt4_plugins\accessible\qtaccessiblewidgets4.dll" SelfReg="false" Sequence="12"/>
<ROW File="qtiff4.dll" Component_="qtiff4.dll" FileName="qtiff4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qtiff4.dll" SelfReg="false" Sequence="23"/>
<ROW File="qtwcodecs4.dll" Component_="qtwcodecs4.dll" FileName="qtwcod~1.dll|qtwcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qtwcodecs4.dll" SelfReg="false" Sequence="16"/>
<ROW File="select.pyd" Component_="bz2.pyd" FileName="select.pyd" Attributes="0" SourcePath="dist\select.pyd" SelfReg="false" Sequence="24"/>
<ROW File="sip.pyd" Component_="bz2.pyd" FileName="sip.pyd" Attributes="0" SourcePath="dist\sip.pyd" SelfReg="false" Sequence="25"/>
<ROW File="socket.pyd" Component_="bz2.pyd" FileName="_socket.pyd" Attributes="0" SourcePath="dist\_socket.pyd" SelfReg="false" Sequence="32"/>
<ROW File="ssl.pyd" Component_="bz2.pyd" FileName="_ssl.pyd" Attributes="0" SourcePath="dist\_ssl.pyd" SelfReg="false" Sequence="33"/>
<ROW File="unicodedata.pyd" Component_="bz2.pyd" FileName="unicod~1.pyd|unicodedata.pyd" Attributes="0" SourcePath="dist\unicodedata.pyd" SelfReg="false" Sequence="26"/>
<ROW File="qcncodecs4.dll" Component_="qcncodecs4.dll" FileName="qcncod~1.dll|qcncodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qcncodecs4.dll" SelfReg="false" Sequence="10"/>
<ROW File="qgif4.dll" Component_="qgif4.dll" FileName="qgif4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qgif4.dll" SelfReg="false" Sequence="15"/>
<ROW File="qico4.dll" Component_="qico4.dll" FileName="qico4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qico4.dll" SelfReg="false" Sequence="16"/>
<ROW File="qjpcodecs4.dll" Component_="qjpcodecs4.dll" FileName="qjpcod~1.dll|qjpcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qjpcodecs4.dll" SelfReg="false" Sequence="11"/>
<ROW File="qjpeg4.dll" Component_="qjpeg4.dll" FileName="qjpeg4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qjpeg4.dll" SelfReg="false" Sequence="17"/>
<ROW File="qkrcodecs4.dll" Component_="qkrcodecs4.dll" FileName="qkrcod~1.dll|qkrcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qkrcodecs4.dll" SelfReg="false" Sequence="12"/>
<ROW File="qmng4.dll" Component_="qmng4.dll" FileName="qmng4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qmng4.dll" SelfReg="false" Sequence="18"/>
<ROW File="qsvg4.dll" Component_="qsvg4.dll" FileName="qsvg4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qsvg4.dll" SelfReg="false" Sequence="19"/>
<ROW File="qsvgicon4.dll" Component_="qsvgicon4.dll" FileName="qsvgic~1.dll|qsvgicon4.dll" Attributes="0" SourcePath="dist\qt4_plugins\iconengines\qsvgicon4.dll" SelfReg="false" Sequence="14"/>
<ROW File="qtaccessiblewidgets4.dll" Component_="qtaccessiblewidgets4.dll" FileName="qtacce~2.dll|qtaccessiblewidgets4.dll" Attributes="0" SourcePath="dist\qt4_plugins\accessible\qtaccessiblewidgets4.dll" SelfReg="false" Sequence="9"/>
<ROW File="qtiff4.dll" Component_="qtiff4.dll" FileName="qtiff4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qtiff4.dll" SelfReg="false" Sequence="20"/>
<ROW File="qtwcodecs4.dll" Component_="qtwcodecs4.dll" FileName="qtwcod~1.dll|qtwcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qtwcodecs4.dll" SelfReg="false" Sequence="13"/>
<ROW File="select.pyd" Component_="bz2.pyd" FileName="select.pyd" Attributes="0" SourcePath="dist\select.pyd" SelfReg="false" Sequence="21"/>
<ROW File="send2trash_win.pyd" Component_="bz2.pyd" FileName="_send2~1.pyd|_send2trash_win.pyd" Attributes="0" SourcePath="dist\_send2trash_win.pyd" SelfReg="false" Sequence="29"/>
<ROW File="sip.pyd" Component_="bz2.pyd" FileName="sip.pyd" Attributes="0" SourcePath="dist\sip.pyd" SelfReg="false" Sequence="22"/>
<ROW File="socket.pyd" Component_="bz2.pyd" FileName="_socket.pyd" Attributes="0" SourcePath="dist\_socket.pyd" SelfReg="false" Sequence="25"/>
<ROW File="ssl.pyd" Component_="bz2.pyd" FileName="_ssl.pyd" Attributes="0" SourcePath="dist\_ssl.pyd" SelfReg="false" Sequence="26"/>
<ROW File="unicodedata.pyd" Component_="bz2.pyd" FileName="unicod~1.pyd|unicodedata.pyd" Attributes="0" SourcePath="dist\unicodedata.pyd" SelfReg="false" Sequence="23"/>
<ROW File="updater.exe" Component_="updater.exe" FileName="updater.exe" Attributes="0" SourcePath="&lt;AI_HOME&gt;updater.exe" SelfReg="false" Sequence="1"/>
<ROW File="win32api.pyd" Component_="bz2.pyd" FileName="win32api.pyd" Attributes="0" SourcePath="dist\win32api.pyd" SelfReg="false" Sequence="27"/>
<ROW File="win32com.shell.shell.pyd" Component_="bz2.pyd" FileName="win32c~1.pyd|win32com.shell.shell.pyd" Attributes="0" SourcePath="dist\win32com.shell.shell.pyd" SelfReg="false" Sequence="28"/>
<ROW File="win32sysloader.pyd" Component_="bz2.pyd" FileName="_win32~1.pyd|_win32sysloader.pyd" Attributes="0" SourcePath="dist\_win32sysloader.pyd" SelfReg="false" Sequence="34"/>
<ROW File="win32trace.pyd" Component_="bz2.pyd" FileName="win32t~1.pyd|win32trace.pyd" Attributes="0" SourcePath="dist\win32trace.pyd" SelfReg="false" Sequence="29"/>
<ROW File="win32ui.pyd" Component_="bz2.pyd" FileName="win32ui.pyd" Attributes="0" SourcePath="dist\win32ui.pyd" SelfReg="false" Sequence="30"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
<ROW BuildKey="DefaultBuild" BuildName="DefaultBuild" BuildOrder="1" BuildType="0" PackageName="install\dupeguru_win_[|ProductVersion]" Languages="en" InstallationType="4" CabsLocation="1" PackageType="1" FilesInsideExe="true" CreateMd5="true" ExtractionFolder="[AppDataFolder][|Manufacturer]\[|ProductName]\install" ExtUI="true" ExeName="dupeguru_win_[|ProductVersion]"/>
@@ -229,10 +217,11 @@
<COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
<ROW Registry="AIShRegAnswer" Root="-1" Key="Software\Caphyon\Advanced Installer\Installs\[ProductCode]" Name="AIShRegAnswer" Value="[AI_SHORTCUTSREG]" Component_="AIShRegAnswer"/>
<ROW Registry="AI_ExePath" Root="-1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Value="[AI_SETUPEXEPATH]" Component_="AI_ExePath"/>
<ROW Registry="CurrentVersion" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="CurrentVersion" Value="[ProductVersion]" Component_="CurrentVersion"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiShortsComponent">
<ROW Shortcut="Check_for_updates" Directory_="SHORTCUTDIR" Name="Checkf~1|Check for update" Component_="updater.exe" Target="[#updater.exe]" Arguments="/checknow" Hotkey="0" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
<ROW Shortcut="Uninstall_dupeGuru" Directory_="SHORTCUTDIR" Name="Uninst~1|Uninstall dupeGuru" Component_="PyWinTypes26.dll" Target="[SystemFolder]msiexec.exe" Arguments="/x [ProductCode]" Hotkey="0" Icon_="SystemFolder_msiexec.exe" IconIndex="0" ShowCmd="1"/>
<ROW Shortcut="Uninstall_dupeGuru" Directory_="SHORTCUTDIR" Name="Uninst~1|Uninstall dupeGuru" Component_="AIShRegAnswer" Target="[SystemFolder]msiexec.exe" Arguments="/x [ProductCode]" Hotkey="0" Icon_="SystemFolder_msiexec.exe" IconIndex="0" ShowCmd="1"/>
<ROW Shortcut="dupeGuru" Directory_="DesktopFolder" Name="dupeGuru" Component_="dupeGuru.exe" Target="[#dupeGuru.exe]" Hotkey="0" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
<ROW Shortcut="dupeGuru_1" Directory_="SHORTCUTDIR" Name="dupeGuru" Component_="dupeGuru.exe" Target="[#dupeGuru.exe]" Hotkey="0" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
</COMPONENT>

View File

@@ -50,6 +50,7 @@ class PreferencesDialog(QDialog, Ui_PreferencesDialog):
setchecked(self.ignoreSmallFilesBox, prefs.ignore_small_files)
self.sizeThresholdEdit.setText(unicode(prefs.small_file_threshold))
self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type)
self.customCommandEdit.setText(prefs.custom_command)
def save(self):
prefs = self.app.prefs
@@ -64,6 +65,7 @@ class PreferencesDialog(QDialog, Ui_PreferencesDialog):
prefs.ignore_small_files = ischecked(self.ignoreSmallFilesBox)
prefs.small_file_threshold = tryint(self.sizeThresholdEdit.text())
prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex()
prefs.custom_command = unicode(self.customCommandEdit.text())
def resetToDefaults(self):
self.load(preferences.Preferences())

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>449</width>
<height>312</height>
<width>308</width>
<height>361</height>
</rect>
</property>
<property name="windowTitle">
@@ -19,9 +19,9 @@
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,0,1">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
@@ -174,15 +174,6 @@
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="wordWeightingBox">
<property name="text">
@@ -220,9 +211,6 @@
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="ignoreSmallFilesBox">
<property name="text">
@@ -319,6 +307,16 @@
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Custom Command (arguments: %d for dupe, %r for ref):</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="customCommandEdit"/>
</item>
</layout>
</item>
<item>

View File

@@ -14,6 +14,10 @@ import base.dg_rc
from app import DupeGuru
if sys.platform == 'win32':
import base.cxfreeze_fix
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setWindowIcon(QIcon(QPixmap(":/logo_se")))