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

Compare commits

...

26 Commits

Author SHA1 Message Date
Virgil Dupras
86ecc8d4d5 Fixed build script 2010-02-10 00:18:25 -08:00
Virgil Dupras
9eca84efe1 se v2.9.2 2010-02-10 08:48:01 +01:00
Virgil Dupras
8a6fb6dcba Updated Andvanced Installer project file for 7.5. 2010-02-09 15:03:36 +00:00
Virgil Dupras
e3706fa923 Fixed qt packaging. 2010-02-09 14:52:09 +00:00
Virgil Dupras
8193bc5f60 build.add_to_pythonpath() now also adds the path to sys.path. 2010-02-09 15:47:22 +01:00
Virgil Dupras
504ecaee5e Straightened out qt's packaging process. 2010-02-09 15:42:48 +01:00
Virgil Dupras
7c9e836572 Straightened out qt's build process. 2010-02-09 15:32:52 +01:00
Virgil Dupras
5db0f09b43 Fixed Reveal File on Qt. 2010-02-09 15:24:57 +01:00
Virgil Dupras
195bc4ef21 Eliminated code duplication in ResultsWindow. 2010-02-09 14:59:35 +01:00
Virgil Dupras
6b190bc184 Fixed a bug where double clicking a column would open the selected file. 2010-02-09 14:55:51 +01:00
Virgil Dupras
39f1cac2c8 Eliminated code duplication in ResultsWindow's awakeFromNib. 2010-02-09 14:50:27 +01:00
Virgil Dupras
d193eed519 [#93 state:fixed] Straightened out selection and matches reloading. 2010-02-09 14:45:14 +01:00
Virgil Dupras
2d80b0e12f Updated hsutil subrepo. 2010-02-08 08:37:40 +01:00
Virgil Dupras
b50d99be9c Added the PyRegistrable cocoa interface. 2010-02-07 16:29:39 +01:00
Virgil Dupras
af41876a5e DetailsPanel is now a subclass of HGWindowController. 2010-02-07 16:19:14 +01:00
Virgil Dupras
76d351d8be Adapted th qt part to core.gui.directory_tree. 2010-02-07 16:00:58 +01:00
Virgil Dupras
b5dd9651c3 Huge refactoring. I moved MGOutline from moneyGuru (as well as everything that comes with it) and used it to create DirectoryOutline for the directories panel. 2010-02-07 15:26:50 +01:00
Virgil Dupras
3e34502014 Added the hsgui subrepo. 2010-02-06 15:35:51 +01:00
Virgil Dupras
5e57f9cbd6 Removed logic duplication across toolkit code in "Reveal Selected" action. 2010-02-06 15:31:35 +01:00
Virgil Dupras
8edb869fdc Removed logic duplication across toolkit code in "Remove Selected" action. 2010-02-06 12:44:21 +01:00
Virgil Dupras
37238c7f57 Removed logic duplication across toolkit code in "Open Selected" action. 2010-02-06 12:36:43 +01:00
Virgil Dupras
9edee82fa1 Removed logic duplication across toolkit code in "Make Reference" action. 2010-02-06 12:27:11 +01:00
Virgil Dupras
f7aaea79af Removed useless add_to_ignore_list() 2010-02-06 12:14:33 +01:00
Virgil Dupras
3c75d2f8b7 Removed logic duplication across toolkit code in "Add to Ignore List" action. 2010-02-06 12:12:20 +01:00
Virgil Dupras
64c67e19d2 Reduced code duplication among editions in ResultsWindow. 2010-02-06 11:40:10 +01:00
Virgil Dupras
d4db8faad8 Added tag pe1.8.2 for changeset 19e40bab2052 2010-02-06 10:49:13 +01:00
47 changed files with 1127 additions and 1551 deletions

View File

@@ -8,3 +8,4 @@ cbcf9c80fee4c908ef2efbf1c143c9e47676c9b2 pe1.8.0
61c4101851bdea3cb37dfb76f0d404c78c7c594c se2.9.1
0e923897a3389331d4ab3debbc40b8dd616199d9 pe1.8.1
2c454eca9ebe93b6cf34916068f828a6a39e3eaf me5.7.1
19e40bab20521d4256acf325dba9b32e95e135c5 pe1.8.2

View File

@@ -18,30 +18,7 @@ import yaml
from hsdocgen import generate_help, filters
from hsutil.build import add_to_pythonpath, print_and_do, build_all_qt_ui, copy_packages
def main():
conf = yaml.load(open('conf.yaml'))
edition = conf['edition']
ui = conf['ui']
dev = conf['dev']
print "Building dupeGuru {0} with UI {1}".format(edition.upper(), ui)
if dev:
print "Building in Dev mode"
add_to_pythonpath('.')
print "Generating Help"
windows = sys.platform == 'win32'
tix = filters.tixgen("https://hardcoded.lighthouseapp.com/projects/31699-dupeguru/tickets/{0}")
help_dir = 'help_{0}'.format(edition)
dest_dir = 'dupeguru_{0}_help'.format(edition) if edition != 'se' else 'dupeguru_help'
help_basepath = op.abspath(help_dir)
help_destpath = op.abspath(op.join(help_dir, dest_dir))
generate_help.main(help_basepath, help_destpath, force_render=not dev, tix=tix, windows=windows)
print "Building dupeGuru"
if edition == 'pe':
os.chdir('core_pe')
os.system('python gen.py')
os.chdir('..')
if ui == 'cocoa':
def build_cocoa(edition, dev, help_destpath):
if not dev:
print "Building help index"
os.system('open -a /Developer/Applications/Utilities/Help\\ Indexer.app {0}'.format(help_destpath))
@@ -86,10 +63,44 @@ def main():
args = ' '.join(args)
os.system('xcodebuild {0}'.format(args))
os.chdir('..')
elif ui == 'qt':
def build_qt(edition, dev):
build_all_qt_ui(op.join('qtlib', 'ui'))
build_all_qt_ui(op.join('qt', 'base'))
build_all_qt_ui(op.join('qt', edition))
print_and_do("pyrcc4 {0} > {1}".format(op.join('qt', 'base', 'dg.qrc'), op.join('qt', 'base', 'dg_rc.py')))
if edition == 'pe':
os.chdir(op.join('qt', edition))
os.system('python gen.py')
os.chdir(op.join('..', '..'))
def main():
conf = yaml.load(open('conf.yaml'))
edition = conf['edition']
ui = conf['ui']
dev = conf['dev']
print "Building dupeGuru {0} with UI {1}".format(edition.upper(), ui)
if dev:
print "Building in Dev mode"
add_to_pythonpath('.')
print "Generating Help"
windows = sys.platform == 'win32'
tix = filters.tixgen("https://hardcoded.lighthouseapp.com/projects/31699-dupeguru/tickets/{0}")
help_dir = 'help_{0}'.format(edition)
dest_dir = 'dupeguru_{0}_help'.format(edition) if edition != 'se' else 'dupeguru_help'
help_basepath = op.abspath(help_dir)
help_destpath = op.abspath(op.join(help_dir, dest_dir))
generate_help.main(help_basepath, help_destpath, force_render=not dev, tix=tix, windows=windows)
print "Building dupeGuru"
if edition == 'pe':
os.chdir('core_pe')
os.system('python gen.py')
os.chdir('..')
if ui == 'cocoa':
build_cocoa(edition, dev, help_destpath)
elif ui == 'qt':
build_qt(edition, dev)
if __name__ == '__main__':
main()

View File

@@ -22,5 +22,3 @@ http://www.hardcoded.net/licenses/hs_license
#define jobCopy @"job_copy"
#define jobMove @"job_move"
#define jobDelete @"job_delete"
#define DEMO_MAX_ACTION_COUNT 10

View File

@@ -7,16 +7,16 @@ http://www.hardcoded.net/licenses/hs_license
*/
#import <Cocoa/Cocoa.h>
#import "HSWindowController.h"
#import "PyApp.h"
#import "PyDetailsPanel.h"
@interface DetailsPanel : NSWindowController
@interface DetailsPanel : HSWindowController
{
IBOutlet NSTableView *detailsTable;
PyDetailsPanel *py;
}
- (id)initWithPy:(PyApp *)aPy;
- (PyDetailsPanel *)py;
- (void)toggleVisibility;

View File

@@ -12,17 +12,14 @@ http://www.hardcoded.net/licenses/hs_license
@implementation DetailsPanel
- (id)initWithPy:(PyApp *)aPy
{
self = [super initWithWindowNibName:@"DetailsPanel"];
self = [super initWithNibName:@"DetailsPanel" pyClassName:@"PyDetailsPanel" pyParent:aPy];
[self window]; //So the detailsTable is initialized.
Class pyClass = [Utils classNamed:@"PyDetailsPanel"];
py = [[pyClass alloc] initWithCocoa:self pyParent:aPy];
return self;
}
- (void)dealloc
- (PyDetailsPanel *)py
{
[py release];
[super dealloc];
return (PyDetailsPanel *)py;
}
- (void)refreshDetails
@@ -44,12 +41,12 @@ http://www.hardcoded.net/licenses/hs_license
/* NSTableView Delegate */
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [py numberOfRows];
return [[self py] numberOfRows];
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)row
{
return [py valueForColumn:[column identifier] row:row];
return [[self py] valueForColumn:[column identifier] row:row];
}
/* Python --> Cocoa */

View File

@@ -0,0 +1,16 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
which should be included with this package. The terms are also available at
http://www.hardcoded.net/licenses/hs_license
*/
#import <Cocoa/Cocoa.h>
#import "HSOutline.h"
#import "PyDirectoryOutline.h"
@interface DirectoryOutline : HSOutline {}
- (id)initWithPyParent:(id)aPyParent view:(HSOutlineView *)aOutlineView;
- (PyDirectoryOutline *)py;
@end;

View File

@@ -0,0 +1,77 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
which should be included with this package. The terms are also available at
http://www.hardcoded.net/licenses/hs_license
*/
#import "DirectoryOutline.h"
@implementation DirectoryOutline
- (id)initWithPyParent:(id)aPyParent view:(HSOutlineView *)aOutlineView
{
self = [super initWithPyClassName:@"PyDirectoryOutline" pyParent:aPyParent view:aOutlineView];
[outlineView registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
return self;
}
- (PyDirectoryOutline *)py
{
return (PyDirectoryOutline *)py;
}
/* Delegate */
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
{
NSPasteboard *pboard;
NSDragOperation sourceDragMask;
sourceDragMask = [info draggingSourceOperationMask];
pboard = [info draggingPasteboard];
if ([[pboard types] containsObject:NSFilenamesPboardType]) {
if (sourceDragMask & NSDragOperationLink)
return NSDragOperationLink;
}
return NSDragOperationNone;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index
{
NSPasteboard *pboard;
NSDragOperation sourceDragMask;
sourceDragMask = [info draggingSourceOperationMask];
pboard = [info draggingPasteboard];
if ([[pboard types] containsObject:NSFilenamesPboardType]) {
NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
if (!(sourceDragMask & NSDragOperationLink))
return NO;
for (NSString *filename in filenames) {
[[self py] addDirectory:filename];
}
}
return YES;
}
- (void)outlineView:(NSOutlineView *)aOutlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
NSTextFieldCell *textCell = cell;
NSIndexPath *path = item;
BOOL selected = [path isEqualTo:[outlineView selectedPath]];
if (selected) {
[textCell setTextColor:[NSColor blackColor]];
return;
}
NSInteger state = [self intProperty:@"state" valueAtPath:path];
if (state == 1) {
[textCell setTextColor:[NSColor blueColor]];
}
else if (state == 2) {
[textCell setTextColor:[NSColor redColor]];
}
else {
[textCell setTextColor:[NSColor blackColor]];
}
}
}
@end

View File

@@ -8,31 +8,23 @@ http://www.hardcoded.net/licenses/hs_license
#import <Cocoa/Cocoa.h>
#import "RecentDirectories.h"
#import "Outline.h"
#import "HSOutlineView.h"
#import "DirectoryOutline.h"
#import "PyDupeGuru.h"
@interface DirectoryOutline : OutlineView
{
}
@end
@protocol DirectoryOutlineDelegate
- (void)outlineView:(NSOutlineView *)outlineView addDirectory:(NSString *)directory;
@end
@interface DirectoryPanel : NSWindowController
{
IBOutlet NSPopUpButton *addButtonPopUp;
IBOutlet DirectoryOutline *directories;
IBOutlet HSOutlineView *outlineView;
IBOutlet NSButton *removeButton;
PyDupeGuruBase *_py;
RecentDirectories *_recentDirectories;
DirectoryOutline *outline;
}
- (id)initWithParentApp:(id)aParentApp;
- (IBAction)askForDirectory:(id)sender;
- (IBAction)changeDirectoryState:(id)sender;
- (IBAction)popupAddDirectoryMenu:(id)sender;
- (IBAction)removeSelectedDirectory:(id)sender;
- (IBAction)toggleVisible:(id)sender;

View File

