mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-23 07:01:39 +00:00
Initial commit.
--HG-- extra : convert_revision : svn%3Ac306627e-7827-47d3-bdf0-9a457c9553a1/trunk%402
This commit is contained in:
17
base/cocoa/AppDelegate.h
Normal file
17
base/cocoa/AppDelegate.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "RecentDirectories.h"
|
||||
#import "PyDupeGuru.h"
|
||||
|
||||
@interface AppDelegateBase : NSObject
|
||||
{
|
||||
IBOutlet PyDupeGuruBase *py;
|
||||
IBOutlet RecentDirectories *recentDirectories;
|
||||
IBOutlet NSMenuItem *unlockMenuItem;
|
||||
|
||||
NSString *_appName;
|
||||
}
|
||||
- (IBAction)unlockApp:(id)sender;
|
||||
|
||||
- (PyDupeGuruBase *)py;
|
||||
- (RecentDirectories *)recentDirectories;
|
||||
@end
|
||||
30
base/cocoa/AppDelegate.m
Normal file
30
base/cocoa/AppDelegate.m
Normal file
@@ -0,0 +1,30 @@
|
||||
#import "AppDelegate.h"
|
||||
#import "ProgressController.h"
|
||||
#import "RegistrationInterface.h"
|
||||
#import "Utils.h"
|
||||
#import "Consts.h"
|
||||
|
||||
@implementation AppDelegateBase
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
_appName = @"";
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IBAction)unlockApp:(id)sender
|
||||
{
|
||||
if ([[self py] isRegistered])
|
||||
return;
|
||||
RegistrationInterface *ri = [[RegistrationInterface alloc] initWithApp:[self py] name:_appName limitDescription:LIMIT_DESC];
|
||||
if ([ri enterCode] == NSOKButton)
|
||||
{
|
||||
NSString *menuTitle = [NSString stringWithFormat:@"Thanks for buying %@",_appName];
|
||||
[unlockMenuItem setTitle:menuTitle];
|
||||
}
|
||||
[ri release];
|
||||
}
|
||||
|
||||
- (PyDupeGuruBase *)py { return py; }
|
||||
- (RecentDirectories *)recentDirectories { return recentDirectories; }
|
||||
@end
|
||||
17
base/cocoa/Consts.h
Normal file
17
base/cocoa/Consts.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#define DuplicateSelectionChangedNotification @"DuplicateSelectionChangedNotification"
|
||||
#define ResultsChangedNotification @"ResultsChangedNotification"
|
||||
#define ResultsMarkingChangedNotification @"ResultsMarkingChangedNotification"
|
||||
#define RegistrationRequired @"RegistrationRequired"
|
||||
#define JobStarted @"JobStarted"
|
||||
#define JobInProgress @"JobInProgress"
|
||||
|
||||
#define jobLoad @"job_load"
|
||||
#define jobScan @"job_scan"
|
||||
#define jobCopy @"job_copy"
|
||||
#define jobMove @"job_move"
|
||||
#define jobDelete @"job_delete"
|
||||
|
||||
#define DEMO_MAX_ACTION_COUNT 10
|
||||
#define LIMIT_DESC @"In the demo version, only 10 duplicates per session can be sent to Trash, moved or copied."
|
||||
25
base/cocoa/DirectoryPanel.h
Normal file
25
base/cocoa/DirectoryPanel.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "RecentDirectories.h"
|
||||
#import "Outline.h"
|
||||
#import "PyDupeGuru.h"
|
||||
|
||||
@interface DirectoryPanelBase : NSWindowController
|
||||
{
|
||||
IBOutlet NSPopUpButton *addButtonPopUp;
|
||||
IBOutlet OutlineView *directories;
|
||||
IBOutlet NSButton *removeButton;
|
||||
|
||||
PyDupeGuruBase *_py;
|
||||
RecentDirectories *_recentDirectories;
|
||||
}
|
||||
- (id)initWithParentApp:(id)aParentApp;
|
||||
|
||||
- (IBAction)askForDirectory:(id)sender;
|
||||
- (IBAction)changeDirectoryState:(id)sender;
|
||||
- (IBAction)popupAddDirectoryMenu:(id)sender;
|
||||
- (IBAction)removeSelectedDirectory:(id)sender;
|
||||
- (IBAction)toggleVisible:(id)sender;
|
||||
|
||||
- (void)addDirectory:(NSString *)directory;
|
||||
- (void)refreshRemoveButtonText;
|
||||
@end
|
||||
178
base/cocoa/DirectoryPanel.m
Normal file
178
base/cocoa/DirectoryPanel.m
Normal file
@@ -0,0 +1,178 @@
|
||||
#import "DirectoryPanel.h"
|
||||
#import "Dialogs.h"
|
||||
#import "Utils.h"
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@implementation DirectoryPanelBase
|
||||
- (id)initWithParentApp:(id)aParentApp
|
||||
{
|
||||
self = [super initWithWindowNibName:@"Directories"];
|
||||
[self window];
|
||||
AppDelegateBase *app = aParentApp;
|
||||
_py = [app py];
|
||||
_recentDirectories = [app recentDirectories];
|
||||
[directories setPy:_py];
|
||||
NSPopUpButtonCell *cell = [[directories tableColumnWithIdentifier:@"1"] dataCell];
|
||||
[cell addItemWithTitle:@"Normal"];
|
||||
[cell addItemWithTitle:@"Reference"];
|
||||
[cell addItemWithTitle:@"Excluded"];
|
||||
for (int i=0;i<[[cell itemArray] count];i++)
|
||||
{
|
||||
NSMenuItem *mi = [[cell itemArray] objectAtIndex:i];
|
||||
[mi setTarget:self];
|
||||
[mi setAction:@selector(changeDirectoryState:)];
|
||||
[mi setTag:i];
|
||||
}
|
||||
[self refreshRemoveButtonText];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(directorySelectionChanged:) name:NSOutlineViewSelectionDidChangeNotification object:directories];
|
||||
return self;
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
|
||||
- (IBAction)askForDirectory:(id)sender
|
||||
{
|
||||
NSOpenPanel *op = [NSOpenPanel openPanel];
|
||||
[op setCanChooseFiles:YES];
|
||||
[op setCanChooseDirectories:YES];
|
||||
[op setAllowsMultipleSelection:NO];
|
||||
[op setTitle:@"Select a directory to add to the scanning list"];
|
||||
[op setDelegate:self];
|
||||
if ([op runModalForTypes:nil] == NSOKButton)
|
||||
{
|
||||
NSString *directory = [[op filenames] objectAtIndex:0];
|
||||
[self addDirectory:directory];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)changeDirectoryState:(id)sender
|
||||
{
|
||||
OVNode *node = [directories itemAtRow:[directories clickedRow]];
|
||||
[_py setDirectory:p2a([node indexPath]) state:i2n([sender tag])];
|
||||
[node resetAllBuffers];
|
||||
[directories display];
|
||||
}
|
||||
|
||||
- (IBAction)popupAddDirectoryMenu:(id)sender
|
||||
{
|
||||
if ([[_recentDirectories directories] count] == 0)
|
||||
{
|
||||
[self askForDirectory:sender];
|
||||
return;
|
||||
}
|
||||
NSMenu *m = [addButtonPopUp menu];
|
||||
while ([m numberOfItems] > 0)
|
||||
[m removeItemAtIndex:0];
|
||||
NSMenuItem *mi = [m addItemWithTitle:@"Add New Directory..." action:@selector(askForDirectory:) keyEquivalent:@""];
|
||||
[mi setTarget:self];
|
||||
[m addItem:[NSMenuItem separatorItem]];
|
||||
[_recentDirectories fillMenu:m];
|
||||
[addButtonPopUp selectItem:nil];
|
||||
[[addButtonPopUp cell] performClickWithFrame:[sender frame] inView:[sender superview]];
|
||||
}
|
||||
|
||||
- (IBAction)removeSelectedDirectory:(id)sender
|
||||
{
|
||||
[[self window] makeKeyAndOrderFront:nil];
|
||||
if ([directories selectedRow] < 0)
|
||||
return;
|
||||
OVNode *node = [directories itemAtRow:[directories selectedRow]];
|
||||
if ([node level] == 1)
|
||||
{
|
||||
[_py removeDirectory:i2n([node index])];
|
||||
[directories reloadData];
|
||||
}
|
||||
else
|
||||
{
|
||||
int state = n2i([[node buffer] objectAtIndex:1]);
|
||||
int newState = state == 2 ? 0 : 2; // If excluded, put it back
|
||||
[_py setDirectory:p2a([node indexPath]) state:i2n(newState)];
|
||||
[node resetAllBuffers];
|
||||
[directories display];
|
||||
}
|
||||
[self refreshRemoveButtonText];
|
||||
}
|
||||
|
||||
- (IBAction)toggleVisible:(id)sender
|
||||
{
|
||||
if ([[self window] isVisible])
|
||||
[[self window] close];
|
||||
else
|
||||
[[self window] makeKeyAndOrderFront:nil];
|
||||
}
|
||||
|
||||
/* Public */
|
||||
|
||||
- (void)addDirectory:(NSString *)directory
|
||||
{
|
||||
int r = [[_py addDirectory:directory] intValue];
|
||||
if (r)
|
||||
{
|
||||
NSString *m;
|
||||
switch (r)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
m = @"This directory already is in the list.";
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
m = @"This directory does not exist.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
[Dialogs showMessage:m];
|
||||
}
|
||||
[directories reloadData];
|
||||
[_recentDirectories addDirectory:directory];
|
||||
[[self window] makeKeyAndOrderFront:nil];
|
||||
}
|
||||
|
||||
- (void)refreshRemoveButtonText
|
||||
{
|
||||
if ([directories selectedRow] < 0)
|
||||
{
|
||||
[removeButton setEnabled:NO];
|
||||
return;
|
||||
}
|
||||
[removeButton setEnabled:YES];
|
||||
OVNode *node = [directories itemAtRow:[directories selectedRow]];
|
||||
int state = n2i([[node buffer] objectAtIndex:1]);
|
||||
NSString *buttonText = state == 2 ? @"Put Back" : @"Remove";
|
||||
[removeButton setTitle:buttonText];
|
||||
}
|
||||
|
||||
/* Delegate */
|
||||
|
||||
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
|
||||
{
|
||||
OVNode *node = item;
|
||||
int state = n2i([[node buffer] objectAtIndex:1]);
|
||||
if ([cell isKindOfClass:[NSTextFieldCell class]])
|
||||
{
|
||||
NSTextFieldCell *textCell = cell;
|
||||
if (state == 1)
|
||||
[textCell setTextColor:[NSColor blueColor]];
|
||||
else if (state == 2)
|
||||
[textCell setTextColor:[NSColor redColor]];
|
||||
else
|
||||
[textCell setTextColor:[NSColor blackColor]];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)path
|
||||
{
|
||||
BOOL isdir;
|
||||
[[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isdir];
|
||||
return isdir;
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
|
||||
- (void)directorySelectionChanged:(NSNotification *)aNotification
|
||||
{
|
||||
[self refreshRemoveButtonText];
|
||||
}
|
||||
|
||||
@end
|
||||
BIN
base/cocoa/English.lproj/InfoPlist.strings
Normal file
BIN
base/cocoa/English.lproj/InfoPlist.strings
Normal file
Binary file not shown.
26
base/cocoa/Info.plist
Normal file
26
base/cocoa/Info.plist
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.yourcompany.yourcocoaframework</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
56
base/cocoa/PyDupeGuru.h
Normal file
56
base/cocoa/PyDupeGuru.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "PyApp.h"
|
||||
|
||||
@interface PyDupeGuruBase : PyApp
|
||||
//Actions
|
||||
- (NSNumber *)addDirectory:(NSString *)name;
|
||||
- (void)removeDirectory:(NSNumber *)index;
|
||||
- (void)setDirectory:(NSArray *)indexPath state:(NSNumber *)state;
|
||||
- (void)loadResults;
|
||||
- (void)saveResults;
|
||||
- (void)loadIgnoreList;
|
||||
- (void)saveIgnoreList;
|
||||
- (void)clearIgnoreList;
|
||||
- (void)purgeIgnoreList;
|
||||
- (NSString *)exportToXHTMLwithColumns:(NSArray *)aColIds xslt:(NSString *)xsltPath css:(NSString *)cssPath;
|
||||
|
||||
- (NSNumber *)doScan;
|
||||
|
||||
- (void)selectPowerMarkerNodePaths:(NSArray *)aIndexPaths;
|
||||
- (void)selectResultNodePaths:(NSArray *)aIndexPaths;
|
||||
|
||||
- (void)toggleSelectedMark;
|
||||
- (void)markAll;
|
||||
- (void)markInvert;
|
||||
- (void)markNone;
|
||||
|
||||
- (void)addSelectedToIgnoreList;
|
||||
- (void)refreshDetailsWithSelected;
|
||||
- (void)removeSelected;
|
||||
- (void)openSelected;
|
||||
- (NSNumber *)renameSelected:(NSString *)aNewName;
|
||||
- (void)revealSelected;
|
||||
- (void)makeSelectedReference;
|
||||
- (void)applyFilter:(NSString *)filter;
|
||||
|
||||
- (void)sortGroupsBy:(NSNumber *)aIdentifier ascending:(NSNumber *)aAscending;
|
||||
- (void)sortDupesBy:(NSNumber *)aIdentifier ascending:(NSNumber *)aAscending;
|
||||
|
||||
- (void)copyOrMove:(NSNumber *)aCopy markedTo:(NSString *)destination recreatePath:(NSNumber *)aRecreateType;
|
||||
- (void)deleteMarked;
|
||||
- (void)removeMarked;
|
||||
|
||||
//Data
|
||||
- (NSNumber *)getIgnoreListCount;
|
||||
- (NSNumber *)getMarkCount;
|
||||
- (NSString *)getStatLine;
|
||||
- (NSNumber *)getOperationalErrorCount;
|
||||
|
||||
//Scanning options
|
||||
- (void)setMinMatchPercentage:(NSNumber *)percentage;
|
||||
- (void)setMixFileKind:(NSNumber *)mix_file_kind;
|
||||
- (void)setDisplayDeltaValues:(NSNumber *)display_delta_values;
|
||||
- (void)setEscapeFilterRegexp:(NSNumber *)escape_filter_regexp;
|
||||
- (void)setRemoveEmptyFolders:(NSNumber *)remove_empty_folders;
|
||||
- (void)setSizeThreshold:(int)size_threshold;
|
||||
@end
|
||||
34
base/cocoa/ResultWindow.h
Normal file
34
base/cocoa/ResultWindow.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "Outline.h"
|
||||
#import "DirectoryPanel.h"
|
||||
#import "PyDupeGuru.h"
|
||||
|
||||
@interface MatchesView : OutlineView
|
||||
- (void)keyDown:(NSEvent *)theEvent;
|
||||
@end
|
||||
|
||||
@interface ResultWindowBase : NSWindowController
|
||||
{
|
||||
@protected
|
||||
IBOutlet PyDupeGuruBase *py;
|
||||
IBOutlet id app;
|
||||
IBOutlet NSView *actionMenuView;
|
||||
IBOutlet NSSegmentedControl *deltaSwitch;
|
||||
IBOutlet NSView *deltaSwitchView;
|
||||
IBOutlet NSView *filterFieldView;
|
||||
IBOutlet MatchesView *matches;
|
||||
IBOutlet NSView *pmSwitchView;
|
||||
|
||||
BOOL _powerMode;
|
||||
BOOL _displayDelta;
|
||||
}
|
||||
- (NSString *)logoImageName;
|
||||
/* Actions */
|
||||
- (IBAction)changeDelta:(id)sender;
|
||||
- (IBAction)copyMarked:(id)sender;
|
||||
- (IBAction)deleteMarked:(id)sender;
|
||||
- (IBAction)expandAll:(id)sender;
|
||||
- (IBAction)moveMarked:(id)sender;
|
||||
/* Notifications */
|
||||
- (void)jobCompleted:(NSNotification *)aNotification;
|
||||
@end
|
||||
316
base/cocoa/ResultWindow.m
Normal file
316
base/cocoa/ResultWindow.m
Normal file
@@ -0,0 +1,316 @@
|
||||
#import "ResultWindow.h"
|
||||
#import "Dialogs.h"
|
||||
#import "ProgressController.h"
|
||||
#import "Utils.h"
|
||||
#import "RegistrationInterface.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "Consts.h"
|
||||
|
||||
#define tbbDirectories @"tbbDirectories"
|
||||
#define tbbDetails @"tbbDetail"
|
||||
#define tbbPreferences @"tbbPreferences"
|
||||
#define tbbPowerMarker @"tbbPowerMarker"
|
||||
#define tbbScan @"tbbScan"
|
||||
#define tbbAction @"tbbAction"
|
||||
#define tbbDelta @"tbbDelta"
|
||||
#define tbbFilter @"tbbFilter"
|
||||
|
||||
@implementation MatchesView
|
||||
- (void)keyDown:(NSEvent *)theEvent
|
||||
{
|
||||
unichar key = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
|
||||
// get flags and strip the lower 16 (device dependant) bits
|
||||
unsigned int flags = ( [theEvent modifierFlags] & 0x00FF );
|
||||
if (((key == NSDeleteFunctionKey) || (key == NSDeleteCharacter)) && (flags == 0))
|
||||
[self sendAction:@selector(removeSelected:) to:[self delegate]];
|
||||
else
|
||||
if ((key == 0x20) && (flags == 0)) // Space
|
||||
[self sendAction:@selector(markSelected:) to:[self delegate]];
|
||||
else
|
||||
[super keyDown:theEvent];
|
||||
}
|
||||
|
||||
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
|
||||
{
|
||||
if (![[tableColumn identifier] isEqual:@"0"])
|
||||
return; //We only want to cover renames.
|
||||
OVNode *node = item;
|
||||
NSString *oldName = [[node buffer] objectAtIndex:0];
|
||||
NSString *newName = object;
|
||||
if (![newName isEqual:oldName])
|
||||
{
|
||||
BOOL renamed = n2b([(PyDupeGuruBase *)py renameSelected:newName]);
|
||||
if (renamed)
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
|
||||
else
|
||||
[Dialogs showMessage:[NSString stringWithFormat:@"The name '%@' already exists.",newName]];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ResultWindowBase
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[self window];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registrationRequired:) name:RegistrationRequired object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobStarted:) name:JobStarted object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobInProgress:) name:JobInProgress object:nil];
|
||||
}
|
||||
|
||||
/* Virtual */
|
||||
- (NSString *)logoImageName
|
||||
{
|
||||
return @"dg_logo32";
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
- (IBAction)changeDelta:(id)sender
|
||||
{
|
||||
_displayDelta = [deltaSwitch selectedSegment] == 1;
|
||||
[py setDisplayDeltaValues:b2n(_displayDelta)];
|
||||
[matches reloadData];
|
||||
[self expandAll:nil];
|
||||
}
|
||||
|
||||
- (IBAction)copyMarked:(id)sender
|
||||
{
|
||||
int mark_count = [[py getMarkCount] intValue];
|
||||
if (!mark_count)
|
||||
return;
|
||||
NSOpenPanel *op = [NSOpenPanel openPanel];
|
||||
[op setCanChooseFiles:NO];
|
||||
[op setCanChooseDirectories:YES];
|
||||
[op setCanCreateDirectories:YES];
|
||||
[op setAllowsMultipleSelection:NO];
|
||||
[op setTitle:@"Select a directory to copy marked files to"];
|
||||
if ([op runModalForTypes:nil] == NSOKButton)
|
||||
{
|
||||
NSString *directory = [[op filenames] objectAtIndex:0];
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
[py copyOrMove:b2n(YES) markedTo:directory recreatePath:[ud objectForKey:@"recreatePathType"]];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)deleteMarked:(id)sender
|
||||
{
|
||||
int mark_count = [[py getMarkCount] intValue];
|
||||
if (!mark_count)
|
||||
return;
|
||||
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to send %d files to Trash. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
|
||||
return;
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
[py setRemoveEmptyFolders:[ud objectForKey:@"removeEmptyFolders"]];
|
||||
[py deleteMarked];
|
||||
}
|
||||
|
||||
- (IBAction)expandAll:(id)sender
|
||||
{
|
||||
for (int i=0;i < [matches numberOfRows];i++)
|
||||
[matches expandItem:[matches itemAtRow:i]];
|
||||
}
|
||||
|
||||
- (IBAction)moveMarked:(id)sender
|
||||
{
|
||||
int mark_count = [[py getMarkCount] intValue];
|
||||
if (!mark_count)
|
||||
return;
|
||||
NSOpenPanel *op = [NSOpenPanel openPanel];
|
||||
[op setCanChooseFiles:NO];
|
||||
[op setCanChooseDirectories:YES];
|
||||
[op setCanCreateDirectories:YES];
|
||||
[op setAllowsMultipleSelection:NO];
|
||||
[op setTitle:@"Select a directory to move marked files to"];
|
||||
if ([op runModalForTypes:nil] == NSOKButton)
|
||||
{
|
||||
NSString *directory = [[op filenames] objectAtIndex:0];
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
[py setRemoveEmptyFolders:[ud objectForKey:@"removeEmptyFolders"]];
|
||||
[py copyOrMove:b2n(NO) markedTo:directory recreatePath:[ud objectForKey:@"recreatePathType"]];
|
||||
}
|
||||
}
|
||||
|
||||
/* Delegate */
|
||||
|
||||
- (void)outlineView:(NSOutlineView *)outlineView didClickTableColumn:(NSTableColumn *)tableColumn
|
||||
{
|
||||
if ([[outlineView sortDescriptors] count] < 1)
|
||||
return;
|
||||
NSSortDescriptor *sd = [[outlineView sortDescriptors] objectAtIndex:0];
|
||||
if (_powerMode)
|
||||
[py sortDupesBy:i2n([[sd key] intValue]) ascending:b2n([sd ascending])];
|
||||
else
|
||||
[py sortGroupsBy:i2n([[sd key] intValue]) ascending:b2n([sd ascending])];
|
||||
[matches reloadData];
|
||||
[self expandAll:nil];
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
- (void)windowWillClose:(NSNotification *)aNotification
|
||||
{
|
||||
[NSApp hide:NSApp];
|
||||
}
|
||||
|
||||
- (void)jobCompleted:(NSNotification *)aNotification
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
|
||||
int 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
|
||||
[Dialogs showMessage:@"All marked files were copied sucessfully."];
|
||||
}
|
||||
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
|
||||
[Dialogs showMessage:@"All marked files were moved sucessfully."];
|
||||
}
|
||||
if ([lastAction isEqualTo:jobDelete])
|
||||
{
|
||||
if (r > 0)
|
||||
[Dialogs showMessage:[NSString stringWithFormat:@"%d file(s) couldn't be sent to Trash. They were kept in the results, and still are marked.",r]];
|
||||
else
|
||||
[Dialogs showMessage:@"All marked files were sucessfully sent to Trash."];
|
||||
}
|
||||
// Re-activate toolbar items right after the progress bar stops showing instead of waiting until
|
||||
// a mouse-over is performed
|
||||
[[[self window] toolbar] validateVisibleItems];
|
||||
}
|
||||
|
||||
- (void)jobInProgress:(NSNotification *)aNotification
|
||||
{
|
||||
[Dialogs showMessage:@"A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again."];
|
||||
}
|
||||
|
||||
- (void)jobStarted:(NSNotification *)aNotification
|
||||
{
|
||||
NSDictionary *ui = [aNotification userInfo];
|
||||
NSString *desc = [ui valueForKey:@"desc"];
|
||||
[[ProgressController mainProgressController] setJobDesc:desc];
|
||||
NSString *jobid = [ui valueForKey:@"jobid"];
|
||||
// NSLog(jobid);
|
||||
[[ProgressController mainProgressController] setJobId:jobid];
|
||||
[[ProgressController mainProgressController] showSheetForParent:[self window]];
|
||||
}
|
||||
|
||||
- (void)registrationRequired:(NSNotification *)aNotification
|
||||
{
|
||||
NSString *msg = @"This is a demo version, which only allows you 10 delete/copy/move actions per session. You cannot continue.";
|
||||
[Dialogs showMessage:msg];
|
||||
}
|
||||
|
||||
/* Toolbar */
|
||||
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
|
||||
{
|
||||
NSToolbarItem *tbi = [[[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier] autorelease];
|
||||
if ([itemIdentifier isEqualTo:tbbDirectories])
|
||||
{
|
||||
[tbi setLabel: @"Directories"];
|
||||
[tbi setToolTip: @"Show/Hide the directories panel."];
|
||||
[tbi setImage: [NSImage imageNamed: @"folder32"]];
|
||||
[tbi setTarget: app];
|
||||
[tbi setAction: @selector(toggleDirectories:)];
|
||||
}
|
||||
else if ([itemIdentifier isEqualTo:tbbDetails])
|
||||
{
|
||||
[tbi setLabel: @"Details"];
|
||||
[tbi setToolTip: @"Show/Hide the details panel."];
|
||||
[tbi setImage: [NSImage imageNamed: @"details32"]];
|
||||
[tbi setTarget: self];
|
||||
[tbi setAction: @selector(toggleDetailsPanel:)];
|
||||
}
|
||||
else if ([itemIdentifier isEqualTo:tbbPreferences])
|
||||
{
|
||||
[tbi setLabel: @"Preferences"];
|
||||
[tbi setToolTip: @"Show the preferences panel."];
|
||||
[tbi setImage: [NSImage imageNamed: @"preferences32"]];
|
||||
[tbi setTarget: self];
|
||||
[tbi setAction: @selector(showPreferencesPanel:)];
|
||||
}
|
||||
else if ([itemIdentifier isEqualTo:tbbPowerMarker])
|
||||
{
|
||||
[tbi setLabel: @"Power Marker"];
|
||||
[tbi setToolTip: @"When enabled, only the duplicates are shown, not the references."];
|
||||
[tbi setView:pmSwitchView];
|
||||
[tbi setMinSize:[pmSwitchView frame].size];
|
||||
[tbi setMaxSize:[pmSwitchView frame].size];
|
||||
}
|
||||
else if ([itemIdentifier isEqualTo:tbbScan])
|
||||
{
|
||||
[tbi setLabel: @"Start Scanning"];
|
||||
[tbi setToolTip: @"Start scanning for duplicates in the selected directories."];
|
||||
[tbi setImage: [NSImage imageNamed:[self logoImageName]]];
|
||||
[tbi setTarget: self];
|
||||
[tbi setAction: @selector(startDuplicateScan:)];
|
||||
}
|
||||
else if ([itemIdentifier isEqualTo:tbbAction])
|
||||
{
|
||||
[tbi setLabel: @"Action"];
|
||||
[tbi setView:actionMenuView];
|
||||
[tbi setMinSize:[actionMenuView frame].size];
|
||||
[tbi setMaxSize:[actionMenuView frame].size];
|
||||
}
|
||||
else if ([itemIdentifier isEqualTo:tbbDelta])
|
||||
{
|
||||
[tbi setLabel: @"Delta Values"];
|
||||
[tbi setToolTip: @"When enabled, this option makes dupeGuru display, where applicable, delta values instead of absolute values."];
|
||||
[tbi setView:deltaSwitchView];
|
||||
[tbi setMinSize:[deltaSwitchView frame].size];
|
||||
[tbi setMaxSize:[deltaSwitchView frame].size];
|
||||
}
|
||||
else if ([itemIdentifier isEqualTo:tbbFilter])
|
||||
{
|
||||
[tbi setLabel: @"Filter"];
|
||||
[tbi setToolTip: @"Filters the results using regular expression."];
|
||||
[tbi setView:filterFieldView];
|
||||
[tbi setMinSize:[filterFieldView frame].size];
|
||||
[tbi setMaxSize:NSMakeSize(1000, [filterFieldView frame].size.height)];
|
||||
}
|
||||
[tbi setPaletteLabel: [tbi label]];
|
||||
return tbi;
|
||||
}
|
||||
|
||||
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
|
||||
{
|
||||
return [NSArray arrayWithObjects:
|
||||
tbbDirectories,
|
||||
tbbDetails,
|
||||
tbbPreferences,
|
||||
tbbPowerMarker,
|
||||
tbbScan,
|
||||
tbbAction,
|
||||
tbbDelta,
|
||||
tbbFilter,
|
||||
NSToolbarSeparatorItemIdentifier,
|
||||
NSToolbarSpaceItemIdentifier,
|
||||
NSToolbarFlexibleSpaceItemIdentifier,
|
||||
nil];
|
||||
}
|
||||
|
||||
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar
|
||||
{
|
||||
return [NSArray arrayWithObjects:
|
||||
tbbScan,
|
||||
tbbAction,
|
||||
tbbDirectories,
|
||||
tbbDetails,
|
||||
tbbPowerMarker,
|
||||
tbbDelta,
|
||||
tbbFilter,
|
||||
nil];
|
||||
}
|
||||
|
||||
- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
|
||||
{
|
||||
return ![[ProgressController mainProgressController] isShown];
|
||||
}
|
||||
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)item
|
||||
{
|
||||
return ![[ProgressController mainProgressController] isShown];
|
||||
}
|
||||
@end
|
||||
408
base/cocoa/dgbase.xcodeproj/project.pbxproj
Normal file
408
base/cocoa/dgbase.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,408 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 44;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; };
|
||||
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
|
||||
CE62CA730CFAF80D0001B6E0 /* ResultWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = CE62CA710CFAF80D0001B6E0 /* ResultWindow.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CE62CA740CFAF80D0001B6E0 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE62CA720CFAF80D0001B6E0 /* ResultWindow.m */; };
|
||||
CE70A0FC0CF8C2560048C314 /* PyDupeGuru.h in Headers */ = {isa = PBXBuildFile; fileRef = CE70A0FB0CF8C2560048C314 /* PyDupeGuru.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CE7D77050CF8987800BA287E /* Consts.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7D77030CF8987800BA287E /* Consts.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CE80DA190FC191320086DCA6 /* RecentDirectories.h in Headers */ = {isa = PBXBuildFile; fileRef = CE80DA170FC191320086DCA6 /* RecentDirectories.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CE80DA1A0FC191320086DCA6 /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DA180FC191320086DCA6 /* RecentDirectories.m */; };
|
||||
CE80DA2E0FC191980086DCA6 /* Dialogs.h in Headers */ = {isa = PBXBuildFile; fileRef = CE80DA250FC191980086DCA6 /* Dialogs.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CE80DA2F0FC191980086DCA6 /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DA260FC191980086DCA6 /* Dialogs.m */; };
|
||||
CE80DA300FC191980086DCA6 /* ProgressController.h in Headers */ = {isa = PBXBuildFile; fileRef = CE80DA270FC191980086DCA6 /* ProgressController.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CE80DA310FC191980086DCA6 /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DA280FC191980086DCA6 /* ProgressController.m */; };
|
||||
CE80DA320FC191980086DCA6 /* PyApp.h in Headers */ = {isa = PBXBuildFile; fileRef = CE80DA290FC191980086DCA6 /* PyApp.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CE80DA330FC191980086DCA6 /* RegistrationInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = CE80DA2A0FC191980086DCA6 /* RegistrationInterface.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CE80DA340FC191980086DCA6 /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DA2B0FC191980086DCA6 /* RegistrationInterface.m */; };
|
||||
CE80DA350FC191980086DCA6 /* Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = CE80DA2C0FC191980086DCA6 /* Utils.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CE80DA360FC191980086DCA6 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DA2D0FC191980086DCA6 /* Utils.m */; };
|
||||
CE80DA3B0FC191C40086DCA6 /* Outline.h in Headers */ = {isa = PBXBuildFile; fileRef = CE80DA370FC191C40086DCA6 /* Outline.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CE80DA3C0FC191C40086DCA6 /* Outline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DA380FC191C40086DCA6 /* Outline.m */; };
|
||||
CE80DA3D0FC191C40086DCA6 /* Table.h in Headers */ = {isa = PBXBuildFile; fileRef = CE80DA390FC191C40086DCA6 /* Table.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
CE80DA3E0FC191C40086DCA6 /* Table.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DA3A0FC191C40086DCA6 /* Table.m */; };
|
||||
CE80DB6D0FC194560086DCA6 /* progress.nib in Resources */ = {isa = PBXBuildFile; fileRef = CE80DB690FC194560086DCA6 /* progress.nib */; };
|
||||
CE80DB6E0FC194560086DCA6 /* registration.nib in Resources */ = {isa = PBXBuildFile; fileRef = CE80DB6B0FC194560086DCA6 /* registration.nib */; };
|
||||
CE895B5F0CFAE78300B5ADEA /* AppDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = CE895B5D0CFAE78300B5ADEA /* AppDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CE895B600CFAE78300B5ADEA /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE895B5E0CFAE78300B5ADEA /* AppDelegate.m */; };
|
||||
CE895C150CFAF0C900B5ADEA /* DirectoryPanel.h in Headers */ = {isa = PBXBuildFile; fileRef = CE895C130CFAF0C900B5ADEA /* DirectoryPanel.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CE895C160CFAF0C900B5ADEA /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE895C140CFAF0C900B5ADEA /* DirectoryPanel.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
||||
0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
|
||||
089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
|
||||
8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
8DC2EF5B0486A6940098B216 /* dgbase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = dgbase.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CE62CA710CFAF80D0001B6E0 /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResultWindow.h; sourceTree = "<group>"; };
|
||||
CE62CA720CFAF80D0001B6E0 /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = "<group>"; };
|
||||
CE70A0FB0CF8C2560048C314 /* PyDupeGuru.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyDupeGuru.h; sourceTree = "<group>"; };
|
||||
CE7D77030CF8987800BA287E /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
|
||||
CE80DA170FC191320086DCA6 /* RecentDirectories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RecentDirectories.h; path = ../../../cocoalib/RecentDirectories.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA180FC191320086DCA6 /* RecentDirectories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RecentDirectories.m; path = ../../../cocoalib/RecentDirectories.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA250FC191980086DCA6 /* Dialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Dialogs.h; path = ../../../cocoalib/Dialogs.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA260FC191980086DCA6 /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA270FC191980086DCA6 /* ProgressController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressController.h; path = ../../../cocoalib/ProgressController.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA280FC191980086DCA6 /* ProgressController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProgressController.m; path = ../../../cocoalib/ProgressController.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA290FC191980086DCA6 /* PyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyApp.h; path = ../../../cocoalib/PyApp.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA2A0FC191980086DCA6 /* RegistrationInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegistrationInterface.h; path = ../../../cocoalib/RegistrationInterface.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA2B0FC191980086DCA6 /* RegistrationInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegistrationInterface.m; path = ../../../cocoalib/RegistrationInterface.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA2C0FC191980086DCA6 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = ../../../cocoalib/Utils.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA2D0FC191980086DCA6 /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Utils.m; path = ../../../cocoalib/Utils.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA370FC191C40086DCA6 /* Outline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Outline.h; path = ../../../cocoalib/Outline.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA380FC191C40086DCA6 /* Outline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Outline.m; path = ../../../cocoalib/Outline.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA390FC191C40086DCA6 /* Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Table.h; path = ../../../cocoalib/Table.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DA3A0FC191C40086DCA6 /* Table.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Table.m; path = ../../../cocoalib/Table.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB6A0FC194560086DCA6 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../../cocoalib/English.lproj/progress.nib; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB6C0FC194560086DCA6 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../../cocoalib/English.lproj/registration.nib; sourceTree = SOURCE_ROOT; };
|
||||
CE895B5D0CFAE78300B5ADEA /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
CE895B5E0CFAE78300B5ADEA /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
CE895C130CFAF0C900B5ADEA /* DirectoryPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectoryPanel.h; sourceTree = "<group>"; };
|
||||
CE895C140CFAF0C900B5ADEA /* DirectoryPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DirectoryPanel.m; sourceTree = "<group>"; };
|
||||
D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
8DC2EF560486A6940098B216 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
034768DFFF38A50411DB9C8B /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8DC2EF5B0486A6940098B216 /* dgbase.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0867D691FE84028FC02AAC07 /* dgbase */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
08FB77AEFE84172EC02AAC07 /* Classes */,
|
||||
CE80DA160FC1910F0086DCA6 /* cocoalib */,
|
||||
32C88DFF0371C24200C91783 /* Other Sources */,
|
||||
089C1665FE841158C02AAC07 /* Resources */,
|
||||
0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */,
|
||||
034768DFFF38A50411DB9C8B /* Products */,
|
||||
);
|
||||
name = dgbase;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */,
|
||||
1058C7B2FEA5585E11CA2CBB /* Other Frameworks */,
|
||||
);
|
||||
name = "External Frameworks and Libraries";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
089C1665FE841158C02AAC07 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CE80DB690FC194560086DCA6 /* progress.nib */,
|
||||
CE80DB6B0FC194560086DCA6 /* registration.nib */,
|
||||
8DC2EF5A0486A6940098B216 /* Info.plist */,
|
||||
089C1666FE841158C02AAC07 /* InfoPlist.strings */,
|
||||
);
|
||||
name = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
08FB77AEFE84172EC02AAC07 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CE62CA710CFAF80D0001B6E0 /* ResultWindow.h */,
|
||||
CE62CA720CFAF80D0001B6E0 /* ResultWindow.m */,
|
||||
CE895C130CFAF0C900B5ADEA /* DirectoryPanel.h */,
|
||||
CE895C140CFAF0C900B5ADEA /* DirectoryPanel.m */,
|
||||
CE895B5D0CFAE78300B5ADEA /* AppDelegate.h */,
|
||||
CE895B5E0CFAE78300B5ADEA /* AppDelegate.m */,
|
||||
);
|
||||
name = Classes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */,
|
||||
);
|
||||
name = "Linked Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0867D6A5FE840307C02AAC07 /* AppKit.framework */,
|
||||
D2F7E79907B2D74100F64583 /* CoreData.framework */,
|
||||
0867D69BFE84028FC02AAC07 /* Foundation.framework */,
|
||||
);
|
||||
name = "Other Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32C88DFF0371C24200C91783 /* Other Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CE70A0FB0CF8C2560048C314 /* PyDupeGuru.h */,
|
||||
CE7D77030CF8987800BA287E /* Consts.h */,
|
||||
);
|
||||
name = "Other Sources";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CE80DA160FC1910F0086DCA6 /* cocoalib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CE80DA370FC191C40086DCA6 /* Outline.h */,
|
||||
CE80DA380FC191C40086DCA6 /* Outline.m */,
|
||||
CE80DA390FC191C40086DCA6 /* Table.h */,
|
||||
CE80DA3A0FC191C40086DCA6 /* Table.m */,
|
||||
CE80DA250FC191980086DCA6 /* Dialogs.h */,
|
||||
CE80DA260FC191980086DCA6 /* Dialogs.m */,
|
||||
CE80DA270FC191980086DCA6 /* ProgressController.h */,
|
||||
CE80DA280FC191980086DCA6 /* ProgressController.m */,
|
||||
CE80DA290FC191980086DCA6 /* PyApp.h */,
|
||||
CE80DA2A0FC191980086DCA6 /* RegistrationInterface.h */,
|
||||
CE80DA2B0FC191980086DCA6 /* RegistrationInterface.m */,
|
||||
CE80DA2C0FC191980086DCA6 /* Utils.h */,
|
||||
CE80DA2D0FC191980086DCA6 /* Utils.m */,
|
||||
CE80DA170FC191320086DCA6 /* RecentDirectories.h */,
|
||||
CE80DA180FC191320086DCA6 /* RecentDirectories.m */,
|
||||
);
|
||||
name = cocoalib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
8DC2EF500486A6940098B216 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CE7D77050CF8987800BA287E /* Consts.h in Headers */,
|
||||
CE70A0FC0CF8C2560048C314 /* PyDupeGuru.h in Headers */,
|
||||
CE895B5F0CFAE78300B5ADEA /* AppDelegate.h in Headers */,
|
||||
CE895C150CFAF0C900B5ADEA /* DirectoryPanel.h in Headers */,
|
||||
CE62CA730CFAF80D0001B6E0 /* ResultWindow.h in Headers */,
|
||||
CE80DA190FC191320086DCA6 /* RecentDirectories.h in Headers */,
|
||||
CE80DA2E0FC191980086DCA6 /* Dialogs.h in Headers */,
|
||||
CE80DA300FC191980086DCA6 /* ProgressController.h in Headers */,
|
||||
CE80DA320FC191980086DCA6 /* PyApp.h in Headers */,
|
||||
CE80DA330FC191980086DCA6 /* RegistrationInterface.h in Headers */,
|
||||
CE80DA350FC191980086DCA6 /* Utils.h in Headers */,
|
||||
CE80DA3B0FC191C40086DCA6 /* Outline.h in Headers */,
|
||||
CE80DA3D0FC191C40086DCA6 /* Table.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
8DC2EF4F0486A6940098B216 /* dgbase */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "dgbase" */;
|
||||
buildPhases = (
|
||||
8DC2EF500486A6940098B216 /* Headers */,
|
||||
8DC2EF520486A6940098B216 /* Resources */,
|
||||
8DC2EF540486A6940098B216 /* Sources */,
|
||||
8DC2EF560486A6940098B216 /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = dgbase;
|
||||
productInstallPath = "$(HOME)/Library/Frameworks";
|
||||
productName = dgbase;
|
||||
productReference = 8DC2EF5B0486A6940098B216 /* dgbase.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
0867D690FE84028FC02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "dgbase" */;
|
||||
compatibilityVersion = "Xcode 3.0";
|
||||
hasScannedForEncodings = 1;
|
||||
mainGroup = 0867D691FE84028FC02AAC07 /* dgbase */;
|
||||
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
8DC2EF4F0486A6940098B216 /* dgbase */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
8DC2EF520486A6940098B216 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */,
|
||||
CE80DB6D0FC194560086DCA6 /* progress.nib in Resources */,
|
||||
CE80DB6E0FC194560086DCA6 /* registration.nib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8DC2EF540486A6940098B216 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CE895B600CFAE78300B5ADEA /* AppDelegate.m in Sources */,
|
||||
CE895C160CFAF0C900B5ADEA /* DirectoryPanel.m in Sources */,
|
||||
CE62CA740CFAF80D0001B6E0 /* ResultWindow.m in Sources */,
|
||||
CE80DA1A0FC191320086DCA6 /* RecentDirectories.m in Sources */,
|
||||
CE80DA2F0FC191980086DCA6 /* Dialogs.m in Sources */,
|
||||
CE80DA310FC191980086DCA6 /* ProgressController.m in Sources */,
|
||||
CE80DA340FC191980086DCA6 /* RegistrationInterface.m in Sources */,
|
||||
CE80DA360FC191980086DCA6 /* Utils.m in Sources */,
|
||||
CE80DA3C0FC191C40086DCA6 /* Outline.m in Sources */,
|
||||
CE80DA3E0FC191C40086DCA6 /* Table.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
089C1666FE841158C02AAC07 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
089C1667FE841158C02AAC07 /* English */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CE80DB690FC194560086DCA6 /* progress.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
CE80DB6A0FC194560086DCA6 /* English */,
|
||||
);
|
||||
name = progress.nib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
CE80DB6B0FC194560086DCA6 /* registration.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
CE80DB6C0FC194560086DCA6 /* English */,
|
||||
);
|
||||
name = registration.nib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
1DEB91AE08733DA50010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../../../cocoalib/build/Release\"",
|
||||
);
|
||||
FRAMEWORK_VERSION = A;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Library/Frameworks";
|
||||
PRODUCT_NAME = dgbase;
|
||||
WRAPPER_EXTENSION = framework;
|
||||
ZERO_LINK = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB91AF08733DA50010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/../../../cocoalib/build/Release\"",
|
||||
);
|
||||
FRAMEWORK_VERSION = A;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = NO;
|
||||
GCC_PREFIX_HEADER = "";
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "@executable_path/../Frameworks";
|
||||
PRODUCT_NAME = dgbase;
|
||||
WRAPPER_EXTENSION = framework;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1DEB91B208733DA50010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
PREBINDING = NO;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB91B308733DA50010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
PREBINDING = NO;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "dgbase" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB91AE08733DA50010E9CD /* Debug */,
|
||||
1DEB91AF08733DA50010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "dgbase" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1DEB91B208733DA50010E9CD /* Debug */,
|
||||
1DEB91B308733DA50010E9CD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
|
||||
}
|
||||
0
base/qt/__init__.py
Normal file
0
base/qt/__init__.py
Normal file
35
base/qt/about_box.py
Normal file
35
base/qt/about_box.py
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: about_box
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-05-09
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from PyQt4.QtCore import Qt, QCoreApplication, SIGNAL
|
||||
from PyQt4.QtGui import QDialog, QDialogButtonBox, QPixmap
|
||||
|
||||
from about_box_ui import Ui_AboutBox
|
||||
|
||||
class AboutBox(QDialog, Ui_AboutBox):
|
||||
def __init__(self, parent, app):
|
||||
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.MSWindowsFixedSizeDialogHint
|
||||
QDialog.__init__(self, parent, flags)
|
||||
self.app = app
|
||||
self._setupUi()
|
||||
|
||||
self.connect(self.buttonBox, SIGNAL('clicked(QAbstractButton*)'), self.buttonClicked)
|
||||
|
||||
def _setupUi(self):
|
||||
self.setupUi(self)
|
||||
# Stuff that can't be done in the Designer
|
||||
self.setWindowTitle(u"About %s" % QCoreApplication.instance().applicationName())
|
||||
self.nameLabel.setText(QCoreApplication.instance().applicationName())
|
||||
self.versionLabel.setText('Version ' + QCoreApplication.instance().applicationVersion())
|
||||
self.logoLabel.setPixmap(QPixmap(':/%s_big' % self.app.LOGO_NAME))
|
||||
self.registerButton = self.buttonBox.addButton("Register", QDialogButtonBox.ActionRole)
|
||||
|
||||
#--- Events
|
||||
def buttonClicked(self, button):
|
||||
if button is self.registerButton:
|
||||
self.app.ask_for_reg_code()
|
||||
|
||||
133
base/qt/about_box.ui
Normal file
133
base/qt/about_box.ui
Normal file
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AboutBox</class>
|
||||
<widget class="QDialog" name="AboutBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>190</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About dupeGuru</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="logoLabel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="dg.qrc">:/logo_me_big</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>dupeGuru</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="versionLabel">
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Copyright Hardcoded Software 2009</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Registered To:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="registeredEmailLabel">
|
||||
<property name="text">
|
||||
<string>UNREGISTERED</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="dg.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>AboutBox</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>AboutBox</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
269
base/qt/app.py
Normal file
269
base/qt/app.py
Normal file
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: app
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-04-25
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
import logging
|
||||
import os.path as op
|
||||
import traceback
|
||||
|
||||
from PyQt4.QtCore import Qt, QTimer, QObject, QCoreApplication, QUrl, SIGNAL
|
||||
from PyQt4.QtGui import QProgressDialog, QDesktopServices, QFileDialog, QDialog, QMessageBox
|
||||
|
||||
from hsutil import job
|
||||
from hsutil.reg import RegistrationRequired
|
||||
|
||||
from dupeguru import data_pe
|
||||
from dupeguru.app import (DupeGuru as DupeGuruBase, JOB_SCAN, JOB_LOAD, JOB_MOVE, JOB_COPY,
|
||||
JOB_DELETE)
|
||||
|
||||
from main_window import MainWindow
|
||||
from directories_dialog import DirectoriesDialog
|
||||
from about_box import AboutBox
|
||||
from reg import Registration
|
||||
from error_report_dialog import ErrorReportDialog
|
||||
|
||||
JOBID2TITLE = {
|
||||
JOB_SCAN: "Scanning for duplicates",
|
||||
JOB_LOAD: "Loading",
|
||||
JOB_MOVE: "Moving",
|
||||
JOB_COPY: "Copying",
|
||||
JOB_DELETE: "Sending files to the recycle bin",
|
||||
}
|
||||
|
||||
class Progress(QProgressDialog, job.ThreadedJobPerformer):
|
||||
def __init__(self, parent):
|
||||
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
||||
QProgressDialog.__init__(self, '', u"Cancel", 0, 100, parent, flags)
|
||||
self.setModal(True)
|
||||
self.setAutoReset(False)
|
||||
self.setAutoClose(False)
|
||||
self._timer = QTimer()
|
||||
self._jobid = ''
|
||||
self.connect(self._timer, SIGNAL('timeout()'), self.updateProgress)
|
||||
|
||||
def updateProgress(self):
|
||||
# the values might change before setValue happens
|
||||
last_progress = self.last_progress
|
||||
last_desc = self.last_desc
|
||||
if not self._job_running or last_progress is None:
|
||||
self._timer.stop()
|
||||
self.close()
|
||||
self.emit(SIGNAL('finished(QString)'), self._jobid)
|
||||
if self._last_error is not None:
|
||||
s = ''.join(traceback.format_exception(*self._last_error))
|
||||
dialog = ErrorReportDialog(self.parent(), s)
|
||||
dialog.exec_()
|
||||
return
|
||||
if self.wasCanceled():
|
||||
self.job_cancelled = True
|
||||
return
|
||||
if last_desc:
|
||||
self.setLabelText(last_desc)
|
||||
self.setValue(last_progress)
|
||||
|
||||
def run(self, jobid, title, target, args=()):
|
||||
self._jobid = jobid
|
||||
self.reset()
|
||||
self.setLabelText('')
|
||||
self.run_threaded(target, args)
|
||||
self.setWindowTitle(title)
|
||||
self.show()
|
||||
self._timer.start(500)
|
||||
|
||||
|
||||
def demo_method(method):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
try:
|
||||
return method(self, *args, **kwargs)
|
||||
except RegistrationRequired:
|
||||
msg = "The demo version of dupeGuru only allows 10 actions (delete/move/copy) per session."
|
||||
QMessageBox.information(self.main_window, 'Demo', msg)
|
||||
|
||||
return wrapper
|
||||
|
||||
class DupeGuru(DupeGuruBase, QObject):
|
||||
LOGO_NAME = '<replace this>'
|
||||
NAME = '<replace this>'
|
||||
DELTA_COLUMNS = frozenset()
|
||||
|
||||
def __init__(self, data_module, appid):
|
||||
appdata = unicode(QDesktopServices.storageLocation(QDesktopServices.DataLocation))
|
||||
DupeGuruBase.__init__(self, data_module, appdata, appid)
|
||||
QObject.__init__(self)
|
||||
self._setup()
|
||||
|
||||
#--- Private
|
||||
def _setup(self):
|
||||
self.selected_dupe = None
|
||||
self.prefs = self._create_preferences()
|
||||
self.prefs.load()
|
||||
self._update_options()
|
||||
self.main_window = self._create_main_window()
|
||||
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.preferences_dialog = self._create_preferences_dialog(self.main_window)
|
||||
self.about_box = AboutBox(self.main_window, self)
|
||||
|
||||
self.reg = Registration(self)
|
||||
self.set_registration(self.prefs.registration_code, self.prefs.registration_email)
|
||||
if not self.registered:
|
||||
self.reg.show_nag()
|
||||
self.main_window.show()
|
||||
self.load()
|
||||
|
||||
self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate)
|
||||
self.connect(self._progress, SIGNAL('finished(QString)'), self.job_finished)
|
||||
|
||||
def _setup_as_registered(self):
|
||||
self.prefs.registration_code = self.registration_code
|
||||
self.prefs.registration_email = self.registration_email
|
||||
self.main_window.actionRegister.setVisible(False)
|
||||
self.about_box.registerButton.hide()
|
||||
self.about_box.registeredEmailLabel.setText(self.prefs.registration_email)
|
||||
|
||||
def _update_options(self):
|
||||
self.scanner.mix_file_kind = self.prefs.mix_file_kind
|
||||
self.options['escape_filter_regexp'] = self.prefs.use_regexp
|
||||
self.options['clean_empty_dirs'] = self.prefs.remove_empty_folders
|
||||
|
||||
#--- Virtual
|
||||
def _create_details_dialog(self, parent):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _create_main_window(self):
|
||||
return MainWindow(app=self)
|
||||
|
||||
def _create_preferences(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _create_preferences_dialog(self, parent):
|
||||
raise NotImplementedError()
|
||||
|
||||
#--- Override
|
||||
def _start_job(self, jobid, func):
|
||||
title = JOBID2TITLE[jobid]
|
||||
try:
|
||||
j = self._progress.create_job()
|
||||
self._progress.run(jobid, title, func, args=(j, ))
|
||||
except job.JobInProgressError:
|
||||
msg = "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again."
|
||||
QMessageBox.information(self.main_window, 'Action in progress', msg)
|
||||
|
||||
#--- Public
|
||||
def add_dupes_to_ignore_list(self, duplicates):
|
||||
for dupe in duplicates:
|
||||
self.AddToIgnoreList(dupe)
|
||||
self.remove_duplicates(duplicates)
|
||||
|
||||
def ApplyFilter(self, filter):
|
||||
DupeGuruBase.ApplyFilter(self, filter)
|
||||
self.emit(SIGNAL('resultsChanged()'))
|
||||
|
||||
def ask_for_reg_code(self):
|
||||
if self.reg.ask_for_code():
|
||||
self._setup_ui_as_registered()
|
||||
|
||||
@demo_method
|
||||
def copy_or_move_marked(self, copy):
|
||||
opname = 'copy' if copy else 'move'
|
||||
title = "Select a directory to {0} marked files to".format(opname)
|
||||
flags = QFileDialog.ShowDirsOnly
|
||||
destination = unicode(QFileDialog.getExistingDirectory(self.main_window, title, '', flags))
|
||||
if not destination:
|
||||
return
|
||||
recreate_path = self.prefs.destination_type
|
||||
DupeGuruBase.copy_or_move_marked(self, copy, destination, recreate_path)
|
||||
|
||||
delete_marked = demo_method(DupeGuruBase.delete_marked)
|
||||
|
||||
def make_reference(self, duplicates):
|
||||
DupeGuruBase.make_reference(self, duplicates)
|
||||
self.emit(SIGNAL('resultsChanged()'))
|
||||
|
||||
def mark_all(self):
|
||||
self.results.mark_all()
|
||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
||||
|
||||
def mark_invert(self):
|
||||
self.results.mark_invert()
|
||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
||||
|
||||
def mark_none(self):
|
||||
self.results.mark_none()
|
||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
||||
|
||||
def open_selected(self):
|
||||
if self.selected_dupe is None:
|
||||
return
|
||||
url = QUrl.fromLocalFile(unicode(self.selected_dupe.path))
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
def remove_duplicates(self, duplicates):
|
||||
self.results.remove_duplicates(duplicates)
|
||||
self.emit(SIGNAL('resultsChanged()'))
|
||||
|
||||
def remove_marked_duplicates(self):
|
||||
marked = [d for d in self.results.dupes if self.results.is_marked(d)]
|
||||
self.remove_duplicates(marked)
|
||||
|
||||
def rename_dupe(self, dupe, newname):
|
||||
try:
|
||||
dupe.move(dupe.parent, newname)
|
||||
return True
|
||||
except (IndexError, fs.FSError) as e:
|
||||
logging.warning("dupeGuru Warning: %s" % unicode(e))
|
||||
return False
|
||||
|
||||
def reveal_selected(self):
|
||||
if self.selected_dupe is None:
|
||||
return
|
||||
url = QUrl.fromLocalFile(unicode(self.selected_dupe.path[:-1]))
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
def select_duplicate(self, dupe):
|
||||
self.selected_dupe = dupe
|
||||
self.emit(SIGNAL('duplicateSelected()'))
|
||||
|
||||
def show_about_box(self):
|
||||
self.about_box.show()
|
||||
|
||||
def show_details(self):
|
||||
self.details_dialog.show()
|
||||
|
||||
def show_directories(self):
|
||||
self.directories_dialog.show()
|
||||
|
||||
def show_help(self):
|
||||
url = QUrl.fromLocalFile(op.abspath('help/intro.htm'))
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
def show_preferences(self):
|
||||
self.preferences_dialog.load()
|
||||
result = self.preferences_dialog.exec_()
|
||||
if result == QDialog.Accepted:
|
||||
self.preferences_dialog.save()
|
||||
self.prefs.save()
|
||||
self._update_options()
|
||||
|
||||
def toggle_marking_for_dupes(self, dupes):
|
||||
for dupe in dupes:
|
||||
self.results.mark_toggle(dupe)
|
||||
self.emit(SIGNAL('dupeMarkingChanged()'))
|
||||
|
||||
#--- Events
|
||||
def application_will_terminate(self):
|
||||
self.Save()
|
||||
self.SaveIgnoreList()
|
||||
|
||||
def job_finished(self, jobid):
|
||||
self.emit(SIGNAL('resultsChanged()'))
|
||||
if jobid == JOB_LOAD:
|
||||
self.emit(SIGNAL('directoriesChanged()'))
|
||||
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)
|
||||
|
||||
78
base/qt/details_table.py
Normal file
78
base/qt/details_table.py
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: details_table
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-05-17
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from PyQt4.QtCore import Qt, SIGNAL, QAbstractTableModel, QVariant
|
||||
from PyQt4.QtGui import QHeaderView, QTableView
|
||||
|
||||
HEADER = ['Attribute', 'Selected', 'Reference']
|
||||
|
||||
class DetailsModel(QAbstractTableModel):
|
||||
def __init__(self, app):
|
||||
QAbstractTableModel.__init__(self)
|
||||
self._app = app
|
||||
self._data = app.data
|
||||
self._dupe_data = None
|
||||
self._ref_data = None
|
||||
self.connect(app, SIGNAL('duplicateSelected()'), self.duplicateSelected)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(HEADER)
|
||||
|
||||
def data(self, index, role):
|
||||
if not index.isValid():
|
||||
return QVariant()
|
||||
if role != Qt.DisplayRole:
|
||||
return QVariant()
|
||||
column = index.column()
|
||||
row = index.row()
|
||||
if column == 0:
|
||||
return QVariant(self._data.COLUMNS[row]['display'])
|
||||
elif column == 1 and self._dupe_data:
|
||||
return QVariant(self._dupe_data[row])
|
||||
elif column == 2 and self._ref_data:
|
||||
return QVariant(self._ref_data[row])
|
||||
return QVariant()
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if orientation == Qt.Horizontal and role == Qt.DisplayRole and section < len(HEADER):
|
||||
return QVariant(HEADER[section])
|
||||
return QVariant()
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self._data.COLUMNS)
|
||||
|
||||
#--- Events
|
||||
def duplicateSelected(self):
|
||||
dupe = self._app.selected_dupe
|
||||
group = self._app.results.get_group_of_duplicate(dupe)
|
||||
ref = group.ref
|
||||
self._dupe_data = self._data.GetDisplayInfo(dupe, group)
|
||||
self._ref_data = self._data.GetDisplayInfo(ref, group)
|
||||
self.reset()
|
||||
|
||||
|
||||
class DetailsTable(QTableView):
|
||||
def __init__(self, *args):
|
||||
QTableView.__init__(self, *args)
|
||||
self.setAlternatingRowColors(True)
|
||||
self.setSelectionBehavior(QTableView.SelectRows)
|
||||
self.setShowGrid(False)
|
||||
|
||||
def setModel(self, model):
|
||||
QTableView.setModel(self, model)
|
||||
# The model needs to be set to set header stuff
|
||||
hheader = self.horizontalHeader()
|
||||
hheader.setHighlightSections(False)
|
||||
hheader.setStretchLastSection(False)
|
||||
hheader.resizeSection(0, 100)
|
||||
hheader.setResizeMode(0, QHeaderView.Fixed)
|
||||
hheader.setResizeMode(1, QHeaderView.Stretch)
|
||||
hheader.setResizeMode(2, QHeaderView.Stretch)
|
||||
vheader = self.verticalHeader()
|
||||
vheader.setVisible(False)
|
||||
vheader.setDefaultSectionSize(18)
|
||||
|
||||
17
base/qt/dg.qrc
Normal file
17
base/qt/dg.qrc
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file alias="details">images/details32.png</file>
|
||||
<file alias="logo_pe">images/dgpe_logo_32.png</file>
|
||||
<file alias="logo_pe_big">images/dgpe_logo_128.png</file>
|
||||
<file alias="logo_me">images/dgme_logo_32.png</file>
|
||||
<file alias="logo_me_big">images/dgme_logo_128.png</file>
|
||||
<file alias="logo_se">images/dgse_logo_32.png</file>
|
||||
<file alias="logo_se_big">images/dgse_logo_128.png</file>
|
||||
<file alias="folder">images/folderwin32.png</file>
|
||||
<file alias="gear">images/gear.png</file>
|
||||
<file alias="preferences">images/preferences32.png</file>
|
||||
<file alias="actions">images/actions32.png</file>
|
||||
<file alias="delta">images/delta32.png</file>
|
||||
<file alias="power_marker">images/power_marker32.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
80
base/qt/directories_dialog.py
Normal file
80
base/qt/directories_dialog.py
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: directories_dialog
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-04-25
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, Qt
|
||||
from PyQt4.QtGui import QDialog, QFileDialog, QHeaderView
|
||||
|
||||
from directories_dialog_ui import Ui_DirectoriesDialog
|
||||
from directories_model import DirectoriesModel, DirectoriesDelegate
|
||||
|
||||
class DirectoriesDialog(QDialog, Ui_DirectoriesDialog):
|
||||
def __init__(self, parent, app):
|
||||
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
||||
QDialog.__init__(self, parent, flags)
|
||||
self.app = app
|
||||
self._setupUi()
|
||||
self._updateRemoveButton()
|
||||
|
||||
self.connect(self.doneButton, SIGNAL('clicked()'), self.doneButtonClicked)
|
||||
self.connect(self.addButton, SIGNAL('clicked()'), self.addButtonClicked)
|
||||
self.connect(self.removeButton, SIGNAL('clicked()'), self.removeButtonClicked)
|
||||
self.connect(self.treeView.selectionModel(), SIGNAL('selectionChanged(QItemSelection,QItemSelection)'), self.selectionChanged)
|
||||
self.connect(self.app, SIGNAL('directoriesChanged()'), self.directoriesChanged)
|
||||
|
||||
def _setupUi(self):
|
||||
self.setupUi(self)
|
||||
# Stuff that can't be done in the Designer
|
||||
self.directoriesModel = DirectoriesModel(self.app)
|
||||
self.directoriesDelegate = DirectoriesDelegate()
|
||||
self.treeView.setItemDelegate(self.directoriesDelegate)
|
||||
self.treeView.setModel(self.directoriesModel)
|
||||
|
||||
header = self.treeView.header()
|
||||
header.setStretchLastSection(False)
|
||||
header.setResizeMode(0, QHeaderView.Stretch)
|
||||
header.setResizeMode(1, QHeaderView.Fixed)
|
||||
header.resizeSection(1, 100)
|
||||
|
||||
def _updateRemoveButton(self):
|
||||
indexes = self.treeView.selectedIndexes()
|
||||
if not indexes:
|
||||
self.removeButton.setEnabled(False)
|
||||
return
|
||||
self.removeButton.setEnabled(True)
|
||||
index = indexes[0]
|
||||
node = index.internalPointer()
|
||||
# label = 'Remove' if node.parent is None else 'Exclude'
|
||||
|
||||
def addButtonClicked(self):
|
||||
title = u"Select a directory to add to the scanning list"
|
||||
flags = QFileDialog.ShowDirsOnly
|
||||
dirpath = unicode(QFileDialog.getExistingDirectory(self, title, '', flags))
|
||||
if not dirpath:
|
||||
return
|
||||
self.app.AddDirectory(dirpath)
|
||||
self.directoriesModel.reset()
|
||||
|
||||
def directoriesChanged(self):
|
||||
self.directoriesModel.reset()
|
||||
|
||||
def doneButtonClicked(self):
|
||||
self.hide()
|
||||
|
||||
def removeButtonClicked(self):
|
||||
indexes = self.treeView.selectedIndexes()
|
||||
if not indexes:
|
||||
return
|
||||
index = indexes[0]
|
||||
node = index.internalPointer()
|
||||
if node.parent is None:
|
||||
row = index.row()
|
||||
del self.app.directories[row]
|
||||
self.directoriesModel.reset()
|
||||
|
||||
def selectionChanged(self, selected, deselected):
|
||||
self._updateRemoveButton()
|
||||
|
||||
133
base/qt/directories_dialog.ui
Normal file
133
base/qt/directories_dialog.ui
Normal file
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DirectoriesDialog</class>
|
||||
<widget class="QDialog" name="DirectoriesDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>420</width>
|
||||
<height>338</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Directories</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeView">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<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="removeButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>91</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="addButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>91</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="doneButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>91</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Done</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
108
base/qt/directories_model.py
Normal file
108
base/qt/directories_model.py
Normal file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: directories_model
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-04-25
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from PyQt4.QtCore import QVariant, QModelIndex, Qt, QRect, QEvent, QPoint
|
||||
from PyQt4.QtGui import QComboBox, QStyledItemDelegate, QMouseEvent, QApplication, QBrush
|
||||
|
||||
from tree_model import TreeNode, TreeModel
|
||||
|
||||
HEADERS = ['Name', 'State']
|
||||
STATES = ['Normal', 'Reference', 'Excluded']
|
||||
|
||||
class DirectoriesDelegate(QStyledItemDelegate):
|
||||
def createEditor(self, parent, option, index):
|
||||
editor = QComboBox(parent);
|
||||
editor.addItems(STATES)
|
||||
return editor
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
value, ok = index.model().data(index, Qt.EditRole).toInt()
|
||||
assert ok
|
||||
editor.setCurrentIndex(value);
|
||||
press = QMouseEvent(QEvent.MouseButtonPress, QPoint(0, 0), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
|
||||
release = QMouseEvent(QEvent.MouseButtonRelease, QPoint(0, 0), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
|
||||
QApplication.sendEvent(editor, press)
|
||||
QApplication.sendEvent(editor, release)
|
||||
# editor.showPopup() # this causes a weird glitch. the ugly workaround is above.
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
value = QVariant(editor.currentIndex())
|
||||
model.setData(index, value, Qt.EditRole)
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
editor.setGeometry(option.rect)
|
||||
|
||||
|
||||
class DirectoryNode(TreeNode):
|
||||
def __init__(self, parent, ref, row):
|
||||
TreeNode.__init__(self, parent, row)
|
||||
self.ref = ref
|
||||
|
||||
def _get_children(self):
|
||||
children = []
|
||||
for index, directory in enumerate(self.ref.dirs):
|
||||
node = DirectoryNode(self, directory, index)
|
||||
children.append(node)
|
||||
return children
|
||||
|
||||
|
||||
class DirectoriesModel(TreeModel):
|
||||
def __init__(self, app):
|
||||
self._dirs = app.directories
|
||||
TreeModel.__init__(self)
|
||||
|
||||
def _root_nodes(self):
|
||||
nodes = []
|
||||
for index, directory in enumerate(self._dirs):
|
||||
nodes.append(DirectoryNode(None, directory, index))
|
||||
return nodes
|
||||
|
||||
def columnCount(self, parent):
|
||||
return 2
|
||||
|
||||
def data(self, index, role):
|
||||
if not index.isValid():
|
||||
return QVariant()
|
||||
node = index.internalPointer()
|
||||
if role == Qt.DisplayRole:
|
||||
if index.column() == 0:
|
||||
return QVariant(node.ref.name)
|
||||
else:
|
||||
return QVariant(STATES[self._dirs.GetState(node.ref.path)])
|
||||
elif role == Qt.EditRole and index.column() == 1:
|
||||
return QVariant(self._dirs.GetState(node.ref.path))
|
||||
elif role == Qt.ForegroundRole:
|
||||
state = self._dirs.GetState(node.ref.path)
|
||||
if state == 1:
|
||||
return QVariant(QBrush(Qt.blue))
|
||||
elif state == 2:
|
||||
return QVariant(QBrush(Qt.red))
|
||||
return QVariant()
|
||||
|
||||
def flags(self, index):
|
||||
if not index.isValid():
|
||||
return 0
|
||||
result = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
if index.column() == 1:
|
||||
result |= Qt.ItemIsEditable
|
||||
return result
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if orientation == Qt.Horizontal:
|
||||
if role == Qt.DisplayRole and section < len(HEADERS):
|
||||
return QVariant(HEADERS[section])
|
||||
return QVariant()
|
||||
|
||||
def setData(self, index, value, role):
|
||||
if not index.isValid() or role != Qt.EditRole or index.column() != 1:
|
||||
return False
|
||||
node = index.internalPointer()
|
||||
state, ok = value.toInt()
|
||||
assert ok
|
||||
self._dirs.SetState(node.ref.path, state)
|
||||
return True
|
||||
|
||||
25
base/qt/error_report_dialog.py
Normal file
25
base/qt/error_report_dialog.py
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: error_report_dialog
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-05-23
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from PyQt4.QtCore import Qt, QUrl
|
||||
from PyQt4.QtGui import QDialog, QDesktopServices
|
||||
|
||||
from error_report_dialog_ui import Ui_ErrorReportDialog
|
||||
|
||||
class ErrorReportDialog(QDialog, Ui_ErrorReportDialog):
|
||||
def __init__(self, parent, error):
|
||||
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
||||
QDialog.__init__(self, parent, flags)
|
||||
self.setupUi(self)
|
||||
self.errorTextEdit.setPlainText(error)
|
||||
|
||||
def accept(self):
|
||||
text = self.errorTextEdit.toPlainText()
|
||||
url = QUrl("mailto:support@hardcoded.net?SUBJECT=Error Report&BODY=%s" % text)
|
||||
QDesktopServices.openUrl(url)
|
||||
QDialog.accept(self)
|
||||
|
||||
117
base/qt/error_report_dialog.ui
Normal file
117
base/qt/error_report_dialog.ui
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ErrorReportDialog</class>
|
||||
<widget class="QDialog" name="ErrorReportDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>553</width>
|
||||
<height>349</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Error Report</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Something went wrong. Would you like to send the error report to Hardcoded Software?</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="errorTextEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<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="dontSendButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>110</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Don't Send</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="sendButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>110</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Send</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>sendButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ErrorReportDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>485</x>
|
||||
<y>320</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>276</x>
|
||||
<y>174</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>dontSendButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ErrorReportDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>373</x>
|
||||
<y>320</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>276</x>
|
||||
<y>174</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
20
base/qt/gen.py
Normal file
20
base/qt/gen.py
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: gen
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-05-22
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
import os
|
||||
|
||||
def print_and_do(cmd):
|
||||
print cmd
|
||||
os.system(cmd)
|
||||
|
||||
print_and_do("pyuic4 main_window.ui > main_window_ui.py")
|
||||
print_and_do("pyuic4 directories_dialog.ui > directories_dialog_ui.py")
|
||||
print_and_do("pyuic4 about_box.ui > about_box_ui.py")
|
||||
print_and_do("pyuic4 reg_submit_dialog.ui > reg_submit_dialog_ui.py")
|
||||
print_and_do("pyuic4 reg_demo_dialog.ui > reg_demo_dialog_ui.py")
|
||||
print_and_do("pyuic4 error_report_dialog.ui > error_report_dialog_ui.py")
|
||||
print_and_do("pyrcc4 dg.qrc > dg_rc.py")
|
||||
304
base/qt/main_window.py
Normal file
304
base/qt/main_window.py
Normal file
@@ -0,0 +1,304 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: main_window
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-04-25
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from PyQt4.QtCore import Qt, QCoreApplication, QProcess, SIGNAL
|
||||
from PyQt4.QtGui import (QMainWindow, QMenu, QPixmap, QIcon, QToolButton, QLabel, QHeaderView,
|
||||
QMessageBox, QInputDialog, QLineEdit)
|
||||
|
||||
from hsutil.misc import nonone
|
||||
|
||||
from dupeguru.app import NoScannableFileError, AllFilesAreRefError
|
||||
|
||||
import dg_rc
|
||||
from main_window_ui import Ui_MainWindow
|
||||
from results_model import ResultsDelegate, ResultsModel
|
||||
|
||||
class MainWindow(QMainWindow, Ui_MainWindow):
|
||||
def __init__(self, app):
|
||||
QMainWindow.__init__(self, None)
|
||||
self.app = app
|
||||
self._last_filter = None
|
||||
self._setupUi()
|
||||
self.resultsDelegate = ResultsDelegate()
|
||||
self.resultsModel = ResultsModel(self.app)
|
||||
self.resultsView.setModel(self.resultsModel)
|
||||
self.resultsView.setItemDelegate(self.resultsDelegate)
|
||||
self._load_columns()
|
||||
self._update_column_actions_status()
|
||||
self.resultsView.expandAll()
|
||||
self._update_status_line()
|
||||
|
||||
self.connect(self.app, SIGNAL('resultsChanged()'), self.resultsChanged)
|
||||
self.connect(self.app, SIGNAL('dupeMarkingChanged()'), self.dupeMarkingChanged)
|
||||
self.connect(self.actionQuit, SIGNAL('triggered()'), QCoreApplication.instance().quit)
|
||||
self.connect(self.resultsView.selectionModel(), SIGNAL('selectionChanged(QItemSelection,QItemSelection)'), self.selectionChanged)
|
||||
self.connect(self.menuColumns, SIGNAL('triggered(QAction*)'), self.columnToggled)
|
||||
self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate)
|
||||
self.connect(self.resultsModel, SIGNAL('modelReset()'), self.resultsReset)
|
||||
|
||||
def _setupUi(self):
|
||||
self.setupUi(self)
|
||||
# Stuff that can't be setup in the Designer
|
||||
h = self.resultsView.header()
|
||||
h.setHighlightSections(False)
|
||||
h.setMovable(True)
|
||||
h.setStretchLastSection(False)
|
||||
h.setDefaultAlignment(Qt.AlignLeft)
|
||||
|
||||
self.setWindowTitle(QCoreApplication.instance().applicationName())
|
||||
self.actionScan.setIcon(QIcon(QPixmap(':/%s' % self.app.LOGO_NAME)))
|
||||
|
||||
# Columns menu
|
||||
menu = self.menuColumns
|
||||
self._column_actions = []
|
||||
for index, column in enumerate(self.app.data.COLUMNS):
|
||||
action = menu.addAction(column['display'])
|
||||
action.setCheckable(True)
|
||||
action.column_index = index
|
||||
self._column_actions.append(action)
|
||||
menu.addSeparator()
|
||||
action = menu.addAction("Reset to Defaults")
|
||||
action.column_index = -1
|
||||
|
||||
# Action menu
|
||||
actionMenu = QMenu('Actions', self.toolBar)
|
||||
actionMenu.setIcon(QIcon(QPixmap(":/actions")))
|
||||
actionMenu.addAction(self.actionDeleteMarked)
|
||||
actionMenu.addAction(self.actionMoveMarked)
|
||||
actionMenu.addAction(self.actionCopyMarked)
|
||||
actionMenu.addAction(self.actionRemoveMarked)
|
||||
actionMenu.addSeparator()
|
||||
actionMenu.addAction(self.actionRemoveSelected)
|
||||
actionMenu.addAction(self.actionIgnoreSelected)
|
||||
actionMenu.addAction(self.actionMakeSelectedReference)
|
||||
actionMenu.addSeparator()
|
||||
actionMenu.addAction(self.actionOpenSelected)
|
||||
actionMenu.addAction(self.actionRevealSelected)
|
||||
actionMenu.addAction(self.actionRenameSelected)
|
||||
self.actionActions.setMenu(actionMenu)
|
||||
button = QToolButton(self.toolBar)
|
||||
button.setDefaultAction(actionMenu.menuAction())
|
||||
button.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
||||
self.actionsButton = button
|
||||
self.toolBar.insertWidget(self.actionActions, button) # the action is a placeholder
|
||||
self.toolBar.removeAction(self.actionActions)
|
||||
|
||||
self.statusLabel = QLabel(self)
|
||||
self.statusbar.addPermanentWidget(self.statusLabel, 1)
|
||||
|
||||
#--- Private
|
||||
def _confirm(self, title, msg, default_button=QMessageBox.Yes):
|
||||
buttons = QMessageBox.Yes | QMessageBox.No
|
||||
answer = QMessageBox.question(self, title, msg, buttons, default_button)
|
||||
return answer == QMessageBox.Yes
|
||||
|
||||
def _load_columns(self):
|
||||
h = self.resultsView.header()
|
||||
h.setResizeMode(QHeaderView.Interactive)
|
||||
prefs = self.app.prefs
|
||||
attrs = zip(prefs.columns_width, prefs.columns_visible)
|
||||
for index, (width, visible) in enumerate(attrs):
|
||||
h.resizeSection(index, width)
|
||||
h.setSectionHidden(index, not visible)
|
||||
h.setResizeMode(0, QHeaderView.Stretch)
|
||||
|
||||
def _redraw_results(self):
|
||||
# HACK. this is the only way I found to update the widget without reseting everything
|
||||
self.resultsView.scroll(0, 1)
|
||||
self.resultsView.scroll(0, -1)
|
||||
|
||||
def _save_columns(self):
|
||||
h = self.resultsView.header()
|
||||
widths = []
|
||||
visible = []
|
||||
for i in range(len(self.app.data.COLUMNS)):
|
||||
widths.append(h.sectionSize(i))
|
||||
visible.append(not h.isSectionHidden(i))
|
||||
prefs = self.app.prefs
|
||||
prefs.columns_width = widths
|
||||
prefs.columns_visible = visible
|
||||
prefs.save()
|
||||
|
||||
def _update_column_actions_status(self):
|
||||
h = self.resultsView.header()
|
||||
for action in self._column_actions:
|
||||
colid = action.column_index
|
||||
action.setChecked(not h.isSectionHidden(colid))
|
||||
|
||||
def _update_status_line(self):
|
||||
self.statusLabel.setText(self.app.stat_line)
|
||||
|
||||
#--- Actions
|
||||
def aboutTriggered(self):
|
||||
self.app.show_about_box()
|
||||
|
||||
def actionsTriggered(self):
|
||||
self.actionsButton.showMenu()
|
||||
|
||||
def addToIgnoreListTriggered(self):
|
||||
dupes = self.resultsView.selectedDupes()
|
||||
if not dupes:
|
||||
return
|
||||
title = "Add to Ignore List"
|
||||
msg = "All selected {0} matches are going to be ignored in all subsequent scans. Continue?".format(len(dupes))
|
||||
if self._confirm(title, msg):
|
||||
self.app.add_dupes_to_ignore_list(dupes)
|
||||
|
||||
def applyFilterTriggered(self):
|
||||
title = "Apply Filter"
|
||||
msg = "Type the filter you want to apply on your results. See help for details."
|
||||
text = nonone(self._last_filter, '[*]')
|
||||
answer, ok = QInputDialog.getText(self, title, msg, QLineEdit.Normal, text)
|
||||
if not ok:
|
||||
return
|
||||
answer = unicode(answer)
|
||||
self.app.ApplyFilter(answer)
|
||||
self._last_filter = answer
|
||||
|
||||
def cancelFilterTriggered(self):
|
||||
self.app.ApplyFilter('')
|
||||
|
||||
def checkForUpdateTriggered(self):
|
||||
QProcess.execute('updater.exe', ['/checknow'])
|
||||
|
||||
def clearIgnoreListTriggered(self):
|
||||
title = "Clear Ignore List"
|
||||
count = len(self.app.scanner.ignore_list)
|
||||
if not count:
|
||||
QMessageBox.information(self, title, "Nothing to clear.")
|
||||
return
|
||||
msg = "Do you really want to remove all {0} items from the ignore list?".format(count)
|
||||
if self._confirm(title, msg, QMessageBox.No):
|
||||
self.app.scanner.ignore_list.Clear()
|
||||
QMessageBox.information(self, title, "Ignore list cleared.")
|
||||
|
||||
def copyTriggered(self):
|
||||
self.app.copy_or_move_marked(True)
|
||||
|
||||
def deleteTriggered(self):
|
||||
count = self.app.results.mark_count
|
||||
if not count:
|
||||
return
|
||||
title = "Delete duplicates"
|
||||
msg = "You are about to send {0} files to the recycle bin. Continue?".format(count)
|
||||
if self._confirm(title, msg):
|
||||
self.app.delete_marked()
|
||||
|
||||
def deltaTriggered(self):
|
||||
self.resultsModel.delta = self.actionDelta.isChecked()
|
||||
self._redraw_results()
|
||||
|
||||
def detailsTriggered(self):
|
||||
self.app.show_details()
|
||||
|
||||
def directoriesTriggered(self):
|
||||
self.app.show_directories()
|
||||
|
||||
def makeReferenceTriggered(self):
|
||||
self.app.make_reference(self.resultsView.selectedDupes())
|
||||
|
||||
def markAllTriggered(self):
|
||||
self.app.mark_all()
|
||||
|
||||
def markInvertTriggered(self):
|
||||
self.app.mark_invert()
|
||||
|
||||
def markNoneTriggered(self):
|
||||
self.app.mark_none()
|
||||
|
||||
def markSelectedTriggered(self):
|
||||
dupes = self.resultsView.selectedDupes()
|
||||
self.app.toggle_marking_for_dupes(dupes)
|
||||
|
||||
def moveTriggered(self):
|
||||
self.app.copy_or_move_marked(False)
|
||||
|
||||
def openTriggered(self):
|
||||
self.app.open_selected()
|
||||
|
||||
def powerMarkerTriggered(self):
|
||||
self.resultsModel.power_marker = self.actionPowerMarker.isChecked()
|
||||
|
||||
def preferencesTriggered(self):
|
||||
self.app.show_preferences()
|
||||
|
||||
def registerTrigerred(self):
|
||||
self.app.ask_for_reg_code()
|
||||
|
||||
def removeMarkedTriggered(self):
|
||||
count = self.app.results.mark_count
|
||||
if not count:
|
||||
return
|
||||
title = "Remove duplicates"
|
||||
msg = "You are about to remove {0} files from results. Continue?".format(count)
|
||||
if self._confirm(title, msg):
|
||||
self.app.remove_marked_duplicates()
|
||||
|
||||
def removeSelectedTriggered(self):
|
||||
dupes = self.resultsView.selectedDupes()
|
||||
if not dupes:
|
||||
return
|
||||
title = "Remove duplicates"
|
||||
msg = "You are about to remove {0} files from results. Continue?".format(len(dupes))
|
||||
if self._confirm(title, msg):
|
||||
self.app.remove_duplicates(dupes)
|
||||
|
||||
def renameTriggered(self):
|
||||
self.resultsView.edit(self.resultsView.selectionModel().currentIndex())
|
||||
|
||||
def revealTriggered(self):
|
||||
self.app.reveal_selected()
|
||||
|
||||
def scanTriggered(self):
|
||||
title = "Start a new scan"
|
||||
if len(self.app.results.groups) > 0:
|
||||
msg = "Are you sure you want to start a new duplicate scan?"
|
||||
if not self._confirm(title, msg):
|
||||
return
|
||||
try:
|
||||
self.app.start_scanning()
|
||||
except NoScannableFileError:
|
||||
msg = "The selected directories contain no scannable file."
|
||||
QMessageBox.warning(self, title, msg)
|
||||
self.app.show_directories()
|
||||
except AllFilesAreRefError:
|
||||
msg = "You cannot make a duplicate scan with only reference directories."
|
||||
QMessageBox.warning(self, title, msg)
|
||||
|
||||
def showHelpTriggered(self):
|
||||
self.app.show_help()
|
||||
|
||||
#--- Events
|
||||
def application_will_terminate(self):
|
||||
self._save_columns()
|
||||
|
||||
def columnToggled(self, action):
|
||||
colid = action.column_index
|
||||
if colid == -1:
|
||||
self.app.prefs.reset_columns()
|
||||
self._load_columns()
|
||||
else:
|
||||
h = self.resultsView.header()
|
||||
h.setSectionHidden(colid, not h.isSectionHidden(colid))
|
||||
self._update_column_actions_status()
|
||||
|
||||
def dupeMarkingChanged(self):
|
||||
self._redraw_results()
|
||||
self._update_status_line()
|
||||
|
||||
def resultsChanged(self):
|
||||
self.resultsView.model().reset()
|
||||
|
||||
def resultsReset(self):
|
||||
self.resultsView.expandAll()
|
||||
self._update_status_line()
|
||||
|
||||
def selectionChanged(self, selected, deselected):
|
||||
index = self.resultsView.selectionModel().currentIndex()
|
||||
dupe = index.internalPointer().dupe if index.isValid() else None
|
||||
self.app.select_duplicate(dupe)
|
||||
|
||||
911
base/qt/main_window.ui
Normal file
911
base/qt/main_window.ui
Normal file
@@ -0,0 +1,911 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>630</width>
|
||||
<height>514</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>dupeGuru</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="ResultsView" name="resultsView">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="itemsExpandable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="expandsOnDoubleClick">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>630</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuColumns">
|
||||
<property name="title">
|
||||
<string>Columns</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuActions">
|
||||
<property name="title">
|
||||
<string>Actions</string>
|
||||
</property>
|
||||
<addaction name="actionDeleteMarked"/>
|
||||
<addaction name="actionMoveMarked"/>
|
||||
<addaction name="actionCopyMarked"/>
|
||||
<addaction name="actionRemoveMarked"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionRemoveSelected"/>
|
||||
<addaction name="actionIgnoreSelected"/>
|
||||
<addaction name="actionMakeSelectedReference"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionOpenSelected"/>
|
||||
<addaction name="actionRevealSelected"/>
|
||||
<addaction name="actionRenameSelected"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionApplyFilter"/>
|
||||
<addaction name="actionCancelFilter"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuMark">
|
||||
<property name="title">
|
||||
<string>Mark</string>
|
||||
</property>
|
||||
<addaction name="actionMarkAll"/>
|
||||
<addaction name="actionMarkNone"/>
|
||||
<addaction name="actionInvertMarking"/>
|
||||
<addaction name="actionMarkSelected"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuModes">
|
||||
<property name="title">
|
||||
<string>Modes</string>
|
||||
</property>
|
||||
<addaction name="actionPowerMarker"/>
|
||||
<addaction name="actionDelta"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuWindow">
|
||||
<property name="title">
|
||||
<string>Windows</string>
|
||||
</property>
|
||||
<addaction name="actionDetails"/>
|
||||
<addaction name="actionDirectories"/>
|
||||
<addaction name="actionPreferences"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuHelp">
|
||||
<property name="title">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
<addaction name="actionShowHelp"/>
|
||||
<addaction name="actionRegister"/>
|
||||
<addaction name="actionCheckForUpdate"/>
|
||||
<addaction name="actionAbout"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="actionScan"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionClearIgnoreList"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionQuit"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuMark"/>
|
||||
<addaction name="menuActions"/>
|
||||
<addaction name="menuColumns"/>
|
||||
<addaction name="menuModes"/>
|
||||
<addaction name="menuWindow"/>
|
||||
<addaction name="menuHelp"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
<property name="windowTitle">
|
||||
<string>toolBar</string>
|
||||
</property>
|
||||
<property name="movable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="floatable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="actionScan"/>
|
||||
<addaction name="actionActions"/>
|
||||
<addaction name="actionDirectories"/>
|
||||
<addaction name="actionDetails"/>
|
||||
<addaction name="actionPreferences"/>
|
||||
<addaction name="actionDelta"/>
|
||||
<addaction name="actionPowerMarker"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar">
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<action name="actionScan">
|
||||
<property name="icon">
|
||||
<iconset resource="dg.qrc">
|
||||
<normaloff>:/logo_pe</normaloff>:/logo_pe</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start Scan</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Start scanning for duplicates</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDirectories">
|
||||
<property name="icon">
|
||||
<iconset resource="dg.qrc">
|
||||
<normaloff>:/folder</normaloff>:/folder</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Directories</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+4</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDetails">
|
||||
<property name="icon">
|
||||
<iconset resource="dg.qrc">
|
||||
<normaloff>:/details</normaloff>:/details</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Details</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+3</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionActions">
|
||||
<property name="icon">
|
||||
<iconset resource="dg.qrc">
|
||||
<normaloff>:/actions</normaloff>:/actions</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Actions</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPreferences">
|
||||
<property name="icon">
|
||||
<iconset resource="dg.qrc">
|
||||
<normaloff>:/preferences</normaloff>:/preferences</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Preferences</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+5</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelta">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="dg.qrc">
|
||||
<normaloff>:/delta</normaloff>:/delta</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delta Values</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+2</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPowerMarker">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="dg.qrc">
|
||||
<normaloff>:/power_marker</normaloff>:/power_marker</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Power Marker</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+1</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDeleteMarked">
|
||||
<property name="text">
|
||||
<string>Send Marked to Recycle Bin</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+D</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMoveMarked">
|
||||
<property name="text">
|
||||
<string>Move Marked to...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+M</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCopyMarked">
|
||||
<property name="text">
|
||||
<string>Copy Marked to...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+M</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRemoveMarked">
|
||||
<property name="text">
|
||||
<string>Remove Marked from Results</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+R</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRemoveSelected">
|
||||
<property name="text">
|
||||
<string>Remove Selected from Results</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Del</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionIgnoreSelected">
|
||||
<property name="text">
|
||||
<string>Add Selected to Ignore List</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+Del</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMakeSelectedReference">
|
||||
<property name="text">
|
||||
<string>Make Selected Reference</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Space</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionOpenSelected">
|
||||
<property name="text">
|
||||
<string>Open Selected with Default Application</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRevealSelected">
|
||||
<property name="text">
|
||||
<string>Open Containing Folder of Selected</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRenameSelected">
|
||||
<property name="text">
|
||||
<string>Rename Selected</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F2</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMarkAll">
|
||||
<property name="text">
|
||||
<string>Mark All</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+A</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMarkNone">
|
||||
<property name="text">
|
||||
<string>Mark None</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+A</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionInvertMarking">
|
||||
<property name="text">
|
||||
<string>Invert Marking</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Alt+A</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMarkSelected">
|
||||
<property name="text">
|
||||
<string>Mark Selected</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClearIgnoreList">
|
||||
<property name="text">
|
||||
<string>Clear Ignore List</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionQuit">
|
||||
<property name="text">
|
||||
<string>Quit</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionApplyFilter">
|
||||
<property name="text">
|
||||
<string>Apply Filter</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+F</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCancelFilter">
|
||||
<property name="text">
|
||||
<string>Cancel Filter</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+F</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionShowHelp">
|
||||
<property name="text">
|
||||
<string>dupeGuru Help</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F1</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="text">
|
||||
<string>About dupeGuru</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRegister">
|
||||
<property name="text">
|
||||
<string>Register dupeGuru</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCheckForUpdate">
|
||||
<property name="text">
|
||||
<string>Check for Update</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ResultsView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>results_model</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="dg.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>actionDirectories</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>directoriesTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionActions</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>actionsTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionCopyMarked</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>copyTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionDeleteMarked</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>deleteTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionDelta</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>deltaTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionDetails</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>detailsTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionIgnoreSelected</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>addToIgnoreListTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionMakeSelectedReference</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>makeReferenceTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionMoveMarked</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>moveTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionOpenSelected</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>openTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionPowerMarker</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>powerMarkerTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionPreferences</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>preferencesTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionRemoveMarked</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>removeMarkedTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionRemoveSelected</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>removeSelectedTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionRevealSelected</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>revealTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionRenameSelected</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>renameTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionScan</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>scanTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionClearIgnoreList</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>clearIgnoreListTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionMarkAll</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>markAllTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionMarkNone</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>markNoneTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionMarkSelected</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>markSelectedTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionInvertMarking</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>markInvertTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionApplyFilter</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>applyFilterTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionCancelFilter</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>cancelFilterTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionShowHelp</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>showHelpTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionAbout</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>aboutTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionRegister</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>registerTrigerred()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>actionCheckForUpdate</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>checkForUpdateTriggered()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>314</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>directoriesTriggered()</slot>
|
||||
<slot>scanTriggered()</slot>
|
||||
<slot>actionsTriggered()</slot>
|
||||
<slot>detailsTriggered()</slot>
|
||||
<slot>preferencesTriggered()</slot>
|
||||
<slot>deltaTriggered()</slot>
|
||||
<slot>powerMarkerTriggered()</slot>
|
||||
<slot>deleteTriggered()</slot>
|
||||
<slot>moveTriggered()</slot>
|
||||
<slot>copyTriggered()</slot>
|
||||
<slot>removeMarkedTriggered()</slot>
|
||||
<slot>removeSelectedTriggered()</slot>
|
||||
<slot>addToIgnoreListTriggered()</slot>
|
||||
<slot>makeReferenceTriggered()</slot>
|
||||
<slot>openTriggered()</slot>
|
||||
<slot>revealTriggered()</slot>
|
||||
<slot>renameTriggered()</slot>
|
||||
<slot>clearIgnoreListTriggered()</slot>
|
||||
<slot>clearPictureCacheTriggered()</slot>
|
||||
<slot>markAllTriggered()</slot>
|
||||
<slot>markNoneTriggered()</slot>
|
||||
<slot>markInvertTriggered()</slot>
|
||||
<slot>markSelectedTriggered()</slot>
|
||||
<slot>applyFilterTriggered()</slot>
|
||||
<slot>cancelFilterTriggered()</slot>
|
||||
<slot>showHelpTriggered()</slot>
|
||||
<slot>aboutTriggered()</slot>
|
||||
<slot>registerTrigerred()</slot>
|
||||
<slot>checkForUpdateTriggered()</slot>
|
||||
</slots>
|
||||
</ui>
|
||||
109
base/qt/preferences.py
Normal file
109
base/qt/preferences.py
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: preferences
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-05-03
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from PyQt4.QtCore import QSettings, QVariant
|
||||
|
||||
from hsutil.misc import tryint
|
||||
|
||||
def variant_to_py(v):
|
||||
value = None
|
||||
ok = False
|
||||
t = v.type()
|
||||
if t == QVariant.String:
|
||||
value = unicode(v.toString())
|
||||
ok = True # anyway
|
||||
# might be bool or int, try them
|
||||
if v == 'true':
|
||||
value = True
|
||||
elif value == 'false':
|
||||
value = False
|
||||
else:
|
||||
value = tryint(value, value)
|
||||
elif t == QVariant.Int:
|
||||
value, ok = v.toInt()
|
||||
elif t == QVariant.Bool:
|
||||
value, ok = v.toBool(), True
|
||||
elif t in (QVariant.List, QVariant.StringList):
|
||||
value, ok = map(variant_to_py, v.toList()), True
|
||||
if not ok:
|
||||
raise TypeError()
|
||||
return value
|
||||
|
||||
class Preferences(object):
|
||||
# (width, is_visible)
|
||||
COLUMNS_DEFAULT_ATTRS = []
|
||||
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
self.reset_columns()
|
||||
|
||||
def _load_specific(self, settings, get):
|
||||
# load prefs specific to the dg edition
|
||||
pass
|
||||
|
||||
def load(self):
|
||||
self.reset()
|
||||
settings = QSettings()
|
||||
def get(name, default):
|
||||
if settings.contains(name):
|
||||
return variant_to_py(settings.value(name))
|
||||
else:
|
||||
return default
|
||||
|
||||
self.filter_hardness = get('FilterHardness', self.filter_hardness)
|
||||
self.mix_file_kind = get('MixFileKind', self.mix_file_kind)
|
||||
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)
|
||||
widths = get('ColumnsWidth', self.columns_width)
|
||||
# only set nonzero values
|
||||
for index, width in enumerate(widths[:len(self.columns_width)]):
|
||||
if width > 0:
|
||||
self.columns_width[index] = width
|
||||
self.columns_visible = get('ColumnsVisible', self.columns_visible)
|
||||
self.registration_code = get('RegistrationCode', self.registration_code)
|
||||
self.registration_email = get('RegistrationEmail', self.registration_email)
|
||||
self._load_specific(settings, get)
|
||||
|
||||
def _reset_specific(self):
|
||||
# reset prefs specific to the dg edition
|
||||
pass
|
||||
|
||||
def reset(self):
|
||||
self.filter_hardness = 95
|
||||
self.mix_file_kind = True
|
||||
self.use_regexp = False
|
||||
self.remove_empty_folders = False
|
||||
self.destination_type = 1
|
||||
self.registration_code = ''
|
||||
self.registration_email = ''
|
||||
self._reset_specific()
|
||||
|
||||
def reset_columns(self):
|
||||
self.columns_width = [width for width, _ in self.COLUMNS_DEFAULT_ATTRS]
|
||||
self.columns_visible = [visible for _, visible in self.COLUMNS_DEFAULT_ATTRS]
|
||||
|
||||
def _save_specific(self, settings, set_):
|
||||
# save prefs specific to the dg edition
|
||||
pass
|
||||
|
||||
def save(self):
|
||||
settings = QSettings()
|
||||
def set_(name, value):
|
||||
settings.setValue(name, QVariant(value))
|
||||
|
||||
set_('FilterHardness', self.filter_hardness)
|
||||
set_('MixFileKind', self.mix_file_kind)
|
||||
set_('UseRegexp', self.use_regexp)
|
||||
set_('RemoveEmptyFolders', self.remove_empty_folders)
|
||||
set_('DestinationType', self.destination_type)
|
||||
set_('ColumnsWidth', self.columns_width)
|
||||
set_('ColumnsVisible', self.columns_visible)
|
||||
set_('RegistrationCode', self.registration_code)
|
||||
set_('RegistrationEmail', self.registration_email)
|
||||
self._save_specific(settings, set_)
|
||||
|
||||
34
base/qt/reg.py
Normal file
34
base/qt/reg.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: reg
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-05-09
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from hashlib import md5
|
||||
|
||||
from PyQt4.QtGui import QDialog
|
||||
|
||||
from reg_submit_dialog import RegSubmitDialog
|
||||
from reg_demo_dialog import RegDemoDialog
|
||||
|
||||
class Registration(object):
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def ask_for_code(self):
|
||||
dialog = RegSubmitDialog(self.app.main_window, self.app.is_code_valid)
|
||||
result = dialog.exec_()
|
||||
code = unicode(dialog.codeEdit.text())
|
||||
email = unicode(dialog.emailEdit.text())
|
||||
dialog.setParent(None) # free it
|
||||
if result == QDialog.Accepted and self.app.is_code_valid(code, email):
|
||||
self.app.set_registration(code, email)
|
||||
return True
|
||||
return False
|
||||
|
||||
def show_nag(self):
|
||||
dialog = RegDemoDialog(self.app.main_window, self)
|
||||
dialog.exec_()
|
||||
dialog.setParent(None) # free it
|
||||
|
||||
45
base/qt/reg_demo_dialog.py
Normal file
45
base/qt/reg_demo_dialog.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: reg_demo_dialog
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-05-10
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, Qt, QUrl, QCoreApplication
|
||||
from PyQt4.QtGui import QDialog, QMessageBox, QDesktopServices
|
||||
|
||||
from reg_demo_dialog_ui import Ui_RegDemoDialog
|
||||
|
||||
class RegDemoDialog(QDialog, Ui_RegDemoDialog):
|
||||
def __init__(self, parent, reg):
|
||||
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
||||
QDialog.__init__(self, parent, flags)
|
||||
self.reg = reg
|
||||
self._setupUi()
|
||||
|
||||
self.connect(self.enterCodeButton, SIGNAL('clicked()'), self.enterCodeClicked)
|
||||
self.connect(self.purchaseButton, SIGNAL('clicked()'), self.purchaseClicked)
|
||||
|
||||
def _setupUi(self):
|
||||
self.setupUi(self)
|
||||
# Stuff that can't be setup in the Designer
|
||||
appname = QCoreApplication.instance().applicationName()
|
||||
title = self.windowTitle()
|
||||
title = title.replace('$appname', appname)
|
||||
self.setWindowTitle(title)
|
||||
title = self.titleLabel.text()
|
||||
title = title.replace('$appname', appname)
|
||||
self.titleLabel.setText(title)
|
||||
desc = self.demoDescLabel.text()
|
||||
desc = desc.replace('$appname', appname)
|
||||
self.demoDescLabel.setText(desc)
|
||||
|
||||
#--- Events
|
||||
def enterCodeClicked(self):
|
||||
if self.reg.ask_for_code():
|
||||
self.accept()
|
||||
|
||||
def purchaseClicked(self):
|
||||
url = QUrl('http://www.hardcoded.net/purchase.htm')
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
140
base/qt/reg_demo_dialog.ui
Normal file
140
base/qt/reg_demo_dialog.ui
Normal file
@@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RegDemoDialog</class>
|
||||
<widget class="QDialog" name="RegDemoDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>387</width>
|
||||
<height>161</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>$appname Demo Version</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="titleLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>$appname Demo Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="demoDescLabel">
|
||||
<property name="text">
|
||||
<string>You are currently running a demo version of $appname. This version has limited functionalities, and you need to buy it to have access to these functionalities.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>In the demo version, only 10 duplicates per session can be sent to the recycle bin, moved or copied.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<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="tryButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>110</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Try Demo</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="enterCodeButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>110</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enter Code</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="purchaseButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>110</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Purchase</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>tryButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>RegDemoDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>112</x>
|
||||
<y>161</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>201</x>
|
||||
<y>94</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
47
base/qt/reg_submit_dialog.py
Normal file
47
base/qt/reg_submit_dialog.py
Normal file
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: reg_submit_dialog
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-05-09
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, Qt, QUrl, QCoreApplication
|
||||
from PyQt4.QtGui import QDialog, QMessageBox, QDesktopServices
|
||||
|
||||
from reg_submit_dialog_ui import Ui_RegSubmitDialog
|
||||
|
||||
class RegSubmitDialog(QDialog, Ui_RegSubmitDialog):
|
||||
def __init__(self, parent, is_valid_func):
|
||||
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
||||
QDialog.__init__(self, parent, flags)
|
||||
self._setupUi()
|
||||
self.is_valid_func = is_valid_func
|
||||
|
||||
self.connect(self.submitButton, SIGNAL('clicked()'), self.submitClicked)
|
||||
self.connect(self.purchaseButton, SIGNAL('clicked()'), self.purchaseClicked)
|
||||
|
||||
def _setupUi(self):
|
||||
self.setupUi(self)
|
||||
# Stuff that can't be setup in the Designer
|
||||
appname = QCoreApplication.instance().applicationName()
|
||||
prompt = self.promptLabel.text()
|
||||
prompt = prompt.replace('$appname', appname)
|
||||
self.promptLabel.setText(prompt)
|
||||
|
||||
#--- Events
|
||||
def purchaseClicked(self):
|
||||
url = QUrl('http://www.hardcoded.net/purchase.htm')
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
def submitClicked(self):
|
||||
code = unicode(self.codeEdit.text())
|
||||
email = unicode(self.emailEdit.text())
|
||||
title = "Registration"
|
||||
if self.is_valid_func(code, email):
|
||||
msg = "This code is valid. Thanks!"
|
||||
QMessageBox.information(self, title, msg)
|
||||
self.accept()
|
||||
else:
|
||||
msg = "This code is invalid"
|
||||
QMessageBox.warning(self, title, msg)
|
||||
|
||||
149
base/qt/reg_submit_dialog.ui
Normal file
149
base/qt/reg_submit_dialog.ui
Normal file
@@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RegSubmitDialog</class>
|
||||
<widget class="QDialog" name="RegSubmitDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>365</width>
|
||||
<height>134</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Enter your registration code</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="promptLabel">
|
||||
<property name="text">
|
||||
<string>Please enter your $appname registration code and registered e-mail (the e-mail you used for the purchase), then press "Submit".</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetNoConstraint</enum>
|
||||
</property>
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||
</property>
|
||||
<property name="labelAlignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="formAlignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Registration code:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Registered e-mail:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="codeEdit"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="emailEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="purchaseButton">
|
||||
<property name="text">
|
||||
<string>Purchase</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</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="cancelButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="submitButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Submit</string>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>cancelButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>RegSubmitDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>260</x>
|
||||
<y>159</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>198</x>
|
||||
<y>97</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
175
base/qt/results_model.py
Normal file
175
base/qt/results_model.py
Normal file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name:
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-04-23
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, Qt, QAbstractItemModel, QVariant, QModelIndex, QRect
|
||||
from PyQt4.QtGui import QBrush, QStyledItemDelegate, QFont, QTreeView, QColor
|
||||
|
||||
from tree_model import TreeNode, TreeModel
|
||||
|
||||
class ResultNode(TreeNode):
|
||||
def __init__(self, model, parent, row, dupe, group):
|
||||
TreeNode.__init__(self, parent, row)
|
||||
self.model = model
|
||||
self.dupe = dupe
|
||||
self.group = group
|
||||
self._normalData = None
|
||||
self._deltaData = None
|
||||
|
||||
def _get_children(self):
|
||||
children = []
|
||||
if self.dupe is self.group.ref:
|
||||
for index, dupe in enumerate(self.group.dupes):
|
||||
children.append(ResultNode(self.model, self, index, dupe, self.group))
|
||||
return children
|
||||
|
||||
def reset(self):
|
||||
self._normalData = None
|
||||
self._deltaData = None
|
||||
|
||||
@property
|
||||
def normalData(self):
|
||||
if self._normalData is None:
|
||||
self._normalData = self.model._data.GetDisplayInfo(self.dupe, self.group, delta=False)
|
||||
return self._normalData
|
||||
|
||||
@property
|
||||
def deltaData(self):
|
||||
if self._deltaData is None:
|
||||
self._deltaData = self.model._data.GetDisplayInfo(self.dupe, self.group, delta=True)
|
||||
return self._deltaData
|
||||
|
||||
|
||||
class ResultsDelegate(QStyledItemDelegate):
|
||||
def initStyleOption(self, option, index):
|
||||
QStyledItemDelegate.initStyleOption(self, option, index)
|
||||
node = index.internalPointer()
|
||||
if node.group.ref is node.dupe:
|
||||
newfont = QFont(option.font)
|
||||
newfont.setBold(True)
|
||||
option.font = newfont
|
||||
|
||||
|
||||
class ResultsModel(TreeModel):
|
||||
def __init__(self, app):
|
||||
self._app = app
|
||||
self._results = app.results
|
||||
self._data = app.data
|
||||
self._delta_columns = app.DELTA_COLUMNS
|
||||
self.delta = False
|
||||
self._power_marker = False
|
||||
TreeModel.__init__(self)
|
||||
|
||||
def _root_nodes(self):
|
||||
nodes = []
|
||||
if self.power_marker:
|
||||
for index, dupe in enumerate(self._results.dupes):
|
||||
group = self._results.get_group_of_duplicate(dupe)
|
||||
nodes.append(ResultNode(self, None, index, dupe, group))
|
||||
else:
|
||||
for index, group in enumerate(self._results.groups):
|
||||
nodes.append(ResultNode(self, None, index, group.ref, group))
|
||||
return nodes
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(self._data.COLUMNS)
|
||||
|
||||
def data(self, index, role):
|
||||
if not index.isValid():
|
||||
return QVariant()
|
||||
node = index.internalPointer()
|
||||
if role == Qt.DisplayRole:
|
||||
data = node.deltaData if self.delta else node.normalData
|
||||
return QVariant(data[index.column()])
|
||||
elif role == Qt.CheckStateRole:
|
||||
if index.column() == 0 and node.dupe is not node.group.ref:
|
||||
state = Qt.Checked if self._results.is_marked(node.dupe) else Qt.Unchecked
|
||||
return QVariant(state)
|
||||
elif role == Qt.ForegroundRole:
|
||||
if node.dupe is node.group.ref or node.dupe.is_ref:
|
||||
return QVariant(QBrush(Qt.blue))
|
||||
elif self.delta and index.column() in self._delta_columns:
|
||||
return QVariant(QBrush(QColor(255, 142, 40))) # orange
|
||||
elif role == Qt.EditRole:
|
||||
if index.column() == 0:
|
||||
return QVariant(node.normalData[index.column()])
|
||||
return QVariant()
|
||||
|
||||
def dupesForIndexes(self, indexes):
|
||||
nodes = [index.internalPointer() for index in indexes]
|
||||
return [node.dupe for node in nodes]
|
||||
|
||||
def flags(self, index):
|
||||
if not index.isValid():
|
||||
return Qt.ItemIsEnabled
|
||||
flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
||||
if index.column() == 0:
|
||||
flags |= Qt.ItemIsUserCheckable | Qt.ItemIsEditable
|
||||
return flags
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if orientation == Qt.Horizontal and role == Qt.DisplayRole and section < len(self._data.COLUMNS):
|
||||
return QVariant(self._data.COLUMNS[section]['display'])
|
||||
|
||||
return QVariant()
|
||||
|
||||
def setData(self, index, value, role):
|
||||
if not index.isValid():
|
||||
return False
|
||||
node = index.internalPointer()
|
||||
if role == Qt.CheckStateRole:
|
||||
if index.column() == 0:
|
||||
self._app.toggle_marking_for_dupes([node.dupe])
|
||||
return True
|
||||
if role == Qt.EditRole:
|
||||
if index.column() == 0:
|
||||
value = unicode(value.toString())
|
||||
if self._app.rename_dupe(node.dupe, value):
|
||||
node.reset()
|
||||
return True
|
||||
return False
|
||||
|
||||
def sort(self, column, order):
|
||||
if self.power_marker:
|
||||
self._results.sort_dupes(column, order == Qt.AscendingOrder, self.delta)
|
||||
else:
|
||||
self._results.sort_groups(column, order == Qt.AscendingOrder)
|
||||
self.reset()
|
||||
|
||||
def toggleMarked(self, indexes):
|
||||
assert indexes
|
||||
dupes = self.dupesForIndexes(indexes)
|
||||
self._app.toggle_marking_for_dupes(dupes)
|
||||
|
||||
#--- Properties
|
||||
@property
|
||||
def power_marker(self):
|
||||
return self._power_marker
|
||||
|
||||
@power_marker.setter
|
||||
def power_marker(self, value):
|
||||
if value == self._power_marker:
|
||||
return
|
||||
self._power_marker = value
|
||||
self.reset()
|
||||
|
||||
|
||||
class ResultsView(QTreeView):
|
||||
#--- Override
|
||||
def keyPressEvent(self, event):
|
||||
if event.text() == ' ':
|
||||
self.model().toggleMarked(self.selectionModel().selectedRows())
|
||||
return
|
||||
QTreeView.keyPressEvent(self, event)
|
||||
|
||||
def setModel(self, model):
|
||||
assert isinstance(model, ResultsModel)
|
||||
QTreeView.setModel(self, model)
|
||||
|
||||
#--- Public
|
||||
def selectedDupes(self):
|
||||
return self.model().dupesForIndexes(self.selectionModel().selectedRows())
|
||||
|
||||
66
base/qt/tree_model.py
Normal file
66
base/qt/tree_model.py
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python
|
||||
# Unit Name: tree_model
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-05-04
|
||||
# $Id$
|
||||
# Copyright 2009 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
from PyQt4.QtCore import Qt, QAbstractItemModel, QVariant, QModelIndex
|
||||
|
||||
class TreeNode(object):
|
||||
def __init__(self, parent, row):
|
||||
self.parent = parent
|
||||
self.row = row
|
||||
self._children = None
|
||||
|
||||
def _get_children(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
if self._children is None:
|
||||
self._children = self._get_children()
|
||||
return self._children
|
||||
|
||||
|
||||
class TreeModel(QAbstractItemModel):
|
||||
def __init__(self):
|
||||
QAbstractItemModel.__init__(self)
|
||||
self._nodes = None
|
||||
|
||||
def _root_nodes(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def index(self, row, column, parent):
|
||||
if not self.nodes:
|
||||
return QModelIndex()
|
||||
if not parent.isValid():
|
||||
return self.createIndex(row, column, self.nodes[row])
|
||||
node = parent.internalPointer()
|
||||
return self.createIndex(row, column, node.children[row])
|
||||
|
||||
def parent(self, index):
|
||||
if not index.isValid():
|
||||
return QModelIndex()
|
||||
node = index.internalPointer()
|
||||
if node.parent is None:
|
||||
return QModelIndex()
|
||||
else:
|
||||
return self.createIndex(node.parent.row, 0, node.parent)
|
||||
|
||||
def reset(self):
|
||||
self._nodes = None
|
||||
QAbstractItemModel.reset(self)
|
||||
|
||||
def rowCount(self, parent):
|
||||
if not parent.isValid():
|
||||
return len(self.nodes)
|
||||
node = parent.internalPointer()
|
||||
return len(node.children)
|
||||
|
||||
@property
|
||||
def nodes(self):
|
||||
if self._nodes is None:
|
||||
self._nodes = self._root_nodes()
|
||||
return self._nodes
|
||||
|
||||
Reference in New Issue
Block a user