@@ -11,48 +11,6 @@ http://www.hardcoded.net/licenses/hs_license
#import "Utils.h"
#import "AppDelegate.h"
@implementation DirectoryOutline
- (void)doInit
{
[super doInit];
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
}
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
{
NSPasteboard *pboard;
NSDragOperation sourceDragMask;
sourceDragMask = [info draggingSourceOperationMask];
pboard = [info draggingPasteboard];
if ([[pboard types] containsObject:NSFilenamesPboardType])
{
if (sourceDragMask & NSDragOperationLink)
return NSDragOperationLink;
}
return NSDragOperationNone;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index
{
NSPasteboard *pboard;
NSDragOperation sourceDragMask;
sourceDragMask = [info draggingSourceOperationMask];
pboard = [info draggingPasteboard];
if ( [[pboard types] containsObject:NSFilenamesPboardType] )
{
NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
if (!(sourceDragMask & NSDragOperationLink))
return NO;
if (([self delegate] == nil) || (![[self delegate] respondsToSelector:@selector(outlineView:addDirectory:)]))
return NO;
for (NSString *filename in filenames)
[[self delegate] outlineView:self addDirectory:filename];
}
return YES;
}
@end
@implementation DirectoryPanel
- (id)initWithParentApp:(id)aParentApp
{
@@ -61,23 +19,19 @@ http://www.hardcoded.net/licenses/hs_license
AppDelegateBase *app = aParentApp;
_py = [app py];
_recentDirectories = [app recentDirectories];
[directories setPy:_py];
NSPopUpButtonCell *cell = [[directories tableColumnWithIdentifier:@"1"] dataCell];
[cell addItemWithTitle:@"Normal"];
[cell addItemWithTitle:@"Reference"];
[cell addItemWithTitle:@"Excluded"];
for (NSInteger i=0;i<[[cell itemArray] count];i++)
{
NSMenuItem *mi = [[cell itemArray] objectAtIndex:i];
[mi setTarget:self];
[mi setAction:@selector(changeDirectoryState:)];
[mi setTag:i];
}
outline = [[DirectoryOutline alloc] initWithPyParent:_py view:outlineView];
[self refreshRemoveButtonText];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(directorySelectionChanged:) name:NSOutlineViewSelectionDidChangeNotification object:directories];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(directorySelectionChanged:)
name:NSOutlineViewSelectionDidChangeNotification object:outlineView];
return self;
}
- (void)dealloc
{
[outline release];
[super dealloc];
}
/* Actions */
- (IBAction)askForDirectory:(id)sender
@@ -95,15 +49,6 @@ http://www.hardcoded.net/licenses/hs_license
}
}
- (IBAction)changeDirectoryState:(id)sender
{
OVNode *node = [directories itemAtRow:[directories clickedRow]];
[_py setDirectory:p2a([node indexPath]) state:i2n([sender tag])];
[node resetAllBuffers];
[directories reloadItem:node reloadChildren:YES];
[directories display];
}
- (IBAction)popupAddDirectoryMenu:(id)sender
{
if ([[_recentDirectories directories] count] == 0)
@@ -125,21 +70,17 @@ http://www.hardcoded.net/licenses/hs_license
- (IBAction)removeSelectedDirectory:(id)sender
{
[[self window] makeKeyAndOrderFront:nil];
if ([directories selectedRow] < 0)
if ([outlineView selectedRow] < 0)
return;
OVNode *node = [directories itemAtRow:[directories selectedRow]];
if ([node level] == 1)
{
[_py removeDirectory:i2n([node index])];
[directories reloadData];
NSIndexPath *path = [outline selectedIndexPath];
NSInteger state = [outline intProperty:@"state" valueAtPath:path];
if (([path length] == 1) && (state != 2)) {
[_py removeDirectory:i2n([path indexAtPosition:0])];
}
else
{
NSInteger state = n2i([[node buffer] objectAtIndex:1]);
else {
NSInteger newState = state == 2 ? 0 : 2; // If excluded, put it back
[_py setDirectory:p2a([node indexPath]) state:i2n(newState)];
[node resetAllBuffers];
[directories display];
[outline setIntProperty:@"state" value:newState atPath:path];
[outlineView display];
}
[self refreshRemoveButtonText];
}
@@ -150,70 +91,40 @@ http://www.hardcoded.net/licenses/hs_license
}
/* Public */
- (void)addDirectory:(NSString *)directory
{
NSInteger r = [[_py addDirectory:directory] intValue];
if (r)
{
if (r) {
NSString *m;
switch (r)
{
case 1:
{
switch (r) {
case 1: {
m = @"This directory already is in the list.";
break;
}
case 2:
{
case 2: {
m = @"This directory does not exist.";
break;
}
}
[Dialogs showMessage:m];
}
[directories reloadData];
[_recentDirectories addDirectory:directory];
[[self window] makeKeyAndOrderFront:nil];
}
- (void)refreshRemoveButtonText
{
if ([directories selectedRow] < 0)
{
if ([outlineView selectedRow] < 0) {
[removeButton setEnabled:NO];
return;
}
[removeButton setEnabled:YES];
OVNode *node = [directories itemAtRow:[directories selectedRow]];
NSInteger state = n2i([[node buffer] objectAtIndex:1]);
NSInteger state = [outline intProperty:@"state" valueAtPath:[outline selectedIndexPath]];
NSString *buttonText = state == 2 ? @"Put Back" : @"Remove";
[removeButton setTitle:buttonText];
}
/* Delegate */
- (void)outlineView:(NSOutlineView *)outlineView addDirectory:(NSString *)directory
{
[self addDirectory:directory];
}
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
OVNode *node = item;
NSInteger state = n2i([[node buffer] objectAtIndex:1]);
if ([cell isKindOfClass:[NSTextFieldCell class]])
{
NSTextFieldCell *textCell = cell;
if (state == 1)
[textCell setTextColor:[NSColor blueColor]];
else if (state == 2)
[textCell setTextColor:[NSColor redColor]];
else
[textCell setTextColor:[NSColor blackColor]];
}
}
- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)path
{
BOOL isdir;

View File

@@ -7,9 +7,9 @@ http://www.hardcoded.net/licenses/hs_license
*/
#import <Cocoa/Cocoa.h>
#import "PyGUI.h"
@interface PyDetailsPanel : NSObject
- (id)initWithCocoa:(id)cocoa pyParent:(id)pyParent;
@interface PyDetailsPanel : PyGUI
- (NSInteger)numberOfRows;
- (id)valueForColumn:(NSString *)column row:(NSInteger)row;
@end

View File

@@ -0,0 +1,14 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
which should be included with this package. The terms are also available at
http://www.hardcoded.net/licenses/hs_license
*/
#import <Cocoa/Cocoa.h>
#import "PyOutline.h"
@interface PyDirectoryOutline : PyOutline
- (void)addDirectory:(NSString *)directoryPath;
@end

View File

@@ -13,7 +13,6 @@ http://www.hardcoded.net/licenses/hs_license
//Actions
- (NSNumber *)addDirectory:(NSString *)name;
- (void)removeDirectory:(NSNumber *)index;
- (void)setDirectory:(NSArray *)indexPath state:(NSNumber *)state;
- (void)loadResults;
- (void)saveResults;
- (void)loadIgnoreList;

View File

@@ -24,10 +24,12 @@ http://www.hardcoded.net/licenses/hs_license
IBOutlet NSSegmentedControl *pmSwitch;
IBOutlet NSTextField *stats;
IBOutlet NSMenu *columnsMenu;
IBOutlet NSSearchField *filterField;
BOOL _powerMode;
BOOL _displayDelta;
NSMutableArray *_resultColumns;
NSMutableIndexSet *_deltaColumns;
NSWindowController *preferencesPanel;
}
/* Helpers */
@@ -41,20 +43,35 @@ http://www.hardcoded.net/licenses/hs_license
- (void)updatePySelection;
- (void)performPySelection:(NSArray *)aIndexPaths;
- (void)refreshStats;
- (void)reloadMatches;
- (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth;
/* Actions */
- (IBAction)clearIgnoreList:(id)sender;
- (IBAction)changeDelta:(id)sender;
- (IBAction)changePowerMarker:(id)sender;
- (IBAction)copyMarked:(id)sender;
- (IBAction)deleteMarked:(id)sender;
- (IBAction)expandAll:(id)sender;
- (IBAction)exportToXHTML:(id)sender;
- (IBAction)filter:(id)sender;
- (IBAction)ignoreSelected:(id)sender;
- (IBAction)markAll:(id)sender;
- (IBAction)markInvert:(id)sender;
- (IBAction)markNone:(id)sender;
- (IBAction)markSelected:(id)sender;
- (IBAction)markToggle:(id)sender;
- (IBAction)moveMarked:(id)sender;
- (IBAction)openClicked:(id)sender;
- (IBAction)openSelected:(id)sender;
- (IBAction)removeMarked:(id)sender;
- (IBAction)removeSelected:(id)sender;
- (IBAction)renameSelected:(id)sender;
- (IBAction)resetColumnsToDefault:(id)sender;
- (IBAction)revealSelected:(id)sender;
- (IBAction)showPreferencesPanel:(id)sender;
- (IBAction)switchSelected:(id)sender;
- (IBAction)toggleColumn:(id)sender;
- (IBAction)toggleDelta:(id)sender;
- (IBAction)toggleDetailsPanel:(id)sender;
- (IBAction)togglePowerMarker:(id)sender;

View File

@@ -50,14 +50,24 @@ http://www.hardcoded.net/licenses/hs_license
@implementation ResultWindowBase
- (void)awakeFromNib
{
_displayDelta = NO;
_powerMode = NO;
[self window];
preferencesPanel = [[NSWindowController alloc] initWithWindowNibName:@"Preferences"];
[self initResultColumns];
[self fillColumnsMenu];
[deltaSwitch setSelectedSegment:0];
[pmSwitch setSelectedSegment:0];
[py setDisplayDeltaValues:b2n(_displayDelta)];
[matches setTarget:self];
[matches setDoubleAction:@selector(openClicked:)];
[self refreshStats];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registrationRequired:) name:RegistrationRequired object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobCompleted:) name:JobCompletedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobStarted:) name:JobStarted object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobInProgress:) name:JobInProgress object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsMarkingChanged:) name:ResultsMarkingChangedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsChanged:) name:ResultsChangedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsUpdated:) name:ResultsUpdatedNotification object:nil];
}
@@ -101,13 +111,9 @@ http://www.hardcoded.net/licenses/hs_license
//Returns an array of identifiers, in order.
- (NSArray *)getColumnsOrder
{
NSTableColumn *col;
NSString *colId;
NSMutableArray *result = [NSMutableArray array];
NSEnumerator *e = [[matches tableColumns] objectEnumerator];
while (col = [e nextObject])
{
colId = [col identifier];
for (NSTableColumn *col in [matches tableColumns]) {
NSString *colId = [col identifier];
[result addObject:colId];
}
return result;
@@ -116,14 +122,9 @@ http://www.hardcoded.net/licenses/hs_license
- (NSDictionary *)getColumnsWidth
{
NSMutableDictionary *result = [NSMutableDictionary dictionary];
NSTableColumn *col;
NSString *colId;
NSNumber *width;
NSEnumerator *e = [[matches tableColumns] objectEnumerator];
while (col = [e nextObject])
{
colId = [col identifier];
width = [NSNumber numberWithDouble:[col width]];
for (NSTableColumn *col in [matches tableColumns]) {
NSString *colId = [col identifier];
NSNumber *width = [NSNumber numberWithDouble:[col width]];
[result setObject:width forKey:colId];
}
return result;
@@ -151,10 +152,9 @@ http://www.hardcoded.net/licenses/hs_license
{
NSMutableArray *r = [NSMutableArray array];
NSArray *selected = [self getSelected:aDupesOnly];
NSEnumerator *e = [selected objectEnumerator];
OVNode *node;
while (node = [e nextObject])
for (OVNode *node in selected) {
[r addObject:p2a([node indexPath])];
}
return r;
}
@@ -165,49 +165,46 @@ http://www.hardcoded.net/licenses/hs_license
- (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth
{
NSTableColumn *col;
NSString *colId;
NSNumber *width;
NSMenuItem *mi;
//Remove all columns
NSEnumerator *e = [[columnsMenu itemArray] objectEnumerator];
while (mi = [e nextObject])
{
if ([mi state] == NSOnState)
for (NSMenuItem *mi in [columnsMenu itemArray]) {
if ([mi state] == NSOnState) {
[self toggleColumn:mi];
}
}
//Add columns and set widths
e = [aColumnsOrder objectEnumerator];
while (colId = [e nextObject])
{
if (![colId isEqual:@"mark"])
{
col = [_resultColumns objectAtIndex:[colId intValue]];
width = [aColumnsWidth objectForKey:[col identifier]];
mi = [columnsMenu itemWithTag:[colId intValue]];
if (width)
[col setWidth:[width floatValue]];
[self toggleColumn:mi];
for (NSString *colId in aColumnsOrder) {
if ([colId isEqual:@"mark"]) {
continue;
}
NSTableColumn *col = [_resultColumns objectAtIndex:[colId intValue]];
NSNumber *width = [aColumnsWidth objectForKey:[col identifier]];
NSMenuItem *mi = [columnsMenu itemWithTag:[colId intValue]];
if (width) {
[col setWidth:[width floatValue]];
}
[self toggleColumn:mi];
}
}
- (void)updatePySelection
{
NSArray *selection;
if (_powerMode)
if (_powerMode) {
selection = [py selectedPowerMarkerNodePaths];
else
}
else {
selection = [py selectedResultNodePaths];
}
[matches selectNodePaths:selection];
}
- (void)performPySelection:(NSArray *)aIndexPaths
{
if (_powerMode)
if (_powerMode) {
[py selectPowerMarkerNodePaths:aIndexPaths];
else
}
else {
[py selectResultNodePaths:aIndexPaths];
}
}
- (void)refreshStats
@@ -215,13 +212,32 @@ http://www.hardcoded.net/licenses/hs_license
[stats setStringValue:[py getStatLine]];
}
/* Reload the matches outline and restore selection from py */
- (void)reloadMatches
{
[matches setDelegate:nil];
[matches reloadData];
[matches expandItem:nil expandChildren:YES];
[matches setDelegate:self];
[self updatePySelection];
}
/* Actions */
- (IBAction)clearIgnoreList:(id)sender
{
NSInteger i = n2i([py getIgnoreListCount]);
if (!i)
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"Do you really want to remove all %d items from the ignore list?",i]] == NSAlertSecondButtonReturn) // NO
return;
[py clearIgnoreList];
}
- (IBAction)changeDelta:(id)sender
{
_displayDelta = [deltaSwitch selectedSegment] == 1;
[py setDisplayDeltaValues:b2n(_displayDelta)];
[matches reloadData];
[self expandAll:nil];
[self reloadMatches];
}
- (IBAction)changePowerMarker:(id)sender
@@ -231,7 +247,7 @@ http://www.hardcoded.net/licenses/hs_license
[matches setTag:2];
else
[matches setTag:0];
[self expandAll:nil];
[matches expandItem:nil expandChildren:YES];
[self outlineView:matches didClickTableColumn:nil];
[self updatePySelection];
}
@@ -267,18 +283,65 @@ http://www.hardcoded.net/licenses/hs_license
[py deleteMarked];
}
- (IBAction)expandAll:(id)sender
{
for (NSInteger i=0;i < [matches numberOfRows];i++)
[matches expandItem:[matches itemAtRow:i]];
}
- (IBAction)exportToXHTML:(id)sender
{
NSString *exported = [py exportToXHTMLwithColumns:[self getColumnsOrder]];
[[NSWorkspace sharedWorkspace] openFile:exported];
}
- (IBAction)filter:(id)sender
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[py setEscapeFilterRegexp:b2n(!n2b([ud objectForKey:@"useRegexpFilter"]))];
[py applyFilter:[filterField stringValue]];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)ignoreSelected:(id)sender
{
NSArray *nodeList = [self getSelected:YES];
if (![nodeList count])
return;
NSString *msg = [NSString stringWithFormat:@"All selected %d matches are going to be ignored in all subsequent scans. Continue?",[nodeList count]];
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) // NO
return;
[py addSelectedToIgnoreList];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)markAll:(id)sender
{
[py markAll];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markInvert:(id)sender
{
[py markInvert];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markNone:(id)sender
{
[py markNone];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:YES]];
[py toggleSelectedMark];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markToggle:(id)sender
{
OVNode *node = [matches itemAtRow:[matches clickedRow]];
[self performPySelection:[NSArray arrayWithObject:p2a([node indexPath])]];
[py toggleSelectedMark];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)moveMarked:(id)sender
{
NSInteger mark_count = [[py getMarkCount] intValue];
@@ -299,11 +362,67 @@ http://www.hardcoded.net/licenses/hs_license
}
}
- (IBAction)openClicked:(id)sender
{
if ([matches clickedRow] < 0) {
return;
}
[matches selectRowIndexes:[NSIndexSet indexSetWithIndex:[matches clickedRow]] byExtendingSelection:NO];
[py openSelected];
}
- (IBAction)openSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:NO]];
[py openSelected];
}
- (IBAction)refresh:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)removeMarked:(id)sender
{
int mark_count = [[py getMarkCount] intValue];
if (!mark_count)
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
return;
[py removeMarked];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)removeSelected:(id)sender
{
NSArray *nodeList = [self getSelected:YES];
if (![nodeList count])
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",[nodeList count]]] == NSAlertSecondButtonReturn) // NO
return;
[self performPySelection:[self getSelectedPaths:YES]];
[py removeSelected];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)renameSelected:(id)sender
{
NSInteger col = [matches columnWithIdentifier:@"0"];
NSInteger row = [matches selectedRow];
[matches editColumn:col row:row withEvent:[NSApp currentEvent] select:YES];
}
- (IBAction)resetColumnsToDefault:(id)sender
{
// Virtual
}
- (IBAction)revealSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:NO]];
[py revealSelected];
}
- (IBAction)showPreferencesPanel:(id)sender
{
[preferencesPanel showWindow:sender];
@@ -315,7 +434,6 @@ http://www.hardcoded.net/licenses/hs_license
// but after a lot of fussing around, believe it or not, it actually is.
NSInteger matchesTag = _powerMode ? 2 : 0;
NSInteger startLen = [[py getOutlineView:matchesTag childCountsForPath:[NSArray array]] count];
[self performPySelection:[self getSelectedPaths:YES]];
[py makeSelectedReference];
[self performPySelection:[self getSelectedPaths:NO]];
// In some cases (when in a filtered view in Power Marker mode, it's possible that the demoted
@@ -347,6 +465,15 @@ http://www.hardcoded.net/licenses/hs_license
}
}
- (IBAction)toggleDelta:(id)sender
{
if ([deltaSwitch selectedSegment] == 1)
[deltaSwitch setSelectedSegment:0];
else
[deltaSwitch setSelectedSegment:1];
[self changeDelta:sender];
}
- (IBAction)toggleDetailsPanel:(id)sender
{
[[(AppDelegateBase *)app detailsPanel] toggleVisibility];
@@ -362,7 +489,6 @@ http://www.hardcoded.net/licenses/hs_license
}
/* Delegate */
- (void)outlineView:(NSOutlineView *)outlineView didClickTableColumn:(NSTableColumn *)tableColumn
{
if ([[outlineView sortDescriptors] count] < 1)
@@ -372,8 +498,31 @@ http://www.hardcoded.net/licenses/hs_license
[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];
[self reloadMatches];
}
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
OVNode *node = item;
if ([[tableColumn identifier] isEqual:@"mark"]) {
[cell setEnabled: [node isMarkable]];
}
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
// Determine if the text color will be blue due to directory being reference.
NSTextFieldCell *textCell = cell;
if ([node isMarkable]) {
[textCell setTextColor:[NSColor blackColor]];
}
else {
[textCell setTextColor:[NSColor blueColor]];
}
if ((_displayDelta) && (_powerMode || ([node level] > 1))) {
NSInteger i = [[tableColumn identifier] integerValue];
if ([_deltaColumns containsIndex:i]) {
[textCell setTextColor:[NSColor orangeColor]];
}
}
}
}
/* Notifications */
@@ -430,7 +579,6 @@ http://www.hardcoded.net/licenses/hs_license
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]];
}
@@ -448,9 +596,7 @@ http://www.hardcoded.net/licenses/hs_license
- (void)resultsChanged:(NSNotification *)aNotification
{
[matches reloadData];
[self expandAll:nil];
[self outlineViewSelectionDidChange:nil];
[self reloadMatches];
[self refreshStats];
}

View File

@@ -2,17 +2,17 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">10B504</string>
<string key="IBDocument.SystemVersion">10C540</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.AppKitVersion">1038.2</string>
<string key="IBDocument.HIToolboxVersion">437.00</string>
<string key="IBDocument.AppKitVersion">1038.25</string>
<string key="IBDocument.HIToolboxVersion">458.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">740</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="6"/>
<integer value="50"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -70,7 +70,6 @@
<int key="NSvFlags">256</int>
<string key="NSFrameSize">{327, 165}</string>
<reference key="NSSuperview" ref="514281221"/>
<int key="NSTag">1</int>
<bool key="NSEnabled">YES</bool>
<object class="NSTableHeaderView" key="NSHeaderView" id="885660940">
<reference key="NSNextResponder" ref="395832192"/>
@@ -88,7 +87,7 @@
<object class="NSMutableArray" key="NSTableColumns">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSTableColumn" id="547470852">
<string key="NSIdentifier">0</string>
<string key="NSIdentifier">name</string>
<double key="NSWidth">236</double>
<double key="NSMinWidth">16</double>
<double key="NSMaxWidth">1000</double>
@@ -142,7 +141,7 @@
<reference key="NSTableView" ref="10140319"/>
</object>
<object class="NSTableColumn" id="50798966">
<string key="NSIdentifier">1</string>
<string key="NSIdentifier">state</string>
<double key="NSWidth">85.35595703125</double>
<double key="NSMinWidth">30.35595703125</double>
<double key="NSMaxWidth">1000</double>
@@ -173,15 +172,41 @@
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">400</int>
<int key="NSPeriodicInterval">75</int>
<nil key="NSMenuItem"/>
<object class="NSMenuItem" key="NSMenuItem" id="71151438">
<reference key="NSMenu" ref="104112446"/>
<string key="NSTitle">Normal</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<int key="NSState">1</int>
<string key="NSAction">_popUpItemAction:</string>
<reference key="NSTarget" ref="867721721"/>
</object>
<bool key="NSMenuItemRespectAlignment">YES</bool>
<object class="NSMenu" key="NSMenu" id="104112446">
<string key="NSTitle"/>
<string key="NSTitle">Normal</string>
<object class="NSMutableArray" key="NSMenuItems">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="71151438"/>
<object class="NSMenuItem" id="828402206">
<reference key="NSMenu" ref="104112446"/>
<string key="NSTitle">Reference</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<string key="NSAction">_popUpItemAction:</string>
<reference key="NSTarget" ref="867721721"/>
</object>
<object class="NSMenuItem" id="142495353">
<reference key="NSMenu" ref="104112446"/>
<string key="NSTitle">Excluded</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<string key="NSAction">_popUpItemAction:</string>
<reference key="NSTarget" ref="867721721"/>
</object>
</object>
<int key="NSSelectedIndex">-1</int>
<bool key="NSNoAutoenable">YES</bool>
<bool key="NSMenuExcludeMarkColumn">YES</bool>
</object>
<int key="NSPreferredEdge">3</int>
<bool key="NSUsesItemFromMenu">YES</bool>
<bool key="NSAltersState">YES</bool>
@@ -189,6 +214,7 @@
</object>
<int key="NSResizingMask">2</int>
<bool key="NSIsResizeable">YES</bool>
<bool key="NSIsEditable">YES</bool>
<reference key="NSTableView" ref="10140319"/>
</object>
</object>
@@ -415,30 +441,6 @@
</object>
<int key="connectionID">19</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">nextKeyView</string>
<reference key="source" ref="963602908"/>
<reference key="destination" ref="10140319"/>
</object>
<int key="connectionID">20</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">nextKeyView</string>
<reference key="source" ref="10140319"/>
<reference key="destination" ref="630693842"/>
</object>
<int key="connectionID">21</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">nextKeyView</string>
<reference key="source" ref="630693842"/>
<reference key="destination" ref="963602908"/>
</object>
<int key="connectionID">22</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">popupAddDirectoryMenu:</string>
@@ -471,22 +473,6 @@
</object>
<int key="connectionID">26</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">directories</string>
<reference key="source" ref="566600593"/>
<reference key="destination" ref="10140319"/>
</object>
<int key="connectionID">27</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="10140319"/>
<reference key="destination" ref="566600593"/>
</object>
<int key="connectionID">29</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">performClose:</string>
@@ -503,6 +489,14 @@
</object>
<int key="connectionID">43</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">outlineView</string>
<reference key="source" ref="566600593"/>
<reference key="destination" ref="10140319"/>
</object>
<int key="connectionID">54</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -685,6 +679,12 @@
<object class="IBObjectRecord">
<int key="objectID">50</int>
<reference key="object" ref="104112446"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="71151438"/>
<reference ref="828402206"/>
<reference ref="142495353"/>
</object>
<reference key="parent" ref="867721721"/>
</object>
<object class="IBObjectRecord">
@@ -702,6 +702,21 @@
<reference key="object" ref="885660940"/>
<reference key="parent" ref="242279311"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">55</int>
<reference key="object" ref="71151438"/>
<reference key="parent" ref="104112446"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">56</int>
<reference key="object" ref="828402206"/>
<reference key="parent" ref="104112446"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">57</int>
<reference key="object" ref="142495353"/>
<reference key="parent" ref="104112446"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
@@ -739,6 +754,7 @@
<string>5.ImportedFromIB2</string>
<string>5.windowTemplate.hasMinSize</string>
<string>5.windowTemplate.minSize</string>
<string>50.IBEditorWindowLastContentRect</string>
<string>50.IBPluginDependency</string>
<string>51.IBPluginDependency</string>
<string>51.IBShouldRemoveOnLegacySave</string>
@@ -746,6 +762,9 @@
<string>52.IBShouldRemoveOnLegacySave</string>
<string>53.IBPluginDependency</string>
<string>53.IBShouldRemoveOnLegacySave</string>
<string>55.IBPluginDependency</string>
<string>56.IBPluginDependency</string>
<string>57.IBPluginDependency</string>
<string>6.IBPluginDependency</string>
<string>6.ImportedFromIB2</string>
<string>7.IBPluginDependency</string>
@@ -761,7 +780,7 @@
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>DirectoryOutline</string>
<string>HSOutlineView</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -788,6 +807,7 @@
<boolean value="YES"/>
<boolean value="YES"/>
<string>{369, 269}</string>
<string>{{98, 740}, {327, 63}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
@@ -796,6 +816,9 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
@@ -821,27 +844,11 @@
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">53</int>
<int key="maxID">57</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">DirectoryOutline</string>
<string key="superclassName">OutlineView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="462913745">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">dgbase/DirectoryPanel.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">DirectoryPanel</string>
<string key="superclassName">NSWindowController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">DirectoryPanel.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">DirectoryPanel</string>
<string key="superclassName">NSWindowController</string>
@@ -850,7 +857,6 @@
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>askForDirectory:</string>
<string>changeDirectoryState:</string>
<string>popupAddDirectoryMenu:</string>
<string>removeSelectedDirectory:</string>
<string>toggleVisible:</string>
@@ -861,7 +867,6 @@
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
@@ -869,60 +874,29 @@
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>addButtonPopUp</string>
<string>directories</string>
<string>outlineView</string>
<string>removeButton</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSPopUpButton</string>
<string>NSOutlineView</string>
<string>HSOutlineView</string>
<string>NSButton</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../base/DirectoryPanel.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">DirectoryPanel</string>
<string key="superclassName">NSWindowController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string>
<string key="minorKey"/>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">DirectoryPanel</string>
<string key="superclassName">NSWindowController</string>
<object class="NSMutableDictionary" key="actions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>askForDirectory:</string>
<string>changeDirectoryState:</string>
<string>popupAddDirectoryMenu:</string>
<string>removeSelectedDirectory:</string>
<string>toggleVisible:</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>addButtonPopUp</string>
<string>directories</string>
<string>removeButton</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSPopUpButton</string>
<string>DirectoryOutline</string>
<string>NSButton</string>
</object>
</object>
<reference key="sourceIdentifier" ref="462913745"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">FirstResponder</string>
<string key="superclassName">NSObject</string>
@@ -932,40 +906,27 @@
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">OutlineView</string>
<string key="className">HSOutlineView</string>
<string key="superclassName">NSOutlineView</string>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">py</string>
<string key="NS.object.0">PyApp</string>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="53364925">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/Outline.h</string>
<string key="minorKey">../views/HSOutlineView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">OutlineView</string>
<string key="superclassName">NSOutlineView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string>
<string key="minorKey"/>
<string key="className">NSObject</string>
<reference key="sourceIdentifier" ref="53364925"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="42597526">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../views/NSTableViewAdditions.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">PyApp</string>
<string key="superclassName">PyRegistrable</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/PyApp.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">PyRegistrable</string>
<string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/PyRegistrable.h</string>
</object>
<string key="className">NSTableView</string>
<reference key="sourceIdentifier" ref="42597526"/>
</object>
</object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
@@ -1497,7 +1458,7 @@
<integer value="3000" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">../../dupeguru.xcodeproj</string>
<string key="IBDocument.LastKnownRelativeProjectPath">../../se/dupeguru.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
</data>
</archive>

View File

@@ -13,26 +13,8 @@ http://www.hardcoded.net/licenses/hs_license
@interface ResultWindow : ResultWindowBase
{
IBOutlet NSSearchField *filterField;
NSString *_lastAction;
NSMutableIndexSet *_deltaColumns;
}
- (IBAction)clearIgnoreList:(id)sender;
- (IBAction)filter:(id)sender;
- (IBAction)ignoreSelected:(id)sender;
- (IBAction)markAll:(id)sender;
- (IBAction)markInvert:(id)sender;
- (IBAction)markNone:(id)sender;
- (IBAction)markSelected:(id)sender;
- (IBAction)markToggle:(id)sender;
- (IBAction)openSelected:(id)sender;
- (IBAction)refresh:(id)sender;
- (IBAction)removeDeadTracks:(id)sender;
- (IBAction)removeMarked:(id)sender;
- (IBAction)removeSelected:(id)sender;
- (IBAction)renameSelected:(id)sender;
- (IBAction)revealSelected:(id)sender;
- (IBAction)startDuplicateScan:(id)sender;
- (IBAction)toggleDelta:(id)sender;
@end

View File

@@ -20,130 +20,16 @@ http://www.hardcoded.net/licenses/hs_license
{
[super awakeFromNib];
[[self window] setTitle:@"dupeGuru Music Edition"];
_displayDelta = NO;
_powerMode = NO;
_deltaColumns = [[NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,7)] retain];
[_deltaColumns removeIndex:6];
[deltaSwitch setSelectedSegment:0];
[pmSwitch setSelectedSegment:0];
[py setDisplayDeltaValues:b2n(_displayDelta)];
[matches setTarget:self];
[matches setDoubleAction:@selector(openSelected:)];
[self refreshStats];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsMarkingChanged:) name:ResultsMarkingChangedNotification object:nil];
}
/* Actions */
- (IBAction)clearIgnoreList:(id)sender
{
NSInteger i = n2i([py getIgnoreListCount]);
if (!i)
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"Do you really want to remove all %d items from the ignore list?",i]] == NSAlertSecondButtonReturn) // NO
return;
[py clearIgnoreList];
}
- (IBAction)filter:(id)sender
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[py setEscapeFilterRegexp:b2n(!n2b([ud objectForKey:@"useRegexpFilter"]))];
[py applyFilter:[filterField stringValue]];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)ignoreSelected:(id)sender
{
NSArray *nodeList = [self getSelected:YES];
if (![nodeList count])
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"All selected %d matches are going to be ignored in all subsequent scans. Continue?",[nodeList count]]] == NSAlertSecondButtonReturn) // NO
return;
[self performPySelection:[self getSelectedPaths:YES]];
[py addSelectedToIgnoreList];
[py removeSelected];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)markAll:(id)sender
{
[py markAll];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markInvert:(id)sender
{
[py markInvert];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markNone:(id)sender
{
[py markNone];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:YES]];
[py toggleSelectedMark];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markToggle:(id)sender
{
OVNode *node = [matches itemAtRow:[matches clickedRow]];
[self performPySelection:[NSArray arrayWithObject:p2a([node indexPath])]];
[py toggleSelectedMark];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)openSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:NO]];
[py openSelected];
}
- (IBAction)refresh:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)removeDeadTracks:(id)sender
{
[(PyDupeGuru *)py scanDeadTracks];
}
- (IBAction)removeMarked:(id)sender
{
int mark_count = [[py getMarkCount] intValue];
if (!mark_count)
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
return;
[py removeMarked];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)removeSelected:(id)sender
{
NSArray *nodeList = [self getSelected:YES];
if (![nodeList count])
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",[nodeList count]]] == NSAlertSecondButtonReturn) // NO
return;
[self performPySelection:[self getSelectedPaths:YES]];
[py removeSelected];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)renameSelected:(id)sender
{
NSInteger col = [matches columnWithIdentifier:@"0"];
NSInteger row = [matches selectedRow];
[matches editColumn:col row:row withEvent:[NSApp currentEvent] select:YES];
}
- (IBAction)resetColumnsToDefault:(id)sender
{
NSMutableArray *columnsOrder = [NSMutableArray array];
@@ -161,12 +47,6 @@ http://www.hardcoded.net/licenses/hs_license
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
}
- (IBAction)revealSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:NO]];
[py revealSelected];
}
- (IBAction)startDuplicateScan:(id)sender
{
if ([matches numberOfRows] > 0)
@@ -199,15 +79,6 @@ http://www.hardcoded.net/licenses/hs_license
}
}
- (IBAction)toggleDelta:(id)sender
{
if ([deltaSwitch selectedSegment] == 1)
[deltaSwitch setSelectedSegment:0];
else
[deltaSwitch setSelectedSegment:1];
[self changeDelta:sender];
}
/* Public */
- (void)initResultColumns
{
@@ -240,31 +111,6 @@ http://www.hardcoded.net/licenses/hs_license
[_resultColumns addObject:[self getColumnForIdentifier:18 title:@"Dupe Count" width:80 refCol:refCol]];
}
/* Delegate */
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
OVNode *node = item;
if ([[tableColumn identifier] isEqual:@"mark"])
{
[cell setEnabled: [node isMarkable]];
}
if ([cell isKindOfClass:[NSTextFieldCell class]])
{
// Determine if the text color will be blue due to directory being reference.
NSTextFieldCell *textCell = cell;
if ([node isMarkable])
[textCell setTextColor:[NSColor blackColor]];
else
[textCell setTextColor:[NSColor blueColor]];
if ((_displayDelta) && (_powerMode || ([node level] > 1)))
{
int i = [[tableColumn identifier] intValue];
if ([_deltaColumns containsIndex:i])
[textCell setTextColor:[NSColor orangeColor]];
}
}
}
/* Notifications */
- (void)jobCompleted:(NSNotification *)aNotification
{

View File

@@ -18,27 +18,27 @@ from hsmedia import aiff, flac, genres, id3v1, id3v2, mp4, mpeg, ogg, wma
class PyDupeGuru(PyDupeGuruBase):
def init(self):
self = super(PyDupeGuru,self).init()
self.app = DupeGuruME()
self.py = DupeGuruME()
return self
def removeDeadTracks(self):
self.app.remove_dead_tracks()
self.py.remove_dead_tracks()
def scanDeadTracks(self):
self.app.scan_dead_tracks()
self.py.scan_dead_tracks()
#---Information
@signature('i@:')
def deadTrackCount(self):
return len(self.app.dead_tracks)
return len(self.py.dead_tracks)
#---Properties
def setMinMatchPercentage_(self, percentage):
self.app.scanner.min_match_percentage = int(percentage)
self.py.scanner.min_match_percentage = int(percentage)
def setScanType_(self, scan_type):
try:
self.app.scanner.scan_type = [
self.py.scanner.scan_type = [
SCAN_TYPE_FILENAME,
SCAN_TYPE_FIELDS,
SCAN_TYPE_FIELDS_NO_ORDER,
@@ -50,16 +50,16 @@ class PyDupeGuru(PyDupeGuruBase):
pass
def setWordWeighting_(self, words_are_weighted):
self.app.scanner.word_weighting = words_are_weighted
self.py.scanner.word_weighting = words_are_weighted
def setMatchSimilarWords_(self, match_similar_words):
self.app.scanner.match_similar_words = match_similar_words
self.py.scanner.match_similar_words = match_similar_words
def enable_scanForTag_(self, enable, scan_tag):
if enable:
self.app.scanner.scanned_tags.add(scan_tag)
self.py.scanner.scanned_tags.add(scan_tag)
else:
self.app.scanner.scanned_tags.discard(scan_tag)
self.py.scanner.scanned_tags.discard(scan_tag)
#---Registration
def appName(self):

View File

@@ -10,28 +10,8 @@ http://www.hardcoded.net/licenses/hs_license
#import "Outline.h"
#import "../base/ResultWindow.h"
@interface ResultWindow : ResultWindowBase
{
IBOutlet NSSearchField *filterField;
NSMutableIndexSet *_deltaColumns;
}
- (IBAction)clearIgnoreList:(id)sender;
@interface ResultWindow : ResultWindowBase {}
- (IBAction)clearPictureCache:(id)sender;
- (IBAction)filter:(id)sender;
- (IBAction)ignoreSelected:(id)sender;
- (IBAction)markAll:(id)sender;
- (IBAction)markInvert:(id)sender;
- (IBAction)markNone:(id)sender;
- (IBAction)markSelected:(id)sender;
- (IBAction)markToggle:(id)sender;
- (IBAction)openSelected:(id)sender;
- (IBAction)refresh:(id)sender;
- (IBAction)removeMarked:(id)sender;
- (IBAction)removeSelected:(id)sender;
- (IBAction)renameSelected:(id)sender;
- (IBAction)revealSelected:(id)sender;
- (IBAction)startDuplicateScan:(id)sender;
- (IBAction)toggleDelta:(id)sender;
- (IBAction)toggleDirectories:(id)sender;
@end

View File

@@ -20,31 +20,12 @@ http://www.hardcoded.net/licenses/hs_license
{
[super awakeFromNib];
[[self window] setTitle:@"dupeGuru Picture Edition"];
_displayDelta = NO;
_powerMode = NO;
_deltaColumns = [[NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,5)] retain];
[_deltaColumns removeIndex:3];
[_deltaColumns removeIndex:4];
[deltaSwitch setSelectedSegment:0];
[pmSwitch setSelectedSegment:0];
[py setDisplayDeltaValues:b2n(_displayDelta)];
[matches setTarget:self];
[matches setDoubleAction:@selector(openSelected:)];
[self refreshStats];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsMarkingChanged:) name:ResultsMarkingChangedNotification object:nil];
}
/* Actions */
- (IBAction)clearIgnoreList:(id)sender
{
int i = n2i([py getIgnoreListCount]);
if (!i)
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"Do you really want to remove all %d items from the ignore list?",i]] == NSAlertSecondButtonReturn) // NO
return;
[py clearIgnoreList];
}
- (IBAction)clearPictureCache:(id)sender
{
if ([Dialogs askYesNo:@"Do you really want to remove all your cached picture analysis?"] == NSAlertSecondButtonReturn) // NO
@@ -52,101 +33,6 @@ http://www.hardcoded.net/licenses/hs_license
[(PyDupeGuru *)py clearPictureCache];
}
- (IBAction)filter:(id)sender
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[py setEscapeFilterRegexp:b2n(!n2b([ud objectForKey:@"useRegexpFilter"]))];
[py applyFilter:[filterField stringValue]];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)ignoreSelected:(id)sender
{
NSArray *nodeList = [self getSelected:YES];
if (![nodeList count])
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"All selected %d matches are going to be ignored in all subsequent scans. Continue?",[nodeList count]]] == NSAlertSecondButtonReturn) // NO
return;
[self performPySelection:[self getSelectedPaths:YES]];
[py addSelectedToIgnoreList];
[py removeSelected];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)markAll:(id)sender
{
[py markAll];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markInvert:(id)sender
{
[py markInvert];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markNone:(id)sender
{
[py markNone];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:YES]];
[py toggleSelectedMark];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markToggle:(id)sender
{
OVNode *node = [matches itemAtRow:[matches clickedRow]];
[self performPySelection:[NSArray arrayWithObject:p2a([node indexPath])]];
[py toggleSelectedMark];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)openSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:NO]];
[py openSelected];
}
- (IBAction)refresh:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)removeMarked:(id)sender
{
int mark_count = [[py getMarkCount] intValue];
if (!mark_count)
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
return;
[py removeMarked];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)removeSelected:(id)sender
{
NSArray *nodeList = [self getSelected:YES];
if (![nodeList count])
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",[nodeList count]]] == NSAlertSecondButtonReturn) // NO
return;
[self performPySelection:[self getSelectedPaths:YES]];
[py removeSelected];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)renameSelected:(id)sender
{
int col = [matches columnWithIdentifier:@"0"];
int row = [matches selectedRow];
[matches editColumn:col row:row withEvent:[NSApp currentEvent] select:YES];
}
- (IBAction)resetColumnsToDefault:(id)sender
{
NSMutableArray *columnsOrder = [NSMutableArray array];
@@ -164,12 +50,6 @@ http://www.hardcoded.net/licenses/hs_license
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
}
- (IBAction)revealSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:NO]];
[py revealSelected];
}
- (IBAction)startDuplicateScan:(id)sender
{
if ([matches numberOfRows] > 0)
@@ -196,15 +76,6 @@ http://www.hardcoded.net/licenses/hs_license
}
}
- (IBAction)toggleDelta:(id)sender
{
if ([deltaSwitch selectedSegment] == 1)
[deltaSwitch setSelectedSegment:0];
else
[deltaSwitch setSelectedSegment:1];
[self changeDelta:sender];
}
- (IBAction)toggleDirectories:(id)sender
{
[(AppDelegate *)app toggleDirectories:sender];
@@ -227,29 +98,4 @@ http://www.hardcoded.net/licenses/hs_license
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Match %" width:58 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Dupe Count" width:80 refCol:refCol]];
}
/* Delegate */
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
OVNode *node = item;
if ([[tableColumn identifier] isEqual:@"mark"])
{
[cell setEnabled: [node isMarkable]];
}
if ([cell isKindOfClass:[NSTextFieldCell class]])
{
// Determine if the text color will be blue due to directory being reference.
NSTextFieldCell *textCell = cell;
if ([node isMarkable])
[textCell setTextColor:[NSColor blackColor]];
else
[textCell setTextColor:[NSColor blueColor]];
if ((_displayDelta) && (_powerMode || ([node level] > 1)))
{
int i = [[tableColumn identifier] intValue];
if ([_deltaColumns containsIndex:i])
[textCell setTextColor:[NSColor orangeColor]];
}
}
}
@end

View File

@@ -13,25 +13,25 @@ from core_pe import block, cache, matchbase, data, _block_osx
class PyDupeGuru(PyDupeGuruBase):
def init(self):
self = super(PyDupeGuru, self).init()
self.app = app_pe_cocoa.DupeGuruPE()
self.py = app_pe_cocoa.DupeGuruPE()
return self
def clearPictureCache(self):
self.app.scanner.clear_picture_cache()
self.py.scanner.clear_picture_cache()
#---Information
def getSelectedDupePath(self):
return unicode(self.app.selected_dupe_path())
return unicode(self.py.selected_dupe_path())
def getSelectedDupeRefPath(self):
return unicode(self.app.selected_dupe_ref_path())
return unicode(self.py.selected_dupe_ref_path())
#---Properties
def setMatchScaled_(self,match_scaled):
self.app.scanner.match_scaled = match_scaled
self.py.scanner.match_scaled = match_scaled
def setMinMatchPercentage_(self,percentage):
self.app.scanner.threshold = int(percentage)
self.py.scanner.threshold = int(percentage)
#---Registration
def appName(self):

View File

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

View File

@@ -13,26 +13,8 @@ http://www.hardcoded.net/licenses/hs_license
@interface ResultWindow : ResultWindowBase
{
IBOutlet NSSearchField *filterField;
NSString *_lastAction;
NSMutableIndexSet *_deltaColumns;
}
- (IBAction)clearIgnoreList:(id)sender;
- (IBAction)filter:(id)sender;
- (IBAction)ignoreSelected:(id)sender;
- (IBAction)markAll:(id)sender;
- (IBAction)markInvert:(id)sender;
- (IBAction)markNone:(id)sender;
- (IBAction)markSelected:(id)sender;
- (IBAction)markToggle:(id)sender;
- (IBAction)openSelected:(id)sender;
- (IBAction)refresh:(id)sender;
- (IBAction)removeMarked:(id)sender;
- (IBAction)removeSelected:(id)sender;
- (IBAction)renameSelected:(id)sender;
- (IBAction)resetColumnsToDefault:(id)sender;
- (IBAction)revealSelected:(id)sender;
- (IBAction)startDuplicateScan:(id)sender;
- (IBAction)toggleDelta:(id)sender;
@end

View File

@@ -18,125 +18,11 @@ http://www.hardcoded.net/licenses/hs_license
- (void)awakeFromNib
{
[super awakeFromNib];
_displayDelta = NO;
_powerMode = NO;
_deltaColumns = [[NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,4)] retain];
[_deltaColumns removeIndex:3];
[deltaSwitch setSelectedSegment:0];
[pmSwitch setSelectedSegment:0];
[py setDisplayDeltaValues:b2n(_displayDelta)];
[matches setTarget:self];
[matches setDoubleAction:@selector(openSelected:)];
[self refreshStats];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsMarkingChanged:) name:ResultsMarkingChangedNotification object:nil];
}
/* Actions */
- (IBAction)clearIgnoreList:(id)sender
{
int i = n2i([py getIgnoreListCount]);
if (!i)
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"Do you really want to remove all %d items from the ignore list?",i]] == NSAlertSecondButtonReturn) // NO
return;
[py clearIgnoreList];
}
- (IBAction)filter:(id)sender
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[py setEscapeFilterRegexp:b2n(!n2b([ud objectForKey:@"useRegexpFilter"]))];
[py applyFilter:[filterField stringValue]];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)ignoreSelected:(id)sender
{
NSArray *nodeList = [self getSelected:YES];
if (![nodeList count])
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"All selected %d matches are going to be ignored in all subsequent scans. Continue?",[nodeList count]]] == NSAlertSecondButtonReturn) // NO
return;
[self performPySelection:[self getSelectedPaths:YES]];
[py addSelectedToIgnoreList];
[py removeSelected];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)markAll:(id)sender
{
[py markAll];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markInvert:(id)sender
{
[py markInvert];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markNone:(id)sender
{
[py markNone];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:YES]];
[py toggleSelectedMark];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)markToggle:(id)sender
{
OVNode *node = [matches itemAtRow:[matches clickedRow]];
[self performPySelection:[NSArray arrayWithObject:p2a([node indexPath])]];
[py toggleSelectedMark];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsMarkingChangedNotification object:self];
}
- (IBAction)openSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:NO]];
[py openSelected];
}
- (IBAction)refresh:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)removeMarked:(id)sender
{
int mark_count = [[py getMarkCount] intValue];
if (!mark_count)
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
return;
[py removeMarked];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)removeSelected:(id)sender
{
NSArray *nodeList = [self getSelected:YES];
if (![nodeList count])
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",[nodeList count]]] == NSAlertSecondButtonReturn) // NO
return;
[self performPySelection:[self getSelectedPaths:YES]];
[py removeSelected];
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
}
- (IBAction)renameSelected:(id)sender
{
int col = [matches columnWithIdentifier:@"0"];
int row = [matches selectedRow];
[matches editColumn:col row:row withEvent:[NSApp currentEvent] select:YES];
}
- (IBAction)resetColumnsToDefault:(id)sender
{
NSMutableArray *columnsOrder = [NSMutableArray array];
@@ -152,12 +38,6 @@ http://www.hardcoded.net/licenses/hs_license
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
}
- (IBAction)revealSelected:(id)sender
{
[self performPySelection:[self getSelectedPaths:NO]];
[py revealSelected];
}
- (IBAction)startDuplicateScan:(id)sender
{
if ([matches numberOfRows] > 0)
@@ -190,15 +70,6 @@ http://www.hardcoded.net/licenses/hs_license
}
- (IBAction)toggleDelta:(id)sender
{
if ([deltaSwitch selectedSegment] == 1)
[deltaSwitch setSelectedSegment:0];
else
[deltaSwitch setSelectedSegment:1];
[self changeDelta:sender];
}
/* Public */
- (void)initResultColumns
{
@@ -216,29 +87,4 @@ http://www.hardcoded.net/licenses/hs_license
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Words Used" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Dupe Count" width:80 refCol:refCol]];
}
/* Delegate */
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
OVNode *node = item;
if ([[tableColumn identifier] isEqual:@"mark"])
{
[cell setEnabled: [node isMarkable]];
}
if ([cell isKindOfClass:[NSTextFieldCell class]])
{
// Determine if the text color will be blue due to directory being reference.
NSTextFieldCell *textCell = cell;
if ([node isMarkable])
[textCell setTextColor:[NSColor blackColor]];
else
[textCell setTextColor:[NSColor blueColor]];
if ((_displayDelta) && (_powerMode || ([node level] > 1)))
{
int i = [[tableColumn identifier] intValue];
if ([_deltaColumns containsIndex:i])
[textCell setTextColor:[NSColor orangeColor]];
}
}
}
@end

View File

@@ -16,16 +16,16 @@ from core_se import fs, data
class PyDupeGuru(PyDupeGuruBase):
def init(self):
self = super(PyDupeGuru,self).init()
self.app = DupeGuru()
self.py = DupeGuru()
return self
#---Properties
def setMinMatchPercentage_(self,percentage):
self.app.scanner.min_match_percentage = int(percentage)
self.py.scanner.min_match_percentage = int(percentage)
def setScanType_(self,scan_type):
try:
self.app.scanner.scan_type = [
self.py.scanner.scan_type = [
scanner.SCAN_TYPE_FILENAME,
scanner.SCAN_TYPE_CONTENT
][scan_type]
@@ -33,14 +33,14 @@ class PyDupeGuru(PyDupeGuruBase):
pass
def setWordWeighting_(self,words_are_weighted):
self.app.scanner.word_weighting = words_are_weighted
self.py.scanner.word_weighting = words_are_weighted
def setMatchSimilarWords_(self,match_similar_words):
self.app.scanner.match_similar_words = match_similar_words
self.py.scanner.match_similar_words = match_similar_words
@signature('v@:i')
def setSizeThreshold_(self, size_threshold):
self.app.scanner.size_threshold = size_threshold
self.py.scanner.size_threshold = size_threshold
#---Registration
def appName(self):

View File

@@ -20,7 +20,15 @@
CE45579B0AE3BC2B005A9546 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; };
CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; };
CE6E0DFE1054E9EF008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */; };
CE76FDC4111EE37C006618EA /* HSOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDBF111EE37C006618EA /* HSOutlineView.m */; };
CE76FDC5111EE37C006618EA /* NSIndexPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */; };
CE76FDC6111EE37C006618EA /* NSTableViewAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC3111EE37C006618EA /* NSTableViewAdditions.m */; };
CE76FDCF111EE38E006618EA /* HSGUIController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC9111EE38E006618EA /* HSGUIController.m */; };
CE76FDD4111EE3A7006618EA /* DirectoryOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDD2111EE3A7006618EA /* DirectoryOutline.m */; };
CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDDE111EE42F006618EA /* HSOutline.m */; };
CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDF6111EE561006618EA /* NSEventAdditions.m */; };
CEAC6811109B0B7E00B43C85 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEAC6810109B0B7E00B43C85 /* Preferences.xib */; };
CEBE4D74111F0EE1009AAC6D /* HSWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CEBE4D73111F0EE1009AAC6D /* HSWindowController.m */; };
CEDD92DA0FDD01640031C7B7 /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDD92D70FDD01640031C7B7 /* BRSingleLineFormatter.m */; };
CEE7EA130FE675C80004E467 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEE7EA120FE675C80004E467 /* DetailsPanel.m */; };
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
@@ -76,7 +84,26 @@
CE45579A0AE3BC2B005A9546 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = "<group>"; };
CE6E7407111C997500C350E3 /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
CE76FDBE111EE37C006618EA /* HSOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutlineView.h; sourceTree = "<group>"; };
CE76FDBF111EE37C006618EA /* HSOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutlineView.m; sourceTree = "<group>"; };
CE76FDC0111EE37C006618EA /* NSIndexPathAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSIndexPathAdditions.h; sourceTree = "<group>"; };
CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSIndexPathAdditions.m; sourceTree = "<group>"; };
CE76FDC2111EE37C006618EA /* NSTableViewAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSTableViewAdditions.h; sourceTree = "<group>"; };
CE76FDC3111EE37C006618EA /* NSTableViewAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSTableViewAdditions.m; sourceTree = "<group>"; };
CE76FDC8111EE38E006618EA /* HSGUIController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSGUIController.h; sourceTree = "<group>"; };
CE76FDC9111EE38E006618EA /* HSGUIController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSGUIController.m; sourceTree = "<group>"; };
CE76FDCD111EE38E006618EA /* PyGUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyGUI.h; sourceTree = "<group>"; };
CE76FDCE111EE38E006618EA /* PyOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyOutline.h; sourceTree = "<group>"; };
CE76FDD1111EE3A7006618EA /* DirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DirectoryOutline.h; path = ../base/DirectoryOutline.h; sourceTree = SOURCE_ROOT; };
CE76FDD2111EE3A7006618EA /* DirectoryOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DirectoryOutline.m; path = ../base/DirectoryOutline.m; sourceTree = SOURCE_ROOT; };
CE76FDD3111EE3A7006618EA /* PyDirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDirectoryOutline.h; path = ../base/PyDirectoryOutline.h; sourceTree = SOURCE_ROOT; };
CE76FDDD111EE42F006618EA /* HSOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutline.h; sourceTree = "<group>"; };
CE76FDDE111EE42F006618EA /* HSOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutline.m; sourceTree = "<group>"; };
CE76FDF5111EE561006618EA /* NSEventAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSEventAdditions.h; path = ../../cocoalib/NSEventAdditions.h; sourceTree = SOURCE_ROOT; };
CE76FDF6111EE561006618EA /* NSEventAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSEventAdditions.m; path = ../../cocoalib/NSEventAdditions.m; sourceTree = SOURCE_ROOT; };
CEAC6810109B0B7E00B43C85 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Preferences.xib; path = xib/Preferences.xib; sourceTree = "<group>"; };
CEBE4D72111F0EE1009AAC6D /* HSWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSWindowController.h; sourceTree = "<group>"; };
CEBE4D73111F0EE1009AAC6D /* HSWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSWindowController.m; sourceTree = "<group>"; };
CEDD92D60FDD01640031C7B7 /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; };
CEDD92D70FDD01640031C7B7 /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; };
CEE7EA110FE675C80004E467 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DetailsPanel.h; path = ../base/DetailsPanel.h; sourceTree = SOURCE_ROOT; };
@@ -216,6 +243,44 @@
path = ../../cocoalib/xib;
sourceTree = SOURCE_ROOT;
};
CE76FDBD111EE37C006618EA /* views */ = {
isa = PBXGroup;
children = (
CE76FDBE111EE37C006618EA /* HSOutlineView.h */,
CE76FDBF111EE37C006618EA /* HSOutlineView.m */,
CE76FDC0111EE37C006618EA /* NSIndexPathAdditions.h */,
CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */,
CE76FDC2111EE37C006618EA /* NSTableViewAdditions.h */,
CE76FDC3111EE37C006618EA /* NSTableViewAdditions.m */,
);
name = views;
path = ../../cocoalib/views;
sourceTree = SOURCE_ROOT;
};
CE76FDC7111EE38E006618EA /* controllers */ = {
isa = PBXGroup;
children = (
CEBE4D72111F0EE1009AAC6D /* HSWindowController.h */,
CEBE4D73111F0EE1009AAC6D /* HSWindowController.m */,
CE76FDDD111EE42F006618EA /* HSOutline.h */,
CE76FDDE111EE42F006618EA /* HSOutline.m */,
CE76FDC8111EE38E006618EA /* HSGUIController.h */,
CE76FDC9111EE38E006618EA /* HSGUIController.m */,
);
name = controllers;
path = ../../cocoalib/controllers;
sourceTree = SOURCE_ROOT;
};
CE76FDCC111EE38E006618EA /* proxies */ = {
isa = PBXGroup;
children = (
CE76FDCD111EE38E006618EA /* PyGUI.h */,
CE76FDCE111EE38E006618EA /* PyOutline.h */,
);
name = proxies;
path = ../../cocoalib/proxies;
sourceTree = SOURCE_ROOT;
};
CEDD92D50FDD01640031C7B7 /* brsinglelineformatter */ = {
isa = PBXGroup;
children = (
@@ -250,6 +315,11 @@
CEFC7F890FC9513600CD5728 /* cocoalib */ = {
isa = PBXGroup;
children = (
CE76FDF5111EE561006618EA /* NSEventAdditions.h */,
CE76FDF6111EE561006618EA /* NSEventAdditions.m */,
CE76FDC7111EE38E006618EA /* controllers */,
CE76FDCC111EE38E006618EA /* proxies */,
CE76FDBD111EE37C006618EA /* views */,
CE19BC5F11199231007CCEB0 /* xib */,
CEDD92D50FDD01640031C7B7 /* brsinglelineformatter */,
CEFC7F8A0FC9517500CD5728 /* Dialogs.h */,
@@ -277,6 +347,9 @@
CEFC7FB00FC9518F00CD5728 /* dgbase */ = {
isa = PBXGroup;
children = (
CE76FDD1111EE3A7006618EA /* DirectoryOutline.h */,
CE76FDD2111EE3A7006618EA /* DirectoryOutline.m */,
CE76FDD3111EE3A7006618EA /* PyDirectoryOutline.h */,
CEFC7FB10FC951A700CD5728 /* AppDelegate.h */,
CEFC7FB20FC951A700CD5728 /* AppDelegate.m */,
CEFC7FB30FC951A700CD5728 /* Consts.h */,
@@ -379,6 +452,14 @@
CEFC7FBB0FC951A700CD5728 /* ResultWindow.m in Sources */,
CEDD92DA0FDD01640031C7B7 /* BRSingleLineFormatter.m in Sources */,
CEE7EA130FE675C80004E467 /* DetailsPanel.m in Sources */,
CE76FDC4111EE37C006618EA /* HSOutlineView.m in Sources */,
CE76FDC5111EE37C006618EA /* NSIndexPathAdditions.m in Sources */,
CE76FDC6111EE37C006618EA /* NSTableViewAdditions.m in Sources */,
CE76FDCF111EE38E006618EA /* HSGUIController.m in Sources */,
CE76FDD4111EE3A7006618EA /* DirectoryOutline.m in Sources */,
CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */,
CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */,
CEBE4D74111F0EE1009AAC6D /* HSWindowController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -84,6 +84,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
def _do_load(self, j):
self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml'))
self.notify('directories_changed')
j = j.start_subjob([1, 9])
self.results.load_from_xml(op.join(self.appdata, 'last_results.xml'), self._get_file, j)
files = flatten(g[:] for g in self.results.groups)
@@ -103,10 +104,18 @@ class DupeGuru(RegistrableApplication, Broadcaster):
path = Path(str_path)
return fs.get_file(path, self.directories.fileclasses)
@staticmethod
def _open_path(path):
raise NotImplementedError()
@staticmethod
def _recycle_dupe(dupe):
raise NotImplementedError()
@staticmethod
def _reveal_path(path):
raise NotImplementedError()
def _select_dupes(self, dupes):
if dupes == self.selected_dupes:
return
@@ -120,17 +129,21 @@ class DupeGuru(RegistrableApplication, Broadcaster):
def add_directory(self, d):
try:
self.directories.add_path(Path(d))
self.notify('directories_changed')
return 0
except directories.AlreadyThereError:
return 1
except directories.InvalidPathError:
return 2
def add_to_ignore_list(self, dupe):
def add_selected_to_ignore_list(self):
dupes = self.without_ref(self.selected_dupes)
for dupe in dupes:
g = self.results.get_group_of_duplicate(dupe)
for other in g:
if other is not dupe:
self.scanner.ignore_list.Ignore(unicode(other.path), unicode(dupe.path))
self.remove_duplicates(dupes)
def apply_filter(self, filter):
self.results.apply_filter(None)
@@ -212,14 +225,36 @@ class DupeGuru(RegistrableApplication, Broadcaster):
p = op.join(self.appdata, 'ignore_list.xml')
self.scanner.ignore_list.load_from_xml(p)
def make_reference(self, duplicates):
def make_selected_reference(self):
dupes = self.without_ref(self.selected_dupes)
changed_groups = set()
for dupe in duplicates:
for dupe in dupes:
g = self.results.get_group_of_duplicate(dupe)
if g not in changed_groups:
self.results.make_ref(dupe)
changed_groups.add(g)
def open_selected(self):
if self.selected_dupes:
self._open_path(self.selected_dupes[0].path)
def remove_directory(self,index):
try:
del self.directories[index]
self.notify('directories_changed')
except IndexError:
pass
def remove_duplicates(self, duplicates):
self.results.remove_duplicates(duplicates)
def remove_selected(self):
self.remove_duplicates(self.selected_dupes)
def reveal_selected(self):
if self.selected_dupes:
self._reveal_path(self.selected_dupes[0].path)
def save(self):
if not op.exists(self.appdata):
os.makedirs(self.appdata)
@@ -248,6 +283,9 @@ class DupeGuru(RegistrableApplication, Broadcaster):
self.results.groups = []
self._start_job(JOB_SCAN, do)
def without_ref(self, dupes):
return [dupe for dupe in dupes if self.results.get_group_of_duplicate(dupe).ref is not dupe]
#--- Properties
@property
def stat_line(self):

View File

@@ -49,6 +49,10 @@ class DupeGuru(app.DupeGuru):
self.display_delta_values = False
#--- Override
@staticmethod
def _open_path(path):
NSWorkspace.sharedWorkspace().openFile_(unicode(path))
@staticmethod
def _recycle_dupe(dupe):
# local import because first appkit import takes a lot of memory. we want to avoid it.
@@ -57,6 +61,10 @@ class DupeGuru(app.DupeGuru):
result, tag = NSWorkspace.sharedWorkspace().performFileOperation_source_destination_files_tag_(
NSWorkspaceRecycleOperation, directory, '', [filename], None)
@staticmethod
def _reveal_path(path):
NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(unicode(path), '')
def _start_job(self, jobid, func):
try:
j = self.progress.create_job()
@@ -79,45 +87,13 @@ class DupeGuru(app.DupeGuru):
except IndexError:
return (None,None)
def get_folder_path(self, node_path, curr_path=None):
if not node_path:
return curr_path
current_index = node_path[0]
if curr_path is None:
curr_path = self.directories[current_index]
else:
curr_path = self.directories.get_subfolders(curr_path)[current_index]
return self.get_folder_path(node_path[1:], curr_path)
#---Public
def AddSelectedToIgnoreList(self):
for dupe in self.selected_dupes:
self.add_to_ignore_list(dupe)
copy_or_move_marked = demo_method(app.DupeGuru.copy_or_move_marked)
delete_marked = demo_method(app.DupeGuru.delete_marked)
def MakeSelectedReference(self):
self.make_reference(self.selected_dupes)
def OpenSelected(self):
# local import because first appkit import takes a lot of memory. we want to avoid it.
if self.selected_dupes:
path = unicode(self.selected_dupes[0].path)
NSWorkspace.sharedWorkspace().openFile_(path)
def PurgeIgnoreList(self):
self.scanner.ignore_list.Filter(lambda f,s:op.exists(f) and op.exists(s))
def RemoveDirectory(self,index):
try:
del self.directories[index]
except IndexError:
pass
def RemoveSelected(self):
self.results.remove_duplicates(self.selected_dupes)
def RenameSelected(self, newname):
try:
d = self.selected_dupes[0]
@@ -127,12 +103,6 @@ class DupeGuru(app.DupeGuru):
logging.warning("dupeGuru Warning: %s" % unicode(e))
return False
def RevealSelected(self):
# local import because first appkit import takes a lot of memory. we want to avoid it.
if self.selected_dupes:
path = unicode(self.selected_dupes[0].path)
NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(path,'')
def start_scanning(self):
self._select_dupes([])
try:
@@ -186,10 +156,6 @@ class DupeGuru(app.DupeGuru):
dupes = [self.results.dupes[row] for row in rows if row in xrange(len(self.results.dupes))]
self._select_dupes(dupes)
def SetDirectoryState(self, node_path, state):
p = self.get_folder_path(node_path)
self.directories.set_state(p, state)
def sort_dupes(self,key,asc):
self.results.sort_dupes(key,asc,self.display_delta_values)
@@ -204,8 +170,6 @@ class DupeGuru(app.DupeGuru):
def GetOutlineViewMaxLevel(self, tag):
if tag == 0:
return 2
elif tag == 1:
return 0
elif tag == 2:
return 1
@@ -215,16 +179,6 @@ class DupeGuru(app.DupeGuru):
if tag == 0: #Normal results
assert not node_path # no other value is possible
return [len(g.dupes) for g in self.results.groups]
elif tag == 1: #Directories
try:
if node_path:
path = self.get_folder_path(node_path)
subfolders = self.directories.get_subfolders(path)
else:
subfolders = self.directories
return [len(self.directories.get_subfolders(path)) for path in subfolders]
except IndexError: # node_path out of range
return []
else: #Power Marker
assert not node_path # no other value is possible
return [0 for d in self.results.dupes]
@@ -244,13 +198,6 @@ class DupeGuru(app.DupeGuru):
g = self.results.get_group_of_duplicate(d)
result = self._get_display_info(d, g, self.display_delta_values)
return result
elif tag == 1: #Directories
try:
path = self.get_folder_path(node_path)
name = unicode(path) if len(node_path) == 1 else path[-1]
return [name, self.directories.get_state(path)]
except IndexError: # node_path out of range
return []
def GetOutlineViewMarked(self, tag, node_path):
# 0=unmarked 1=marked 2=unmarkable
@@ -258,8 +205,6 @@ class DupeGuru(app.DupeGuru):
return
if not node_path:
return 2
if tag == 1: #Directories
return 2
if tag == 0: #Normal results
g, d = self.GetObjects(node_path)
else: #Power Marker

View File

@@ -9,204 +9,164 @@
# Common interface for all editions' dg_cocoa unit.
from hsutil.cocoa.objcmin import NSObject
from hsutil.cocoa import signature
from hsutil.reg import InvalidCodeError
from hsutil.cocoa.inter import signature, PyOutline, PyGUIObject, PyRegistrable
from .gui.details_panel import DetailsPanel
from .gui.directory_tree import DirectoryTree
# Fix py2app's problems on relative imports
from core import app, app_cocoa, data, directories, engine, export, ignore, results, fs, scanner
from hsutil import conflict
class PyApp(NSObject):
pass #fake class
class PyDupeGuruBase(PyApp):
class PyDupeGuruBase(PyRegistrable):
#---Directories
def addDirectory_(self, directory):
return self.app.add_directory(directory)
return self.py.add_directory(directory)
def removeDirectory_(self, index):
self.app.RemoveDirectory(index)
def setDirectory_state_(self, node_path, state):
self.app.SetDirectoryState(node_path, state)
self.py.remove_directory(index)
#---Results
def clearIgnoreList(self):
self.app.scanner.ignore_list.Clear()
self.py.scanner.ignore_list.Clear()
def doScan(self):
return self.app.start_scanning()
return self.py.start_scanning()
def exportToXHTMLwithColumns_(self, column_ids):
return self.app.export_to_xhtml(column_ids)
return self.py.export_to_xhtml(column_ids)
def loadIgnoreList(self):
self.app.load_ignore_list()
self.py.load_ignore_list()
def loadResults(self):
self.app.load()
self.py.load()
def markAll(self):
self.app.results.mark_all()
self.py.results.mark_all()
def markNone(self):
self.app.results.mark_none()
self.py.results.mark_none()
def markInvert(self):
self.app.results.mark_invert()
self.py.results.mark_invert()
def purgeIgnoreList(self):
self.app.PurgeIgnoreList()
self.py.PurgeIgnoreList()
def toggleSelectedMark(self):
self.app.ToggleSelectedMarkState()
self.py.ToggleSelectedMarkState()
def saveIgnoreList(self):
self.app.save_ignore_list()
self.py.save_ignore_list()
def saveResults(self):
self.app.save()
self.py.save()
def selectedResultNodePaths(self):
return self.app.selected_result_node_paths()
return self.py.selected_result_node_paths()
def selectResultNodePaths_(self,node_paths):
self.app.SelectResultNodePaths(node_paths)
self.py.SelectResultNodePaths(node_paths)
def selectedPowerMarkerNodePaths(self):
return self.app.selected_powermarker_node_paths()
return self.py.selected_powermarker_node_paths()
def selectPowerMarkerNodePaths_(self,node_paths):
self.app.SelectPowerMarkerNodePaths(node_paths)
self.py.SelectPowerMarkerNodePaths(node_paths)
#---Actions
def addSelectedToIgnoreList(self):
self.app.AddSelectedToIgnoreList()
self.py.add_selected_to_ignore_list()
def deleteMarked(self):
self.app.delete_marked()
self.py.delete_marked()
def applyFilter_(self, filter):
self.app.apply_filter(filter)
self.py.apply_filter(filter)
def makeSelectedReference(self):
self.app.MakeSelectedReference()
self.py.make_selected_reference()
def copyOrMove_markedTo_recreatePath_(self, copy, destination, recreate_path):
self.app.copy_or_move_marked(copy, destination, recreate_path)
self.py.copy_or_move_marked(copy, destination, recreate_path)
def openSelected(self):
self.app.OpenSelected()
self.py.open_selected()
def removeMarked(self):
self.app.results.perform_on_marked(lambda x:True, True)
self.py.results.perform_on_marked(lambda x:True, True)
def removeSelected(self):
self.app.RemoveSelected()
self.py.remove_selected()
def renameSelected_(self,newname):
return self.app.RenameSelected(newname)
return self.py.RenameSelected(newname)
def revealSelected(self):
self.app.RevealSelected()
self.py.reveal_selected()
#---Misc
def sortDupesBy_ascending_(self, key, asc):
self.app.sort_dupes(key, asc)
self.py.sort_dupes(key, asc)
def sortGroupsBy_ascending_(self, key, asc):
self.app.sort_groups(key, asc)
self.py.sort_groups(key, asc)
#---Information
def getIgnoreListCount(self):
return len(self.app.scanner.ignore_list)
return len(self.py.scanner.ignore_list)
def getMarkCount(self):
return self.app.results.mark_count
return self.py.results.mark_count
def getStatLine(self):
return self.app.stat_line
return self.py.stat_line
def getOperationalErrorCount(self):
return self.app.last_op_error_count
return self.py.last_op_error_count
#---Data
@signature('i@:i')
def getOutlineViewMaxLevel_(self, tag):
return self.app.GetOutlineViewMaxLevel(tag)
return self.py.GetOutlineViewMaxLevel(tag)
@signature('@@:i@')
def getOutlineView_childCountsForPath_(self, tag, node_path):
return self.app.GetOutlineViewChildCounts(tag, node_path)
return self.py.GetOutlineViewChildCounts(tag, node_path)
def getOutlineView_valuesForIndexes_(self, tag, node_path):
return self.app.GetOutlineViewValues(tag, node_path)
return self.py.GetOutlineViewValues(tag, node_path)
def getOutlineView_markedAtIndexes_(self, tag, node_path):
return self.app.GetOutlineViewMarked(tag, node_path)
def getTableViewCount_(self, tag):
return self.app.GetTableViewCount(tag)
def getTableViewMarkedIndexes_(self, tag):
return self.app.GetTableViewMarkedIndexes(tag)
def getTableView_valuesForRow_(self, tag, row):
return self.app.GetTableViewValues(tag, row)
return self.py.GetOutlineViewMarked(tag, node_path)
#---Properties
def setMixFileKind_(self, mix_file_kind):
self.app.scanner.mix_file_kind = mix_file_kind
self.py.scanner.mix_file_kind = mix_file_kind
def setDisplayDeltaValues_(self, display_delta_values):
self.app.display_delta_values= display_delta_values
self.py.display_delta_values= display_delta_values
def setEscapeFilterRegexp_(self, escape_filter_regexp):
self.app.options['escape_filter_regexp'] = escape_filter_regexp
self.py.options['escape_filter_regexp'] = escape_filter_regexp
def setRemoveEmptyFolders_(self, remove_empty_folders):
self.app.options['clean_empty_dirs'] = remove_empty_folders
self.py.options['clean_empty_dirs'] = remove_empty_folders
#---Worker
def getJobProgress(self):
return self.app.progress.last_progress
return self.py.progress.last_progress
def getJobDesc(self):
return self.app.progress.last_desc
return self.py.progress.last_desc
def cancelJob(self):
self.app.progress.job_cancelled = True
#---Registration
def demoLimitDescription(self):
return self.app.DEMO_LIMIT_DESC
@signature('i@:')
def isRegistered(self):
return self.app.registered
def isCodeValid_withEmail_(self, code, email):
try:
self.app.validate_code(code, email)
return None
except InvalidCodeError as e:
return unicode(e)
def setRegisteredCode_andEmail_(self, code, email):
self.app.set_registration(code, email)
self.py.progress.job_cancelled = True
class PyDetailsPanel(NSObject):
def initWithCocoa_pyParent_(self, cocoa, pyparent):
super(PyDetailsPanel, self).init()
self.cocoa = cocoa
self.py = DetailsPanel(self, pyparent.app)
return self
class PyDetailsPanel(PyGUIObject):
py_class = DetailsPanel
@signature('i@:')
def numberOfRows(self):
return self.py.row_count()
@@ -215,7 +175,10 @@ class PyDetailsPanel(NSObject):
def valueForColumn_row_(self, column, row):
return self.py.row(row)[int(column)]
# python --> cocoa
def refresh(self):
self.cocoa.refresh()
class PyDirectoryOutline(PyOutline):
py_class = DirectoryTree
def addDirectory_(self, path):
self.py.add_directory(path)

23
core/gui/base.py Normal file
View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2010-02-06
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
from hsutil.notify import Listener
class GUIObject(Listener):
def __init__(self, view, app):
Listener.__init__(self, app)
self.view = view
self.app = app
def directories_changed(self):
pass
def dupes_selected(self):
pass

View File

@@ -7,13 +7,11 @@
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
from hsutil.notify import Listener
from .base import GUIObject
class DetailsPanel(Listener):
class DetailsPanel(GUIObject):
def __init__(self, view, app):
Listener.__init__(self, app)
self.app = app
self.view = view
GUIObject.__init__(self, view, app)
self._table = []
self._refresh()
self.connect()

View File

@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2010-02-06
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
from hsgui.tree import Tree, Node
from ..directories import STATE_NORMAL, STATE_REFERENCE, STATE_EXCLUDED
from .base import GUIObject
STATE_ORDER = [STATE_NORMAL, STATE_REFERENCE, STATE_EXCLUDED]
# Lazily loads children
class DirectoryNode(Node):
def __init__(self, app, path, name):
Node.__init__(self, name)
self._app = app
self._directory_path = path
self._loaded = False
self._state = STATE_ORDER.index(self._app.directories.get_state(path))
def __len__(self):
if not self._loaded:
self._load()
return Node.__len__(self)
def _load(self):
self.clear()
subpaths = self._app.directories.get_subfolders(self._directory_path)
for path in subpaths:
self.append(DirectoryNode(self._app, path, path[-1]))
self._loaded = True
# The state propery is an index to the combobox
@property
def state(self):
return self._state
@state.setter
def state(self, value):
if value == self._state:
return
self._state = value
state = STATE_ORDER[value]
self._app.directories.set_state(self._directory_path, state)
class DirectoryTree(GUIObject, Tree):
def __init__(self, view, app):
GUIObject.__init__(self, view, app)
Tree.__init__(self)
self.connect()
self._refresh()
self.view.refresh()
def _refresh(self):
self.clear()
for path in self.app.directories:
self.append(DirectoryNode(self.app, path, unicode(path)))
def add_directory(self, path):
self.app.add_directory(path)
#--- Event Handlers
def directories_changed(self):
self._refresh()
self.view.refresh()

View File

@@ -27,6 +27,7 @@ except ImportError:
from nose.plugins.skip import SkipTest
raise SkipTest("These tests can only be run on OS X")
from ..gui.details_panel import DetailsPanel
from ..gui.directory_tree import DirectoryTree
class DupeGuru(DupeGuruBase):
def __init__(self):
@@ -61,6 +62,8 @@ class TCDupeGuru(TestCase):
self.app = DupeGuru()
self.dpanel_gui = CallLogger()
self.dpanel = DetailsPanel(self.dpanel_gui, self.app)
self.dtree_gui = CallLogger()
self.dtree = DirectoryTree(self.dtree_gui, self.app)
self.objects,self.matches,self.groups = GetTestGroups()
self.app.results.groups = self.groups
tmppath = self.tmppath()
@@ -150,7 +153,7 @@ class TCDupeGuru(TestCase):
objects = self.objects
paths = [[0, 0], [0, 1], [1]]
app.SelectResultNodePaths(paths)
app.RemoveSelected()
app.remove_selected()
# The first 2 dupes have been removed. The 3rd one is a ref. it stays there, in first pos.
eq_(app.selected_result_node_paths(), [[0]]) # no exception
@@ -206,7 +209,7 @@ class TCDupeGuru(TestCase):
objects = self.objects
paths = r2np([0, 1, 2])
app.SelectPowerMarkerNodePaths(paths)
app.RemoveSelected()
app.remove_selected()
eq_(app.selected_powermarker_node_paths(), []) # no exception
def test_selectPowerMarkerRows(self):
@@ -264,9 +267,9 @@ class TCDupeGuru(TestCase):
objects = self.objects
groups = self.groups
app.SelectPowerMarkerNodePaths(r2np([0,2]))
app.MakeSelectedReference()
self.assert_(groups[0].ref is objects[1])
self.assert_(groups[1].ref is objects[4])
app.make_selected_reference()
assert groups[0].ref is objects[1]
assert groups[1].ref is objects[4]
def test_makeSelectedReference_by_selecting_two_dupes_in_the_same_group(self):
app = self.app
@@ -274,20 +277,20 @@ class TCDupeGuru(TestCase):
groups = self.groups
app.SelectPowerMarkerNodePaths(r2np([0,1,2]))
#Only 0 and 2 must go ref, not 1 because it is a part of the same group
app.MakeSelectedReference()
self.assert_(groups[0].ref is objects[1])
self.assert_(groups[1].ref is objects[4])
app.make_selected_reference()
assert groups[0].ref is objects[1]
assert groups[1].ref is objects[4]
def test_removeSelected(self):
app = self.app
app.SelectPowerMarkerNodePaths(r2np([0,2]))
app.RemoveSelected()
self.assertEqual(1,len(app.results.dupes))
app.RemoveSelected()
self.assertEqual(1,len(app.results.dupes))
app.remove_selected()
eq_(len(app.results.dupes), 1)
app.remove_selected()
eq_(len(app.results.dupes), 1)
app.SelectPowerMarkerNodePaths(r2np([0,2]))
app.RemoveSelected()
self.assertEqual(0,len(app.results.dupes))
app.remove_selected()
eq_(len(app.results.dupes), 0)
def test_addDirectory_simple(self):
# There's already a directory in self.app, so adding another once makes 2 of em
@@ -307,10 +310,10 @@ class TCDupeGuru(TestCase):
def test_ignore(self):
app = self.app
app.SelectPowerMarkerNodePaths(r2np([2])) #The dupe of the second, 2 sized group
app.AddSelectedToIgnoreList()
app.add_selected_to_ignore_list()
self.assertEqual(1,len(app.scanner.ignore_list))
app.SelectPowerMarkerNodePaths(r2np([0])) #first dupe of the 3 dupes group
app.AddSelectedToIgnoreList()
app.add_selected_to_ignore_list()
#BOTH the ref and the other dupe should have been added
self.assertEqual(3,len(app.scanner.ignore_list))
@@ -337,22 +340,7 @@ class TCDupeGuru(TestCase):
app = self.app
app.scanner.ignore_list.Ignore = FakeIgnore
app.SelectPowerMarkerNodePaths(r2np([2])) #The dupe of the second, 2 sized group
app.AddSelectedToIgnoreList()
def test_GetOutlineViewChildCounts_out_of_range(self):
# Out of range requests don't crash and return an empty value
app = self.app
# [0, 2] is out of range
eq_(app.GetOutlineViewChildCounts(1, [0, 2]), []) # no crash
def test_GetOutlineViewValues_out_of_range(self):
# Out of range requests don't crash and return an empty value
app = self.app
# [0, 2] is out of range
# Directories
eq_(app.GetOutlineViewValues(1, [0, 2]), []) # no crash
# Normal results
app.GetOutlineViewValues(0, [42, 0]) # no crash
app.add_selected_to_ignore_list()
class TCDupeGuru_renameSelected(TestCase):

View File

@@ -1,3 +1,10 @@
- date: 2010-02-10
version: 2.9.2
description: |
* dupeGuru is now 64-bit on Mac OS X!
* Fixed a crash upon quitting when support folder is not present. (#83)
* Fixed a crash during sorting. (#85)
* Fixed selection glitches, especially while renaming. (#93)
- date: 2010-01-13
version: 2.9.1
description: |

View File

@@ -10,10 +10,59 @@
import sys
import os
import os.path as op
import shutil
import yaml
from hsutil.build import build_dmg, add_to_pythonpath
from hsutil.build import build_dmg, add_to_pythonpath, print_and_do
def package_cocoa(edition):
app_path = {
'se': 'cocoa/se/build/release/dupeGuru.app',
'me': 'cocoa/me/build/release/dupeGuru ME.app',
'pe': 'cocoa/pe/build/release/dupeGuru PE.app',
}[edition]
build_dmg(app_path, '.')
def package_qt(edition):
# On Windows, PyInstaller is used to build an exe (py2exe creates a very bad looking icon)
# The release version is outdated. Use at least r672 on http://svn.pyinstaller.org/trunk
if sys.platform != "win32":
print "Qt packaging only works under Windows."
return
add_to_pythonpath('.')
add_to_pythonpath('qt')
add_to_pythonpath(op.join('qt', edition))
os.chdir(op.join('qt', edition))
from app import DupeGuru
# Removing build and dist
if op.exists('build'):
shutil.rmtree('build')
if op.exists('dist'):
shutil.rmtree('dist')
version = DupeGuru.VERSION
versioncomma = version.replace('.', ', ') + ', 0'
verinfo = open('verinfo').read()
verinfo = verinfo.replace('$versioncomma', versioncomma).replace('$version', version)
fp = open('verinfo_tmp', 'w')
fp.write(verinfo)
fp.close()
print_and_do("python C:\\Python26\\pyinstaller\\Build.py dg{0}.spec".format(edition))
os.remove('verinfo_tmp')
print_and_do("del dist\\*90.dll") # They're in vcredist, no need to include them
print_and_do("del dist\\POWRPROF.dll") # no need of that crap
print_and_do("del dist\\SHLWAPI.dll") # no need of that crap
print_and_do("xcopy /Y /S /I ..\\..\\help_me\\dupeguru_me_help dist\\help")
# AdvancedInstaller.com has to be in your PATH
# this is so we don'a have to re-commit installer.aip at every version change
shutil.copy('installer.aip', 'installer_tmp.aip')
print_and_do('AdvancedInstaller.com /edit installer_tmp.aip /SetVersion %s' % version)
print_and_do('AdvancedInstaller.com /build installer_tmp.aip -force')
os.remove('installer_tmp.aip')
os.chdir(op.join('..', '..'))
def main():
conf = yaml.load(open('conf.yaml'))
@@ -25,21 +74,9 @@ def main():
return
print "Packaging dupeGuru {0} with UI {1}".format(edition.upper(), ui)
if ui == 'cocoa':
app_path = {
'se': 'cocoa/se/build/release/dupeGuru.app',
'me': 'cocoa/me/build/release/dupeGuru ME.app',
'pe': 'cocoa/pe/build/release/dupeGuru PE.app',
}[edition]
build_dmg(app_path, '.')
package_cocoa(edition)
elif ui == 'qt':
if sys.platform != "win32":
print "Qt packaging only works under Windows."
return
add_to_pythonpath('.')
add_to_pythonpath('qt')
os.chdir(op.join('qt', edition))
os.system('python build.py')
os.chdir(op.join('..', '..'))
package_qt(edition)
if __name__ == '__main__':
main()

View File

@@ -117,10 +117,19 @@ class DupeGuru(DupeGuruBase, QObject):
raise NotImplementedError()
#--- Override
@staticmethod
def _open_path(path):
url = QUrl.fromLocalFile(unicode(path))
QDesktopServices.openUrl(url)
@staticmethod
def _recycle_dupe(dupe):
platform.recycle_file(dupe.path)
@staticmethod
def _reveal_path(path):
DupeGuru._open_path(path[:-1])
def _start_job(self, jobid, func):
title = JOBID2TITLE[jobid]
try:
@@ -130,19 +139,19 @@ class DupeGuru(DupeGuruBase, QObject):
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.add_to_ignore_list(dupe)
self.remove_duplicates(duplicates)
def add_selected_to_ignore_list(self):
dupes = self.without_ref(self.selected_dupes)
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.main_window._confirm(title, msg):
DupeGuruBase.add_selected_to_ignore_list(self)
def apply_filter(self, filter):
DupeGuruBase.apply_filter(self, filter)
self.emit(SIGNAL('resultsChanged()'))
def askForRegCode(self):
self.reg.ask_for_code()
@demo_method
def copy_or_move_marked(self, copy):
opname = 'copy' if copy else 'move'
@@ -156,10 +165,27 @@ class DupeGuru(DupeGuruBase, QObject):
delete_marked = demo_method(DupeGuruBase.delete_marked)
def make_reference(self, duplicates):
DupeGuruBase.make_reference(self, duplicates)
def make_selected_reference(self):
DupeGuruBase.make_selected_reference(self)
self.emit(SIGNAL('resultsChanged()'))
def remove_duplicates(self, duplicates):
DupeGuruBase.remove_duplicates(self, duplicates)
self.emit(SIGNAL('resultsChanged()'))
def remove_selected(self):
dupes = self.without_ref(self.selected_dupes)
if not dupes:
return
title = "Remove duplicates"
msg = "You are about to remove {0} files from results. Continue?".format(len(dupes))
if self.main_window._confirm(title, msg):
DupeGuruBase.remove_selected(self)
#--- Public
def askForRegCode(self):
self.reg.ask_for_code()
def mark_all(self):
self.results.mark_all()
self.emit(SIGNAL('dupeMarkingChanged()'))
@@ -174,18 +200,7 @@ class DupeGuru(DupeGuruBase, QObject):
def openDebugLog(self):
debugLogPath = op.join(self.appdata, 'debug.log')
url = QUrl.fromLocalFile(debugLogPath)
QDesktopServices.openUrl(url)
def open_selected(self):
if not self.selected_dupes:
return
url = QUrl.fromLocalFile(unicode(self.selected_dupes[0].path))
QDesktopServices.openUrl(url)
def remove_duplicates(self, duplicates):
self.results.remove_duplicates(duplicates)
self.emit(SIGNAL('resultsChanged()'))
self._open_path(debugLogPath)
def remove_marked_duplicates(self):
marked = [d for d in self.results.dupes if self.results.is_marked(d)]
@@ -199,14 +214,8 @@ class DupeGuru(DupeGuruBase, QObject):
logging.warning("dupeGuru Warning: %s" % unicode(e))
return False
def reveal_selected(self):
if not self.selected_dupes:
return
url = QUrl.fromLocalFile(unicode(self.selected_dupe[0].path[:-1]))
QDesktopServices.openUrl(url)
def select_duplicate(self, dupe):
self._select_dupes([dupe])
def select_dupes(self, dupes):
self._select_dupes(dupes)
def show_about_box(self):
self.about_box.show()
@@ -245,9 +254,7 @@ class DupeGuru(DupeGuruBase, QObject):
def job_finished(self, jobid):
self.emit(SIGNAL('resultsChanged()'))
if jobid == JOB_LOAD:
self.emit(SIGNAL('directoriesChanged()'))
elif jobid in (JOB_MOVE, JOB_COPY, JOB_DELETE) and self.last_op_error_count > 0:
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)
elif jobid == JOB_SCAN:

View File

@@ -26,7 +26,6 @@ class DirectoriesDialog(QDialog, Ui_DirectoriesDialog):
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)
@@ -60,10 +59,6 @@ class DirectoriesDialog(QDialog, Ui_DirectoriesDialog):
return
self.lastAddedFolder = dirpath
self.app.add_directory(dirpath)
self.directoriesModel.reset()
def directoriesChanged(self):
self.directoriesModel.reset()
def doneButtonClicked(self):
self.hide()
@@ -76,8 +71,7 @@ class DirectoriesDialog(QDialog, Ui_DirectoriesDialog):
node = index.internalPointer()
if node.parent is None:
row = index.row()
del self.app.directories[row]
self.directoriesModel.reset()
self.app.remove_directory(row)
def selectionChanged(self, selected, deselected):
self._updateRemoveButton()

View File

@@ -12,7 +12,9 @@ from PyQt4.QtCore import QModelIndex, Qt, QRect, QEvent, QPoint, QUrl
from PyQt4.QtGui import (QComboBox, QStyledItemDelegate, QMouseEvent, QApplication, QBrush, QStyle,
QStyleOptionComboBox, QStyleOptionViewItemV4)
from qtlib.tree_model import TreeNode, TreeModel
from qtlib.tree_model import RefNode, TreeModel
from core.gui.directory_tree import DirectoryTree
HEADERS = ['Name', 'State']
STATES = ['Normal', 'Reference', 'Excluded']
@@ -58,36 +60,16 @@ class DirectoriesDelegate(QStyledItemDelegate):
editor.setGeometry(option.rect)
class DirectoryNode(TreeNode):
def __init__(self, model, parent, ref, row):
TreeNode.__init__(self, model, parent, row)
self.ref = ref
def _createNode(self, ref, row):
return DirectoryNode(self.model, self, ref, row)
def _getChildren(self):
return self.model.dirs.get_subfolders(self.ref)
@property
def name(self):
if self.parent is not None:
return self.ref[-1]
else:
return unicode(self.ref)
class DirectoriesModel(TreeModel):
def __init__(self, app):
self.app = app
self.dirs = app.directories
TreeModel.__init__(self)
self.model = DirectoryTree(self, app)
def _createNode(self, ref, row):
return DirectoryNode(self, None, ref, row)
return RefNode(self, None, ref, row)
def _getChildren(self):
return self.dirs
return list(self.model)
def columnCount(self, parent):
return 2
@@ -96,15 +78,16 @@ class DirectoriesModel(TreeModel):
if not index.isValid():
return None
node = index.internalPointer()
ref = node.ref
if role == Qt.DisplayRole:
if index.column() == 0:
return node.name
return ref.name
else:
return STATES[self.dirs.get_state(node.ref)]
return STATES[ref.state]
elif role == Qt.EditRole and index.column() == 1:
return self.dirs.get_state(node.ref)
return ref.state
elif role == Qt.ForegroundRole:
state = self.dirs.get_state(node.ref)
state = ref.state
if state == 1:
return QBrush(Qt.blue)
elif state == 2:
@@ -121,7 +104,7 @@ class DirectoriesModel(TreeModel):
urls = unicode(unquoted, 'utf-8').split('\r\n')
paths = [unicode(QUrl(url).toLocalFile()) for url in urls if url]
for path in paths:
self.app.add_directory(path)
self.model.add_directory(path)
self.reset()
return True
@@ -146,7 +129,8 @@ class DirectoriesModel(TreeModel):
if not index.isValid() or role != Qt.EditRole or index.column() != 1:
return False
node = index.internalPointer()
self.dirs.set_state(node.ref, value)
ref = node.ref
ref.state = value
return True
def supportedDropActions(self):
@@ -154,3 +138,7 @@ class DirectoriesModel(TreeModel):
# work with ActionMove either. So screw that, and accept anything.
return Qt.ActionMask
#--- model --> view
def refresh(self):
self.reset()

View File

@@ -142,13 +142,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
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)
self.app.add_selected_to_ignore_list()
def applyFilterTriggered(self):
title = "Apply Filter"
@@ -211,7 +205,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
QDesktopServices.openUrl(url)
def makeReferenceTriggered(self):
self.app.make_reference(self.resultsView.selectedDupes())
self.app.make_selected_reference()
def markAllTriggered(self):
self.app.mark_all()
@@ -254,13 +248,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
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)
self.app.remove_selected()
def renameTriggered(self):
self.resultsView.edit(self.resultsView.selectionModel().currentIndex())
@@ -324,7 +312,5 @@ class MainWindow(QMainWindow, Ui_MainWindow):
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)
self.app.select_dupes(self.resultsView.selectedDupes())

View File

@@ -1,43 +0,0 @@
#!/usr/bin/env python
# Created By: Virgil Dupras
# Created On: 2009-05-22
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
# On Windows, PyInstaller is used to build an exe (py2exe creates a very bad looking icon)
# The release version is outdated. Use at least r672 on http://svn.pyinstaller.org/trunk
import os
import os.path as op
import shutil
from hsutil.build import print_and_do
from app import DupeGuru
# Removing build and dist
if op.exists('build'):
shutil.rmtree('build')
if op.exists('dist'):
shutil.rmtree('dist')
version = DupeGuru.VERSION
versioncomma = version.replace('.', ', ') + ', 0'
verinfo = open('verinfo').read()
verinfo = verinfo.replace('$versioncomma', versioncomma).replace('$version', version)
fp = open('verinfo_tmp', 'w')
fp.write(verinfo)
fp.close()
print_and_do("python C:\\Python26\\pyinstaller\\Build.py dgme.spec")
os.remove('verinfo_tmp')
print_and_do("del dist\\*90.dll") # They're in vcredist, no need to include them
print_and_do("xcopy /Y /S /I ..\\..\\help_me\\dupeguru_me_help dist\\help")
# AdvancedInstaller.com has to be in your PATH
shutil.copy('installer.aip', 'installer_tmp.aip') # this is so we don'a have to re-commit installer.aip at every version change
print_and_do('AdvancedInstaller.com /edit installer_tmp.aip /SetVersion %s' % version)
print_and_do('AdvancedInstaller.com /build installer_tmp.aip -force')
os.remove('installer_tmp.aip')

View File

@@ -1,18 +0,0 @@
#!/usr/bin/env python
# Created By: Virgil Dupras
# Created On: 2009-05-22
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
import os
import os.path as op
from hsutil.build import print_and_do, build_all_qt_ui
build_all_qt_ui(op.join('..', '..', 'qtlib', 'ui'))
build_all_qt_ui(op.join('..', 'base'))
build_all_qt_ui('.')
print_and_do("pyrcc4 {0} > {1}".format(op.join('..', 'base', 'dg.qrc'), op.join('..', 'base', 'dg_rc.py')))

View File

@@ -1,45 +0,0 @@
#!/usr/bin/env python
# Created By: Virgil Dupras
# Created On: 2009-05-22
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
# On Windows, PyInstaller is used to build an exe (py2exe creates a very bad looking icon
# The release version is outdated. Use at least r672 on http://svn.pyinstaller.org/trunk
import os
import os.path as op
import shutil
from app import DupeGuru
def print_and_do(cmd):
print cmd
os.system(cmd)
# Removing build and dist
if op.exists('build'):
shutil.rmtree('build')
if op.exists('dist'):
shutil.rmtree('dist')
version = DupeGuru.VERSION
versioncomma = version.replace('.', ', ') + ', 0'
verinfo = open('verinfo').read()
verinfo = verinfo.replace('$versioncomma', versioncomma).replace('$version', version)
fp = open('verinfo_tmp', 'w')
fp.write(verinfo)
fp.close()
print_and_do("python C:\\Python26\\pyinstaller\\Build.py dgpe.spec")
os.remove('verinfo_tmp')
print_and_do("del dist\\*90.dll") # They're in vcredist, no need to include them
print_and_do("xcopy /Y /S /I ..\\..\\help_pe\\dupeguru_pe_help dist\\help")
# AdvancedInstaller.com has to be in your PATH
shutil.copy('installer.aip', 'installer_tmp.aip') # this is so we don'a have to re-commit installer.aip at every version change
print_and_do('AdvancedInstaller.com /edit installer_tmp.aip /SetVersion %s' % version)
print_and_do('AdvancedInstaller.com /build installer_tmp.aip -force')
os.remove('installer_tmp.aip')

View File

@@ -10,13 +10,6 @@
import os
import os.path as op
from hsutil.build import print_and_do, build_all_qt_ui
build_all_qt_ui(op.join('..', '..', 'qtlib', 'ui'))
build_all_qt_ui(op.join('..', 'base'))
build_all_qt_ui('.')
print_and_do("pyrcc4 {0} > {1}".format(op.join('..', 'base', 'dg.qrc'), op.join('..', 'base', 'dg_rc.py')))
def move(src, dst):
if not op.exists(src):
return

View File

@@ -26,7 +26,7 @@ class Directories(DirectoriesBase):
class DupeGuru(DupeGuruBase):
LOGO_NAME = 'logo_se'
NAME = 'dupeGuru'
VERSION = '2.9.1'
VERSION = '2.9.2'
DELTA_COLUMNS = frozenset([2, 4, 5])
def __init__(self):

View File

@@ -1,45 +0,0 @@
#!/usr/bin/env python
# Created By: Virgil Dupras
# Created On: 2009-05-24
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
# On Windows, PyInstaller is used to build an exe (py2exe creates a very bad looking icon
# The release version is outdated. Use at least r672 on http://svn.pyinstaller.org/trunk
import os
import os.path as op
import shutil
from app import DupeGuru
def print_and_do(cmd):
print cmd
os.system(cmd)
# Removing build and dist
if op.exists('build'):
shutil.rmtree('build')
if op.exists('dist'):
shutil.rmtree('dist')
version = DupeGuru.VERSION
versioncomma = version.replace('.', ', ') + ', 0'
verinfo = open('verinfo').read()
verinfo = verinfo.replace('$versioncomma', versioncomma).replace('$version', version)
fp = open('verinfo_tmp', 'w')
fp.write(verinfo)
fp.close()
print_and_do("python C:\\Python26\\pyinstaller\\Build.py dgse.spec")
os.remove('verinfo_tmp')
print_and_do("del dist\\*90.dll") # They're in vcredist, no need to include them
print_and_do("xcopy /Y /S /I ..\\..\\help_se\\dupeguru_help dist\\help")
# AdvancedInstaller.com has to be in your PATH
shutil.copy('installer.aip', 'installer_tmp.aip') # this is so we don'a have to re-commit installer.aip at every version change
print_and_do('AdvancedInstaller.com /edit installer_tmp.aip /SetVersion %s' % version)
print_and_do('AdvancedInstaller.com /build installer_tmp.aip -force')
os.remove('installer_tmp.aip')

View File

@@ -1,18 +0,0 @@
#!/usr/bin/env python
# Created By: Virgil Dupras
# Created On: 2009-05-24
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
import os
import os.path as op
from hsutil.build import print_and_do, build_all_qt_ui
build_all_qt_ui(op.join('..', '..', 'qtlib', 'ui'))
build_all_qt_ui(op.join('..', 'base'))
build_all_qt_ui('.')
print_and_do("pyrcc4 {0} > {1}".format(op.join('..', 'base', 'dg.qrc'), op.join('..', 'base', 'dg_rc.py')))

View File

@@ -1,30 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DOCUMENT type="Advanced Installer" CreateVersion="4.4.1" version="4.9.2" modules="professional" RootPath="." Language="en">
<DOCUMENT type="Advanced Installer" CreateVersion="4.4.1" version="7.5" modules="professional" RootPath="." Language="en">
<COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
<ROW Property="AI_DESKTOP_SH" Value="1" Type="4"/>
<ROW Property="AI_QUICKLAUNCH_SH" Value="1" Type="4"/>
<ROW Property="AI_FINDEXE_TITLE" Value="Select the installation package for [|ProductName]" ValueLocId="AI.Property.FindExeTitle"/>
<ROW Property="AI_SHORTCUTSREG" Value="0|0|0|"/>
<ROW Property="AI_STARTMENU_SH" Value="1" Type="4"/>
<ROW Property="AI_STARTUP_SH" Value="1" Type="4"/>
<ROW Property="ALLUSERS" Value="2"/>
<ROW Property="ARPCOMMENTS" Value="This installer database contains the logic and data required to install [|ProductName]." ValueLocId="*"/>
<ROW Property="ARPCONTACT" Value="support@hardcoded.net"/>
<ROW Property="ARPHELPLINK" Value="http://www.hardcoded.net/support/"/>
<ROW Property="ARPURLINFOABOUT" Value="http://www.hardcoded.net/dupeguru/"/>
<ROW Property="ARPURLUPDATEINFO" Value="http://www.hardcoded.net/dupeguru/"/>
<ROW Property="BannerBitmap" Value="default_banner.bmp" Type="1"/>
<ROW Property="BannerBitmap" MultiBuildValue="DefaultBuild:banner_image.jpg" Type="1"/>
<ROW Property="CTRLS" Value="2"/>
<ROW Property="DialogBitmap" Value="default_dialog.bmp" Type="1"/>
<ROW Property="DialogBitmap" MultiBuildValue="DefaultBuild:dialog_image.jpg" Type="1"/>
<ROW Property="Manufacturer" Value="Hardcoded Software" ValueLocId="*"/>
<ROW Property="ProductCode" Value="1033:{D1E765C2-98C4-49AF-80DA-A5F803EB4FC3} "/>
<ROW Property="ProductCode" Value="1033:{D1E765C2-98C4-49AF-80DA-A5F803EB4FC3} " Type="16"/>
<ROW Property="ProductLanguage" Value="1033"/>
<ROW Property="ProductName" Value="dupeGuru" ValueLocId="*"/>
<ROW Property="ProductVersion" Value="2.7.0"/>
<ROW Property="RUNAPPLICATION" Value="1" Type="4"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
<ROW Property="UpgradeCode" Value="{33E0D6C8-D7C6-46ED-B1A9-ECFE409EC9D5}"/>
<ROW Property="WindowsFamily9X" Value="Windows 9x/ME"/>
<ROW Property="WindowsTypeNT" Value="Windows 2000"/>
<ROW Property="WindowsFamily9X" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
<ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows 2000" ValueLocId="-"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
<ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1"/>
@@ -33,132 +30,124 @@
<ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
<ROW Directory="accessible_DIR" Directory_Parent="qt4_plugins_DIR" DefaultDir="access~1|accessible"/>
<ROW Directory="codecs_DIR" Directory_Parent="qt4_plugins_DIR" DefaultDir="codecs"/>
<ROW Directory="help_DIR" Directory_Parent="APPDIR" DefaultDir="help"/>
<ROW Directory="gen_py_DIR" Directory_Parent="support_DIR" DefaultDir="gen_py"/>
<ROW Directory="iconengines_DIR" Directory_Parent="qt4_plugins_DIR" DefaultDir="iconen~1|iconengines"/>
<ROW Directory="imageformats_DIR" Directory_Parent="qt4_plugins_DIR" DefaultDir="imagef~1|imageformats"/>
<ROW Directory="images_DIR" Directory_Parent="help_DIR" DefaultDir="images"/>
<ROW Directory="qt4_plugins_DIR" Directory_Parent="APPDIR" DefaultDir="qt4_pl~1|qt4_plugins"/>
<ROW Directory="support_DIR" Directory_Parent="APPDIR" DefaultDir="support"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
<ROW Component="AIShRegAnswer" ComponentId="{775090B3-2E56-40F5-9DD8-24A2F82DA601}" Directory_="APPDIR" Attributes="4" KeyPath="AIShRegAnswer" FullKeyPath="HK_UM\Software\Caphyon\Advanced Installer\Installs\[ProductCode]\AIShRegAnswer"/>
<ROW Component="MSVCP90.dll" ComponentId="{EA7F99CE-AA38-4676-863A-4BBD78B635F3}" Directory_="APPDIR" Attributes="0" KeyPath="MSVCP90.dll" FullKeyPath="APPDIR\MSVCP90.dll"/>
<ROW Component="MSVCR90.dll" ComponentId="{9C4646F4-7CB2-4BA2-9F00-16B38EDEF590}" Directory_="APPDIR" Attributes="0" KeyPath="MSVCR90.dll" FullKeyPath="APPDIR\MSVCR90.dll"/>
<ROW Component="POWRPROF.dll" ComponentId="{2896ECE4-56FD-452A-A1CE-5C95919136C6}" Directory_="APPDIR" Attributes="0" KeyPath="POWRPROF.dll" FullKeyPath="APPDIR\POWRPROF.dll"/>
<ROW Component="PyWinTypes26.dll" ComponentId="{B664FF8C-C60F-423C-9AC4-26144896E583}" Directory_="APPDIR" Attributes="0" KeyPath="PyWinTypes26.dll" FullKeyPath="APPDIR\PyWinTypes26.dll"/>
<ROW Component="QtCore4.dll" ComponentId="{F517476C-BC6D-40B6-A063-5A10680ECA05}" Directory_="APPDIR" Attributes="0" KeyPath="QtCore4.dll" FullKeyPath="APPDIR\QtCore4.dll"/>
<ROW Component="QtGui4.dll" ComponentId="{4915BAC4-AFB0-42E1-BF2E-D4C3E58D4BEE}" Directory_="APPDIR" Attributes="0" KeyPath="QtGui4.dll" FullKeyPath="APPDIR\QtGui4.dll"/>
<ROW Component="SHLWAPI.dll" ComponentId="{E533841B-68B8-459F-8C67-6D7721B9E926}" Directory_="APPDIR" Attributes="0" KeyPath="SHLWAPI.dll" FullKeyPath="APPDIR\SHLWAPI.dll"/>
<ROW Component="bz2.pyd" ComponentId="{E03E8F51-0E8D-40A2-9ED0-A8EA0ED4CD19}" Directory_="APPDIR" Attributes="0" KeyPath="bz2.pyd" FullKeyPath="APPDIR"/>
<ROW Component="credits.htm" ComponentId="{0F4F2A16-8BC1-44C0-AEAF-B62CD08992B9}" Directory_="help_DIR" Attributes="0" KeyPath="credits.htm" FullKeyPath="APPDIR\help"/>
<ROW Component="dupeGuru.exe" ComponentId="{A8FFC84F-B54B-4883-B9FD-5C545AF0E51C}" Directory_="APPDIR" Attributes="0" KeyPath="dupeGuru.exe" FullKeyPath="APPDIR\dupeGuru.exe"/>
<ROW Component="hs_title.png" ComponentId="{161F629F-409B-468A-AD7C-8832B1FA7D83}" Directory_="images_DIR" Attributes="0" KeyPath="hs_title.png" FullKeyPath="APPDIR\help\images"/>
<ROW Component="iertutil.dll" ComponentId="{408B35EA-AD4A-449A-9A6C-DE5301667BB4}" Directory_="APPDIR" Attributes="0" KeyPath="iertutil.dll" FullKeyPath="APPDIR\iertutil.dll"/>
<ROW Component="mfc90.dll" ComponentId="{570D1F1E-F914-4E0A-AC2B-A8DAEDA57D06}" Directory_="APPDIR" Attributes="0" KeyPath="mfc90.dll" FullKeyPath="APPDIR\mfc90.dll"/>
<ROW Component="python26.dll" ComponentId="{C47E3AEB-FCE1-4A7D-90AF-26D52100756F}" Directory_="APPDIR" Attributes="0" KeyPath="python26.dll" FullKeyPath="APPDIR\python26.dll"/>
<ROW Component="pythoncom26.dll" ComponentId="{474C48BA-8C13-428C-B932-49C65A1619FC}" Directory_="APPDIR" Attributes="0" KeyPath="pythoncom26.dll" FullKeyPath="APPDIR\pythoncom26.dll"/>
<ROW Component="qcncodecs4.dll" ComponentId="{1FA15E05-79B4-490E-8BE7-2915DAFECDA0}" Directory_="codecs_DIR" Attributes="0" KeyPath="qcncodecs4.dll" FullKeyPath="APPDIR\qt4_plugins\codecs\qcncodecs4.dll"/>
<ROW Component="qgif4.dll" ComponentId="{12390BD7-63E5-4BAE-A760-84D6E47387F3}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qgif4.dll" FullKeyPath="APPDIR\qt4_plugins\imageformats\qgif4.dll"/>
<ROW Component="qico4.dll" ComponentId="{7EC94828-5141-4383-BB9C-89C6AE543237}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qico4.dll" FullKeyPath="APPDIR\qt4_plugins\imageformats\qico4.dll"/>
<ROW Component="qjpcodecs4.dll" ComponentId="{A607689B-97DD-4F1F-9655-7EEC2D934A75}" Directory_="codecs_DIR" Attributes="0" KeyPath="qjpcodecs4.dll" FullKeyPath="APPDIR\qt4_plugins\codecs\qjpcodecs4.dll"/>
<ROW Component="qjpeg4.dll" ComponentId="{58EB6546-1E7D-48E3-A407-08B945D68317}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qjpeg4.dll" FullKeyPath="APPDIR\qt4_plugins\imageformats\qjpeg4.dll"/>
<ROW Component="qkrcodecs4.dll" ComponentId="{3D322ADA-9D38-4B5F-8335-44BDE935D5D7}" Directory_="codecs_DIR" Attributes="0" KeyPath="qkrcodecs4.dll" FullKeyPath="APPDIR\qt4_plugins\codecs\qkrcodecs4.dll"/>
<ROW Component="qmng4.dll" ComponentId="{11B243B6-A6E5-4282-A58B-5A4F5A2CB253}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qmng4.dll" FullKeyPath="APPDIR\qt4_plugins\imageformats\qmng4.dll"/>
<ROW Component="qsvg4.dll" ComponentId="{D689DDEB-D4E9-4DE0-B32B-85FD25C40726}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qsvg4.dll" FullKeyPath="APPDIR\qt4_plugins\imageformats\qsvg4.dll"/>
<ROW Component="qsvgicon4.dll" ComponentId="{6EF94FDD-3E92-4886-925F-B264C962E7EF}" Directory_="iconengines_DIR" Attributes="0" KeyPath="qsvgicon4.dll" FullKeyPath="APPDIR\qt4_plugins\iconengines\qsvgicon4.dll"/>
<ROW Component="qt4_plugins" ComponentId="{EC4153B6-0269-4A4F-BF8A-46CB0884773E}" Directory_="qt4_plugins_DIR" Attributes="0"/>
<ROW Component="qtaccessiblewidgets4.dll" ComponentId="{0DB9EE4C-922F-42AC-80CC-4EA3CBBB1629}" Directory_="accessible_DIR" Attributes="0" KeyPath="qtaccessiblewidgets4.dll" FullKeyPath="APPDIR\qt4_plugins\accessible\qtaccessiblewidgets4.dll"/>
<ROW Component="qtiff4.dll" ComponentId="{660F482B-9508-4A26-BC1A-610E84829CA4}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qtiff4.dll" FullKeyPath="APPDIR\qt4_plugins\imageformats\qtiff4.dll"/>
<ROW Component="qtwcodecs4.dll" ComponentId="{68610953-B652-4340-BBB9-B1EEB3A6AF7A}" Directory_="codecs_DIR" Attributes="0" KeyPath="qtwcodecs4.dll" FullKeyPath="APPDIR\qt4_plugins\codecs\qtwcodecs4.dll"/>
<ROW Component="updater.exe" ComponentId="{CB63C33D-EB1B-420A-8BAA-CD380923F12B}" Directory_="APPDIR" Attributes="0" KeyPath="updater.exe" FullKeyPath="APPDIR\updater.exe"/>
<ROW Component="urlmon.dll" ComponentId="{BCF9A9E0-49E9-4EB2-9159-694FDF98F0AA}" Directory_="APPDIR" Attributes="0" KeyPath="urlmon.dll" FullKeyPath="APPDIR\urlmon.dll"/>
<ROW Component="AIShRegAnswer" ComponentId="{775090B3-2E56-40F5-9DD8-24A2F82DA601}" Directory_="APPDIR" Attributes="4" KeyPath="AIShRegAnswer"/>
<ROW Component="AI_ExePath" ComponentId="{56EC90C1-F906-4F6E-95B2-1876CAA651C2}" Directory_="APPDIR" Attributes="4" KeyPath="AI_ExePath"/>
<ROW Component="PyWinTypes26.dll" ComponentId="{B664FF8C-C60F-423C-9AC4-26144896E583}" Directory_="APPDIR" Attributes="0" KeyPath="PyWinTypes26.dll"/>
<ROW Component="QtCore4.dll" ComponentId="{F517476C-BC6D-40B6-A063-5A10680ECA05}" Directory_="APPDIR" Attributes="0" KeyPath="QtCore4.dll"/>
<ROW Component="QtGui4.dll" ComponentId="{4915BAC4-AFB0-42E1-BF2E-D4C3E58D4BEE}" Directory_="APPDIR" Attributes="0" KeyPath="QtGui4.dll"/>
<ROW Component="bz2.pyd" ComponentId="{E03E8F51-0E8D-40A2-9ED0-A8EA0ED4CD19}" Directory_="APPDIR" Attributes="0" KeyPath="bz2.pyd" Type="0"/>
<ROW Component="dupeGuru.exe" ComponentId="{A8FFC84F-B54B-4883-B9FD-5C545AF0E51C}" Directory_="APPDIR" Attributes="0" KeyPath="dupeGuru.exe"/>
<ROW Component="init_.py" ComponentId="{7B86D715-9C99-4021-8A3C-437BEA4986BC}" Directory_="gen_py_DIR" Attributes="0" KeyPath="init_.py" Type="0"/>
<ROW Component="python26.dll" ComponentId="{C47E3AEB-FCE1-4A7D-90AF-26D52100756F}" Directory_="APPDIR" Attributes="0" KeyPath="python26.dll"/>
<ROW Component="pythoncom26.dll" ComponentId="{474C48BA-8C13-428C-B932-49C65A1619FC}" Directory_="APPDIR" Attributes="0" KeyPath="pythoncom26.dll"/>
<ROW Component="qcncodecs4.dll" ComponentId="{1FA15E05-79B4-490E-8BE7-2915DAFECDA0}" Directory_="codecs_DIR" Attributes="0" KeyPath="qcncodecs4.dll"/>
<ROW Component="qgif4.dll" ComponentId="{12390BD7-63E5-4BAE-A760-84D6E47387F3}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qgif4.dll"/>
<ROW Component="qico4.dll" ComponentId="{7EC94828-5141-4383-BB9C-89C6AE543237}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qico4.dll"/>
<ROW Component="qjpcodecs4.dll" ComponentId="{A607689B-97DD-4F1F-9655-7EEC2D934A75}" Directory_="codecs_DIR" Attributes="0" KeyPath="qjpcodecs4.dll"/>
<ROW Component="qjpeg4.dll" ComponentId="{58EB6546-1E7D-48E3-A407-08B945D68317}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qjpeg4.dll"/>
<ROW Component="qkrcodecs4.dll" ComponentId="{3D322ADA-9D38-4B5F-8335-44BDE935D5D7}" Directory_="codecs_DIR" Attributes="0" KeyPath="qkrcodecs4.dll"/>
<ROW Component="qmng4.dll" ComponentId="{11B243B6-A6E5-4282-A58B-5A4F5A2CB253}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qmng4.dll"/>
<ROW Component="qsvg4.dll" ComponentId="{D689DDEB-D4E9-4DE0-B32B-85FD25C40726}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qsvg4.dll"/>
<ROW Component="qsvgicon4.dll" ComponentId="{6EF94FDD-3E92-4886-925F-B264C962E7EF}" Directory_="iconengines_DIR" Attributes="0" KeyPath="qsvgicon4.dll"/>
<ROW Component="qtaccessiblecompatwidgets4.dll" ComponentId="{D0CC18F8-1B1D-40E6-BFB3-14F2DE96B6ED}" Directory_="accessible_DIR" Attributes="0" KeyPath="qtaccessiblecompatwidgets4.dll"/>
<ROW Component="qtaccessiblewidgets4.dll" ComponentId="{0DB9EE4C-922F-42AC-80CC-4EA3CBBB1629}" Directory_="accessible_DIR" Attributes="0" KeyPath="qtaccessiblewidgets4.dll"/>
<ROW Component="qtiff4.dll" ComponentId="{660F482B-9508-4A26-BC1A-610E84829CA4}" Directory_="imageformats_DIR" Attributes="0" KeyPath="qtiff4.dll"/>
<ROW Component="qtwcodecs4.dll" ComponentId="{68610953-B652-4340-BBB9-B1EEB3A6AF7A}" Directory_="codecs_DIR" Attributes="0" KeyPath="qtwcodecs4.dll"/>
<ROW Component="updater.exe" ComponentId="{CB63C33D-EB1B-420A-8BAA-CD380923F12B}" Directory_="APPDIR" Attributes="0" KeyPath="updater.exe"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
<ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="updater.exe dupeGuru.exe AIShRegAnswer iertutil.dll mfc90.dll MSVCP90.dll MSVCR90.dll POWRPROF.dll python26.dll pythoncom26.dll PyWinTypes26.dll QtCore4.dll QtGui4.dll SHLWAPI.dll urlmon.dll bz2.pyd qtaccessiblewidgets4.dll qcncodecs4.dll qjpcodecs4.dll qkrcodecs4.dll qtwcodecs4.dll qsvgicon4.dll qgif4.dll qico4.dll qjpeg4.dll qmng4.dll qsvg4.dll qtiff4.dll qt4_plugins credits.htm hs_title.png"/>
<ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="updater.exe dupeGuru.exe AIShRegAnswer python26.dll pythoncom26.dll PyWinTypes26.dll QtCore4.dll QtGui4.dll bz2.pyd qtaccessiblewidgets4.dll qcncodecs4.dll qjpcodecs4.dll qkrcodecs4.dll qtwcodecs4.dll qsvgicon4.dll qgif4.dll qico4.dll qjpeg4.dll qmng4.dll qsvg4.dll qtiff4.dll AI_ExePath qtaccessiblecompatwidgets4.dll init_.py"/>
<ATTRIBUTE name="CurrentFeature" value="MainFeature"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
<ROW File="MSVCP90.dll" Component_="MSVCP90.dll" FileName="MSVCP90.dll" Attributes="0" SourcePath="dist\MSVCP90.dll" SelfReg="false" Sequence="5"/>
<ROW File="MSVCR90.dll" Component_="MSVCR90.dll" FileName="MSVCR90.dll" Attributes="0" SourcePath="dist\MSVCR90.dll" SelfReg="false" Sequence="6"/>
<ROW File="POWRPROF.dll" Component_="POWRPROF.dll" FileName="POWRPROF.dll" Attributes="0" SourcePath="dist\POWRPROF.dll" SelfReg="false" Sequence="7"/>
<ROW File="PyQt4.QtCore.pyd" Component_="bz2.pyd" FileName="PyQt4Q~1.pyd|PyQt4.QtCore.pyd" Attributes="0" SourcePath="dist\PyQt4.QtCore.pyd" SelfReg="false" Sequence="17"/>
<ROW File="PyQt4.QtGui.pyd" Component_="bz2.pyd" FileName="PyQt4Q~2.pyd|PyQt4.QtGui.pyd" Attributes="0" SourcePath="dist\PyQt4.QtGui.pyd" SelfReg="false" Sequence="18"/>
<ROW File="PyWinTypes26.dll" Component_="PyWinTypes26.dll" FileName="PyWinT~1.dll|PyWinTypes26.dll" Attributes="0" SourcePath="dist\PyWinTypes26.dll" SelfReg="false" Sequence="10"/>
<ROW File="QtCore4.dll" Component_="QtCore4.dll" FileName="QtCore4.dll" Attributes="0" SourcePath="dist\QtCore4.dll" SelfReg="false" Sequence="11"/>
<ROW File="QtGui4.dll" Component_="QtGui4.dll" FileName="QtGui4.dll" Attributes="0" SourcePath="dist\QtGui4.dll" SelfReg="false" Sequence="12"/>
<ROW File="SHLWAPI.dll" Component_="SHLWAPI.dll" FileName="SHLWAPI.dll" Attributes="0" SourcePath="dist\SHLWAPI.dll" SelfReg="false" Sequence="13"/>
<ROW File="bz2.pyd" Component_="bz2.pyd" FileName="bz2.pyd" Attributes="0" SourcePath="dist\bz2.pyd" SelfReg="false" Sequence="15"/>
<ROW File="credits.htm" Component_="credits.htm" FileName="credits.htm" Attributes="0" SourcePath="dist\help\credits.htm" SelfReg="false" Sequence="44"/>
<ROW File="ctypes.pyd" Component_="bz2.pyd" FileName="_ctypes.pyd" Attributes="0" SourcePath="dist\_ctypes.pyd" SelfReg="false" Sequence="38"/>
<ROW File="directories.htm" Component_="credits.htm" FileName="direct~1.htm|directories.htm" Attributes="0" SourcePath="dist\help\directories.htm" SelfReg="false" Sequence="45"/>
<ROW File="PyQt4.QtCore.pyd" Component_="bz2.pyd" FileName="PyQt4Q~1.pyd|PyQt4.QtCore.pyd" Attributes="0" SourcePath="dist\PyQt4.QtCore.pyd" SelfReg="false" Sequence="10"/>
<ROW File="PyQt4.QtGui.pyd" Component_="bz2.pyd" FileName="PyQt4Q~2.pyd|PyQt4.QtGui.pyd" Attributes="0" SourcePath="dist\PyQt4.QtGui.pyd" SelfReg="false" Sequence="11"/>
<ROW File="PyWinTypes26.dll" Component_="PyWinTypes26.dll" FileName="PyWinT~1.dll|PyWinTypes26.dll" Attributes="0" SourcePath="dist\PyWinTypes26.dll" SelfReg="false" Sequence="5"/>
<ROW File="QtCore4.dll" Component_="QtCore4.dll" FileName="QtCore4.dll" Attributes="0" SourcePath="dist\QtCore4.dll" SelfReg="false" Sequence="6"/>
<ROW File="QtGui4.dll" Component_="QtGui4.dll" FileName="QtGui4.dll" Attributes="0" SourcePath="dist\QtGui4.dll" SelfReg="false" Sequence="7"/>
<ROW File="bz2.pyd" Component_="bz2.pyd" FileName="bz2.pyd" Attributes="0" SourcePath="dist\bz2.pyd" SelfReg="false" Sequence="8"/>
<ROW File="dupeGuru.exe" Component_="dupeGuru.exe" FileName="dupeGuru.exe" Attributes="0" SourcePath="dist\dupeGuru.exe" SelfReg="false" Sequence="2"/>
<ROW File="faq.htm" Component_="credits.htm" FileName="faq.htm" Attributes="0" SourcePath="dist\help\faq.htm" SelfReg="false" Sequence="46"/>
<ROW File="hardcoded.css" Component_="credits.htm" FileName="hardco~1.css|hardcoded.css" Attributes="0" SourcePath="dist\help\hardcoded.css" SelfReg="false" Sequence="47"/>
<ROW File="hashlib.pyd" Component_="bz2.pyd" FileName="_hashlib.pyd" Attributes="0" SourcePath="dist\_hashlib.pyd" SelfReg="false" Sequence="39"/>
<ROW File="hs_title.png" Component_="hs_title.png" FileName="hs_title.png" Attributes="0" SourcePath="dist\help\images\hs_title.png" SelfReg="false" Sequence="48"/>
<ROW File="iertutil.dll" Component_="iertutil.dll" FileName="iertutil.dll" Attributes="0" SourcePath="dist\iertutil.dll" SelfReg="false" Sequence="3"/>
<ROW File="intro.htm" Component_="credits.htm" FileName="intro.htm" Attributes="0" SourcePath="dist\help\intro.htm" SelfReg="false" Sequence="49"/>
<ROW File="mfc90.dll" Component_="mfc90.dll" FileName="mfc90.dll" Attributes="0" SourcePath="dist\mfc90.dll" SelfReg="false" Sequence="4"/>
<ROW File="multiprocessing.pyd" Component_="bz2.pyd" FileName="_multi~1.pyd|_multiprocessing.pyd" Attributes="0" SourcePath="dist\_multiprocessing.pyd" SelfReg="false" Sequence="40"/>
<ROW File="power_marker.htm" Component_="credits.htm" FileName="power_~1.htm|power_marker.htm" Attributes="0" SourcePath="dist\help\power_marker.htm" SelfReg="false" Sequence="50"/>
<ROW File="preferences.htm" Component_="credits.htm" FileName="prefer~1.htm|preferences.htm" Attributes="0" SourcePath="dist\help\preferences.htm" SelfReg="false" Sequence="51"/>
<ROW File="pyexpat.pyd" Component_="bz2.pyd" FileName="pyexpat.pyd" Attributes="0" SourcePath="dist\pyexpat.pyd" SelfReg="false" Sequence="16"/>
<ROW File="python26.dll" Component_="python26.dll" FileName="python26.dll" Attributes="0" SourcePath="dist\python26.dll" SelfReg="false" Sequence="8"/>
<ROW File="pythoncom26.dll" Component_="pythoncom26.dll" FileName="python~1.dll|pythoncom26.dll" Attributes="0" SourcePath="dist\pythoncom26.dll" SelfReg="false" Sequence="9"/>
<ROW File="qcncodecs4.dll" Component_="qcncodecs4.dll" FileName="qcncod~1.dll|qcncodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qcncodecs4.dll" SelfReg="false" Sequence="20"/>
<ROW File="qgif4.dll" Component_="qgif4.dll" FileName="qgif4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qgif4.dll" SelfReg="false" Sequence="25"/>
<ROW File="qico4.dll" Component_="qico4.dll" FileName="qico4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qico4.dll" SelfReg="false" Sequence="26"/>
<ROW File="qjpcodecs4.dll" Component_="qjpcodecs4.dll" FileName="qjpcod~1.dll|qjpcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qjpcodecs4.dll" SelfReg="false" Sequence="21"/>
<ROW File="qjpeg4.dll" Component_="qjpeg4.dll" FileName="qjpeg4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qjpeg4.dll" SelfReg="false" Sequence="27"/>
<ROW File="qkrcodecs4.dll" Component_="qkrcodecs4.dll" FileName="qkrcod~1.dll|qkrcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qkrcodecs4.dll" SelfReg="false" Sequence="22"/>
<ROW File="qmng4.dll" Component_="qmng4.dll" FileName="qmng4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qmng4.dll" SelfReg="false" Sequence="28"/>
<ROW File="qsvg4.dll" Component_="qsvg4.dll" FileName="qsvg4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qsvg4.dll" SelfReg="false" Sequence="29"/>
<ROW File="qsvgicon4.dll" Component_="qsvgicon4.dll" FileName="qsvgic~1.dll|qsvgicon4.dll" Attributes="0" SourcePath="dist\qt4_plugins\iconengines\qsvgicon4.dll" SelfReg="false" Sequence="24"/>
<ROW File="qtaccessiblewidgets4.dll" Component_="qtaccessiblewidgets4.dll" FileName="qtacce~2.dll|qtaccessiblewidgets4.dll" Attributes="0" SourcePath="dist\qt4_plugins\accessible\qtaccessiblewidgets4.dll" SelfReg="false" Sequence="19"/>
<ROW File="qtiff4.dll" Component_="qtiff4.dll" FileName="qtiff4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qtiff4.dll" SelfReg="false" Sequence="30"/>
<ROW File="qtwcodecs4.dll" Component_="qtwcodecs4.dll" FileName="qtwcod~1.dll|qtwcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qtwcodecs4.dll" SelfReg="false" Sequence="23"/>
<ROW File="quick_start.htm" Component_="credits.htm" FileName="quick_~1.htm|quick_start.htm" Attributes="0" SourcePath="dist\help\quick_start.htm" SelfReg="false" Sequence="52"/>
<ROW File="results.htm" Component_="credits.htm" FileName="results.htm" Attributes="0" SourcePath="dist\help\results.htm" SelfReg="false" Sequence="53"/>
<ROW File="select.pyd" Component_="bz2.pyd" FileName="select.pyd" Attributes="0" SourcePath="dist\select.pyd" SelfReg="false" Sequence="31"/>
<ROW File="sip.pyd" Component_="bz2.pyd" FileName="sip.pyd" Attributes="0" SourcePath="dist\sip.pyd" SelfReg="false" Sequence="32"/>
<ROW File="socket.pyd" Component_="bz2.pyd" FileName="_socket.pyd" Attributes="0" SourcePath="dist\_socket.pyd" SelfReg="false" Sequence="41"/>
<ROW File="ssl.pyd" Component_="bz2.pyd" FileName="_ssl.pyd" Attributes="0" SourcePath="dist\_ssl.pyd" SelfReg="false" Sequence="42"/>
<ROW File="unicodedata.pyd" Component_="bz2.pyd" FileName="unicod~1.pyd|unicodedata.pyd" Attributes="0" SourcePath="dist\unicodedata.pyd" SelfReg="false" Sequence="33"/>
<ROW File="updater.exe" Component_="updater.exe" FileName="updater.exe" Attributes="0" SourcePath="&lt;updater.exe&gt;" SelfReg="false" Sequence="1"/>
<ROW File="urlmon.dll" Component_="urlmon.dll" FileName="urlmon.dll" Attributes="0" SourcePath="dist\urlmon.dll" SelfReg="false" Sequence="14"/>
<ROW File="versions.htm" Component_="credits.htm" FileName="versions.htm" Attributes="0" SourcePath="dist\help\versions.htm" SelfReg="false" Sequence="54"/>
<ROW File="win32api.pyd" Component_="bz2.pyd" FileName="win32api.pyd" Attributes="0" SourcePath="dist\win32api.pyd" SelfReg="false" Sequence="34"/>
<ROW File="win32com.shell.shell.pyd" Component_="bz2.pyd" FileName="win32c~1.pyd|win32com.shell.shell.pyd" Attributes="0" SourcePath="dist\win32com.shell.shell.pyd" SelfReg="false" Sequence="35"/>
<ROW File="win32sysloader.pyd" Component_="bz2.pyd" FileName="_win32~1.pyd|_win32sysloader.pyd" Attributes="0" SourcePath="dist\_win32sysloader.pyd" SelfReg="false" Sequence="43"/>
<ROW File="win32trace.pyd" Component_="bz2.pyd" FileName="win32t~1.pyd|win32trace.pyd" Attributes="0" SourcePath="dist\win32trace.pyd" SelfReg="false" Sequence="36"/>
<ROW File="win32ui.pyd" Component_="bz2.pyd" FileName="win32ui.pyd" Attributes="0" SourcePath="dist\win32ui.pyd" SelfReg="false" Sequence="37"/>
<ROW File="hashlib.pyd" Component_="bz2.pyd" FileName="_hashlib.pyd" Attributes="0" SourcePath="dist\_hashlib.pyd" SelfReg="false" Sequence="31"/>
<ROW File="init_.py" Component_="init_.py" FileName="__init__.py" Attributes="0" SourcePath="dist\support\gen_py\__init__.py" SelfReg="false" Sequence="36"/>
<ROW File="pyexpat.pyd" Component_="bz2.pyd" FileName="pyexpat.pyd" Attributes="0" SourcePath="dist\pyexpat.pyd" SelfReg="false" Sequence="9"/>
<ROW File="python26.dll" Component_="python26.dll" FileName="python26.dll" Attributes="0" SourcePath="dist\python26.dll" SelfReg="false" Sequence="3"/>
<ROW File="pythoncom26.dll" Component_="pythoncom26.dll" FileName="python~1.dll|pythoncom26.dll" Attributes="0" SourcePath="dist\pythoncom26.dll" SelfReg="false" Sequence="4"/>
<ROW File="qcncodecs4.dll" Component_="qcncodecs4.dll" FileName="qcncod~1.dll|qcncodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qcncodecs4.dll" SelfReg="false" Sequence="13"/>
<ROW File="qgif4.dll" Component_="qgif4.dll" FileName="qgif4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qgif4.dll" SelfReg="false" Sequence="18"/>
<ROW File="qico4.dll" Component_="qico4.dll" FileName="qico4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qico4.dll" SelfReg="false" Sequence="19"/>
<ROW File="qjpcodecs4.dll" Component_="qjpcodecs4.dll" FileName="qjpcod~1.dll|qjpcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qjpcodecs4.dll" SelfReg="false" Sequence="14"/>
<ROW File="qjpeg4.dll" Component_="qjpeg4.dll" FileName="qjpeg4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qjpeg4.dll" SelfReg="false" Sequence="20"/>
<ROW File="qkrcodecs4.dll" Component_="qkrcodecs4.dll" FileName="qkrcod~1.dll|qkrcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qkrcodecs4.dll" SelfReg="false" Sequence="15"/>
<ROW File="qmng4.dll" Component_="qmng4.dll" FileName="qmng4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qmng4.dll" SelfReg="false" Sequence="21"/>
<ROW File="qsvg4.dll" Component_="qsvg4.dll" FileName="qsvg4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qsvg4.dll" SelfReg="false" Sequence="22"/>
<ROW File="qsvgicon4.dll" Component_="qsvgicon4.dll" FileName="qsvgic~1.dll|qsvgicon4.dll" Attributes="0" SourcePath="dist\qt4_plugins\iconengines\qsvgicon4.dll" SelfReg="false" Sequence="17"/>
<ROW File="qtaccessiblecompatwidgets4.dll" Component_="qtaccessiblecompatwidgets4.dll" FileName="qtacce~1.dll|qtaccessiblecompatwidgets4.dll" Attributes="0" SourcePath="dist\qt4_plugins\accessible\qtaccessiblecompatwidgets4.dll" SelfReg="false" Sequence="35"/>
<ROW File="qtaccessiblewidgets4.dll" Component_="qtaccessiblewidgets4.dll" FileName="qtacce~2.dll|qtaccessiblewidgets4.dll" Attributes="0" SourcePath="dist\qt4_plugins\accessible\qtaccessiblewidgets4.dll" SelfReg="false" Sequence="12"/>
<ROW File="qtiff4.dll" Component_="qtiff4.dll" FileName="qtiff4.dll" Attributes="0" SourcePath="dist\qt4_plugins\imageformats\qtiff4.dll" SelfReg="false" Sequence="23"/>
<ROW File="qtwcodecs4.dll" Component_="qtwcodecs4.dll" FileName="qtwcod~1.dll|qtwcodecs4.dll" Attributes="0" SourcePath="dist\qt4_plugins\codecs\qtwcodecs4.dll" SelfReg="false" Sequence="16"/>
<ROW File="select.pyd" Component_="bz2.pyd" FileName="select.pyd" Attributes="0" SourcePath="dist\select.pyd" SelfReg="false" Sequence="24"/>
<ROW File="sip.pyd" Component_="bz2.pyd" FileName="sip.pyd" Attributes="0" SourcePath="dist\sip.pyd" SelfReg="false" Sequence="25"/>
<ROW File="socket.pyd" Component_="bz2.pyd" FileName="_socket.pyd" Attributes="0" SourcePath="dist\_socket.pyd" SelfReg="false" Sequence="32"/>
<ROW File="ssl.pyd" Component_="bz2.pyd" FileName="_ssl.pyd" Attributes="0" SourcePath="dist\_ssl.pyd" SelfReg="false" Sequence="33"/>
<ROW File="unicodedata.pyd" Component_="bz2.pyd" FileName="unicod~1.pyd|unicodedata.pyd" Attributes="0" SourcePath="dist\unicodedata.pyd" SelfReg="false" Sequence="26"/>
<ROW File="updater.exe" Component_="updater.exe" FileName="updater.exe" Attributes="0" SourcePath="&lt;AI_HOME&gt;updater.exe" SelfReg="false" Sequence="1"/>
<ROW File="win32api.pyd" Component_="bz2.pyd" FileName="win32api.pyd" Attributes="0" SourcePath="dist\win32api.pyd" SelfReg="false" Sequence="27"/>
<ROW File="win32com.shell.shell.pyd" Component_="bz2.pyd" FileName="win32c~1.pyd|win32com.shell.shell.pyd" Attributes="0" SourcePath="dist\win32com.shell.shell.pyd" SelfReg="false" Sequence="28"/>
<ROW File="win32sysloader.pyd" Component_="bz2.pyd" FileName="_win32~1.pyd|_win32sysloader.pyd" Attributes="0" SourcePath="dist\_win32sysloader.pyd" SelfReg="false" Sequence="34"/>
<ROW File="win32trace.pyd" Component_="bz2.pyd" FileName="win32t~1.pyd|win32trace.pyd" Attributes="0" SourcePath="dist\win32trace.pyd" SelfReg="false" Sequence="29"/>
<ROW File="win32ui.pyd" Component_="bz2.pyd" FileName="win32ui.pyd" Attributes="0" SourcePath="dist\win32ui.pyd" SelfReg="false" Sequence="30"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
<ROW BuildKey="DefaultBuild" BuildName="DefaultBuild" BuildOrder="1" BuildType="0" PackageName="install\dupeguru_win_[|ProductVersion]" Languages="en" InstallationType="4" CabsLocation="1" PackageType="1" FilesInsideExe="true" CreateMd5="true" ExtractionFolder="[AppDataFolder][|Manufacturer]\[|ProductName]\install" ExtUI="true" ExeName="dupeguru_win_[|ProductVersion]"/>
<ATTRIBUTE name="CurrentBuild" value="DefaultBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
<ROW Path="&lt;ui.ail&gt;"/>
<ROW Path="&lt;ui_en.ail&gt;"/>
<ROW Path="&lt;AI_DICTS&gt;ui.ail"/>
<ROW Path="&lt;AI_DICTS&gt;ui_en.ail"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
<ROW Fragment="FolderDlg.aip" Path="&lt;FolderDlg.aip&gt;"/>
<ROW Fragment="ShortcutsDlg.aip" Path="&lt;ShortcutsDlg.aip&gt;"/>
<ROW Fragment="StaticUIStrings.aip" Path="&lt;StaticUIStrings.aip&gt;"/>
<ROW Fragment="UI.aip" Path="&lt;UI.aip&gt;"/>
<ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
<ROW Fragment="FolderDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\FolderDlg.aip"/>
<ROW Fragment="SequenceDialogs.aip" Path="&lt;AI_THEMES&gt;classic\fragments\SequenceDialogs.aip"/>
<ROW Fragment="Sequences.aip" Path="&lt;AI_FRAGS&gt;Sequences.aip"/>
<ROW Fragment="ShortcutsDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\ShortcutsDlg.aip"/>
<ROW Fragment="StaticUIStrings.aip" Path="&lt;AI_FRAGS&gt;StaticUIStrings.aip"/>
<ROW Fragment="UI.aip" Path="&lt;AI_THEMES&gt;classic\fragments\UI.aip"/>
<ROW Fragment="Validation.aip" Path="&lt;AI_FRAGS&gt;Validation.aip"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiActionTextComponent">
<ROW Action="AI_DeleteLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
<ROW Action="AI_DeleteRLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
<ROW Action="AI_ExtractLzma" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiAppSearchComponent">
<ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_CU" Builds="DefaultBuild"/>
<ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_LM" Builds="DefaultBuild"/>
<ROW Property="AI_SHORTCUTSREG" Signature_="AI_ShRegOptionMachine"/>
<ROW Property="AI_SHORTCUTSREG" Signature_="AI_ShRegOptionUser"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiBinaryComponent">
<ROW Name="aicustact.dll" SourcePath="&lt;aicustact.dll&gt;"/>
<ROW Name="default_banner.bmp" SourcePath="&lt;default-banner.bmp&gt;"/>
<ROW Name="default_dialog.bmp" SourcePath="&lt;default-dialog.bmp&gt;"/>
<ROW Name="Prereq.dll" SourcePath="&lt;AI_CUSTACTS&gt;Prereq.dll"/>
<ROW Name="aicustact.dll" SourcePath="&lt;AI_CUSTACTS&gt;aicustact.dll"/>
<ROW Name="banner_image.jpg" SourcePath="&lt;AI_THEMES&gt;classic\resources\banner-image.jpg"/>
<ROW Name="dialog_image.jpg" SourcePath="&lt;AI_THEMES&gt;classic\resources\dialog-image.jpg"/>
<ROW Name="lzmaextractor.dll" SourcePath="&lt;AI_CUSTACTS&gt;lzmaextractor.dll"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiControlComponent">
<ATTRIBUTE name="FixedSizeBitmaps" value="0"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiControlConditionComponent">
<ROW Dialog_="ShortcutsDlg" Control_="StartmenuShortcutsCheckBox" Action="Show" Condition="(Not Installed)"/>
<ROW Dialog_="ShortcutsDlg" Control_="QuickLaunchShorcutsCheckBox" Action="Hide" Condition="(Not Installed)"/>
<ROW Dialog_="ShortcutsDlg" Control_="QuickLaunchShorcutsCheckBox" Action="Hide" Condition="(Not Installed) AND (VersionNT&lt;&quot;601&quot;)"/>
<ROW Dialog_="ShortcutsDlg" Control_="StartupShorcutsCheckBox" Action="Hide" Condition="(Not Installed)"/>
<ATTRIBUTE name="DeletedRows" value="ShortcutsDlg#QuickLaunchShorcutsCheckBox#Show#(Not Installed)@ShortcutsDlg#StartmenuShortcutsCheckBox#Show#(Not Installed)@ShortcutsDlg#StartupShorcutsCheckBox#Show#(Not Installed)"/>
<ATTRIBUTE name="DeletedRows" value="ShortcutsDlg#QuickLaunchShorcutsCheckBox#Show#(Not Installed) AND (VersionNT&lt;&quot;601&quot;)@ShortcutsDlg#StartupShorcutsCheckBox#Show#(Not Installed)@ShortcutsDlg#StartmenuShortcutsCheckBox#Show#(Not Installed)@ShortcutsDlg#QuickLaunchShorcutsCheckBox#Show#(Not Installed)"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiControlEventComponent">
<ROW Dialog_="FolderDlg" Control_="Back" Event="NewDialog" Argument="ShortcutsDlg" Condition="AI_INSTALL" Ordering="1"/>
@@ -172,15 +161,21 @@
<ROW Dialog_="ShortcutsDlg" Control_="Back" Event="NewDialog" Argument="WelcomeDlg" Condition="AI_INSTALL" Ordering="1"/>
<ROW Dialog_="ShortcutsDlg" Control_="Next" Event="NewDialog" Argument="FolderDlg" Condition="AI_INSTALL" Ordering="1"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCreateFolderComponent">
<ROW Directory_="qt4_plugins_DIR" Component_="qt4_plugins"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
<ROW Action="AI_AppSearchEx" Type="1" Source="Prereq.dll" Target="DoAppSearchEx"/>
<ROW Action="AI_DELETE_SHORTCUTS" Type="1" Source="aicustact.dll" Target="DeleteShortcuts"/>
<ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
<ROW Action="AI_DeleteCadLzma" Type="51" Source="AI_DeleteLzma" Target="[AI_SETUPEXEPATH]"/>
<ROW Action="AI_DeleteLzma" Type="1025" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
<ROW Action="AI_DeleteRCadLzma" Type="51" Source="AI_DeleteRLzma" Target="[AI_SETUPEXEPATH]"/>
<ROW Action="AI_DeleteRLzma" Type="1281" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
<ROW Action="AI_ExtractCadLzma" Type="51" Source="AI_ExtractLzma" Target="[AI_SETUPEXEPATH]"/>
<ROW Action="AI_ExtractLzma" Type="1025" Source="lzmaextractor.dll" Target="ExtractLZMAFiles"/>
<ROW Action="AI_FindExeLzma" Type="1" Source="lzmaextractor.dll" Target="FindEXE"/>
<ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#dupeGuru.exe]"/>
<ROW Action="AI_PREPARE_UPGRADE" Type="1" Source="aicustact.dll" Target="PrepareUpgrade"/>
<ROW Action="AI_RESTORE_LOCATION" Type="1" Source="aicustact.dll" Target="RestoreLocation"/>
<ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
<ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
<ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
<ROW Action="AI_STORE_LOCATION" Type="51" Source="ARPINSTALLLOCATION" Target="[APPDIR]"/>
<ROW Action="AI_UPDATER_UNINSTALL" Type="18" Source="updater.exe" Target="/clean silent"/>
<ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]"/>
@@ -188,7 +183,7 @@
<ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
<ROW Name="SystemFolder_msiexec.exe" SourcePath="&lt;uninstall.ico&gt;" Index="0"/>
<ROW Name="SystemFolder_msiexec.exe" SourcePath="&lt;AI_RES&gt;uninstall.ico" Index="0"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiIniFileComponent">
<ROW IniFile="AppDir" FileName="updater.ini" DirProperty="APPDIR" Section="General" Key="AppDir" Value="[APPDIR]" Action="0" Component_="updater.exe"/>
@@ -204,51 +199,59 @@
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="740"/>
<ROW Action="AI_STORE_LOCATION" Condition="Not Installed" Sequence="1545"/>
<ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1300"/>
<ROW Action="AI_UPDATER_UNINSTALL" Condition="($updater.exe = 2) AND (?updater.exe = 3) AND NOT (UPGRADINGPRODUCTCODE)" Sequence="1549"/>
<ROW Action="AI_UPDATER_UNINSTALL" Condition="($updater.exe = 2) AND (?updater.exe = 3) AND NOT (UPGRADINGPRODUCTCODE)" Sequence="1547"/>
<ROW Action="AI_DELETE_SHORTCUTS" Condition="NOT (REMOVE=&quot;ALL&quot;)" Sequence="1449"/>
<ROW Action="AI_AppSearchEx" Sequence="101"/>
<ROW Action="AI_DeleteCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (NOT PATCH)" Sequence="199" Builds="DefaultBuild"/>
<ROW Action="AI_DeleteRCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (NOT PATCH)" Sequence="198" Builds="DefaultBuild"/>
<ROW Action="AI_ExtractCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (NOT PATCH)" Sequence="197" Builds="DefaultBuild"/>
<ROW Action="AI_FindExeLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (NOT PATCH)" Sequence="196" Builds="DefaultBuild"/>
<ROW Action="AI_ExtractLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (NOT PATCH)" Sequence="1549" Builds="DefaultBuild"/>
<ROW Action="AI_DeleteRLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (NOT PATCH)" Sequence="1548" Builds="DefaultBuild"/>
<ROW Action="AI_DeleteLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (NOT PATCH)" Sequence="6599" Builds="DefaultBuild"/>
<ROW Action="AI_ResolveKnownFolders" Sequence="51"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="740"/>
<ROW Action="AI_AppSearchEx" Sequence="101"/>
<ROW Action="AI_ResolveKnownFolders" Sequence="51"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
<ROW Condition="Version9X OR VersionNT64 OR (VersionNT &gt;= 500 )" Description="[ProductName] can not be installed on systems earlier than [WindowsTypeNT]"/>
<ROW Condition="VersionNT" Description="[ProductName] can not be installed on [WindowsFamily9X]"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiMediaComponent">
<ATTRIBUTE name="CabsLocation" value="1"/>
<ATTRIBUTE name="Compress" value="1"/>
<ATTRIBUTE name="CreateMd5" value="true"/>
<ATTRIBUTE name="EXEName" value="dupeguru_win_[|ProductVersion]"/>
<ATTRIBUTE name="InstallationType" value="4"/>
<ATTRIBUTE name="Package" value="6"/>
<ATTRIBUTE name="PackageName" value="install\dupeguru_win_[|ProductVersion]"/>
<ROW Condition="Version9X OR VersionNT64 OR (VersionNT &gt;= 500 )" Description="[ProductName] cannot be installed on systems earlier than [WindowsTypeNT]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
<ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsFamily9X]" DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiRegLocatorComponent">
<ROW Signature_="AI_EXE_PATH_CU" Root="1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
<ROW Signature_="AI_EXE_PATH_LM" Root="2" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
<ROW Signature_="AI_ShRegOptionMachine" Root="2" Key="Software\Caphyon\Advanced Installer\Installs\[ProductCode]" Name="AIShRegAnswer" Type="2"/>
<ROW Signature_="AI_ShRegOptionUser" Root="1" Key="Software\Caphyon\Advanced Installer\Installs\[ProductCode]" Name="AIShRegAnswer" Type="2"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
<ROW Registry="AIShRegAnswer" Root="-1" Key="Software\Caphyon\Advanced Installer\Installs\[ProductCode]" Name="AIShRegAnswer" Value="[AI_SHORTCUTSREG]" Component_="AIShRegAnswer"/>
<ROW Registry="AI_ExePath" Root="-1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Value="[AI_SETUPEXEPATH]" Component_="AI_ExePath"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiShortsComponent">
<ROW Shortcut="Check_for_updates" Directory_="SHORTCUTDIR" Name="Checkf~1|Check for update" Component_="updater.exe" Target="[#updater.exe]" Arguments="/checknow" Hotkey="0" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
<ROW Shortcut="Uninstall_dupeGuru" Directory_="SHORTCUTDIR" Name="Uninst~1|Uninstall dupeGuru" Component_="MSVCP90.dll" Target="[SystemFolder]msiexec.exe" Arguments="/x [ProductCode]" Hotkey="0" Icon_="SystemFolder_msiexec.exe" IconIndex="0" ShowCmd="1"/>
<ROW Shortcut="Uninstall_dupeGuru" Directory_="SHORTCUTDIR" Name="Uninst~1|Uninstall dupeGuru" Component_="PyWinTypes26.dll" Target="[SystemFolder]msiexec.exe" Arguments="/x [ProductCode]" Hotkey="0" Icon_="SystemFolder_msiexec.exe" IconIndex="0" ShowCmd="1"/>
<ROW Shortcut="dupeGuru" Directory_="DesktopFolder" Name="dupeGuru" Component_="dupeGuru.exe" Target="[#dupeGuru.exe]" Hotkey="0" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
<ROW Shortcut="dupeGuru_1" Directory_="SHORTCUTDIR" Name="dupeGuru" Component_="dupeGuru.exe" Target="[#dupeGuru.exe]" Hotkey="0" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiThemeComponent">
<ATTRIBUTE name="UsedTheme" value="classic"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiUpgradeComponent">
<ROW UpgradeCode="[|UpgradeCode]" VersionMax="[|ProductVersion]" Attributes="1025" ActionProperty="OLDPRODUCTS"/>
<ROW UpgradeCode="[|UpgradeCode]" VersionMin="[|ProductVersion]" Attributes="2" ActionProperty="AI_NEWERPRODUCTFOUND"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.PreReqComponent">
<ROW PrereqKey="0" DisplayName="Visual C++ 2008 SP1 Redistributable" SetupFileUrl="http://download.hardcoded.net/vcredist_90sp1_x86.exe" Location="1" ExactSize="4216840" MinWin9xVer="37" MinWinNTVer="17" Operator="1" ComLine="/q" Sequence="1" MD5="5689d43c3b201dd3810fa3bba4a6476a"/>
<ATTRIBUTE name="ExtractionFolder" value="[AppDataFolder][|Manufacturer]\[|ProductName]\install"/>
<ROW PrereqKey="0" DisplayName="Visual C++ 2008 SP1 Redistributable" SetupFileUrl="http://download.hardcoded.net/vcredist_90sp1_x86.exe" Location="1" ExactSize="4216840" MinWin9xVer="37" MinWinNTVer="17" Operator="1" ComLine="/q" MD5="5689d43c3b201dd3810fa3bba4a6476a"/>
<ATTRIBUTE name="PrereqsOrder" value="0"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.PreReqSearchComponent">
<ROW Prereq="0" SearchType="9" SearchString="HKLM\SOFTWARE\Microsoft\DevDiv\VC\Servicing\9.0\RED\1033\SP" RefContent="M1" Order="1"/>
<ROW SearchKey="SP" Prereq="0" SearchType="9" SearchString="HKLM\SOFTWARE\Microsoft\DevDiv\VC\Servicing\9.0\RED\1033\SP" RefContent="M1" Order="1" Property="PreReqSearch"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.SynchronizedFolderComponent">
<ROW Directory_="APPDIR" SourcePath="dist" ExcludePattern="*~|#*#|%*%|._|CVS|.cvsignore|SCCS|vssver.scc|mssccprj.scc|vssver2.scc|.svn|.DS_Store|*.vshost.*" ExcludeFlags="6"/>
<ROW Directory_="APPDIR" SourcePath="dist" Feature="MainFeature" ExcludePattern="*~|#*#|%*%|._|CVS|.cvsignore|SCCS|vssver.scc|mssccprj.scc|vssver2.scc|.svn|.DS_Store|*.vshost.*" ExcludeFlags="6"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.UpdaterComponent">
<ROW Updater="updater.exe" URL="URL" SearchFreq="CheckFrequency" DownloadsFolder="DownloadsFolder" ID="ID" TargetDir="AppDir" AppName="ApplicationName" CompanyName="CompanyName" UnistallCASeq="AI_UPDATER_UNINSTALL"/>