mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-25 08:01:39 +00:00
Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86ecc8d4d5 | ||
|
|
9eca84efe1 | ||
|
|
8a6fb6dcba | ||
|
|
e3706fa923 | ||
|
|
8193bc5f60 | ||
|
|
504ecaee5e | ||
|
|
7c9e836572 | ||
|
|
5db0f09b43 | ||
|
|
195bc4ef21 | ||
|
|
6b190bc184 | ||
|
|
39f1cac2c8 | ||
|
|
d193eed519 | ||
|
|
2d80b0e12f | ||
|
|
b50d99be9c | ||
|
|
af41876a5e | ||
|
|
76d351d8be | ||
|
|
b5dd9651c3 | ||
|
|
3e34502014 | ||
|
|
5e57f9cbd6 | ||
|
|
8edb869fdc | ||
|
|
37238c7f57 | ||
|
|
9edee82fa1 | ||
|
|
f7aaea79af | ||
|
|
3c75d2f8b7 | ||
|
|
64c67e19d2 | ||
|
|
d4db8faad8 | ||
|
|
7957b73b4a | ||
|
|
69838c44af | ||
|
|
8e2953aef6 | ||
|
|
8dda616502 | ||
|
|
484512e35b | ||
|
|
c8cd05c07d | ||
|
|
7ffefe6259 | ||
|
|
cd9b7f2f11 | ||
|
|
b372974437 | ||
|
|
7464e0f799 | ||
|
|
25e12f1775 | ||
|
|
6416469f78 | ||
|
|
922ce5ae36 | ||
|
|
9ca8a199c0 | ||
|
|
a570406ac8 | ||
|
|
719edb6b6e | ||
|
|
d075218621 | ||
|
|
7509943938 | ||
|
|
774da9d2f8 | ||
|
|
978fd383e8 | ||
|
|
8551fc23fe | ||
|
|
fb711edeeb | ||
|
|
352a21acaa | ||
|
|
0b9d936317 | ||
|
|
dc500243e9 | ||
|
|
21203b8341 | ||
|
|
5b03447640 | ||
|
|
d34158db2c | ||
|
|
65a17390c7 | ||
|
|
0e96f0917c | ||
|
|
3d62a7e64a | ||
|
|
962805936e | ||
|
|
967aeecf5b | ||
|
|
348b039fa3 | ||
|
|
6e9b1f4fa3 | ||
|
|
f1d447d1aa | ||
|
|
a7c6f47dbe | ||
|
|
0446e89bfe | ||
|
|
e41457913f | ||
|
|
cea1ec7641 | ||
|
|
cc362deb87 | ||
|
|
7ec64e8a3d | ||
|
|
ff2461df9d | ||
|
|
192cd2733c | ||
|
|
ecef95469d | ||
|
|
55d30d5e4b | ||
|
|
2d5502cc2f | ||
|
|
5cda4a1eb4 | ||
|
|
812b914b70 | ||
|
|
9b870ad863 | ||
|
|
0f250ac92d | ||
|
|
552e6b7836 | ||
|
|
28f70b281b | ||
|
|
32d9b573c0 | ||
|
|
fc76a843d5 | ||
|
|
06607aabb2 |
10
.hgignore
10
.hgignore
@@ -6,16 +6,20 @@ syntax: glob
|
||||
*.mode1v3
|
||||
*.pbxuser
|
||||
*.tm_build_errors
|
||||
*.pyd
|
||||
conf.yaml
|
||||
build
|
||||
core_pe/modules/block/block.c
|
||||
core_pe/modules/cache/cache.c
|
||||
cocoa/*/Info.plist
|
||||
cocoa/*/build
|
||||
cocoa/*/dg_cocoa.plugin
|
||||
qt/base/*_rc.py
|
||||
qt/base/*_ui.py
|
||||
qt/se/*_ui.py
|
||||
qt/*/*_ui.py
|
||||
qt/*/build
|
||||
qt/*/dist
|
||||
qt/*/install
|
||||
qt/*/logdict*.log
|
||||
qt/*/warn*.txt
|
||||
help_se/dupeguru_help
|
||||
help_me/dupeguru_me_help
|
||||
help_pe/dupeguru_pe_help
|
||||
4
.hgtags
4
.hgtags
@@ -5,3 +5,7 @@ a8f232f880b6f9ada565d472996a627ebf69b6e9 before-tiger-drop
|
||||
321d15e818cf9a3f1fc037543090bb2fca2cccd7 me5.7.0
|
||||
adc73ccd14b1386cb04dee773c53a2d126800e31 se2.9.0
|
||||
cbcf9c80fee4c908ef2efbf1c143c9e47676c9b2 pe1.8.0
|
||||
61c4101851bdea3cb37dfb76f0d404c78c7c594c se2.9.1
|
||||
0e923897a3389331d4ab3debbc40b8dd616199d9 pe1.8.1
|
||||
2c454eca9ebe93b6cf34916068f828a6a39e3eaf me5.7.1
|
||||
19e40bab20521d4256acf325dba9b32e95e135c5 pe1.8.2
|
||||
|
||||
14
README
14
README
@@ -30,22 +30,21 @@ General dependencies
|
||||
- Mako, to generate help files. (http://www.makotemplates.org/)
|
||||
- PyYaml, for help files and the build system. (http://pyyaml.org/)
|
||||
- Nose, to run unit tests. (http://somethingaboutorange.com/mrl/projects/nose/)
|
||||
- Cython to compile a few optimized bottlenecks. (http://www.cython.org/)
|
||||
- Python Imaging Library for dupeGuru PE. (http://www.pythonware.com/products/pil/)
|
||||
|
||||
OS X prerequisites
|
||||
-----
|
||||
|
||||
- XCode 3.1 (http://developer.apple.com/TOOLS/xcode/)
|
||||
- Sparkle (http://sparkle.andymatuschak.org/)
|
||||
- PyObjC. Although Tiger support has been dropped with dupeGuru 1.7, I still use PyObjC 1.4 because funky stuff happens with newer releases. However, it's mostly related to packaging with py2app. (http://pyobjc.sourceforge.net/)
|
||||
- PyObjC 2.2. (http://pyobjc.sourceforge.net/)
|
||||
- py2app (http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html)
|
||||
|
||||
Windows prerequisites
|
||||
---
|
||||
|
||||
- Visual Studio 2008 (Express is enough) is needed to build the Cython extensions. (http://www.microsoft.com/Express/)
|
||||
- Visual Studio 2008 (Express is enough) is needed to build C extensions. (http://www.microsoft.com/Express/)
|
||||
- PyQt 4.6 (http://www.riverbankcomputing.co.uk/news)
|
||||
- Python Imaging Library for dupeGuru PE. (http://www.pythonware.com/products/pil/)
|
||||
- PyInstaller, if you want to build a exe. You don't need it if you just want to run dupeGuru. (http://www.pyinstaller.org/)
|
||||
- Advanced Installer, if you want to build the installer file. (http://www.advancedinstaller.com/)
|
||||
|
||||
@@ -56,7 +55,7 @@ First, make sure you meet the dependencies listed in the section above. Then you
|
||||
|
||||
python configure.py
|
||||
|
||||
If you want, you can specify a UI to use with the `--ui` option. So, if you want to build dupeGuru with Qt on OS X, then you have to type `python configure.py --ui=qt`. You can also use the `--dev` flag to indicate a dev build (it will build `mg_cocoa.plugin` in alias mode).
|
||||
If you want, you can specify a UI to use with the `--ui` option. So, if you want to build dupeGuru with Qt on OS X, then you have to type `python configure.py --ui=qt`. You can also use the `--dev` flag to indicate a dev build (it will build `dg_cocoa.plugin` in alias mode and use the "dev" config in XCode).
|
||||
|
||||
Then, just build the thing and then run it with:
|
||||
|
||||
@@ -66,3 +65,8 @@ Then, just build the thing and then run it with:
|
||||
If you want to create ready-to-upload package, run:
|
||||
|
||||
python package.py
|
||||
|
||||
64-bit on OS X
|
||||
---
|
||||
|
||||
The "release" configuration of dupeGuru's XCode project build with archs "i386 x86_64 ppc". However there are currently problems with py2app and 64 bit. If you want to correctly build 64-bit apps, refer to http://www.hardcoded.net/articles/building-64-bit-pyobjc-applications-with-py2app.htm .
|
||||
99
build.py
99
build.py
@@ -18,6 +18,62 @@ 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 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))
|
||||
|
||||
print "Building dg_cocoa.plugin"
|
||||
if op.exists('build'):
|
||||
shutil.rmtree('build')
|
||||
os.mkdir('build')
|
||||
if not dev:
|
||||
specific_packages = {
|
||||
'se': ['core_se'],
|
||||
'me': ['core_me', 'hsmedia'],
|
||||
'pe': ['core_pe'],
|
||||
}[edition]
|
||||
copy_packages(['core', 'hsutil'] + specific_packages, 'build')
|
||||
cocoa_project_path = 'cocoa/{0}'.format(edition)
|
||||
shutil.copy(op.join(cocoa_project_path, 'dg_cocoa.py'), 'build')
|
||||
os.chdir('build')
|
||||
script_args = ['py2app', '-A'] if dev else ['py2app']
|
||||
setup(
|
||||
script_args = script_args,
|
||||
plugin = ['dg_cocoa.py'],
|
||||
setup_requires = ['py2app'],
|
||||
)
|
||||
os.chdir('..')
|
||||
pluginpath = op.join(cocoa_project_path, 'dg_cocoa.plugin')
|
||||
if op.exists(pluginpath):
|
||||
shutil.rmtree(pluginpath)
|
||||
shutil.move('build/dist/dg_cocoa.plugin', pluginpath)
|
||||
if dev:
|
||||
# In alias mode, the tweakings we do to the pythonpath aren't counted in. We have to
|
||||
# manually put a .pth in the plugin
|
||||
pthpath = op.join(pluginpath, 'Contents/Resources/dev.pth')
|
||||
open(pthpath, 'w').write(op.abspath('.'))
|
||||
os.chdir(cocoa_project_path)
|
||||
print "Building the XCode project"
|
||||
args = []
|
||||
if dev:
|
||||
args.append('-configuration dev')
|
||||
else:
|
||||
args.append('-configuration release')
|
||||
args = ' '.join(args)
|
||||
os.system('xcodebuild {0}'.format(args))
|
||||
os.chdir('..')
|
||||
|
||||
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']
|
||||
@@ -42,48 +98,9 @@ def main():
|
||||
os.system('python gen.py')
|
||||
os.chdir('..')
|
||||
if ui == 'cocoa':
|
||||
if not dev:
|
||||
print "Building help index"
|
||||
os.system('open -a /Developer/Applications/Utilities/Help\\ Indexer.app {0}'.format(help_destpath))
|
||||
|
||||
print "Building dg_cocoa.plugin"
|
||||
if op.exists('build'):
|
||||
shutil.rmtree('build')
|
||||
os.mkdir('build')
|
||||
if not dev:
|
||||
specific_packages = {
|
||||
'se': ['core_se'],
|
||||
'me': ['core_me', 'hsmedia'],
|
||||
'pe': ['core_pe'],
|
||||
}[edition]
|
||||
copy_packages(['core', 'hsutil'] + specific_packages, 'build')
|
||||
cocoa_project_path = 'cocoa/{0}'.format(edition)
|
||||
shutil.copy(op.join(cocoa_project_path, 'dg_cocoa.py'), 'build')
|
||||
os.chdir('build')
|
||||
script_args = ['py2app', '-A'] if dev else ['py2app']
|
||||
setup(
|
||||
script_args = script_args,
|
||||
plugin = ['dg_cocoa.py'],
|
||||
setup_requires = ['py2app'],
|
||||
)
|
||||
os.chdir('..')
|
||||
pluginpath = op.join(cocoa_project_path, 'dg_cocoa.plugin')
|
||||
if op.exists(pluginpath):
|
||||
shutil.rmtree(pluginpath)
|
||||
shutil.move('build/dist/dg_cocoa.plugin', pluginpath)
|
||||
if dev:
|
||||
# In alias mode, the tweakings we do to the pythonpath aren't counted in. We have to
|
||||
# manually put a .pth in the plugin
|
||||
pthpath = op.join(pluginpath, 'Contents/Resources/dev.pth')
|
||||
open(pthpath, 'w').write(op.abspath('.'))
|
||||
os.chdir(cocoa_project_path)
|
||||
print "Building the XCode project"
|
||||
os.system('xcodebuild')
|
||||
os.chdir('..')
|
||||
build_cocoa(edition, dev, help_destpath)
|
||||
elif ui == 'qt':
|
||||
os.chdir(op.join('qt', edition))
|
||||
os.system('python gen.py')
|
||||
os.chdir(op.join('..', '..'))
|
||||
build_qt(edition, dev)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -11,6 +11,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
#import "PyDupeGuru.h"
|
||||
#import "ResultWindow.h"
|
||||
#import "DetailsPanel.h"
|
||||
#import "DirectoryPanel.h"
|
||||
|
||||
@interface AppDelegateBase : NSObject
|
||||
{
|
||||
@@ -19,11 +20,15 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
IBOutlet NSMenuItem *unlockMenuItem;
|
||||
IBOutlet ResultWindowBase *result;
|
||||
|
||||
DetailsPanelBase *_detailsPanel;
|
||||
DirectoryPanel *_directoryPanel;
|
||||
DetailsPanel *_detailsPanel;
|
||||
BOOL _savedResults;
|
||||
}
|
||||
- (IBAction)unlockApp:(id)sender;
|
||||
|
||||
- (PyDupeGuruBase *)py;
|
||||
- (RecentDirectories *)recentDirectories;
|
||||
- (DetailsPanelBase *)detailsPanel; // Virtual
|
||||
- (DirectoryPanel *)directoryPanel;
|
||||
- (DetailsPanel *)detailsPanel;
|
||||
- (void)saveResults;
|
||||
@end
|
||||
|
||||
@@ -11,6 +11,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
#import "RegistrationInterface.h"
|
||||
#import "Utils.h"
|
||||
#import "Consts.h"
|
||||
#import <Sparkle/SUUpdater.h>
|
||||
|
||||
@implementation AppDelegateBase
|
||||
- (IBAction)unlockApp:(id)sender
|
||||
@@ -28,7 +29,29 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
- (PyDupeGuruBase *)py { return py; }
|
||||
- (RecentDirectories *)recentDirectories { return recentDirectories; }
|
||||
- (DetailsPanelBase *)detailsPanel { return nil; } // Virtual
|
||||
- (DirectoryPanel *)directoryPanel
|
||||
{
|
||||
if (!_directoryPanel)
|
||||
_directoryPanel = [[DirectoryPanel alloc] initWithParentApp:self];
|
||||
return _directoryPanel;
|
||||
}
|
||||
|
||||
- (DetailsPanel *)detailsPanel
|
||||
{
|
||||
if (!_detailsPanel)
|
||||
_detailsPanel = [[DetailsPanel alloc] initWithPy:py];
|
||||
return _detailsPanel;
|
||||
}
|
||||
|
||||
- (void)saveResults
|
||||
{
|
||||
if (_savedResults) {
|
||||
return;
|
||||
}
|
||||
[py saveIgnoreList];
|
||||
[py saveResults];
|
||||
_savedResults = YES;
|
||||
}
|
||||
|
||||
/* Delegate */
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
@@ -48,5 +71,47 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
//Restore results
|
||||
[py loadIgnoreList];
|
||||
[py loadResults];
|
||||
_savedResults = NO;
|
||||
}
|
||||
|
||||
- (void)applicationWillBecomeActive:(NSNotification *)aNotification
|
||||
{
|
||||
if (![[result window] isVisible])
|
||||
[result showWindow:NSApp];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
[ud setObject: [result getColumnsOrder] forKey:@"columnsOrder"];
|
||||
[ud setObject: [result getColumnsWidth] forKey:@"columnsWidth"];
|
||||
[self saveResults];
|
||||
NSInteger sc = [ud integerForKey:@"sessionCountSinceLastIgnorePurge"];
|
||||
if (sc >= 10)
|
||||
{
|
||||
sc = -1;
|
||||
[py purgeIgnoreList];
|
||||
}
|
||||
sc++;
|
||||
[ud setInteger:sc forKey:@"sessionCountSinceLastIgnorePurge"];
|
||||
// NSApplication does not release nib instances objects, we must do it manually
|
||||
// Well, it isn't needed because the memory is freed anyway (we are quitting the application
|
||||
// But I need to release RecentDirectories so it saves the user defaults
|
||||
[recentDirectories release];
|
||||
}
|
||||
|
||||
- (void)recentDirecoryClicked:(NSString *)directory
|
||||
{
|
||||
[[self directoryPanel] addDirectory:directory];
|
||||
}
|
||||
|
||||
/* SUUpdater delegate */
|
||||
|
||||
- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update untilInvoking:(NSInvocation *)invocation;
|
||||
{
|
||||
/* If results aren't saved now, we might get a weird utf-8 lookup error when saving later.
|
||||
**/
|
||||
[self saveResults];
|
||||
return NO;
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -8,7 +8,6 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#define DuplicateSelectionChangedNotification @"DuplicateSelectionChangedNotification"
|
||||
/* ResultsChangedNotification happens on major changes, which requires a complete reload of the data*/
|
||||
#define ResultsChangedNotification @"ResultsChangedNotification"
|
||||
/* ResultsChangedNotification happens on minor changes, which requires buffer flush*/
|
||||
@@ -23,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
|
||||
@@ -7,19 +7,19 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "HSWindowController.h"
|
||||
#import "PyApp.h"
|
||||
#import "Table.h"
|
||||
#import "PyDetailsPanel.h"
|
||||
|
||||
|
||||
@interface DetailsPanelBase : NSWindowController
|
||||
@interface DetailsPanel : HSWindowController
|
||||
{
|
||||
IBOutlet TableView *detailsTable;
|
||||
IBOutlet NSTableView *detailsTable;
|
||||
}
|
||||
- (id)initWithPy:(PyApp *)aPy;
|
||||
- (PyDetailsPanel *)py;
|
||||
|
||||
- (void)refresh;
|
||||
- (void)toggleVisibility;
|
||||
|
||||
/* Notifications */
|
||||
- (void)duplicateSelectionChanged:(NSNotification *)aNotification;
|
||||
/* Python --> Cocoa */
|
||||
- (void)refresh;
|
||||
@end
|
||||
@@ -7,38 +7,53 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
|
||||
#import "DetailsPanel.h"
|
||||
#import "Consts.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation DetailsPanelBase
|
||||
@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.
|
||||
[detailsTable setPy:aPy];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(duplicateSelectionChanged:) name:DuplicateSelectionChangedNotification object:nil];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)refresh
|
||||
- (PyDetailsPanel *)py
|
||||
{
|
||||
return (PyDetailsPanel *)py;
|
||||
}
|
||||
|
||||
- (void)refreshDetails
|
||||
{
|
||||
[detailsTable reloadData];
|
||||
}
|
||||
|
||||
- (void)toggleVisibility
|
||||
{
|
||||
if ([[self window] isVisible])
|
||||
if ([[self window] isVisible]) {
|
||||
[[self window] close];
|
||||
else
|
||||
{
|
||||
[self refresh]; // selection might have changed since last time
|
||||
}
|
||||
else {
|
||||
[self refreshDetails]; // selection might have changed since last time
|
||||
[[self window] orderFront:nil];
|
||||
}
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
- (void)duplicateSelectionChanged:(NSNotification *)aNotification
|
||||
/* NSTableView Delegate */
|
||||
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
||||
{
|
||||
if ([[self window] isVisible])
|
||||
[self refresh];
|
||||
return [[self py] numberOfRows];
|
||||
}
|
||||
|
||||
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)row
|
||||
{
|
||||
return [[self py] valueForColumn:[column identifier] row:row];
|
||||
}
|
||||
|
||||
/* Python --> Cocoa */
|
||||
- (void)refresh
|
||||
{
|
||||
if ([[self window] isVisible]) {
|
||||
[self refreshDetails];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -7,9 +7,10 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "../base/DirectoryPanel.h"
|
||||
#import "HSOutline.h"
|
||||
#import "PyDirectoryOutline.h"
|
||||
|
||||
@interface DirectoryPanel : DirectoryPanelBase
|
||||
{
|
||||
}
|
||||
@end
|
||||
@interface DirectoryOutline : HSOutline {}
|
||||
- (id)initWithPyParent:(id)aPyParent view:(HSOutlineView *)aOutlineView;
|
||||
- (PyDirectoryOutline *)py;
|
||||
@end;
|
||||
77
cocoa/base/DirectoryOutline.m
Normal file
77
cocoa/base/DirectoryOutline.m
Normal 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
|
||||
@@ -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 DirectoryPanelBase : NSWindowController
|
||||
@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;
|
||||
|
||||
@@ -11,49 +11,7 @@ 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 DirectoryPanelBase
|
||||
@implementation DirectoryPanel
|
||||
- (id)initWithParentApp:(id)aParentApp
|
||||
{
|
||||
self = [super initWithWindowNibName:@"DirectoryPanel"];
|
||||
@@ -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 (int 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
|
||||
{
|
||||
int state = n2i([[node buffer] objectAtIndex:1]);
|
||||
int newState = state == 2 ? 0 : 2; // If excluded, put it back
|
||||
[_py setDirectory:p2a([node indexPath]) state:i2n(newState)];
|
||||
[node resetAllBuffers];
|
||||
[directories display];
|
||||
else {
|
||||
NSInteger newState = state == 2 ? 0 : 2; // If excluded, put it back
|
||||
[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
|
||||
{
|
||||
int r = [[_py addDirectory:directory] intValue];
|
||||
if (r)
|
||||
{
|
||||
NSInteger r = [[_py addDirectory:directory] intValue];
|
||||
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]];
|
||||
int 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;
|
||||
int state = n2i([[node buffer] objectAtIndex:1]);
|
||||
if ([cell isKindOfClass:[NSTextFieldCell class]])
|
||||
{
|
||||
NSTextFieldCell *textCell = cell;
|
||||
if (state == 1)
|
||||
[textCell setTextColor:[NSColor blueColor]];
|
||||
else if (state == 2)
|
||||
[textCell setTextColor:[NSColor redColor]];
|
||||
else
|
||||
[textCell setTextColor:[NSColor blackColor]];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)path
|
||||
{
|
||||
BOOL isdir;
|
||||
|
||||
@@ -7,7 +7,9 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "../base/DetailsPanel.h"
|
||||
#import "PyGUI.h"
|
||||
|
||||
@interface DetailsPanel : DetailsPanelBase
|
||||
@interface PyDetailsPanel : PyGUI
|
||||
- (NSInteger)numberOfRows;
|
||||
- (id)valueForColumn:(NSString *)column row:(NSInteger)row;
|
||||
@end
|
||||
@@ -7,8 +7,8 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "../base/DetailsPanel.h"
|
||||
#import "PyOutline.h"
|
||||
|
||||
|
||||
@interface DetailsPanel : DetailsPanelBase
|
||||
@interface PyDirectoryOutline : PyOutline
|
||||
- (void)addDirectory:(NSString *)directoryPath;
|
||||
@end
|
||||
@@ -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;
|
||||
@@ -35,7 +34,6 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
- (void)markNone;
|
||||
|
||||
- (void)addSelectedToIgnoreList;
|
||||
- (void)refreshDetailsWithSelected;
|
||||
- (void)removeSelected;
|
||||
- (void)openSelected;
|
||||
- (NSNumber *)renameSelected:(NSString *)aNewName;
|
||||
@@ -62,5 +60,5 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
- (void)setDisplayDeltaValues:(NSNumber *)display_delta_values;
|
||||
- (void)setEscapeFilterRegexp:(NSNumber *)escape_filter_regexp;
|
||||
- (void)setRemoveEmptyFolders:(NSNumber *)remove_empty_folders;
|
||||
- (void)setSizeThreshold:(int)size_threshold;
|
||||
- (void)setSizeThreshold:(NSInteger)size_threshold;
|
||||
@end
|
||||
|
||||
@@ -24,15 +24,17 @@ 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 */
|
||||
- (void)fillColumnsMenu;
|
||||
- (NSTableColumn *)getColumnForIdentifier:(int)aIdentifier title:(NSString *)aTitle width:(int)aWidth refCol:(NSTableColumn *)aColumn;
|
||||
- (NSTableColumn *)getColumnForIdentifier:(NSInteger)aIdentifier title:(NSString *)aTitle width:(NSInteger)aWidth refCol:(NSTableColumn *)aColumn;
|
||||
- (NSArray *)getColumnsOrder;
|
||||
- (NSDictionary *)getColumnsWidth;
|
||||
- (NSArray *)getSelected:(BOOL)aDupesOnly;
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
{
|
||||
unichar key = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
|
||||
// get flags and strip the lower 16 (device dependant) bits
|
||||
unsigned int flags = ( [theEvent modifierFlags] & 0x00FF );
|
||||
NSUInteger flags = ( [theEvent modifierFlags] & 0x00FF );
|
||||
if (((key == NSDeleteFunctionKey) || (key == NSDeleteCharacter)) && (flags == 0))
|
||||
[self sendAction:@selector(removeSelected:) to:[self delegate]];
|
||||
else
|
||||
@@ -50,16 +50,26 @@ 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(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(resultsChanged:) name:ResultsChangedNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsUpdated:) name:ResultsUpdatedNotification 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];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
@@ -85,9 +95,9 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
[mi setTarget:self];
|
||||
}
|
||||
|
||||
- (NSTableColumn *)getColumnForIdentifier:(int)aIdentifier title:(NSString *)aTitle width:(int)aWidth refCol:(NSTableColumn *)aColumn
|
||||
- (NSTableColumn *)getColumnForIdentifier:(NSInteger)aIdentifier title:(NSString *)aTitle width:(NSInteger)aWidth refCol:(NSTableColumn *)aColumn
|
||||
{
|
||||
NSNumber *n = [NSNumber numberWithInt:aIdentifier];
|
||||
NSNumber *n = [NSNumber numberWithInteger:aIdentifier];
|
||||
NSTableColumn *col = [[NSTableColumn alloc] initWithIdentifier:[n stringValue]];
|
||||
[col setWidth:aWidth];
|
||||
[col setEditable:NO];
|
||||
@@ -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 numberWithFloat:[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;
|
||||
@@ -136,7 +137,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
NSIndexSet *indexes = [matches selectedRowIndexes];
|
||||
NSMutableArray *nodeList = [NSMutableArray array];
|
||||
OVNode *node;
|
||||
int i = [indexes firstIndex];
|
||||
NSInteger i = [indexes firstIndex];
|
||||
while (i != NSNotFound)
|
||||
{
|
||||
node = [matches itemAtRow:i];
|
||||
@@ -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)
|
||||
[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]];
|
||||
for (NSMenuItem *mi in [columnsMenu itemArray]) {
|
||||
if ([mi state] == NSOnState) {
|
||||
[self toggleColumn:mi];
|
||||
}
|
||||
}
|
||||
//Add columns and set widths
|
||||
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)
|
||||
selection = [py selectedPowerMarkerNodePaths];
|
||||
else
|
||||
selection = [py selectedResultNodePaths];
|
||||
[matches selectNodePaths:selection];
|
||||
NSArray *selection;
|
||||
if (_powerMode) {
|
||||
selection = [py selectedPowerMarkerNodePaths];
|
||||
}
|
||||
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,14 +247,14 @@ 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];
|
||||
[self updatePySelection];
|
||||
}
|
||||
|
||||
- (IBAction)copyMarked:(id)sender
|
||||
{
|
||||
int mark_count = [[py getMarkCount] intValue];
|
||||
NSInteger mark_count = [[py getMarkCount] intValue];
|
||||
if (!mark_count)
|
||||
return;
|
||||
NSOpenPanel *op = [NSOpenPanel openPanel];
|
||||
@@ -257,7 +273,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
- (IBAction)deleteMarked:(id)sender
|
||||
{
|
||||
int mark_count = [[py getMarkCount] intValue];
|
||||
NSInteger mark_count = [[py getMarkCount] intValue];
|
||||
if (!mark_count)
|
||||
return;
|
||||
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to send %d files to Trash. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
|
||||
@@ -267,21 +283,68 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
[py deleteMarked];
|
||||
}
|
||||
|
||||
- (IBAction)expandAll:(id)sender
|
||||
{
|
||||
for (int 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
|
||||
{
|
||||
int mark_count = [[py getMarkCount] intValue];
|
||||
NSInteger mark_count = [[py getMarkCount] intValue];
|
||||
if (!mark_count)
|
||||
return;
|
||||
NSOpenPanel *op = [NSOpenPanel openPanel];
|
||||
@@ -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];
|
||||
@@ -313,10 +432,10 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
{
|
||||
// It might look like a complicated way to get the length of the current dupe list on the py side
|
||||
// but after a lot of fussing around, believe it or not, it actually is.
|
||||
int matchesTag = _powerMode ? 2 : 0;
|
||||
int startLen = [[py getOutlineView:matchesTag childCountsForPath:[NSArray array]] count];
|
||||
[self performPySelection:[self getSelectedPaths:YES]];
|
||||
NSInteger matchesTag = _powerMode ? 2 : 0;
|
||||
NSInteger startLen = [[py getOutlineView:matchesTag childCountsForPath:[NSArray array]] count];
|
||||
[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
|
||||
// ref is not a part of the filter, making the table smaller. In those cases, we want to do a
|
||||
// complete reload of the table to avoid a crash.
|
||||
@@ -346,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];
|
||||
@@ -361,7 +489,6 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
}
|
||||
|
||||
/* Delegate */
|
||||
|
||||
- (void)outlineView:(NSOutlineView *)outlineView didClickTableColumn:(NSTableColumn *)tableColumn
|
||||
{
|
||||
if ([[outlineView sortDescriptors] count] < 1)
|
||||
@@ -371,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 */
|
||||
@@ -384,7 +534,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
- (void)jobCompleted:(NSNotification *)aNotification
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
|
||||
int r = n2i([py getOperationalErrorCount]);
|
||||
NSInteger r = n2i([py getOperationalErrorCount]);
|
||||
id lastAction = [[ProgressController mainProgressController] jobId];
|
||||
if ([lastAction isEqualTo:jobCopy]) {
|
||||
if (r > 0)
|
||||
@@ -429,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]];
|
||||
}
|
||||
@@ -440,17 +589,26 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
[Dialogs showMessage:msg];
|
||||
}
|
||||
|
||||
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
|
||||
{
|
||||
[self performPySelection:[self getSelectedPaths:NO]];
|
||||
}
|
||||
|
||||
- (void)resultsChanged:(NSNotification *)aNotification
|
||||
{
|
||||
[matches reloadData];
|
||||
[self expandAll:nil];
|
||||
[self outlineViewSelectionDidChange:nil];
|
||||
[self reloadMatches];
|
||||
[self refreshStats];
|
||||
}
|
||||
|
||||
- (void)resultsMarkingChanged:(NSNotification *)aNotification
|
||||
{
|
||||
[matches invalidateMarkings];
|
||||
[self refreshStats];
|
||||
}
|
||||
|
||||
- (void)resultsUpdated:(NSNotification *)aNotification
|
||||
{
|
||||
[matches invalidateBuffers];
|
||||
[matches invalidateBuffers];
|
||||
[matches invalidateMarkings];
|
||||
}
|
||||
|
||||
|
||||
@@ -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="5"/>
|
||||
<integer value="6"/>
|
||||
</object>
|
||||
<object class="NSArray" key="IBDocument.PluginDependencies">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
@@ -297,13 +297,21 @@
|
||||
</object>
|
||||
<int key="connectionID">12</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBOutletConnection" key="connection">
|
||||
<string key="label">dataSource</string>
|
||||
<reference key="source" ref="251969872"/>
|
||||
<reference key="destination" ref="449947658"/>
|
||||
</object>
|
||||
<int key="connectionID">21</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBOutletConnection" key="connection">
|
||||
<string key="label">detailsTable</string>
|
||||
<reference key="source" ref="449947658"/>
|
||||
<reference key="destination" ref="251969872"/>
|
||||
</object>
|
||||
<int key="connectionID">13</int>
|
||||
<int key="connectionID">22</int>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||
@@ -438,15 +446,22 @@
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="NSArray" key="dict.sortedKeys">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<string>-3.IBPluginDependency</string>
|
||||
<string>10.IBPluginDependency</string>
|
||||
<string>10.ImportedFromIB2</string>
|
||||
<string>11.IBPluginDependency</string>
|
||||
<string>11.ImportedFromIB2</string>
|
||||
<string>15.IBPluginDependency</string>
|
||||
<string>15.IBShouldRemoveOnLegacySave</string>
|
||||
<string>16.IBPluginDependency</string>
|
||||
<string>16.IBShouldRemoveOnLegacySave</string>
|
||||
<string>17.IBPluginDependency</string>
|
||||
<string>17.IBShouldRemoveOnLegacySave</string>
|
||||
<string>18.IBPluginDependency</string>
|
||||
<string>18.IBShouldRemoveOnLegacySave</string>
|
||||
<string>19.IBPluginDependency</string>
|
||||
<string>19.IBShouldRemoveOnLegacySave</string>
|
||||
<string>20.IBPluginDependency</string>
|
||||
<string>20.IBShouldRemoveOnLegacySave</string>
|
||||
<string>5.IBEditorWindowLastContentRect</string>
|
||||
<string>5.IBPluginDependency</string>
|
||||
@@ -458,7 +473,6 @@
|
||||
<string>6.ImportedFromIB2</string>
|
||||
<string>7.IBPluginDependency</string>
|
||||
<string>7.ImportedFromIB2</string>
|
||||
<string>8.CustomClassName</string>
|
||||
<string>8.IBPluginDependency</string>
|
||||
<string>8.ImportedFromIB2</string>
|
||||
<string>9.IBPluginDependency</string>
|
||||
@@ -467,14 +481,21 @@
|
||||
<object class="NSMutableArray" key="dict.values">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>{{109, 656}, {451, 161}}</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
@@ -486,7 +507,6 @@
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>TableView</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
@@ -509,81 +529,34 @@
|
||||
</object>
|
||||
</object>
|
||||
<nil key="sourceID"/>
|
||||
<int key="maxID">20</int>
|
||||
<int key="maxID">22</int>
|
||||
</object>
|
||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
||||
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">DetailsPanel</string>
|
||||
<string key="superclassName">DetailsPanelBase</string>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBProjectSource</string>
|
||||
<string key="minorKey">DetailsPanel.h</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">DetailsPanel</string>
|
||||
<string key="superclassName">DetailsPanelBase</string>
|
||||
<string key="superclassName">NSWindowController</string>
|
||||
<object class="NSMutableDictionary" key="outlets">
|
||||
<string key="NS.key.0">detailsTable</string>
|
||||
<string key="NS.object.0">NSTableView</string>
|
||||
</object>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBUserSource</string>
|
||||
<string key="minorKey"/>
|
||||
<string key="majorKey">IBProjectSource</string>
|
||||
<string key="minorKey">../base/DetailsPanel.h</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">DetailsPanelBase</string>
|
||||
<string key="className">DetailsPanel</string>
|
||||
<string key="superclassName">NSWindowController</string>
|
||||
<object class="NSMutableDictionary" key="outlets">
|
||||
<string key="NS.key.0">detailsTable</string>
|
||||
<string key="NS.object.0">TableView</string>
|
||||
</object>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBProjectSource</string>
|
||||
<string key="minorKey">dgbase/DetailsPanel.h</string>
|
||||
<string key="majorKey">IBUserSource</string>
|
||||
<string key="minorKey"/>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">FirstResponder</string>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBUserSource</string>
|
||||
<string key="minorKey"/>
|
||||
</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>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">TableView</string>
|
||||
<string key="superclassName">NSTableView</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">
|
||||
<string key="majorKey">IBProjectSource</string>
|
||||
<string key="minorKey">cocoalib/Table.h</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">TableView</string>
|
||||
<string key="superclassName">NSTableView</string>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBUserSource</string>
|
||||
<string key="minorKey"/>
|
||||
|
||||
@@ -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>
|
||||
<bool key="NSNoAutoenable">YES</bool>
|
||||
<bool key="NSMenuExcludeMarkColumn">YES</bool>
|
||||
</object>
|
||||
<int key="NSSelectedIndex">-1</int>
|
||||
<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,78 +844,19 @@
|
||||
</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">DirectoryPanelBase</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">DirectoryPanelBase</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>NSOutlineView</string>
|
||||
<string>NSButton</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBUserSource</string>
|
||||
<string key="minorKey"/>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">DirectoryPanelBase</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>
|
||||
@@ -903,7 +867,6 @@
|
||||
<string>id</string>
|
||||
<string>id</string>
|
||||
<string>id</string>
|
||||
<string>id</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSMutableDictionary" key="outlets">
|
||||
@@ -911,17 +874,28 @@
|
||||
<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>DirectoryOutline</string>
|
||||
<string>HSOutlineView</string>
|
||||
<string>NSButton</string>
|
||||
</object>
|
||||
</object>
|
||||
<reference key="sourceIdentifier" ref="462913745"/>
|
||||
<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">FirstResponder</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>
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<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>
|
||||
@@ -83,11 +83,9 @@
|
||||
<string key="NSToolbarItemPaletteLabel">Power Marker</string>
|
||||
<nil key="NSToolbarItemToolTip"/>
|
||||
<object class="NSSegmentedControl" key="NSToolbarItemView" id="35398541">
|
||||
<reference key="NSNextResponder"/>
|
||||
<nil key="NSNextResponder"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{7, 14}, {67, 24}}</string>
|
||||
<reference key="NSSuperview"/>
|
||||
<reference key="NSWindow"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSSegmentedCell" key="NSCell" id="431579725">
|
||||
<int key="NSCellFlags">67239424</int>
|
||||
@@ -178,11 +176,9 @@
|
||||
<string key="NSToolbarItemPaletteLabel">Filter</string>
|
||||
<nil key="NSToolbarItemToolTip"/>
|
||||
<object class="NSSearchField" key="NSToolbarItemView" id="1013657232">
|
||||
<reference key="NSNextResponder"/>
|
||||
<nil key="NSNextResponder"/>
|
||||
<int key="NSvFlags">258</int>
|
||||
<string key="NSFrame">{{0, 14}, {81, 22}}</string>
|
||||
<reference key="NSSuperview"/>
|
||||
<reference key="NSWindow"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSSearchFieldCell" key="NSCell" id="484816507">
|
||||
<int key="NSCellFlags">343014976</int>
|
||||
@@ -324,11 +320,9 @@
|
||||
<string key="NSToolbarItemPaletteLabel">Action</string>
|
||||
<nil key="NSToolbarItemToolTip"/>
|
||||
<object class="NSPopUpButton" key="NSToolbarItemView" id="165812138">
|
||||
<reference key="NSNextResponder"/>
|
||||
<nil key="NSNextResponder"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{0, 14}, {58, 26}}</string>
|
||||
<reference key="NSSuperview"/>
|
||||
<reference key="NSWindow"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSPopUpButtonCell" key="NSCell" id="436420677">
|
||||
<int key="NSCellFlags">-2076049856</int>
|
||||
@@ -531,11 +525,9 @@
|
||||
<string key="NSToolbarItemPaletteLabel">Delta Values</string>
|
||||
<nil key="NSToolbarItemToolTip"/>
|
||||
<object class="NSSegmentedControl" key="NSToolbarItemView" id="311230297">
|
||||
<reference key="NSNextResponder"/>
|
||||
<nil key="NSNextResponder"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{4, 14}, {67, 24}}</string>
|
||||
<reference key="NSSuperview"/>
|
||||
<reference key="NSWindow"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSSegmentedCell" key="NSCell" id="211272396">
|
||||
<int key="NSCellFlags">67239424</int>
|
||||
@@ -2227,6 +2219,14 @@
|
||||
</object>
|
||||
<int key="connectionID">1174</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBOutletConnection" key="connection">
|
||||
<string key="label">delegate</string>
|
||||
<reference key="source" ref="23220930"/>
|
||||
<reference key="destination" ref="91622651"/>
|
||||
</object>
|
||||
<int key="connectionID">1175</int>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||
<object class="NSArray" key="orderedObjects">
|
||||
@@ -3584,7 +3584,7 @@
|
||||
</object>
|
||||
</object>
|
||||
<nil key="sourceID"/>
|
||||
<int key="maxID">1174</int>
|
||||
<int key="maxID">1175</int>
|
||||
</object>
|
||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
||||
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
||||
|
||||
@@ -9,16 +9,11 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "../base/AppDelegate.h"
|
||||
#import "ResultWindow.h"
|
||||
#import "DirectoryPanel.h"
|
||||
#import "PyDupeGuru.h"
|
||||
|
||||
@interface AppDelegate : AppDelegateBase
|
||||
{
|
||||
DirectoryPanel *_directoryPanel;
|
||||
}
|
||||
@interface AppDelegate : AppDelegateBase {}
|
||||
- (IBAction)openWebsite:(id)sender;
|
||||
- (IBAction)toggleDirectories:(id)sender;
|
||||
|
||||
- (DirectoryPanel *)directoryPanel;
|
||||
- (PyDupeGuru *)py;
|
||||
@end
|
||||
|
||||
@@ -13,6 +13,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
#import "../../cocoalib/ValueTransformers.h"
|
||||
#import "../../cocoalib/Dialogs.h"
|
||||
#import "DetailsPanel.h"
|
||||
#import "DirectoryPanel.h"
|
||||
#import "Consts.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
@@ -65,52 +66,22 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
[[self directoryPanel] toggleVisible:sender];
|
||||
}
|
||||
|
||||
|
||||
- (DetailsPanelBase *)detailsPanel
|
||||
{
|
||||
if (!_detailsPanel)
|
||||
_detailsPanel = [[DetailsPanel alloc] initWithPy:py];
|
||||
return _detailsPanel;
|
||||
}
|
||||
|
||||
- (DirectoryPanel *)directoryPanel
|
||||
{
|
||||
if (!_directoryPanel)
|
||||
_directoryPanel = [[DirectoryPanel alloc] initWithParentApp:self];
|
||||
_directoryPanel = [[DirectoryPanelME alloc] initWithParentApp:self];
|
||||
return _directoryPanel;
|
||||
}
|
||||
- (PyDupeGuru *)py { return (PyDupeGuru *)py; }
|
||||
|
||||
//Delegate
|
||||
- (void)applicationWillBecomeActive:(NSNotification *)aNotification
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
if (![[result window] isVisible])
|
||||
[result showWindow:NSApp];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
[ud setObject: [result getColumnsOrder] forKey:@"columnsOrder"];
|
||||
[ud setObject: [result getColumnsWidth] forKey:@"columnsWidth"];
|
||||
[py saveIgnoreList];
|
||||
[py saveResults];
|
||||
int sc = [ud integerForKey:@"sessionCountSinceLastIgnorePurge"];
|
||||
if (sc >= 10)
|
||||
{
|
||||
sc = -1;
|
||||
[py purgeIgnoreList];
|
||||
}
|
||||
sc++;
|
||||
[ud setInteger:sc forKey:@"sessionCountSinceLastIgnorePurge"];
|
||||
// NSApplication does not release nib instances objects, we must do it manually
|
||||
// Well, it isn't needed because the memory is freed anyway (we are quitting the application
|
||||
// But I need to release RecentDirectories so it saves the user defaults
|
||||
[recentDirectories release];
|
||||
}
|
||||
|
||||
- (void)recentDirecoryClicked:(NSString *)directory
|
||||
{
|
||||
[[self directoryPanel] addDirectory:directory];
|
||||
NSMenu *actionsMenu = [[[NSApp mainMenu] itemWithTitle:@"Actions"] submenu];
|
||||
// index 3 is just after "Export Results to XHTML"
|
||||
NSMenuItem *mi = [actionsMenu insertItemWithTitle:@"Remove Dead Tracks in iTunes"
|
||||
action:@selector(removeDeadTracks:) keyEquivalent:@"" atIndex:3];
|
||||
[mi setTarget:result];
|
||||
[super applicationDidFinishLaunching:aNotification];
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
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 "DetailsPanel.h"
|
||||
|
||||
@implementation DetailsPanel
|
||||
@end
|
||||
@@ -9,7 +9,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "../base/DirectoryPanel.h"
|
||||
|
||||
@interface DirectoryPanel : DirectoryPanelBase
|
||||
@interface DirectoryPanelME : DirectoryPanel
|
||||
{
|
||||
}
|
||||
- (IBAction)addiTunes:(id)sender;
|
||||
|
||||
@@ -8,7 +8,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
#import "DirectoryPanel.h"
|
||||
|
||||
@implementation DirectoryPanel
|
||||
@implementation DirectoryPanelME
|
||||
- (IBAction)addiTunes:(id)sender
|
||||
{
|
||||
[self addDirectory:[@"~/Music/iTunes/iTunes Music" stringByExpandingTildeInPath]];
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>hsft</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>5.7.0</string>
|
||||
<string>5.7.1</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -19,5 +19,5 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
- (void)enable:(NSNumber *)enable scanForTag:(NSString *)tag;
|
||||
- (void)scanDeadTracks;
|
||||
- (void)removeDeadTracks;
|
||||
- (int)deadTrackCount;
|
||||
- (NSInteger)deadTrackCount;
|
||||
@end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
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)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
|
||||
{
|
||||
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];
|
||||
@@ -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)
|
||||
@@ -187,7 +67,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
[_py setWordWeighting:[ud objectForKey:@"wordWeighting"]];
|
||||
[_py setMixFileKind:[ud objectForKey:@"mixFileKind"]];
|
||||
[_py setMatchSimilarWords:[ud objectForKey:@"matchSimilarWords"]];
|
||||
int r = n2i([py doScan]);
|
||||
NSInteger r = n2i([py doScan]);
|
||||
[matches reloadData];
|
||||
[self refreshStats];
|
||||
if (r == 1)
|
||||
@@ -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
|
||||
{
|
||||
@@ -272,7 +118,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
id lastAction = [[ProgressController mainProgressController] jobId];
|
||||
if ([lastAction isEqualTo:jobScanDeadTracks])
|
||||
{
|
||||
int deadTrackCount = [(PyDupeGuru *)py deadTrackCount];
|
||||
NSInteger deadTrackCount = [(PyDupeGuru *)py deadTrackCount];
|
||||
if (deadTrackCount > 0)
|
||||
{
|
||||
NSString *msg = @"Your iTunes Library contains %d dead tracks ready to be removed. Continue?";
|
||||
@@ -285,17 +131,4 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
|
||||
{
|
||||
[self performPySelection:[self getSelectedPaths:NO]];
|
||||
[py refreshDetailsWithSelected];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:DuplicateSelectionChangedNotification object:self];
|
||||
}
|
||||
|
||||
- (void)resultsMarkingChanged:(NSNotification *)aNotification
|
||||
{
|
||||
[matches invalidateMarkings];
|
||||
[self refreshStats];
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -4,182 +4,41 @@
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
import objc
|
||||
from AppKit import *
|
||||
from hsutil.cocoa import signature
|
||||
|
||||
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
|
||||
from core_me.app_cocoa import DupeGuruME
|
||||
from core.scanner import (SCAN_TYPE_FILENAME, SCAN_TYPE_FIELDS, SCAN_TYPE_FIELDS_NO_ORDER,
|
||||
SCAN_TYPE_TAG, SCAN_TYPE_CONTENT, SCAN_TYPE_CONTENT_AUDIO)
|
||||
|
||||
# Fix py2app imports which chokes on relative imports
|
||||
from core_me import app_cocoa, data, fs, scanner
|
||||
from core import app, app_cocoa, data, directories, engine, export, ignore, results, scanner, fs
|
||||
from hsmedia import aiff, flac, genres, id3v1, id3v2, mp4, mpeg, ogg, wma
|
||||
from hsutil import conflict
|
||||
|
||||
class PyApp(NSObject):
|
||||
pass #fake class
|
||||
|
||||
class PyDupeGuru(PyApp):
|
||||
class PyDupeGuru(PyDupeGuruBase):
|
||||
def init(self):
|
||||
self = super(PyDupeGuru,self).init()
|
||||
self.app = DupeGuruME()
|
||||
self.py = DupeGuruME()
|
||||
return self
|
||||
|
||||
#---Directories
|
||||
def addDirectory_(self,directory):
|
||||
return self.app.add_directory(directory)
|
||||
|
||||
def removeDirectory_(self,index):
|
||||
self.app.RemoveDirectory(index)
|
||||
|
||||
def setDirectory_state_(self,node_path,state):
|
||||
self.app.SetDirectoryState(node_path,state)
|
||||
|
||||
#---Results
|
||||
def clearIgnoreList(self):
|
||||
self.app.scanner.ignore_list.Clear()
|
||||
|
||||
def doScan(self):
|
||||
return self.app.start_scanning()
|
||||
|
||||
def exportToXHTMLwithColumns_(self, column_ids):
|
||||
return self.app.export_to_xhtml(column_ids)
|
||||
|
||||
def loadIgnoreList(self):
|
||||
self.app.load_ignore_list()
|
||||
|
||||
def loadResults(self):
|
||||
self.app.load()
|
||||
|
||||
def markAll(self):
|
||||
self.app.results.mark_all()
|
||||
|
||||
def markNone(self):
|
||||
self.app.results.mark_none()
|
||||
|
||||
def markInvert(self):
|
||||
self.app.results.mark_invert()
|
||||
|
||||
def purgeIgnoreList(self):
|
||||
self.app.PurgeIgnoreList()
|
||||
|
||||
def toggleSelectedMark(self):
|
||||
self.app.ToggleSelectedMarkState()
|
||||
|
||||
def saveIgnoreList(self):
|
||||
self.app.save_ignore_list()
|
||||
|
||||
def saveResults(self):
|
||||
self.app.save()
|
||||
|
||||
def refreshDetailsWithSelected(self):
|
||||
self.app.RefreshDetailsWithSelected()
|
||||
|
||||
def selectedResultNodePaths(self):
|
||||
return self.app.selected_result_node_paths()
|
||||
|
||||
def selectResultNodePaths_(self,node_paths):
|
||||
self.app.SelectResultNodePaths(node_paths)
|
||||
|
||||
def selectedPowerMarkerNodePaths(self):
|
||||
return self.app.selected_powermarker_node_paths()
|
||||
|
||||
def selectPowerMarkerNodePaths_(self,node_paths):
|
||||
self.app.SelectPowerMarkerNodePaths(node_paths)
|
||||
|
||||
#---Actions
|
||||
def addSelectedToIgnoreList(self):
|
||||
self.app.AddSelectedToIgnoreList()
|
||||
|
||||
def applyFilter_(self, filter):
|
||||
self.app.apply_filter(filter)
|
||||
|
||||
def deleteMarked(self):
|
||||
self.app.delete_marked()
|
||||
|
||||
def makeSelectedReference(self):
|
||||
self.app.MakeSelectedReference()
|
||||
|
||||
def copyOrMove_markedTo_recreatePath_(self,copy,destination,recreate_path):
|
||||
self.app.copy_or_move_marked(copy, destination, recreate_path)
|
||||
|
||||
def openSelected(self):
|
||||
self.app.OpenSelected()
|
||||
|
||||
def removeDeadTracks(self):
|
||||
self.app.remove_dead_tracks()
|
||||
|
||||
def removeMarked(self):
|
||||
self.app.results.perform_on_marked(lambda x:True, True)
|
||||
|
||||
def removeSelected(self):
|
||||
self.app.RemoveSelected()
|
||||
|
||||
def renameSelected_(self,newname):
|
||||
return self.app.RenameSelected(newname)
|
||||
|
||||
def revealSelected(self):
|
||||
self.app.RevealSelected()
|
||||
self.py.remove_dead_tracks()
|
||||
|
||||
def scanDeadTracks(self):
|
||||
self.app.scan_dead_tracks()
|
||||
|
||||
#---Misc
|
||||
def sortDupesBy_ascending_(self,key,asc):
|
||||
self.app.sort_dupes(key,asc)
|
||||
|
||||
def sortGroupsBy_ascending_(self,key,asc):
|
||||
self.app.sort_groups(key,asc)
|
||||
self.py.scan_dead_tracks()
|
||||
|
||||
#---Information
|
||||
@objc.signature('i@:')
|
||||
@signature('i@:')
|
||||
def deadTrackCount(self):
|
||||
return len(self.app.dead_tracks)
|
||||
|
||||
def getIgnoreListCount(self):
|
||||
return len(self.app.scanner.ignore_list)
|
||||
|
||||
def getMarkCount(self):
|
||||
return self.app.results.mark_count
|
||||
|
||||
def getStatLine(self):
|
||||
return self.app.stat_line
|
||||
|
||||
def getOperationalErrorCount(self):
|
||||
return self.app.last_op_error_count
|
||||
|
||||
#---Data
|
||||
@objc.signature('i@:i')
|
||||
def getOutlineViewMaxLevel_(self, tag):
|
||||
return self.app.GetOutlineViewMaxLevel(tag)
|
||||
|
||||
@objc.signature('@@:i@')
|
||||
def getOutlineView_childCountsForPath_(self, tag, node_path):
|
||||
return self.app.GetOutlineViewChildCounts(tag, node_path)
|
||||
|
||||
def getOutlineView_valuesForIndexes_(self,tag,node_path):
|
||||
return self.app.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 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,
|
||||
@@ -191,54 +50,18 @@ class PyDupeGuru(PyApp):
|
||||
pass
|
||||
|
||||
def setWordWeighting_(self, words_are_weighted):
|
||||
self.app.scanner.word_weighting = words_are_weighted
|
||||
|
||||
def setMixFileKind_(self, mix_file_kind):
|
||||
self.app.scanner.mix_file_kind = mix_file_kind
|
||||
|
||||
def setDisplayDeltaValues_(self, display_delta_values):
|
||||
self.app.display_delta_values = display_delta_values
|
||||
self.py.scanner.word_weighting = words_are_weighted
|
||||
|
||||
def setMatchSimilarWords_(self, match_similar_words):
|
||||
self.app.scanner.match_similar_words = match_similar_words
|
||||
|
||||
def setEscapeFilterRegexp_(self, escape_filter_regexp):
|
||||
self.app.options['escape_filter_regexp'] = escape_filter_regexp
|
||||
|
||||
def setRemoveEmptyFolders_(self, remove_empty_folders):
|
||||
self.app.options['clean_empty_dirs'] = remove_empty_folders
|
||||
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)
|
||||
|
||||
#---Worker
|
||||
def getJobProgress(self):
|
||||
return self.app.progress.last_progress
|
||||
|
||||
def getJobDesc(self):
|
||||
return self.app.progress.last_desc
|
||||
|
||||
def cancelJob(self):
|
||||
self.app.progress.job_cancelled = True
|
||||
self.py.scanner.scanned_tags.discard(scan_tag)
|
||||
|
||||
#---Registration
|
||||
def appName(self):
|
||||
return "dupeGuru Music Edition"
|
||||
|
||||
def demoLimitDescription(self):
|
||||
return self.app.DEMO_LIMIT_DESC
|
||||
|
||||
@objc.signature('i@:')
|
||||
def isRegistered(self):
|
||||
return self.app.registered
|
||||
|
||||
@objc.signature('i@:@@')
|
||||
def isCodeValid_withEmail_(self, code, email):
|
||||
return self.app.is_code_valid(code, email)
|
||||
|
||||
def setRegisteredCode_andEmail_(self, code, email):
|
||||
self.app.set_registration(code, email)
|
||||
|
||||
|
||||
@@ -30,18 +30,17 @@
|
||||
CE3FBDD31094637800B72D77 /* DetailsPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3FBDD11094637800B72D77 /* DetailsPanel.xib */; };
|
||||
CE3FBDD41094637800B72D77 /* DirectoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3FBDD21094637800B72D77 /* DirectoryPanel.xib */; };
|
||||
CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */; };
|
||||
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */; };
|
||||
CE4B59C91119919700C06C9E /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C61119919700C06C9E /* progress.xib */; };
|
||||
CE4B59CA1119919700C06C9E /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C71119919700C06C9E /* registration.xib */; };
|
||||
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE10FC6C12E00EC695D /* Dialogs.m */; };
|
||||
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */; };
|
||||
CE515DF50FC6C12E00EC695D /* Outline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE50FC6C12E00EC695D /* Outline.m */; };
|
||||
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE70FC6C12E00EC695D /* ProgressController.m */; };
|
||||
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */; };
|
||||
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */; };
|
||||
CE515DF90FC6C12E00EC695D /* Table.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEE0FC6C12E00EC695D /* Table.m */; };
|
||||
CE515DFA0FC6C12E00EC695D /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DF00FC6C12E00EC695D /* Utils.m */; };
|
||||
CE515DFB0FC6C12E00EC695D /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DF20FC6C12E00EC695D /* ValueTransformers.m */; };
|
||||
CE515E020FC6C13E00EC695D /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE515DFC0FC6C13E00EC695D /* ErrorReportWindow.xib */; };
|
||||
CE515E030FC6C13E00EC695D /* progress.nib in Resources */ = {isa = PBXBuildFile; fileRef = CE515DFE0FC6C13E00EC695D /* progress.nib */; };
|
||||
CE515E040FC6C13E00EC695D /* registration.nib in Resources */ = {isa = PBXBuildFile; fileRef = CE515E000FC6C13E00EC695D /* registration.nib */; };
|
||||
CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E160FC6C19300EC695D /* AppDelegate.m */; };
|
||||
CE515E1E0FC6C19300EC695D /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E190FC6C19300EC695D /* DirectoryPanel.m */; };
|
||||
CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E1C0FC6C19300EC695D /* ResultWindow.m */; };
|
||||
@@ -51,8 +50,6 @@
|
||||
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
|
||||
CE900AD2109B238600754048 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD1109B238600754048 /* Preferences.xib */; };
|
||||
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD6109B2A9B00754048 /* MainMenu.xib */; };
|
||||
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECA899A09DB132E00A3D774 /* DetailsPanel.h */; };
|
||||
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CECA899B09DB132E00A3D774 /* DetailsPanel.m */; };
|
||||
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
|
||||
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; };
|
||||
CEFC295509C89FF200D9F998 /* details32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295309C89FF200D9F998 /* details32.png */; };
|
||||
@@ -67,7 +64,6 @@
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */,
|
||||
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */,
|
||||
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -88,11 +84,14 @@
|
||||
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
||||
CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
||||
CE381C9B09914ADF003581CE /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = ResultWindow.h; sourceTree = SOURCE_ROOT; };
|
||||
CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dg_cocoa.plugin; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; };
|
||||
CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; };
|
||||
CE3FBDD11094637800B72D77 /* DetailsPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = DetailsPanel.xib; path = ../../base/xib/DetailsPanel.xib; sourceTree = "<group>"; };
|
||||
CE3FBDD21094637800B72D77 /* DirectoryPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = DirectoryPanel.xib; path = ../../base/xib/DirectoryPanel.xib; sourceTree = "<group>"; };
|
||||
CE49DEF20FDFEB810098617B /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; };
|
||||
CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; };
|
||||
CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ErrorReportWindow.xib; sourceTree = "<group>"; };
|
||||
CE4B59C61119919700C06C9E /* progress.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = progress.xib; sourceTree = "<group>"; };
|
||||
CE4B59C71119919700C06C9E /* registration.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = registration.xib; sourceTree = "<group>"; };
|
||||
CE515DE00FC6C12E00EC695D /* Dialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Dialogs.h; path = ../../cocoalib/Dialogs.h; sourceTree = SOURCE_ROOT; };
|
||||
CE515DE10FC6C12E00EC695D /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; };
|
||||
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
|
||||
@@ -106,15 +105,10 @@
|
||||
CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RecentDirectories.m; path = ../../cocoalib/RecentDirectories.m; sourceTree = SOURCE_ROOT; };
|
||||
CE515DEB0FC6C12E00EC695D /* RegistrationInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegistrationInterface.h; path = ../../cocoalib/RegistrationInterface.h; sourceTree = SOURCE_ROOT; };
|
||||
CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegistrationInterface.m; path = ../../cocoalib/RegistrationInterface.m; sourceTree = SOURCE_ROOT; };
|
||||
CE515DED0FC6C12E00EC695D /* Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Table.h; path = ../../cocoalib/Table.h; sourceTree = SOURCE_ROOT; };
|
||||
CE515DEE0FC6C12E00EC695D /* Table.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Table.m; path = ../../cocoalib/Table.m; sourceTree = SOURCE_ROOT; };
|
||||
CE515DEF0FC6C12E00EC695D /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = ../../cocoalib/Utils.h; sourceTree = SOURCE_ROOT; };
|
||||
CE515DF00FC6C12E00EC695D /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Utils.m; path = ../../cocoalib/Utils.m; sourceTree = SOURCE_ROOT; };
|
||||
CE515DF10FC6C12E00EC695D /* ValueTransformers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueTransformers.h; path = ../../cocoalib/ValueTransformers.h; sourceTree = SOURCE_ROOT; };
|
||||
CE515DF20FC6C12E00EC695D /* ValueTransformers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ValueTransformers.m; path = ../../cocoalib/ValueTransformers.m; sourceTree = SOURCE_ROOT; };
|
||||
CE515DFD0FC6C13E00EC695D /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = ../../cocoalib/English.lproj/ErrorReportWindow.xib; sourceTree = "<group>"; };
|
||||
CE515DFF0FC6C13E00EC695D /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/progress.nib; sourceTree = "<group>"; };
|
||||
CE515E010FC6C13E00EC695D /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/registration.nib; sourceTree = "<group>"; };
|
||||
CE515E150FC6C19300EC695D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ../base/AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
||||
CE515E160FC6C19300EC695D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ../base/AppDelegate.m; sourceTree = SOURCE_ROOT; };
|
||||
CE515E170FC6C19300EC695D /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = ../base/Consts.h; sourceTree = SOURCE_ROOT; };
|
||||
@@ -133,6 +127,7 @@
|
||||
CE900AD6109B2A9B00754048 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; };
|
||||
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; };
|
||||
CECA899B09DB132E00A3D774 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DetailsPanel.m; sourceTree = "<group>"; };
|
||||
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
||||
CEEB135109C837A2004D2330 /* dupeguru.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = dupeguru.icns; sourceTree = "<group>"; };
|
||||
CEFC294509C89E3D00D9F998 /* folder32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = folder32.png; path = ../../images/folder32.png; sourceTree = SOURCE_ROOT; };
|
||||
CEFC295309C89FF200D9F998 /* details32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = details32.png; path = ../../images/details32.png; sourceTree = SOURCE_ROOT; };
|
||||
@@ -263,13 +258,22 @@
|
||||
path = ../../cocoalib/brsinglelineformatter;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
CE4B59C41119919700C06C9E /* xib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */,
|
||||
CE4B59C61119919700C06C9E /* progress.xib */,
|
||||
CE4B59C71119919700C06C9E /* registration.xib */,
|
||||
);
|
||||
name = xib;
|
||||
path = ../../cocoalib/xib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
CE515DDD0FC6C09400EC695D /* cocoalib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CE4B59C41119919700C06C9E /* xib */,
|
||||
CE49DEF10FDFEB810098617B /* brsinglelineformatter */,
|
||||
CE515DFC0FC6C13E00EC695D /* ErrorReportWindow.xib */,
|
||||
CE515DFE0FC6C13E00EC695D /* progress.nib */,
|
||||
CE515E000FC6C13E00EC695D /* registration.nib */,
|
||||
CE515DE00FC6C12E00EC695D /* Dialogs.h */,
|
||||
CE515DE10FC6C12E00EC695D /* Dialogs.m */,
|
||||
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */,
|
||||
@@ -283,8 +287,6 @@
|
||||
CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */,
|
||||
CE515DEB0FC6C12E00EC695D /* RegistrationInterface.h */,
|
||||
CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */,
|
||||
CE515DED0FC6C12E00EC695D /* Table.h */,
|
||||
CE515DEE0FC6C12E00EC695D /* Table.m */,
|
||||
CE515DEF0FC6C12E00EC695D /* Utils.h */,
|
||||
CE515DF00FC6C12E00EC695D /* Utils.m */,
|
||||
CE515DF10FC6C12E00EC695D /* ValueTransformers.h */,
|
||||
@@ -304,6 +306,7 @@
|
||||
CE515E180FC6C19300EC695D /* DirectoryPanel.h */,
|
||||
CE515E190FC6C19300EC695D /* DirectoryPanel.m */,
|
||||
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */,
|
||||
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */,
|
||||
CE515E1B0FC6C19300EC695D /* ResultWindow.h */,
|
||||
CE515E1C0FC6C19300EC695D /* ResultWindow.m */,
|
||||
);
|
||||
@@ -371,14 +374,14 @@
|
||||
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */,
|
||||
CEFC295509C89FF200D9F998 /* details32.png in Resources */,
|
||||
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */,
|
||||
CE515E020FC6C13E00EC695D /* ErrorReportWindow.xib in Resources */,
|
||||
CE515E030FC6C13E00EC695D /* progress.nib in Resources */,
|
||||
CE515E040FC6C13E00EC695D /* registration.nib in Resources */,
|
||||
CE6E0E9F1054EB97008D9390 /* dsa_pub.pem in Resources */,
|
||||
CE3FBDD31094637800B72D77 /* DetailsPanel.xib in Resources */,
|
||||
CE3FBDD41094637800B72D77 /* DirectoryPanel.xib in Resources */,
|
||||
CE900AD2109B238600754048 /* Preferences.xib in Resources */,
|
||||
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */,
|
||||
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */,
|
||||
CE4B59C91119919700C06C9E /* progress.xib in Resources */,
|
||||
CE4B59CA1119919700C06C9E /* registration.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -393,14 +396,12 @@
|
||||
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */,
|
||||
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */,
|
||||
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */,
|
||||
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */,
|
||||
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */,
|
||||
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */,
|
||||
CE515DF50FC6C12E00EC695D /* Outline.m in Sources */,
|
||||
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */,
|
||||
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */,
|
||||
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */,
|
||||
CE515DF90FC6C12E00EC695D /* Table.m in Sources */,
|
||||
CE515DFA0FC6C12E00EC695D /* Utils.m in Sources */,
|
||||
CE515DFB0FC6C12E00EC695D /* ValueTransformers.m in Sources */,
|
||||
CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */,
|
||||
@@ -413,58 +414,55 @@
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
CE515DFC0FC6C13E00EC695D /* ErrorReportWindow.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
CE515DFD0FC6C13E00EC695D /* English */,
|
||||
);
|
||||
name = ErrorReportWindow.xib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
CE515DFE0FC6C13E00EC695D /* progress.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
CE515DFF0FC6C13E00EC695D /* English */,
|
||||
);
|
||||
name = progress.nib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
CE515E000FC6C13E00EC695D /* registration.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
CE515E010FC6C13E00EC695D /* English */,
|
||||
);
|
||||
name = registration.nib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
C01FCF4C08A954540054247B /* Release */ = {
|
||||
C01FCF4C08A954540054247B /* release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
PRODUCT_NAME = "dupeGuru ME";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = Release;
|
||||
name = release;
|
||||
};
|
||||
C01FCF5008A954540054247B /* Release */ = {
|
||||
C01FCF5008A954540054247B /* release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)";
|
||||
ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386";
|
||||
ARCHS = (
|
||||
i386,
|
||||
x86_64,
|
||||
ppc,
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.5;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
|
||||
};
|
||||
name = Release;
|
||||
name = release;
|
||||
};
|
||||
CED596C5111AF56D00C0CF2B /* dev */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(NATIVE_ARCH_ACTUAL)";
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.5;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
|
||||
};
|
||||
name = dev;
|
||||
};
|
||||
CED596C6111AF56D00C0CF2B /* dev */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
PRODUCT_NAME = "dupeGuru ME";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = dev;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
@@ -472,18 +470,20 @@
|
||||
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "dupeguru" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C01FCF4C08A954540054247B /* Release */,
|
||||
C01FCF4C08A954540054247B /* release */,
|
||||
CED596C6111AF56D00C0CF2B /* dev */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
defaultConfigurationName = release;
|
||||
};
|
||||
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C01FCF5008A954540054247B /* Release */,
|
||||
C01FCF5008A954540054247B /* release */,
|
||||
CED596C5111AF56D00C0CF2B /* dev */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
defaultConfigurationName = release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
|
||||
@@ -7,10 +7,12 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "Utils.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
[Utils setPluginName:@"dg_cocoa"];
|
||||
NSString *pluginPath = [[NSBundle mainBundle]
|
||||
pathForResource:@"dg_cocoa"
|
||||
ofType:@"plugin"];
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
<object class="NSTextFieldCell" key="NSCell" id="602722407">
|
||||
<int key="NSCellFlags">67239424</int>
|
||||
<int key="NSCellFlags2">71303168</int>
|
||||
<string key="NSContents">Less results</string>
|
||||
<string key="NSContents">Fewer results</string>
|
||||
<reference key="NSSupport" ref="262032469"/>
|
||||
<reference key="NSControlView" ref="735544762"/>
|
||||
<reference key="NSBackgroundColor" ref="221998487"/>
|
||||
|
||||
@@ -8,16 +8,11 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "../base/AppDelegate.h"
|
||||
#import "DirectoryPanel.h"
|
||||
#import "PyDupeGuru.h"
|
||||
|
||||
@interface AppDelegate : AppDelegateBase
|
||||
{
|
||||
DirectoryPanel *_directoryPanel;
|
||||
}
|
||||
@interface AppDelegate : AppDelegateBase {}
|
||||
- (IBAction)openWebsite:(id)sender;
|
||||
- (IBAction)toggleDirectories:(id)sender;
|
||||
|
||||
- (DirectoryPanel *)directoryPanel;
|
||||
- (PyDupeGuru *)py;
|
||||
@end
|
||||
|
||||
@@ -13,6 +13,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
#import "ValueTransformers.h"
|
||||
#import "Consts.h"
|
||||
#import "DetailsPanel.h"
|
||||
#import "DirectoryPanel.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
+ (void)initialize
|
||||
@@ -40,10 +41,10 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
return self;
|
||||
}
|
||||
|
||||
- (DetailsPanelBase *)detailsPanel
|
||||
- (DetailsPanel *)detailsPanel
|
||||
{
|
||||
if (!_detailsPanel)
|
||||
_detailsPanel = [[DetailsPanel alloc] initWithPy:py];
|
||||
_detailsPanel = [[DetailsPanelPE alloc] initWithPy:py];
|
||||
return _detailsPanel;
|
||||
}
|
||||
|
||||
@@ -60,7 +61,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
- (DirectoryPanel *)directoryPanel
|
||||
{
|
||||
if (!_directoryPanel)
|
||||
_directoryPanel = [[DirectoryPanel alloc] initWithParentApp:self];
|
||||
_directoryPanel = [[DirectoryPanelPE alloc] initWithParentApp:self];
|
||||
return _directoryPanel;
|
||||
}
|
||||
- (PyDupeGuru *)py { return (PyDupeGuru *)py; }
|
||||
@@ -75,36 +76,4 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
[mi setKeyEquivalentModifierMask:NSCommandKeyMask|NSShiftKeyMask];
|
||||
[super applicationDidFinishLaunching:aNotification];
|
||||
}
|
||||
|
||||
- (void)applicationWillBecomeActive:(NSNotification *)aNotification
|
||||
{
|
||||
if (![[result window] isVisible])
|
||||
[result showWindow:NSApp];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
[ud setObject: [result getColumnsOrder] forKey:@"columnsOrder"];
|
||||
[ud setObject: [result getColumnsWidth] forKey:@"columnsWidth"];
|
||||
[py saveIgnoreList];
|
||||
[py saveResults];
|
||||
int sc = [ud integerForKey:@"sessionCountSinceLastIgnorePurge"];
|
||||
if (sc >= 10)
|
||||
{
|
||||
sc = -1;
|
||||
[py purgeIgnoreList];
|
||||
}
|
||||
sc++;
|
||||
[ud setInteger:sc forKey:@"sessionCountSinceLastIgnorePurge"];
|
||||
// NSApplication does not release nib instances objects, we must do it manually
|
||||
// Well, it isn't needed because the memory is freed anyway (we are quitting the application
|
||||
// But I need to release RecentDirectories so it saves the user defaults
|
||||
[recentDirectories release];
|
||||
}
|
||||
|
||||
- (void)recentDirecoryClicked:(NSString *)directory
|
||||
{
|
||||
[[self directoryPanel] addDirectory:directory];
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -9,14 +9,14 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "../base/DetailsPanel.h"
|
||||
|
||||
@interface DetailsPanel : DetailsPanelBase
|
||||
@interface DetailsPanelPE : DetailsPanel
|
||||
{
|
||||
IBOutlet NSImageView *dupeImage;
|
||||
IBOutlet NSProgressIndicator *dupeProgressIndicator;
|
||||
IBOutlet NSImageView *refImage;
|
||||
IBOutlet NSProgressIndicator *refProgressIndicator;
|
||||
|
||||
PyApp *py;
|
||||
PyApp *pyApp;
|
||||
BOOL _needsRefresh;
|
||||
NSString *_dupePath;
|
||||
NSString *_refPath;
|
||||
|
||||
@@ -13,11 +13,11 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
#import "DetailsPanel.h"
|
||||
#import "Consts.h"
|
||||
|
||||
@implementation DetailsPanel
|
||||
@implementation DetailsPanelPE
|
||||
- (id)initWithPy:(PyApp *)aPy
|
||||
{
|
||||
self = [super initWithPy:aPy];
|
||||
py = aPy;
|
||||
pyApp = aPy;
|
||||
_needsRefresh = YES;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imageLoaded:) name:ImageLoadedNotification object:self];
|
||||
return self;
|
||||
@@ -36,18 +36,18 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
[pool release];
|
||||
}
|
||||
|
||||
- (void)refresh
|
||||
- (void)refreshDetails
|
||||
{
|
||||
if (!_needsRefresh)
|
||||
return;
|
||||
[detailsTable reloadData];
|
||||
|
||||
NSString *refPath = [(PyDupeGuru *)py getSelectedDupeRefPath];
|
||||
NSString *refPath = [(PyDupeGuru *)pyApp getSelectedDupeRefPath];
|
||||
if (_refPath != nil)
|
||||
[_refPath autorelease];
|
||||
_refPath = [refPath retain];
|
||||
[NSThread detachNewThreadSelector:@selector(loadImageAsync:) toTarget:self withObject:refPath];
|
||||
NSString *dupePath = [(PyDupeGuru *)py getSelectedDupePath];
|
||||
NSString *dupePath = [(PyDupeGuru *)pyApp getSelectedDupePath];
|
||||
if (_dupePath != nil)
|
||||
[_dupePath autorelease];
|
||||
_dupePath = [dupePath retain];
|
||||
@@ -59,12 +59,6 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
- (void)duplicateSelectionChanged:(NSNotification *)aNotification
|
||||
{
|
||||
_needsRefresh = YES;
|
||||
[super duplicateSelectionChanged:aNotification];
|
||||
}
|
||||
|
||||
- (void)imageLoaded:(NSNotification *)aNotification
|
||||
{
|
||||
NSString *imagePath = [[aNotification userInfo] valueForKey:@"imagePath"];
|
||||
@@ -80,4 +74,11 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
[dupeProgressIndicator stopAnimation:nil];
|
||||
}
|
||||
}
|
||||
|
||||
/* Python --> Cocoa */
|
||||
- (void)refresh
|
||||
{
|
||||
_needsRefresh = YES;
|
||||
[super refresh];
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -9,7 +9,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "../base/DirectoryPanel.h"
|
||||
|
||||
@interface DirectoryPanel : DirectoryPanelBase
|
||||
@interface DirectoryPanelPE : DirectoryPanel
|
||||
{
|
||||
}
|
||||
- (IBAction)addiPhoto:(id)sender;
|
||||
|
||||
@@ -11,7 +11,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
static NSString* jobAddIPhoto = @"jobAddIPhoto";
|
||||
|
||||
@implementation DirectoryPanel
|
||||
@implementation DirectoryPanelPE
|
||||
- (id)initWithParentApp:(id)aParentApp
|
||||
{
|
||||
self = [super initWithParentApp:aParentApp];
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>hsft</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.8.0</string>
|
||||
<string>1.8.2</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
|
||||
@interface PictureBlocks : NSObject {
|
||||
}
|
||||
+ (NSString *)getBlocksFromImagePath:(NSString *)imagePath blockCount:(NSNumber *)blockCount;
|
||||
+ (NSSize)getImageSize:(NSString *)imagePath;
|
||||
@end
|
||||
|
||||
|
||||
NSString* GetBlocks(NSString *filePath, int blockCount);
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
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 "PictureBlocks.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation PictureBlocks
|
||||
+ (NSString *)getBlocksFromImagePath:(NSString *)imagePath blockCount:(NSNumber *)blockCount
|
||||
{
|
||||
return GetBlocks(imagePath, n2i(blockCount));
|
||||
}
|
||||
|
||||
+ (NSSize)getImageSize:(NSString *)imagePath
|
||||
{
|
||||
CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)imagePath, kCFURLPOSIXPathStyle, FALSE);
|
||||
CGImageSourceRef source = CGImageSourceCreateWithURL(fileURL, NULL);
|
||||
if (source == NULL)
|
||||
return NSMakeSize(0, 0);
|
||||
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
|
||||
if (image == NULL)
|
||||
return NSMakeSize(0, 0);
|
||||
size_t width = CGImageGetWidth(image);
|
||||
size_t height = CGImageGetHeight(image);
|
||||
CGImageRelease(image);
|
||||
CFRelease(source);
|
||||
CFRelease(fileURL);
|
||||
return NSMakeSize(width, height);
|
||||
}
|
||||
@end
|
||||
|
||||
CGContextRef MyCreateBitmapContext (int width, int height)
|
||||
{
|
||||
CGContextRef context = NULL;
|
||||
CGColorSpaceRef colorSpace;
|
||||
void * bitmapData;
|
||||
int bitmapByteCount;
|
||||
int bitmapBytesPerRow;
|
||||
|
||||
bitmapBytesPerRow = (width * 4);
|
||||
bitmapByteCount = (bitmapBytesPerRow * height);
|
||||
|
||||
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
|
||||
|
||||
// calloc() must be used to allocate bitmapData here because the buffer has to be zeroed.
|
||||
// If it's not zeroes, when images with transparency are drawn in the context, this buffer
|
||||
// will stay with undefined pixels, which means that two pictures with the same pixels will
|
||||
// most likely have different blocks (which is not supposed to happen).
|
||||
bitmapData = calloc(bitmapByteCount, 1);
|
||||
if (bitmapData == NULL)
|
||||
{
|
||||
fprintf (stderr, "Memory not allocated!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context = CGBitmapContextCreate (bitmapData,width,height,8,bitmapBytesPerRow,colorSpace,kCGImageAlphaNoneSkipLast);
|
||||
if (context== NULL)
|
||||
{
|
||||
free (bitmapData);
|
||||
fprintf (stderr, "Context not created!");
|
||||
return NULL;
|
||||
}
|
||||
CGColorSpaceRelease( colorSpace );
|
||||
return context;
|
||||
}
|
||||
|
||||
// returns 0x00RRGGBB
|
||||
int GetBlock(unsigned char *imageData, int imageWidth, int imageHeight, int boxX, int boxY, int boxW, int boxH)
|
||||
{
|
||||
int i,j;
|
||||
int totalR = 0;
|
||||
int totalG = 0;
|
||||
int totalB = 0;
|
||||
for(i = boxY; i < boxY + boxH; i++)
|
||||
{
|
||||
for(j = boxX; j < boxX + boxW; j++)
|
||||
{
|
||||
int offset = (i * imageWidth * 4) + (j * 4);
|
||||
totalR += *(imageData + offset);
|
||||
totalG += *(imageData + offset + 1);
|
||||
totalB += *(imageData + offset + 2);
|
||||
}
|
||||
}
|
||||
int pixelCount = boxH * boxW;
|
||||
int result = 0;
|
||||
result += (totalR / pixelCount) << 16;
|
||||
result += (totalG / pixelCount) << 8;
|
||||
result += (totalB / pixelCount);
|
||||
return result;
|
||||
}
|
||||
|
||||
NSString* GetBlocks (NSString* filePath, int blockCount)
|
||||
{
|
||||
CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)filePath, kCFURLPOSIXPathStyle, FALSE);
|
||||
CGImageSourceRef source = CGImageSourceCreateWithURL(fileURL, NULL);
|
||||
if (source == NULL)
|
||||
return NULL;
|
||||
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
|
||||
if (image == NULL)
|
||||
return NULL;
|
||||
size_t width = CGImageGetWidth(image);
|
||||
size_t height = CGImageGetHeight(image);
|
||||
CGContextRef myContext = MyCreateBitmapContext(width, height);
|
||||
CGRect myBoundingBox = CGRectMake (0, 0, width, height);
|
||||
CGContextDrawImage(myContext, myBoundingBox, image);
|
||||
unsigned char *bitmapData = CGBitmapContextGetData(myContext);
|
||||
if (bitmapData == NULL)
|
||||
return NULL;
|
||||
|
||||
int blockHeight = height / blockCount;
|
||||
if (blockHeight < 1)
|
||||
blockHeight = 1;
|
||||
int blockWidth = width / blockCount;
|
||||
if (blockWidth < 1)
|
||||
blockWidth = 1;
|
||||
//blockCount might have changed
|
||||
int blockXCount = (width / blockWidth);
|
||||
int blockYCount = (height / blockHeight);
|
||||
|
||||
CFMutableArrayRef blocks = CFArrayCreateMutable(NULL, blockXCount * blockYCount, &kCFTypeArrayCallBacks);
|
||||
int i,j;
|
||||
for(i = 0; i < blockYCount; i++)
|
||||
{
|
||||
for(j = 0; j < blockXCount; j++)
|
||||
{
|
||||
int block = GetBlock(bitmapData, width, height, j * blockWidth, i * blockHeight, blockWidth, blockHeight);
|
||||
CFStringRef strBlock = CFStringCreateWithFormat(NULL, NULL, CFSTR("%06x"), block);
|
||||
CFArrayAppendValue(blocks, strBlock);
|
||||
CFRelease(strBlock);
|
||||
}
|
||||
}
|
||||
|
||||
CGContextRelease (myContext);
|
||||
if (bitmapData) free(bitmapData);
|
||||
CGImageRelease(image);
|
||||
CFRelease(source);
|
||||
CFRelease(fileURL);
|
||||
|
||||
CFStringRef result = CFStringCreateByCombiningStrings(NULL, blocks, CFSTR(""));
|
||||
CFRelease(blocks);
|
||||
return (NSString *)result;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,43 +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]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
|
||||
{
|
||||
[self performPySelection:[self getSelectedPaths:NO]];
|
||||
[py refreshDetailsWithSelected];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:DuplicateSelectionChangedNotification object:self];
|
||||
}
|
||||
|
||||
- (void)resultsMarkingChanged:(NSNotification *)aNotification
|
||||
{
|
||||
[matches invalidateMarkings];
|
||||
[self refreshStats];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -4,215 +4,36 @@
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
import objc
|
||||
from AppKit import *
|
||||
|
||||
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
|
||||
from core_pe import app_cocoa as app_pe_cocoa
|
||||
|
||||
# Fix py2app imports which chokes on relative imports
|
||||
from core import app, app_cocoa, data, directories, engine, export, ignore, results, scanner
|
||||
from core_pe import block, cache, matchbase, data
|
||||
from hsutil import conflict
|
||||
from core_pe import block, cache, matchbase, data, _block_osx
|
||||
|
||||
class PyApp(NSObject):
|
||||
pass #fake class
|
||||
|
||||
class PyDupeGuru(PyApp):
|
||||
class PyDupeGuru(PyDupeGuruBase):
|
||||
def init(self):
|
||||
self = super(PyDupeGuru,self).init()
|
||||
self.app = app_pe_cocoa.DupeGuruPE()
|
||||
self = super(PyDupeGuru, self).init()
|
||||
self.py = app_pe_cocoa.DupeGuruPE()
|
||||
return self
|
||||
|
||||
#---Directories
|
||||
def addDirectory_(self,directory):
|
||||
return self.app.add_directory(directory)
|
||||
|
||||
def removeDirectory_(self,index):
|
||||
self.app.RemoveDirectory(index)
|
||||
|
||||
def setDirectory_state_(self,node_path,state):
|
||||
self.app.SetDirectoryState(node_path,state)
|
||||
|
||||
#---Results
|
||||
def clearIgnoreList(self):
|
||||
self.app.scanner.ignore_list.Clear()
|
||||
|
||||
def clearPictureCache(self):
|
||||
self.app.scanner.cached_blocks.clear()
|
||||
|
||||
def doScan(self):
|
||||
return self.app.start_scanning()
|
||||
|
||||
def exportToXHTMLwithColumns_(self, column_ids):
|
||||
return self.app.export_to_xhtml(column_ids)
|
||||
|
||||
def loadIgnoreList(self):
|
||||
self.app.load_ignore_list()
|
||||
|
||||
def loadResults(self):
|
||||
self.app.load()
|
||||
|
||||
def markAll(self):
|
||||
self.app.results.mark_all()
|
||||
|
||||
def markNone(self):
|
||||
self.app.results.mark_none()
|
||||
|
||||
def markInvert(self):
|
||||
self.app.results.mark_invert()
|
||||
|
||||
def purgeIgnoreList(self):
|
||||
self.app.PurgeIgnoreList()
|
||||
|
||||
def toggleSelectedMark(self):
|
||||
self.app.ToggleSelectedMarkState()
|
||||
|
||||
def saveIgnoreList(self):
|
||||
self.app.save_ignore_list()
|
||||
|
||||
def saveResults(self):
|
||||
self.app.save()
|
||||
|
||||
def refreshDetailsWithSelected(self):
|
||||
self.app.RefreshDetailsWithSelected()
|
||||
|
||||
def selectedResultNodePaths(self):
|
||||
return self.app.selected_result_node_paths()
|
||||
|
||||
def selectResultNodePaths_(self,node_paths):
|
||||
self.app.SelectResultNodePaths(node_paths)
|
||||
|
||||
def selectedPowerMarkerNodePaths(self):
|
||||
return self.app.selected_powermarker_node_paths()
|
||||
|
||||
def selectPowerMarkerNodePaths_(self,node_paths):
|
||||
self.app.SelectPowerMarkerNodePaths(node_paths)
|
||||
|
||||
#---Actions
|
||||
def addSelectedToIgnoreList(self):
|
||||
self.app.AddSelectedToIgnoreList()
|
||||
|
||||
def deleteMarked(self):
|
||||
self.app.delete_marked()
|
||||
|
||||
def applyFilter_(self, filter):
|
||||
self.app.apply_filter(filter)
|
||||
|
||||
def makeSelectedReference(self):
|
||||
self.app.MakeSelectedReference()
|
||||
|
||||
def copyOrMove_markedTo_recreatePath_(self,copy,destination,recreate_path):
|
||||
self.app.copy_or_move_marked(copy, destination, recreate_path)
|
||||
|
||||
def openSelected(self):
|
||||
self.app.OpenSelected()
|
||||
|
||||
def removeMarked(self):
|
||||
self.app.results.perform_on_marked(lambda x:True,True)
|
||||
|
||||
def removeSelected(self):
|
||||
self.app.RemoveSelected()
|
||||
|
||||
def renameSelected_(self,newname):
|
||||
return self.app.RenameSelected(newname)
|
||||
|
||||
def revealSelected(self):
|
||||
self.app.RevealSelected()
|
||||
|
||||
#---Misc
|
||||
def sortDupesBy_ascending_(self,key,asc):
|
||||
self.app.sort_dupes(key,asc)
|
||||
|
||||
def sortGroupsBy_ascending_(self,key,asc):
|
||||
self.app.sort_groups(key,asc)
|
||||
self.py.scanner.clear_picture_cache()
|
||||
|
||||
#---Information
|
||||
def getIgnoreListCount(self):
|
||||
return len(self.app.scanner.ignore_list)
|
||||
|
||||
def getMarkCount(self):
|
||||
return self.app.results.mark_count
|
||||
|
||||
def getStatLine(self):
|
||||
return self.app.stat_line
|
||||
|
||||
def getOperationalErrorCount(self):
|
||||
return self.app.last_op_error_count
|
||||
|
||||
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())
|
||||
|
||||
#---Data
|
||||
@objc.signature('i@:i')
|
||||
def getOutlineViewMaxLevel_(self, tag):
|
||||
return self.app.GetOutlineViewMaxLevel(tag)
|
||||
|
||||
@objc.signature('@@:i@')
|
||||
def getOutlineView_childCountsForPath_(self, tag, node_path):
|
||||
return self.app.GetOutlineViewChildCounts(tag, node_path)
|
||||
|
||||
def getOutlineView_valuesForIndexes_(self,tag,node_path):
|
||||
return self.app.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 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)
|
||||
|
||||
def setMixFileKind_(self,mix_file_kind):
|
||||
self.app.scanner.mix_file_kind = mix_file_kind
|
||||
|
||||
def setDisplayDeltaValues_(self,display_delta_values):
|
||||
self.app.display_delta_values= display_delta_values
|
||||
|
||||
def setEscapeFilterRegexp_(self, escape_filter_regexp):
|
||||
self.app.options['escape_filter_regexp'] = escape_filter_regexp
|
||||
|
||||
def setRemoveEmptyFolders_(self, remove_empty_folders):
|
||||
self.app.options['clean_empty_dirs'] = remove_empty_folders
|
||||
|
||||
#---Worker
|
||||
def getJobProgress(self):
|
||||
return self.app.progress.last_progress
|
||||
|
||||
def getJobDesc(self):
|
||||
return self.app.progress.last_desc
|
||||
|
||||
def cancelJob(self):
|
||||
self.app.progress.job_cancelled = True
|
||||
self.py.scanner.threshold = int(percentage)
|
||||
|
||||
#---Registration
|
||||
def appName(self):
|
||||
return "dupeGuru Picture Edition"
|
||||
|
||||
def demoLimitDescription(self):
|
||||
return self.app.DEMO_LIMIT_DESC
|
||||
|
||||
@objc.signature('i@:')
|
||||
def isRegistered(self):
|
||||
return self.app.registered
|
||||
|
||||
@objc.signature('i@:@@')
|
||||
def isCodeValid_withEmail_(self, code, email):
|
||||
return self.app.is_code_valid(code, email)
|
||||
|
||||
def setRegisteredCode_andEmail_(self, code, email):
|
||||
self.app.set_registration(code, email)
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
CE031751109B340A00517EE6 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE031750109B340A00517EE6 /* Preferences.xib */; };
|
||||
CE031754109B345200517EE6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE031753109B345200517EE6 /* MainMenu.xib */; };
|
||||
CE073F6309CAE1A3005C1D2F /* dupeguru_pe_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_pe_help */; };
|
||||
CE0C46AA0FA0647E000BE99B /* PictureBlocks.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0C46A90FA0647E000BE99B /* PictureBlocks.m */; };
|
||||
CE15C8A80ADEB8B50061D4A5 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; };
|
||||
CE15C8C00ADEB8D40061D4A5 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; };
|
||||
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
|
||||
@@ -23,20 +22,19 @@
|
||||
CE6E0F3D1054EC62008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0F3C1054EC62008D9390 /* dsa_pub.pem */; };
|
||||
CE77C89E10946C6D0078B0DB /* DirectoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE77C89C10946C6D0078B0DB /* DirectoryPanel.xib */; };
|
||||
CE77C8A810946CE20078B0DB /* DetailsPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE77C8A710946CE20078B0DB /* DetailsPanel.xib */; };
|
||||
CE7AC9181119911200D02F6C /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */; };
|
||||
CE7AC9191119911200D02F6C /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9161119911200D02F6C /* progress.xib */; };
|
||||
CE7AC91A1119911200D02F6C /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9171119911200D02F6C /* registration.xib */; };
|
||||
CE80DB2E0FC192D60086DCA6 /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB1C0FC192D60086DCA6 /* Dialogs.m */; };
|
||||
CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB1E0FC192D60086DCA6 /* HSErrorReportWindow.m */; };
|
||||
CE80DB300FC192D60086DCA6 /* Outline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB200FC192D60086DCA6 /* Outline.m */; };
|
||||
CE80DB310FC192D60086DCA6 /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB220FC192D60086DCA6 /* ProgressController.m */; };
|
||||
CE80DB320FC192D60086DCA6 /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB250FC192D60086DCA6 /* RecentDirectories.m */; };
|
||||
CE80DB330FC192D60086DCA6 /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB270FC192D60086DCA6 /* RegistrationInterface.m */; };
|
||||
CE80DB340FC192D60086DCA6 /* Table.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB290FC192D60086DCA6 /* Table.m */; };
|
||||
CE80DB350FC192D60086DCA6 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB2B0FC192D60086DCA6 /* Utils.m */; };
|
||||
CE80DB360FC192D60086DCA6 /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB2D0FC192D60086DCA6 /* ValueTransformers.m */; };
|
||||
CE80DB470FC193650086DCA6 /* NSNotificationAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB460FC193650086DCA6 /* NSNotificationAdditions.m */; };
|
||||
CE80DB4A0FC193770086DCA6 /* NSImageAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB490FC193770086DCA6 /* NSImageAdditions.m */; };
|
||||
CE80DB760FC194760086DCA6 /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE80DB700FC194760086DCA6 /* ErrorReportWindow.xib */; };
|
||||
CE80DB770FC194760086DCA6 /* progress.nib in Resources */ = {isa = PBXBuildFile; fileRef = CE80DB720FC194760086DCA6 /* progress.nib */; };
|
||||
CE80DB780FC194760086DCA6 /* registration.nib in Resources */ = {isa = PBXBuildFile; fileRef = CE80DB740FC194760086DCA6 /* registration.nib */; };
|
||||
CE80DB8A0FC1951C0086DCA6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB830FC1951C0086DCA6 /* AppDelegate.m */; };
|
||||
CE80DB8B0FC1951C0086DCA6 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */; };
|
||||
CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB890FC1951C0086DCA6 /* ResultWindow.m */; };
|
||||
@@ -77,14 +75,13 @@
|
||||
CE031750109B340A00517EE6 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; };
|
||||
CE031753109B345200517EE6 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; };
|
||||
CE073F5409CAE1A3005C1D2F /* dupeguru_pe_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_pe_help; path = ../../help_pe/dupeguru_pe_help; sourceTree = SOURCE_ROOT; };
|
||||
CE0C46A80FA0647E000BE99B /* PictureBlocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PictureBlocks.h; sourceTree = "<group>"; };
|
||||
CE0C46A90FA0647E000BE99B /* PictureBlocks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PictureBlocks.m; sourceTree = "<group>"; };
|
||||
CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
|
||||
CE18126F111C9D5100E49FCE /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
||||
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
|
||||
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
||||
CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
||||
CE381C9B09914ADF003581CE /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = ResultWindow.h; sourceTree = SOURCE_ROOT; };
|
||||
CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dg_cocoa.plugin; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; };
|
||||
CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; };
|
||||
CE6044EA0FE6796200B71262 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DetailsPanel.h; path = ../base/DetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
||||
CE6044EB0FE6796200B71262 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DetailsPanel.m; path = ../base/DetailsPanel.m; sourceTree = SOURCE_ROOT; };
|
||||
CE68EE6509ABC48000971085 /* DirectoryPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DirectoryPanel.h; sourceTree = SOURCE_ROOT; };
|
||||
@@ -92,6 +89,9 @@
|
||||
CE6E0F3C1054EC62008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = "<group>"; };
|
||||
CE77C89C10946C6D0078B0DB /* DirectoryPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = DirectoryPanel.xib; path = ../../base/xib/DirectoryPanel.xib; sourceTree = "<group>"; };
|
||||
CE77C8A710946CE20078B0DB /* DetailsPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DetailsPanel.xib; sourceTree = "<group>"; };
|
||||
CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ErrorReportWindow.xib; sourceTree = "<group>"; };
|
||||
CE7AC9161119911200D02F6C /* progress.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = progress.xib; sourceTree = "<group>"; };
|
||||
CE7AC9171119911200D02F6C /* registration.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = registration.xib; sourceTree = "<group>"; };
|
||||
CE80DB1B0FC192D60086DCA6 /* Dialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Dialogs.h; path = ../../cocoalib/Dialogs.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB1C0FC192D60086DCA6 /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB1D0FC192D60086DCA6 /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
|
||||
@@ -105,8 +105,6 @@
|
||||
CE80DB250FC192D60086DCA6 /* RecentDirectories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RecentDirectories.m; path = ../../cocoalib/RecentDirectories.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB260FC192D60086DCA6 /* RegistrationInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegistrationInterface.h; path = ../../cocoalib/RegistrationInterface.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB270FC192D60086DCA6 /* RegistrationInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegistrationInterface.m; path = ../../cocoalib/RegistrationInterface.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB280FC192D60086DCA6 /* Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Table.h; path = ../../cocoalib/Table.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB290FC192D60086DCA6 /* Table.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Table.m; path = ../../cocoalib/Table.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB2A0FC192D60086DCA6 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = ../../cocoalib/Utils.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB2B0FC192D60086DCA6 /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Utils.m; path = ../../cocoalib/Utils.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB2C0FC192D60086DCA6 /* ValueTransformers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueTransformers.h; path = ../../cocoalib/ValueTransformers.h; sourceTree = SOURCE_ROOT; };
|
||||
@@ -115,9 +113,6 @@
|
||||
CE80DB460FC193650086DCA6 /* NSNotificationAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSNotificationAdditions.m; path = ../../cocoalib/NSNotificationAdditions.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB480FC193770086DCA6 /* NSImageAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSImageAdditions.h; path = ../../cocoalib/NSImageAdditions.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB490FC193770086DCA6 /* NSImageAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSImageAdditions.m; path = ../../cocoalib/NSImageAdditions.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB710FC194760086DCA6 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = ../../cocoalib/English.lproj/ErrorReportWindow.xib; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB730FC194760086DCA6 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/progress.nib; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB750FC194760086DCA6 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/registration.nib; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB820FC1951C0086DCA6 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ../base/AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB830FC1951C0086DCA6 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ../base/AppDelegate.m; sourceTree = SOURCE_ROOT; };
|
||||
CE80DB840FC1951C0086DCA6 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = ../base/Consts.h; sourceTree = SOURCE_ROOT; };
|
||||
@@ -155,8 +150,6 @@
|
||||
080E96DDFE201D6D7F000001 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CE0C46A80FA0647E000BE99B /* PictureBlocks.h */,
|
||||
CE0C46A90FA0647E000BE99B /* PictureBlocks.m */,
|
||||
CE381C9509914ACE003581CE /* AppDelegate.h */,
|
||||
CE381C9409914ACE003581CE /* AppDelegate.m */,
|
||||
CE848A1809DD85810004CB44 /* Consts.h */,
|
||||
@@ -254,13 +247,22 @@
|
||||
path = xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CE7AC9141119911200D02F6C /* xib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */,
|
||||
CE7AC9161119911200D02F6C /* progress.xib */,
|
||||
CE7AC9171119911200D02F6C /* registration.xib */,
|
||||
);
|
||||
name = xib;
|
||||
path = ../../cocoalib/xib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
CE80DB1A0FC192AB0086DCA6 /* cocoalib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CE7AC9141119911200D02F6C /* xib */,
|
||||
CEBAE4220FDA97E000B7887D /* brsinglelineformatter */,
|
||||
CE80DB700FC194760086DCA6 /* ErrorReportWindow.xib */,
|
||||
CE80DB720FC194760086DCA6 /* progress.nib */,
|
||||
CE80DB740FC194760086DCA6 /* registration.nib */,
|
||||
CE80DB480FC193770086DCA6 /* NSImageAdditions.h */,
|
||||
CE80DB490FC193770086DCA6 /* NSImageAdditions.m */,
|
||||
CE80DB450FC193650086DCA6 /* NSNotificationAdditions.h */,
|
||||
@@ -278,8 +280,6 @@
|
||||
CE80DB250FC192D60086DCA6 /* RecentDirectories.m */,
|
||||
CE80DB260FC192D60086DCA6 /* RegistrationInterface.h */,
|
||||
CE80DB270FC192D60086DCA6 /* RegistrationInterface.m */,
|
||||
CE80DB280FC192D60086DCA6 /* Table.h */,
|
||||
CE80DB290FC192D60086DCA6 /* Table.m */,
|
||||
CE80DB2A0FC192D60086DCA6 /* Utils.h */,
|
||||
CE80DB2B0FC192D60086DCA6 /* Utils.m */,
|
||||
CE80DB2C0FC192D60086DCA6 /* ValueTransformers.h */,
|
||||
@@ -299,6 +299,7 @@
|
||||
CE80DB850FC1951C0086DCA6 /* DirectoryPanel.h */,
|
||||
CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */,
|
||||
CE80DB870FC1951C0086DCA6 /* PyDupeGuru.h */,
|
||||
CE18126F111C9D5100E49FCE /* PyDetailsPanel.h */,
|
||||
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */,
|
||||
CE80DB890FC1951C0086DCA6 /* ResultWindow.m */,
|
||||
);
|
||||
@@ -377,14 +378,14 @@
|
||||
CEFC295509C89FF200D9F998 /* details32.png in Resources */,
|
||||
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */,
|
||||
CEFCDE2D0AB0418600C33A93 /* dgpe_logo_32.png in Resources */,
|
||||
CE80DB760FC194760086DCA6 /* ErrorReportWindow.xib in Resources */,
|
||||
CE80DB770FC194760086DCA6 /* progress.nib in Resources */,
|
||||
CE80DB780FC194760086DCA6 /* registration.nib in Resources */,
|
||||
CE6E0F3D1054EC62008D9390 /* dsa_pub.pem in Resources */,
|
||||
CE77C89E10946C6D0078B0DB /* DirectoryPanel.xib in Resources */,
|
||||
CE77C8A810946CE20078B0DB /* DetailsPanel.xib in Resources */,
|
||||
CE031751109B340A00517EE6 /* Preferences.xib in Resources */,
|
||||
CE031754109B345200517EE6 /* MainMenu.xib in Resources */,
|
||||
CE7AC9181119911200D02F6C /* ErrorReportWindow.xib in Resources */,
|
||||
CE7AC9191119911200D02F6C /* progress.xib in Resources */,
|
||||
CE7AC91A1119911200D02F6C /* registration.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -400,14 +401,12 @@
|
||||
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */,
|
||||
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */,
|
||||
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */,
|
||||
CE0C46AA0FA0647E000BE99B /* PictureBlocks.m in Sources */,
|
||||
CE80DB2E0FC192D60086DCA6 /* Dialogs.m in Sources */,
|
||||
CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */,
|
||||
CE80DB300FC192D60086DCA6 /* Outline.m in Sources */,
|
||||
CE80DB310FC192D60086DCA6 /* ProgressController.m in Sources */,
|
||||
CE80DB320FC192D60086DCA6 /* RecentDirectories.m in Sources */,
|
||||
CE80DB330FC192D60086DCA6 /* RegistrationInterface.m in Sources */,
|
||||
CE80DB340FC192D60086DCA6 /* Table.m in Sources */,
|
||||
CE80DB350FC192D60086DCA6 /* Utils.m in Sources */,
|
||||
CE80DB360FC192D60086DCA6 /* ValueTransformers.m in Sources */,
|
||||
CE80DB470FC193650086DCA6 /* NSNotificationAdditions.m in Sources */,
|
||||
@@ -422,66 +421,55 @@
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
CE80DB700FC194760086DCA6 /* ErrorReportWindow.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
CE80DB710FC194760086DCA6 /* English */,
|
||||
);
|
||||
name = ErrorReportWindow.xib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
CE80DB720FC194760086DCA6 /* progress.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
CE80DB730FC194760086DCA6 /* English */,
|
||||
);
|
||||
name = progress.nib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
CE80DB740FC194760086DCA6 /* registration.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
CE80DB750FC194760086DCA6 /* English */,
|
||||
);
|
||||
name = registration.nib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
C01FCF4C08A954540054247B /* Release */ = {
|
||||
C01FCF4C08A954540054247B /* release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(FRAMEWORK_SEARCH_PATHS)",
|
||||
"$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
|
||||
);
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
PRODUCT_NAME = "dupeGuru PE";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = Release;
|
||||
name = release;
|
||||
};
|
||||
C01FCF5008A954540054247B /* Release */ = {
|
||||
C01FCF5008A954540054247B /* release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)";
|
||||
ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386";
|
||||
ARCHS = (
|
||||
i386,
|
||||
x86_64,
|
||||
ppc,
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.5;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
|
||||
};
|
||||
name = Release;
|
||||
name = release;
|
||||
};
|
||||
CEE00FF0111AF37400BC1A77 /* dev */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(NATIVE_ARCH_ACTUAL)";
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.5;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
|
||||
};
|
||||
name = dev;
|
||||
};
|
||||
CEE00FF1111AF37400BC1A77 /* dev */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
PRODUCT_NAME = "dupeGuru PE";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = dev;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
@@ -489,18 +477,20 @@
|
||||
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "dupeguru" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C01FCF4C08A954540054247B /* Release */,
|
||||
C01FCF4C08A954540054247B /* release */,
|
||||
CEE00FF1111AF37400BC1A77 /* dev */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
defaultConfigurationName = release;
|
||||
};
|
||||
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C01FCF5008A954540054247B /* Release */,
|
||||
C01FCF5008A954540054247B /* release */,
|
||||
CEE00FF0111AF37400BC1A77 /* dev */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
defaultConfigurationName = release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
|
||||
@@ -7,10 +7,12 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "Utils.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
[Utils setPluginName:@"dg_cocoa"];
|
||||
NSString *pluginPath = [[NSBundle mainBundle]
|
||||
pathForResource:@"dg_cocoa"
|
||||
ofType:@"plugin"];
|
||||
|
||||
@@ -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="18"/>
|
||||
<integer value="7"/>
|
||||
</object>
|
||||
<object class="NSArray" key="IBDocument.PluginDependencies">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
@@ -30,7 +30,7 @@
|
||||
<object class="NSMutableArray" key="IBDocument.RootObjects" id="433298071">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="NSCustomObject" id="449950342">
|
||||
<string key="NSClassName">DetailsPanel</string>
|
||||
<string key="NSClassName">DetailsPanelPE</string>
|
||||
</object>
|
||||
<object class="NSCustomObject" id="175405098">
|
||||
<string key="NSClassName">FirstResponder</string>
|
||||
@@ -487,6 +487,14 @@
|
||||
</object>
|
||||
<int key="connectionID">31</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBOutletConnection" key="connection">
|
||||
<string key="label">dataSource</string>
|
||||
<reference key="source" ref="1061505056"/>
|
||||
<reference key="destination" ref="449950342"/>
|
||||
</object>
|
||||
<int key="connectionID">43</int>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||
<object class="NSArray" key="orderedObjects">
|
||||
@@ -768,7 +776,6 @@
|
||||
<string>6.ImportedFromIB2</string>
|
||||
<string>7.IBPluginDependency</string>
|
||||
<string>7.ImportedFromIB2</string>
|
||||
<string>8.CustomClassName</string>
|
||||
<string>8.IBPluginDependency</string>
|
||||
<string>8.ImportedFromIB2</string>
|
||||
<string>9.IBPluginDependency</string>
|
||||
@@ -825,7 +832,6 @@
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>TableView</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
@@ -848,14 +854,26 @@
|
||||
</object>
|
||||
</object>
|
||||
<nil key="sourceID"/>
|
||||
<int key="maxID">42</int>
|
||||
<int key="maxID">43</int>
|
||||
</object>
|
||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
||||
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">DetailsPanel</string>
|
||||
<string key="superclassName">DetailsPanelBase</string>
|
||||
<string key="superclassName">NSWindowController</string>
|
||||
<object class="NSMutableDictionary" key="outlets">
|
||||
<string key="NS.key.0">detailsTable</string>
|
||||
<string key="NS.object.0">NSTableView</string>
|
||||
</object>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBProjectSource</string>
|
||||
<string key="minorKey">../base/DetailsPanel.h</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">DetailsPanelPE</string>
|
||||
<string key="superclassName">DetailsPanel</string>
|
||||
<object class="NSMutableDictionary" key="outlets">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<object class="NSArray" key="dict.sortedKeys">
|
||||
@@ -879,8 +897,8 @@
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">DetailsPanel</string>
|
||||
<string key="superclassName">DetailsPanelBase</string>
|
||||
<string key="className">DetailsPanelPE</string>
|
||||
<string key="superclassName">DetailsPanel</string>
|
||||
<object class="NSMutableDictionary" key="outlets">
|
||||
<string key="NS.key.0">detailsTable</string>
|
||||
<string key="NS.object.0">NSTableView</string>
|
||||
@@ -890,18 +908,6 @@
|
||||
<string key="minorKey"/>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">DetailsPanelBase</string>
|
||||
<string key="superclassName">NSWindowController</string>
|
||||
<object class="NSMutableDictionary" key="outlets">
|
||||
<string key="NS.key.0">detailsTable</string>
|
||||
<string key="NS.object.0">TableView</string>
|
||||
</object>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBProjectSource</string>
|
||||
<string key="minorKey">dgbase/DetailsPanel.h</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">FirstResponder</string>
|
||||
<string key="superclassName">NSObject</string>
|
||||
@@ -910,34 +916,6 @@
|
||||
<string key="minorKey"/>
|
||||
</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">TableView</string>
|
||||
<string key="superclassName">NSTableView</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">
|
||||
<string key="majorKey">IBProjectSource</string>
|
||||
<string key="minorKey">cocoalib/Table.h</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBPartialClassDescription">
|
||||
<string key="className">TableView</string>
|
||||
<string key="superclassName">NSTableView</string>
|
||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||
<string key="majorKey">IBUserSource</string>
|
||||
<string key="minorKey"/>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
<object class="NSTextFieldCell" key="NSCell" id="397705219">
|
||||
<int key="NSCellFlags">67239424</int>
|
||||
<int key="NSCellFlags2">71303168</int>
|
||||
<string key="NSContents">Less results</string>
|
||||
<string key="NSContents">Fewer results</string>
|
||||
<reference key="NSSupport" ref="649492068"/>
|
||||
<reference key="NSControlView" ref="171701149"/>
|
||||
<reference key="NSBackgroundColor" ref="71910056"/>
|
||||
|
||||
@@ -8,16 +8,11 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "../base/AppDelegate.h"
|
||||
#import "DirectoryPanel.h"
|
||||
#import "PyDupeGuru.h"
|
||||
|
||||
@interface AppDelegate : AppDelegateBase
|
||||
{
|
||||
DirectoryPanel *_directoryPanel;
|
||||
}
|
||||
@interface AppDelegate : AppDelegateBase {}
|
||||
- (IBAction)openWebsite:(id)sender;
|
||||
- (IBAction)toggleDirectories:(id)sender;
|
||||
|
||||
- (DirectoryPanel *)directoryPanel;
|
||||
- (PyDupeGuru *)py;
|
||||
@end
|
||||
|
||||
@@ -12,6 +12,7 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
#import "../../cocoalib/Utils.h"
|
||||
#import "../../cocoalib/ValueTransformers.h"
|
||||
#import "DetailsPanel.h"
|
||||
#import "DirectoryPanel.h"
|
||||
#import "Consts.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
@@ -56,52 +57,5 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
[[self directoryPanel] toggleVisible:sender];
|
||||
}
|
||||
|
||||
- (DirectoryPanel *)directoryPanel
|
||||
{
|
||||
if (!_directoryPanel)
|
||||
_directoryPanel = [[DirectoryPanel alloc] initWithParentApp:self];
|
||||
return _directoryPanel;
|
||||
}
|
||||
|
||||
- (DetailsPanelBase *)detailsPanel
|
||||
{
|
||||
if (!_detailsPanel)
|
||||
_detailsPanel = [[DetailsPanel alloc] initWithPy:py];
|
||||
return _detailsPanel;
|
||||
}
|
||||
|
||||
- (PyDupeGuru *)py { return (PyDupeGuru *)py; }
|
||||
|
||||
//Delegate
|
||||
- (void)applicationWillBecomeActive:(NSNotification *)aNotification
|
||||
{
|
||||
if (![[result window] isVisible])
|
||||
[result showWindow:NSApp];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
[ud setObject: [result getColumnsOrder] forKey:@"columnsOrder"];
|
||||
[ud setObject: [result getColumnsWidth] forKey:@"columnsWidth"];
|
||||
[py saveResults];
|
||||
int sc = [ud integerForKey:@"sessionCountSinceLastIgnorePurge"];
|
||||
if (sc >= 10)
|
||||
{
|
||||
sc = -1;
|
||||
[py purgeIgnoreList];
|
||||
}
|
||||
sc++;
|
||||
[ud setInteger:sc forKey:@"sessionCountSinceLastIgnorePurge"];
|
||||
[py saveIgnoreList];
|
||||
// NSApplication does not release nib instances objects, we must do it manually
|
||||
// Well, it isn't needed because the memory is freed anyway (we are quitting the application
|
||||
// But I need to release RecentDirectories so it saves the user defaults
|
||||
[recentDirectories release];
|
||||
}
|
||||
|
||||
- (void)recentDirecoryClicked:(NSString *)directory
|
||||
{
|
||||
[[self directoryPanel] addDirectory:directory];
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
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 "DetailsPanel.h"
|
||||
|
||||
@implementation DetailsPanel
|
||||
@end
|
||||
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
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 "DirectoryPanel.h"
|
||||
|
||||
@implementation DirectoryPanel
|
||||
@end
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,43 +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]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
|
||||
{
|
||||
[self performPySelection:[self getSelectedPaths:NO]];
|
||||
[py refreshDetailsWithSelected];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:DuplicateSelectionChangedNotification object:self];
|
||||
}
|
||||
|
||||
- (void)resultsMarkingChanged:(NSNotification *)aNotification
|
||||
{
|
||||
[matches invalidateMarkings];
|
||||
[self refreshStats];
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -4,170 +4,28 @@
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
import objc
|
||||
from AppKit import *
|
||||
from hsutil.cocoa import signature
|
||||
|
||||
from core_se.app_cocoa import DupeGuru
|
||||
from core import scanner
|
||||
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
|
||||
from core_se.app_cocoa import DupeGuru
|
||||
|
||||
# Fix py2app imports with chokes on relative imports
|
||||
from core_se import fs, data
|
||||
from core import app, app_cocoa, data, directories, engine, export, ignore, results, fs
|
||||
from hsutil import conflict
|
||||
|
||||
class PyApp(NSObject):
|
||||
pass #fake class
|
||||
|
||||
class PyDupeGuru(PyApp):
|
||||
class PyDupeGuru(PyDupeGuruBase):
|
||||
def init(self):
|
||||
self = super(PyDupeGuru,self).init()
|
||||
self.app = DupeGuru()
|
||||
self.py = DupeGuru()
|
||||
return self
|
||||
|
||||
#---Directories
|
||||
def addDirectory_(self,directory):
|
||||
return self.app.add_directory(directory)
|
||||
|
||||
def removeDirectory_(self,index):
|
||||
self.app.RemoveDirectory(index)
|
||||
|
||||
def setDirectory_state_(self,node_path,state):
|
||||
self.app.SetDirectoryState(node_path,state)
|
||||
|
||||
#---Results
|
||||
def clearIgnoreList(self):
|
||||
self.app.scanner.ignore_list.Clear()
|
||||
|
||||
def doScan(self):
|
||||
return self.app.start_scanning()
|
||||
|
||||
def exportToXHTMLwithColumns_(self, column_ids):
|
||||
return self.app.export_to_xhtml(column_ids)
|
||||
|
||||
def loadIgnoreList(self):
|
||||
self.app.load_ignore_list()
|
||||
|
||||
def loadResults(self):
|
||||
self.app.load()
|
||||
|
||||
def markAll(self):
|
||||
self.app.results.mark_all()
|
||||
|
||||
def markNone(self):
|
||||
self.app.results.mark_none()
|
||||
|
||||
def markInvert(self):
|
||||
self.app.results.mark_invert()
|
||||
|
||||
def purgeIgnoreList(self):
|
||||
self.app.PurgeIgnoreList()
|
||||
|
||||
def toggleSelectedMark(self):
|
||||
self.app.ToggleSelectedMarkState()
|
||||
|
||||
def saveIgnoreList(self):
|
||||
self.app.save_ignore_list()
|
||||
|
||||
def saveResults(self):
|
||||
self.app.save()
|
||||
|
||||
def refreshDetailsWithSelected(self):
|
||||
self.app.RefreshDetailsWithSelected()
|
||||
|
||||
def selectedResultNodePaths(self):
|
||||
return self.app.selected_result_node_paths()
|
||||
|
||||
def selectResultNodePaths_(self,node_paths):
|
||||
self.app.SelectResultNodePaths(node_paths)
|
||||
|
||||
def selectedPowerMarkerNodePaths(self):
|
||||
return self.app.selected_powermarker_node_paths()
|
||||
|
||||
def selectPowerMarkerNodePaths_(self,node_paths):
|
||||
self.app.SelectPowerMarkerNodePaths(node_paths)
|
||||
|
||||
#---Actions
|
||||
def addSelectedToIgnoreList(self):
|
||||
self.app.AddSelectedToIgnoreList()
|
||||
|
||||
def deleteMarked(self):
|
||||
self.app.delete_marked()
|
||||
|
||||
def applyFilter_(self, filter):
|
||||
self.app.apply_filter(filter)
|
||||
|
||||
def makeSelectedReference(self):
|
||||
self.app.MakeSelectedReference()
|
||||
|
||||
def copyOrMove_markedTo_recreatePath_(self,copy,destination,recreate_path):
|
||||
self.app.copy_or_move_marked(copy, destination, recreate_path)
|
||||
|
||||
def openSelected(self):
|
||||
self.app.OpenSelected()
|
||||
|
||||
def removeMarked(self):
|
||||
self.app.results.perform_on_marked(lambda x:True, True)
|
||||
|
||||
def removeSelected(self):
|
||||
self.app.RemoveSelected()
|
||||
|
||||
def renameSelected_(self,newname):
|
||||
return self.app.RenameSelected(newname)
|
||||
|
||||
def revealSelected(self):
|
||||
self.app.RevealSelected()
|
||||
|
||||
#---Misc
|
||||
def sortDupesBy_ascending_(self,key,asc):
|
||||
self.app.sort_dupes(key,asc)
|
||||
|
||||
def sortGroupsBy_ascending_(self,key,asc):
|
||||
self.app.sort_groups(key,asc)
|
||||
|
||||
#---Information
|
||||
def getIgnoreListCount(self):
|
||||
return len(self.app.scanner.ignore_list)
|
||||
|
||||
def getMarkCount(self):
|
||||
return self.app.results.mark_count
|
||||
|
||||
def getStatLine(self):
|
||||
return self.app.stat_line
|
||||
|
||||
def getOperationalErrorCount(self):
|
||||
return self.app.last_op_error_count
|
||||
|
||||
#---Data
|
||||
@objc.signature('i@:i')
|
||||
def getOutlineViewMaxLevel_(self, tag):
|
||||
return self.app.GetOutlineViewMaxLevel(tag)
|
||||
|
||||
@objc.signature('@@:i@')
|
||||
def getOutlineView_childCountsForPath_(self, tag, node_path):
|
||||
return self.app.GetOutlineViewChildCounts(tag, node_path)
|
||||
|
||||
def getOutlineView_valuesForIndexes_(self,tag,node_path):
|
||||
return self.app.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)
|
||||
|
||||
#---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]
|
||||
@@ -175,52 +33,16 @@ class PyDupeGuru(PyApp):
|
||||
pass
|
||||
|
||||
def setWordWeighting_(self,words_are_weighted):
|
||||
self.app.scanner.word_weighting = words_are_weighted
|
||||
|
||||
def setMixFileKind_(self,mix_file_kind):
|
||||
self.app.scanner.mix_file_kind = mix_file_kind
|
||||
|
||||
def setDisplayDeltaValues_(self,display_delta_values):
|
||||
self.app.display_delta_values= display_delta_values
|
||||
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 setEscapeFilterRegexp_(self, escape_filter_regexp):
|
||||
self.app.options['escape_filter_regexp'] = escape_filter_regexp
|
||||
|
||||
def setRemoveEmptyFolders_(self, remove_empty_folders):
|
||||
self.app.options['clean_empty_dirs'] = remove_empty_folders
|
||||
|
||||
@objc.signature('v@:i')
|
||||
@signature('v@:i')
|
||||
def setSizeThreshold_(self, size_threshold):
|
||||
self.app.scanner.size_threshold = size_threshold
|
||||
|
||||
#---Worker
|
||||
def getJobProgress(self):
|
||||
return self.app.progress.last_progress
|
||||
|
||||
def getJobDesc(self):
|
||||
return self.app.progress.last_desc
|
||||
|
||||
def cancelJob(self):
|
||||
self.app.progress.job_cancelled = True
|
||||
self.py.scanner.size_threshold = size_threshold
|
||||
|
||||
#---Registration
|
||||
def appName(self):
|
||||
return "dupeGuru"
|
||||
|
||||
def demoLimitDescription(self):
|
||||
return self.app.DEMO_LIMIT_DESC
|
||||
|
||||
@objc.signature('i@:')
|
||||
def isRegistered(self):
|
||||
return self.app.registered
|
||||
|
||||
@objc.signature('i@:@@')
|
||||
def isCodeValid_withEmail_(self, code, email):
|
||||
return self.app.is_code_valid(code, email)
|
||||
|
||||
def setRegisteredCode_andEmail_(self, code, email):
|
||||
self.app.set_registration(code, email)
|
||||
|
||||
|
||||
@@ -10,17 +10,25 @@
|
||||
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
|
||||
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
|
||||
CE073F6309CAE1A3005C1D2F /* dupeguru_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_help */; };
|
||||
CE19BC6311199231007CCEB0 /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */; };
|
||||
CE19BC6411199231007CCEB0 /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6111199231007CCEB0 /* progress.xib */; };
|
||||
CE19BC6511199231007CCEB0 /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6211199231007CCEB0 /* registration.xib */; };
|
||||
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
|
||||
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9A09914ADF003581CE /* ResultWindow.m */; };
|
||||
CE381D0509915304003581CE /* dg_cocoa.plugin in Resources */ = {isa = PBXBuildFile; fileRef = CE381CF509915304003581CE /* dg_cocoa.plugin */; };
|
||||
CE3A46FA109B212E002ABFD5 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3A46F9109B212E002ABFD5 /* MainMenu.xib */; };
|
||||
CE45579B0AE3BC2B005A9546 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; };
|
||||
CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; };
|
||||
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE68EE6609ABC48000971085 /* DirectoryPanel.m */; };
|
||||
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 */; };
|
||||
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECA899A09DB132E00A3D774 /* DetailsPanel.h */; };
|
||||
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CECA899B09DB132E00A3D774 /* DetailsPanel.m */; };
|
||||
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 */; };
|
||||
@@ -35,12 +43,8 @@
|
||||
CEFC7FA10FC9517500CD5728 /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F910FC9517500CD5728 /* ProgressController.m */; };
|
||||
CEFC7FA20FC9517500CD5728 /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F950FC9517500CD5728 /* RecentDirectories.m */; };
|
||||
CEFC7FA30FC9517500CD5728 /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F970FC9517500CD5728 /* RegistrationInterface.m */; };
|
||||
CEFC7FA40FC9517500CD5728 /* Table.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F990FC9517500CD5728 /* Table.m */; };
|
||||
CEFC7FA50FC9517500CD5728 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F9B0FC9517500CD5728 /* Utils.m */; };
|
||||
CEFC7FA60FC9517500CD5728 /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F9D0FC9517500CD5728 /* ValueTransformers.m */; };
|
||||
CEFC7FAD0FC9518A00CD5728 /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEFC7FA70FC9518A00CD5728 /* ErrorReportWindow.xib */; };
|
||||
CEFC7FAE0FC9518A00CD5728 /* progress.nib in Resources */ = {isa = PBXBuildFile; fileRef = CEFC7FA90FC9518A00CD5728 /* progress.nib */; };
|
||||
CEFC7FAF0FC9518A00CD5728 /* registration.nib in Resources */ = {isa = PBXBuildFile; fileRef = CEFC7FAB0FC9518A00CD5728 /* registration.nib */; };
|
||||
CEFC7FB90FC951A700CD5728 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7FB20FC951A700CD5728 /* AppDelegate.m */; };
|
||||
CEFC7FBA0FC951A700CD5728 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7FB50FC951A700CD5728 /* DirectoryPanel.m */; };
|
||||
CEFC7FBB0FC951A700CD5728 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7FB80FC951A700CD5728 /* ResultWindow.m */; };
|
||||
@@ -54,7 +58,6 @@
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */,
|
||||
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -69,6 +72,9 @@
|
||||
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; };
|
||||
8D1107320486CEB800E47090 /* dupeGuru.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = dupeGuru.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CE073F5409CAE1A3005C1D2F /* dupeguru_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_help; path = ../../help_se/dupeguru_help; sourceTree = "<group>"; };
|
||||
CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ErrorReportWindow.xib; sourceTree = "<group>"; };
|
||||
CE19BC6111199231007CCEB0 /* progress.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = progress.xib; sourceTree = "<group>"; };
|
||||
CE19BC6211199231007CCEB0 /* registration.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = registration.xib; sourceTree = "<group>"; };
|
||||
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
|
||||
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
||||
CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
||||
@@ -76,12 +82,28 @@
|
||||
CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; };
|
||||
CE3A46F9109B212E002ABFD5 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../base/xib/MainMenu.xib; sourceTree = "<group>"; };
|
||||
CE45579A0AE3BC2B005A9546 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
|
||||
CE68EE6509ABC48000971085 /* DirectoryPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DirectoryPanel.h; sourceTree = SOURCE_ROOT; };
|
||||
CE68EE6609ABC48000971085 /* DirectoryPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DirectoryPanel.m; sourceTree = SOURCE_ROOT; };
|
||||
CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = "<group>"; };
|
||||
CE6E7407111C997500C350E3 /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
||||
CE76FDBE111EE37C006618EA /* HSOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutlineView.h; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; };
|
||||
CECA899B09DB132E00A3D774 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DetailsPanel.m; 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; };
|
||||
@@ -106,15 +128,10 @@
|
||||
CEFC7F950FC9517500CD5728 /* RecentDirectories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RecentDirectories.m; path = ../../cocoalib/RecentDirectories.m; sourceTree = SOURCE_ROOT; };
|
||||
CEFC7F960FC9517500CD5728 /* RegistrationInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegistrationInterface.h; path = ../../cocoalib/RegistrationInterface.h; sourceTree = SOURCE_ROOT; };
|
||||
CEFC7F970FC9517500CD5728 /* RegistrationInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegistrationInterface.m; path = ../../cocoalib/RegistrationInterface.m; sourceTree = SOURCE_ROOT; };
|
||||
CEFC7F980FC9517500CD5728 /* Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Table.h; path = ../../cocoalib/Table.h; sourceTree = SOURCE_ROOT; };
|
||||
CEFC7F990FC9517500CD5728 /* Table.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Table.m; path = ../../cocoalib/Table.m; sourceTree = SOURCE_ROOT; };
|
||||
CEFC7F9A0FC9517500CD5728 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = ../../cocoalib/Utils.h; sourceTree = SOURCE_ROOT; };
|
||||
CEFC7F9B0FC9517500CD5728 /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Utils.m; path = ../../cocoalib/Utils.m; sourceTree = SOURCE_ROOT; };
|
||||
CEFC7F9C0FC9517500CD5728 /* ValueTransformers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueTransformers.h; path = ../../cocoalib/ValueTransformers.h; sourceTree = SOURCE_ROOT; };
|
||||
CEFC7F9D0FC9517500CD5728 /* ValueTransformers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ValueTransformers.m; path = ../../cocoalib/ValueTransformers.m; sourceTree = SOURCE_ROOT; };
|
||||
CEFC7FA80FC9518A00CD5728 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = ../../cocoalib/English.lproj/ErrorReportWindow.xib; sourceTree = "<group>"; };
|
||||
CEFC7FAA0FC9518A00CD5728 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/progress.nib; sourceTree = "<group>"; };
|
||||
CEFC7FAC0FC9518A00CD5728 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/registration.nib; sourceTree = "<group>"; };
|
||||
CEFC7FB10FC951A700CD5728 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ../base/AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
||||
CEFC7FB20FC951A700CD5728 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ../base/AppDelegate.m; sourceTree = SOURCE_ROOT; };
|
||||
CEFC7FB30FC951A700CD5728 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = ../base/Consts.h; sourceTree = SOURCE_ROOT; };
|
||||
@@ -144,10 +161,6 @@
|
||||
children = (
|
||||
CE381C9509914ACE003581CE /* AppDelegate.h */,
|
||||
CE381C9409914ACE003581CE /* AppDelegate.m */,
|
||||
CECA899A09DB132E00A3D774 /* DetailsPanel.h */,
|
||||
CECA899B09DB132E00A3D774 /* DetailsPanel.m */,
|
||||
CE68EE6509ABC48000971085 /* DirectoryPanel.h */,
|
||||
CE68EE6609ABC48000971085 /* DirectoryPanel.m */,
|
||||
CEFF18A009A4D387005E6321 /* PyDupeGuru.h */,
|
||||
CE381C9B09914ADF003581CE /* ResultWindow.h */,
|
||||
CE381C9A09914ADF003581CE /* ResultWindow.m */,
|
||||
@@ -219,6 +232,55 @@
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CE19BC5F11199231007CCEB0 /* xib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */,
|
||||
CE19BC6111199231007CCEB0 /* progress.xib */,
|
||||
CE19BC6211199231007CCEB0 /* registration.xib */,
|
||||
);
|
||||
name = xib;
|
||||
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 = (
|
||||
@@ -253,10 +315,13 @@
|
||||
CEFC7F890FC9513600CD5728 /* cocoalib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CE76FDF5111EE561006618EA /* NSEventAdditions.h */,
|
||||
CE76FDF6111EE561006618EA /* NSEventAdditions.m */,
|
||||
CE76FDC7111EE38E006618EA /* controllers */,
|
||||
CE76FDCC111EE38E006618EA /* proxies */,
|
||||
CE76FDBD111EE37C006618EA /* views */,
|
||||
CE19BC5F11199231007CCEB0 /* xib */,
|
||||
CEDD92D50FDD01640031C7B7 /* brsinglelineformatter */,
|
||||
CEFC7FA70FC9518A00CD5728 /* ErrorReportWindow.xib */,
|
||||
CEFC7FA90FC9518A00CD5728 /* progress.nib */,
|
||||
CEFC7FAB0FC9518A00CD5728 /* registration.nib */,
|
||||
CEFC7F8A0FC9517500CD5728 /* Dialogs.h */,
|
||||
CEFC7F8B0FC9517500CD5728 /* Dialogs.m */,
|
||||
CEFC7F8C0FC9517500CD5728 /* HSErrorReportWindow.h */,
|
||||
@@ -271,8 +336,6 @@
|
||||
CEFC7F950FC9517500CD5728 /* RecentDirectories.m */,
|
||||
CEFC7F960FC9517500CD5728 /* RegistrationInterface.h */,
|
||||
CEFC7F970FC9517500CD5728 /* RegistrationInterface.m */,
|
||||
CEFC7F980FC9517500CD5728 /* Table.h */,
|
||||
CEFC7F990FC9517500CD5728 /* Table.m */,
|
||||
CEFC7F9A0FC9517500CD5728 /* Utils.h */,
|
||||
CEFC7F9B0FC9517500CD5728 /* Utils.m */,
|
||||
CEFC7F9C0FC9517500CD5728 /* ValueTransformers.h */,
|
||||
@@ -284,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 */,
|
||||
@@ -292,6 +358,7 @@
|
||||
CEFC7FB40FC951A700CD5728 /* DirectoryPanel.h */,
|
||||
CEFC7FB50FC951A700CD5728 /* DirectoryPanel.m */,
|
||||
CEFC7FB60FC951A700CD5728 /* PyDupeGuru.h */,
|
||||
CE6E7407111C997500C350E3 /* PyDetailsPanel.h */,
|
||||
CEFC7FB70FC951A700CD5728 /* ResultWindow.h */,
|
||||
CEFC7FB80FC951A700CD5728 /* ResultWindow.m */,
|
||||
);
|
||||
@@ -351,14 +418,14 @@
|
||||
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */,
|
||||
CEFC295509C89FF200D9F998 /* details32.png in Resources */,
|
||||
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */,
|
||||
CEFC7FAD0FC9518A00CD5728 /* ErrorReportWindow.xib in Resources */,
|
||||
CEFC7FAE0FC9518A00CD5728 /* progress.nib in Resources */,
|
||||
CEFC7FAF0FC9518A00CD5728 /* registration.nib in Resources */,
|
||||
CE6E0DFE1054E9EF008D9390 /* dsa_pub.pem in Resources */,
|
||||
CEEFC0F810945D9F001F3A39 /* DirectoryPanel.xib in Resources */,
|
||||
CEEFC0FB10945E37001F3A39 /* DetailsPanel.xib in Resources */,
|
||||
CEAC6811109B0B7E00B43C85 /* Preferences.xib in Resources */,
|
||||
CE3A46FA109B212E002ABFD5 /* MainMenu.xib in Resources */,
|
||||
CE19BC6311199231007CCEB0 /* ErrorReportWindow.xib in Resources */,
|
||||
CE19BC6411199231007CCEB0 /* progress.xib in Resources */,
|
||||
CE19BC6511199231007CCEB0 /* registration.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -372,15 +439,12 @@
|
||||
8D11072D0486CEB800E47090 /* main.m in Sources */,
|
||||
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */,
|
||||
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */,
|
||||
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */,
|
||||
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */,
|
||||
CEFC7F9E0FC9517500CD5728 /* Dialogs.m in Sources */,
|
||||
CEFC7F9F0FC9517500CD5728 /* HSErrorReportWindow.m in Sources */,
|
||||
CEFC7FA00FC9517500CD5728 /* Outline.m in Sources */,
|
||||
CEFC7FA10FC9517500CD5728 /* ProgressController.m in Sources */,
|
||||
CEFC7FA20FC9517500CD5728 /* RecentDirectories.m in Sources */,
|
||||
CEFC7FA30FC9517500CD5728 /* RegistrationInterface.m in Sources */,
|
||||
CEFC7FA40FC9517500CD5728 /* Table.m in Sources */,
|
||||
CEFC7FA50FC9517500CD5728 /* Utils.m in Sources */,
|
||||
CEFC7FA60FC9517500CD5728 /* ValueTransformers.m in Sources */,
|
||||
CEFC7FB90FC951A700CD5728 /* AppDelegate.m in Sources */,
|
||||
@@ -388,68 +452,68 @@
|
||||
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;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
CEFC7FA70FC9518A00CD5728 /* ErrorReportWindow.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
CEFC7FA80FC9518A00CD5728 /* English */,
|
||||
);
|
||||
name = ErrorReportWindow.xib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
CEFC7FA90FC9518A00CD5728 /* progress.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
CEFC7FAA0FC9518A00CD5728 /* English */,
|
||||
);
|
||||
name = progress.nib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
CEFC7FAB0FC9518A00CD5728 /* registration.nib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
CEFC7FAC0FC9518A00CD5728 /* English */,
|
||||
);
|
||||
name = registration.nib;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
C01FCF4C08A954540054247B /* Release */ = {
|
||||
C01FCF4C08A954540054247B /* release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
);
|
||||
FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(SRCROOT)/../../base/cocoa/build/Release\"";
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
PRODUCT_NAME = dupeGuru;
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = Release;
|
||||
name = release;
|
||||
};
|
||||
C01FCF5008A954540054247B /* Release */ = {
|
||||
C01FCF5008A954540054247B /* release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)";
|
||||
ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386";
|
||||
ARCHS = (
|
||||
i386,
|
||||
x86_64,
|
||||
ppc,
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.5;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
|
||||
};
|
||||
name = Release;
|
||||
name = release;
|
||||
};
|
||||
CE85E84F111AF63D00187B0D /* dev */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(NATIVE_ARCH_ACTUAL)";
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.5;
|
||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
|
||||
};
|
||||
name = dev;
|
||||
};
|
||||
CE85E850111AF63D00187B0D /* dev */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(HOME)/Applications";
|
||||
PRODUCT_NAME = dupeGuru;
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = dev;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
@@ -457,18 +521,20 @@
|
||||
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "dupeguru" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C01FCF4C08A954540054247B /* Release */,
|
||||
C01FCF4C08A954540054247B /* release */,
|
||||
CE85E850111AF63D00187B0D /* dev */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
defaultConfigurationName = release;
|
||||
};
|
||||
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C01FCF5008A954540054247B /* Release */,
|
||||
C01FCF5008A954540054247B /* release */,
|
||||
CE85E84F111AF63D00187B0D /* dev */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
defaultConfigurationName = release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
|
||||
@@ -7,10 +7,12 @@ http://www.hardcoded.net/licenses/hs_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "Utils.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
[Utils setPluginName:@"dg_cocoa"];
|
||||
NSString *pluginPath = [[NSBundle mainBundle]
|
||||
pathForResource:@"dg_cocoa"
|
||||
ofType:@"plugin"];
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
<object class="NSTextFieldCell" key="NSCell" id="569479200">
|
||||
<int key="NSCellFlags">67239424</int>
|
||||
<int key="NSCellFlags2">71303168</int>
|
||||
<string key="NSContents">Less results</string>
|
||||
<string key="NSContents">Fewer results</string>
|
||||
<reference key="NSSupport" ref="1004672526"/>
|
||||
<reference key="NSControlView" ref="1008577648"/>
|
||||
<reference key="NSBackgroundColor" ref="623994344"/>
|
||||
|
||||
74
core/app.py
74
core/app.py
@@ -17,6 +17,7 @@ from hsutil import io, files
|
||||
from hsutil.path import Path
|
||||
from hsutil.reg import RegistrableApplication, RegistrationRequired
|
||||
from hsutil.misc import flatten, first
|
||||
from hsutil.notify import Broadcaster
|
||||
from hsutil.str import escape
|
||||
|
||||
from . import directories, results, scanner, export, fs
|
||||
@@ -33,11 +34,12 @@ class NoScannableFileError(Exception):
|
||||
class AllFilesAreRefError(Exception):
|
||||
pass
|
||||
|
||||
class DupeGuru(RegistrableApplication):
|
||||
class DupeGuru(RegistrableApplication, Broadcaster):
|
||||
DEMO_LIMIT_DESC = "In the demo version, only 10 duplicates per session can be sent to the recycle bin, moved or copied."
|
||||
|
||||
def __init__(self, data_module, appdata, appid):
|
||||
RegistrableApplication.__init__(self, appid)
|
||||
Broadcaster.__init__(self)
|
||||
self.appdata = appdata
|
||||
if not op.exists(self.appdata):
|
||||
os.makedirs(self.appdata)
|
||||
@@ -51,6 +53,7 @@ class DupeGuru(RegistrableApplication):
|
||||
'escape_filter_regexp': True,
|
||||
'clean_empty_dirs': False,
|
||||
}
|
||||
self.selected_dupes = []
|
||||
|
||||
def _demo_check(self):
|
||||
if self.registered:
|
||||
@@ -81,6 +84,7 @@ class DupeGuru(RegistrableApplication):
|
||||
|
||||
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)
|
||||
@@ -100,10 +104,24 @@ class DupeGuru(RegistrableApplication):
|
||||
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
|
||||
self.selected_dupes = dupes
|
||||
self.notify('dupes_selected')
|
||||
|
||||
def _start_job(self, jobid, func):
|
||||
# func(j)
|
||||
raise NotImplementedError()
|
||||
@@ -111,17 +129,21 @@ class DupeGuru(RegistrableApplication):
|
||||
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):
|
||||
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))
|
||||
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)
|
||||
@@ -203,24 +225,45 @@ class DupeGuru(RegistrableApplication):
|
||||
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 save(self):
|
||||
def open_selected(self):
|
||||
if self.selected_dupes:
|
||||
self._open_path(self.selected_dupes[0].path)
|
||||
|
||||
def remove_directory(self,index):
|
||||
try:
|
||||
self.directories.save_to_file(op.join(self.appdata, 'last_directories.xml'))
|
||||
self.results.save_to_xml(op.join(self.appdata, 'last_results.xml'))
|
||||
except LookupError:
|
||||
# This is that weird issue from #39 that sometimes happens when auto-updating with
|
||||
# Sparkle. Just ignore it.
|
||||
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)
|
||||
self.directories.save_to_file(op.join(self.appdata, 'last_directories.xml'))
|
||||
self.results.save_to_xml(op.join(self.appdata, 'last_results.xml'))
|
||||
|
||||
def save_ignore_list(self):
|
||||
if not op.exists(self.appdata):
|
||||
os.makedirs(self.appdata)
|
||||
p = op.join(self.appdata, 'ignore_list.xml')
|
||||
self.scanner.ignore_list.save_to_xml(p)
|
||||
|
||||
@@ -240,6 +283,9 @@ class DupeGuru(RegistrableApplication):
|
||||
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):
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
import objc
|
||||
from Foundation import *
|
||||
from AppKit import *
|
||||
import logging
|
||||
import os.path as op
|
||||
|
||||
from hsutil import io, cocoa, job
|
||||
from hsutil import cocoa, job
|
||||
from hsutil.cocoa import install_exception_hook
|
||||
from hsutil.cocoa.objcmin import (NSNotificationCenter, NSUserDefaults,
|
||||
NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSUserDomainMask,
|
||||
NSWorkspace, NSWorkspaceRecycleOperation)
|
||||
from hsutil.misc import stripnone
|
||||
from hsutil.reg import RegistrationRequired
|
||||
|
||||
@@ -47,20 +47,23 @@ class DupeGuru(app.DupeGuru):
|
||||
app.DupeGuru.__init__(self, data_module, appdata, appid)
|
||||
self.progress = cocoa.ThreadedJobPerformer()
|
||||
self.display_delta_values = False
|
||||
self.selected_dupes = []
|
||||
self.RefreshDetailsTable(None,None)
|
||||
|
||||
#--- 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.
|
||||
directory = unicode(dupe.path[:-1])
|
||||
filename = dupe.name
|
||||
if objc.__version__ == '1.4': # For a while, we have to support this.
|
||||
result, tag = NSWorkspace.sharedWorkspace().performFileOperation_source_destination_files_tag_(
|
||||
NSWorkspaceRecycleOperation, directory, '', [filename])
|
||||
else:
|
||||
result, tag = NSWorkspace.sharedWorkspace().performFileOperation_source_destination_files_tag_(
|
||||
NSWorkspaceRecycleOperation, directory, '', [filename], None)
|
||||
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:
|
||||
@@ -84,61 +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)
|
||||
|
||||
def RefreshDetailsTable(self,dupe,group):
|
||||
l1 = self._get_display_info(dupe, group, False)
|
||||
# we don't want the two sides of the table to display the stats for the same file
|
||||
ref = group.ref if group is not None and group.ref is not dupe else None
|
||||
l2 = self._get_display_info(ref, group, False)
|
||||
names = [c['display'] for c in self.data.COLUMNS]
|
||||
self.details_table = zip(names,l1,l2)
|
||||
|
||||
#---Public
|
||||
def AddSelectedToIgnoreList(self):
|
||||
for dupe in self.selected_dupes:
|
||||
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):
|
||||
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 RefreshDetailsWithSelected(self):
|
||||
if self.selected_dupes:
|
||||
self.RefreshDetailsTable(
|
||||
self.selected_dupes[0],
|
||||
self.results.get_group_of_duplicate(self.selected_dupes[0])
|
||||
)
|
||||
else:
|
||||
self.RefreshDetailsTable(None,None)
|
||||
|
||||
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]
|
||||
@@ -148,13 +103,8 @@ class DupeGuru(app.DupeGuru):
|
||||
logging.warning("dupeGuru Warning: %s" % unicode(e))
|
||||
return False
|
||||
|
||||
def RevealSelected(self):
|
||||
if self.selected_dupes:
|
||||
path = unicode(self.selected_dupes[0].path)
|
||||
NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(path,'')
|
||||
|
||||
def start_scanning(self):
|
||||
self.RefreshDetailsTable(None, None)
|
||||
self._select_dupes([])
|
||||
try:
|
||||
app.DupeGuru.start_scanning(self)
|
||||
return 0
|
||||
@@ -199,17 +149,12 @@ class DupeGuru(app.DupeGuru):
|
||||
return g.ref
|
||||
|
||||
selected = [extract_dupe(self.GetObjects(p)) for p in node_paths]
|
||||
self.selected_dupes = [dupe for dupe in selected if dupe is not None]
|
||||
self._select_dupes([dupe for dupe in selected if dupe is not None])
|
||||
|
||||
def SelectPowerMarkerNodePaths(self,node_paths):
|
||||
rows = [p[0] for p in node_paths]
|
||||
self.selected_dupes = [
|
||||
self.results.dupes[row] for row in rows if row in xrange(len(self.results.dupes))
|
||||
]
|
||||
|
||||
def SetDirectoryState(self, node_path, state):
|
||||
p = self.get_folder_path(node_path)
|
||||
self.directories.set_state(p, state)
|
||||
dupes = [self.results.dupes[row] for row in rows if row in xrange(len(self.results.dupes))]
|
||||
self._select_dupes(dupes)
|
||||
|
||||
def sort_dupes(self,key,asc):
|
||||
self.results.sort_dupes(key,asc,self.display_delta_values)
|
||||
@@ -225,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
|
||||
|
||||
@@ -236,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]
|
||||
@@ -258,20 +191,13 @@ class DupeGuru(app.DupeGuru):
|
||||
if tag in (0,2): #Normal results / Power Marker
|
||||
if tag == 0:
|
||||
g, d = self.GetObjects(node_path)
|
||||
if d is None:
|
||||
if (d is None) and (g is not None):
|
||||
d = g.ref
|
||||
else:
|
||||
d = self.results.dupes[node_path[0]]
|
||||
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
|
||||
@@ -279,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
|
||||
@@ -292,15 +216,3 @@ class DupeGuru(app.DupeGuru):
|
||||
else:
|
||||
return 0
|
||||
|
||||
def GetTableViewCount(self, tag):
|
||||
if self.progress._job_running:
|
||||
return 0
|
||||
return len(self.details_table)
|
||||
|
||||
def GetTableViewMarkedIndexes(self,tag):
|
||||
return []
|
||||
|
||||
def GetTableViewValues(self,tag,row):
|
||||
return self.details_table[row]
|
||||
|
||||
|
||||
|
||||
184
core/app_cocoa_inter.py
Normal file
184
core/app_cocoa_inter.py
Normal file
@@ -0,0 +1,184 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2010-02-02
|
||||
# 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
|
||||
|
||||
# Common interface for all editions' dg_cocoa unit.
|
||||
|
||||
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 PyDupeGuruBase(PyRegistrable):
|
||||
#---Directories
|
||||
def addDirectory_(self, directory):
|
||||
return self.py.add_directory(directory)
|
||||
|
||||
def removeDirectory_(self, index):
|
||||
self.py.remove_directory(index)
|
||||
|
||||
#---Results
|
||||
def clearIgnoreList(self):
|
||||
self.py.scanner.ignore_list.Clear()
|
||||
|
||||
def doScan(self):
|
||||
return self.py.start_scanning()
|
||||
|
||||
def exportToXHTMLwithColumns_(self, column_ids):
|
||||
return self.py.export_to_xhtml(column_ids)
|
||||
|
||||
def loadIgnoreList(self):
|
||||
self.py.load_ignore_list()
|
||||
|
||||
def loadResults(self):
|
||||
self.py.load()
|
||||
|
||||
def markAll(self):
|
||||
self.py.results.mark_all()
|
||||
|
||||
def markNone(self):
|
||||
self.py.results.mark_none()
|
||||
|
||||
def markInvert(self):
|
||||
self.py.results.mark_invert()
|
||||
|
||||
def purgeIgnoreList(self):
|
||||
self.py.PurgeIgnoreList()
|
||||
|
||||
def toggleSelectedMark(self):
|
||||
self.py.ToggleSelectedMarkState()
|
||||
|
||||
def saveIgnoreList(self):
|
||||
self.py.save_ignore_list()
|
||||
|
||||
def saveResults(self):
|
||||
self.py.save()
|
||||
|
||||
def selectedResultNodePaths(self):
|
||||
return self.py.selected_result_node_paths()
|
||||
|
||||
def selectResultNodePaths_(self,node_paths):
|
||||
self.py.SelectResultNodePaths(node_paths)
|
||||
|
||||
def selectedPowerMarkerNodePaths(self):
|
||||
return self.py.selected_powermarker_node_paths()
|
||||
|
||||
def selectPowerMarkerNodePaths_(self,node_paths):
|
||||
self.py.SelectPowerMarkerNodePaths(node_paths)
|
||||
|
||||
#---Actions
|
||||
def addSelectedToIgnoreList(self):
|
||||
self.py.add_selected_to_ignore_list()
|
||||
|
||||
def deleteMarked(self):
|
||||
self.py.delete_marked()
|
||||
|
||||
def applyFilter_(self, filter):
|
||||
self.py.apply_filter(filter)
|
||||
|
||||
def makeSelectedReference(self):
|
||||
self.py.make_selected_reference()
|
||||
|
||||
def copyOrMove_markedTo_recreatePath_(self, copy, destination, recreate_path):
|
||||
self.py.copy_or_move_marked(copy, destination, recreate_path)
|
||||
|
||||
def openSelected(self):
|
||||
self.py.open_selected()
|
||||
|
||||
def removeMarked(self):
|
||||
self.py.results.perform_on_marked(lambda x:True, True)
|
||||
|
||||
def removeSelected(self):
|
||||
self.py.remove_selected()
|
||||
|
||||
def renameSelected_(self,newname):
|
||||
return self.py.RenameSelected(newname)
|
||||
|
||||
def revealSelected(self):
|
||||
self.py.reveal_selected()
|
||||
|
||||
#---Misc
|
||||
def sortDupesBy_ascending_(self, key, asc):
|
||||
self.py.sort_dupes(key, asc)
|
||||
|
||||
def sortGroupsBy_ascending_(self, key, asc):
|
||||
self.py.sort_groups(key, asc)
|
||||
|
||||
#---Information
|
||||
def getIgnoreListCount(self):
|
||||
return len(self.py.scanner.ignore_list)
|
||||
|
||||
def getMarkCount(self):
|
||||
return self.py.results.mark_count
|
||||
|
||||
def getStatLine(self):
|
||||
return self.py.stat_line
|
||||
|
||||
def getOperationalErrorCount(self):
|
||||
return self.py.last_op_error_count
|
||||
|
||||
#---Data
|
||||
@signature('i@:i')
|
||||
def getOutlineViewMaxLevel_(self, tag):
|
||||
return self.py.GetOutlineViewMaxLevel(tag)
|
||||
|
||||
@signature('@@:i@')
|
||||
def getOutlineView_childCountsForPath_(self, tag, node_path):
|
||||
return self.py.GetOutlineViewChildCounts(tag, node_path)
|
||||
|
||||
def getOutlineView_valuesForIndexes_(self, tag, node_path):
|
||||
return self.py.GetOutlineViewValues(tag, node_path)
|
||||
|
||||
def getOutlineView_markedAtIndexes_(self, tag, node_path):
|
||||
return self.py.GetOutlineViewMarked(tag, node_path)
|
||||
|
||||
#---Properties
|
||||
def setMixFileKind_(self, mix_file_kind):
|
||||
self.py.scanner.mix_file_kind = mix_file_kind
|
||||
|
||||
def setDisplayDeltaValues_(self, display_delta_values):
|
||||
self.py.display_delta_values= display_delta_values
|
||||
|
||||
def setEscapeFilterRegexp_(self, escape_filter_regexp):
|
||||
self.py.options['escape_filter_regexp'] = escape_filter_regexp
|
||||
|
||||
def setRemoveEmptyFolders_(self, remove_empty_folders):
|
||||
self.py.options['clean_empty_dirs'] = remove_empty_folders
|
||||
|
||||
#---Worker
|
||||
def getJobProgress(self):
|
||||
return self.py.progress.last_progress
|
||||
|
||||
def getJobDesc(self):
|
||||
return self.py.progress.last_desc
|
||||
|
||||
def cancelJob(self):
|
||||
self.py.progress.job_cancelled = True
|
||||
|
||||
|
||||
class PyDetailsPanel(PyGUIObject):
|
||||
py_class = DetailsPanel
|
||||
@signature('i@:')
|
||||
def numberOfRows(self):
|
||||
return self.py.row_count()
|
||||
|
||||
@signature('@@:@i')
|
||||
def valueForColumn_row_(self, column, row):
|
||||
return self.py.row(row)[int(column)]
|
||||
|
||||
|
||||
class PyDirectoryOutline(PyOutline):
|
||||
py_class = DirectoryTree
|
||||
|
||||
def addDirectory_(self, path):
|
||||
self.py.add_directory(path)
|
||||
|
||||
10
core/fs.py
10
core/fs.py
@@ -160,8 +160,16 @@ def get_file(path, fileclasses=[File]):
|
||||
|
||||
def get_files(path, fileclasses=[File]):
|
||||
assert all(issubclass(fileclass, File) for fileclass in fileclasses)
|
||||
def combine_paths(p1, p2):
|
||||
try:
|
||||
return p1 + p2
|
||||
except Exception:
|
||||
# This is temporary debug logging for #84.
|
||||
logging.warning("Failed to combine %r and %r.", p1, p2)
|
||||
raise
|
||||
|
||||
try:
|
||||
paths = [path + name for name in io.listdir(path)]
|
||||
paths = [combine_paths(path, name) for name in io.listdir(path)]
|
||||
result = []
|
||||
for path in paths:
|
||||
file = get_file(path, fileclasses=fileclasses)
|
||||
|
||||
0
core/gui/__init__.py
Normal file
0
core/gui/__init__.py
Normal file
23
core/gui/base.py
Normal file
23
core/gui/base.py
Normal 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
|
||||
|
||||
45
core/gui/details_panel.py
Normal file
45
core/gui/details_panel.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2010-02-05
|
||||
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "HS" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/hs_license
|
||||
|
||||
from .base import GUIObject
|
||||
|
||||
class DetailsPanel(GUIObject):
|
||||
def __init__(self, view, app):
|
||||
GUIObject.__init__(self, view, app)
|
||||
self._table = []
|
||||
self._refresh()
|
||||
self.connect()
|
||||
|
||||
#--- Private
|
||||
def _refresh(self):
|
||||
if self.app.selected_dupes:
|
||||
dupe = self.app.selected_dupes[0]
|
||||
group = self.app.results.get_group_of_duplicate(dupe)
|
||||
else:
|
||||
dupe = None
|
||||
group = None
|
||||
l1 = self.app._get_display_info(dupe, group, False)
|
||||
# we don't want the two sides of the table to display the stats for the same file
|
||||
ref = group.ref if group is not None and group.ref is not dupe else None
|
||||
l2 = self.app._get_display_info(ref, group, False)
|
||||
names = [c['display'] for c in self.app.data.COLUMNS]
|
||||
self._table = zip(names, l1, l2)
|
||||
|
||||
#--- Public
|
||||
def row_count(self):
|
||||
return len(self._table)
|
||||
|
||||
def row(self, row_index):
|
||||
return self._table[row_index]
|
||||
|
||||
#--- Event Handlers
|
||||
def dupes_selected(self):
|
||||
self._refresh()
|
||||
self.view.refresh()
|
||||
|
||||
72
core/gui/directory_tree.py
Normal file
72
core/gui/directory_tree.py
Normal 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()
|
||||
|
||||
@@ -26,6 +26,8 @@ try:
|
||||
except ImportError:
|
||||
from nose.plugins.skip import SkipTest
|
||||
raise SkipTest("These tests can only be run on OS X")
|
||||
from ..gui.details_panel import DetailsPanel
|
||||
from ..gui.directory_tree import DirectoryTree
|
||||
|
||||
class DupeGuru(DupeGuruBase):
|
||||
def __init__(self):
|
||||
@@ -38,9 +40,30 @@ def r2np(rows):
|
||||
#Transforms a list of rows [1,2,3] into a list of node paths [[1],[2],[3]]
|
||||
return [[i] for i in rows]
|
||||
|
||||
class CallLogger(object):
|
||||
"""This is a dummy object that logs all calls made to it.
|
||||
|
||||
It is used to simulate the GUI layer.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
def __getattr__(self, func_name):
|
||||
def func(*args, **kw):
|
||||
self.calls.append(func_name)
|
||||
return func
|
||||
|
||||
def clear_calls(self):
|
||||
del self.calls[:]
|
||||
|
||||
|
||||
class TCDupeGuru(TestCase):
|
||||
def setUp(self):
|
||||
self.app = DupeGuru()
|
||||
self.dpanel_gui = CallLogger()
|
||||
self.dpanel = DetailsPanel(self.dpanel_gui, self.app)
|
||||
self.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()
|
||||
@@ -48,6 +71,44 @@ class TCDupeGuru(TestCase):
|
||||
io.mkdir(tmppath + 'bar')
|
||||
self.app.directories.add_path(tmppath)
|
||||
|
||||
def check_gui_calls(self, gui, expected, verify_order=False):
|
||||
"""Checks that the expected calls have been made to 'gui', then clears the log.
|
||||
|
||||
`expected` is an iterable of strings representing method names.
|
||||
If `verify_order` is True, the order of the calls matters.
|
||||
"""
|
||||
if verify_order:
|
||||
eq_(gui.calls, expected)
|
||||
else:
|
||||
eq_(set(gui.calls), set(expected))
|
||||
gui.clear_calls()
|
||||
|
||||
def check_gui_calls_partial(self, gui, expected=None, not_expected=None):
|
||||
"""Checks that the expected calls have been made to 'gui', then clears the log.
|
||||
|
||||
`expected` is an iterable of strings representing method names. Order doesn't matter.
|
||||
Moreover, if calls have been made that are not in expected, no failure occur.
|
||||
`not_expected` can be used for a more explicit check (rather than calling `check_gui_calls`
|
||||
with an empty `expected`) to assert that calls have *not* been made.
|
||||
"""
|
||||
calls = set(gui.calls)
|
||||
if expected is not None:
|
||||
expected = set(expected)
|
||||
not_called = expected - calls
|
||||
assert not not_called, u"These calls haven't been made: {0}".format(not_called)
|
||||
if not_expected is not None:
|
||||
not_expected = set(not_expected)
|
||||
called = not_expected & calls
|
||||
assert not called, u"These calls shouldn't have been made: {0}".format(called)
|
||||
gui.clear_calls()
|
||||
|
||||
def clear_gui_calls(self):
|
||||
for attr in dir(self):
|
||||
if attr.endswith('_gui'):
|
||||
gui = getattr(self, attr)
|
||||
if hasattr(gui, 'calls'): # We might have test methods ending with '_gui'
|
||||
gui.clear_calls()
|
||||
|
||||
def test_GetObjects(self):
|
||||
app = self.app
|
||||
objects = self.objects
|
||||
@@ -92,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
|
||||
|
||||
@@ -148,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):
|
||||
@@ -194,33 +255,21 @@ class TCDupeGuru(TestCase):
|
||||
self.assert_(app.results.is_marked(objects[4]))
|
||||
|
||||
def test_refreshDetailsWithSelected(self):
|
||||
def mock_refresh(dupe,group):
|
||||
self.called = True
|
||||
if self.app.selected_dupes:
|
||||
self.assert_(dupe is self.app.selected_dupes[0])
|
||||
self.assert_(group is self.app.results.get_group_of_duplicate(dupe))
|
||||
else:
|
||||
self.assert_(dupe is None)
|
||||
self.assert_(group is None)
|
||||
|
||||
self.app.RefreshDetailsTable = mock_refresh
|
||||
self.called = False
|
||||
self.app.SelectPowerMarkerNodePaths(r2np([0,2]))
|
||||
self.app.RefreshDetailsWithSelected()
|
||||
self.assert_(self.called)
|
||||
self.called = False
|
||||
eq_(self.dpanel.row(0), ('Filename', 'bar bleh', 'foo bar'))
|
||||
self.check_gui_calls(self.dpanel_gui, ['refresh'])
|
||||
self.app.SelectPowerMarkerNodePaths([])
|
||||
self.app.RefreshDetailsWithSelected()
|
||||
self.assert_(self.called)
|
||||
eq_(self.dpanel.row(0), ('Filename', '---', '---'))
|
||||
self.check_gui_calls(self.dpanel_gui, ['refresh'])
|
||||
|
||||
def test_makeSelectedReference(self):
|
||||
app = self.app
|
||||
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
|
||||
@@ -228,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
|
||||
@@ -261,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))
|
||||
|
||||
@@ -291,19 +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
|
||||
eq_(app.GetOutlineViewValues(1, [0, 2]), []) # no crash
|
||||
app.add_selected_to_ignore_list()
|
||||
|
||||
|
||||
class TCDupeGuru_renameSelected(TestCase):
|
||||
|
||||
@@ -31,7 +31,7 @@ def GetDisplayInfo(dupe, group, delta):
|
||||
dupe.name,
|
||||
format_path(dupe.path),
|
||||
format_size(size, 0, 1, False),
|
||||
dupe.extension,
|
||||
dupe.extension if hasattr(dupe, 'extension') else '---',
|
||||
]
|
||||
|
||||
def GetDupeSortKey(dupe, get_group, key, delta):
|
||||
|
||||
@@ -85,9 +85,9 @@ def GetDupeSortKey(dupe, get_group, key, delta):
|
||||
return m.percentage
|
||||
if key == 18:
|
||||
return 0
|
||||
r = cmp_value(getattr(dupe, COLUMNS[key]['attr']))
|
||||
r = cmp_value(getattr(dupe, COLUMNS[key]['attr'], ''))
|
||||
if delta and (key in (2, 3, 4, 7, 8)):
|
||||
r -= cmp_value(getattr(get_group().ref, COLUMNS[key]['attr']))
|
||||
r -= cmp_value(getattr(get_group().ref, COLUMNS[key]['attr'], ''))
|
||||
return r
|
||||
|
||||
def GetGroupSortKey(group, key):
|
||||
@@ -95,4 +95,4 @@ def GetGroupSortKey(group, key):
|
||||
return group.percentage
|
||||
if key == 18:
|
||||
return len(group)
|
||||
return cmp_value(getattr(group.ref, COLUMNS[key]['attr']))
|
||||
return cmp_value(getattr(group.ref, COLUMNS[key]['attr'], ''))
|
||||
|
||||
@@ -11,25 +11,19 @@ import logging
|
||||
import plistlib
|
||||
import re
|
||||
|
||||
from Foundation import *
|
||||
from AppKit import *
|
||||
from appscript import app, k, CommandError
|
||||
|
||||
from hsutil import io
|
||||
from hsutil.str import get_file_ext
|
||||
from hsutil.path import Path
|
||||
from hsutil.cocoa import as_fetch
|
||||
from hsutil.cocoa.objcmin import NSUserDefaults, NSURL
|
||||
|
||||
from core import fs
|
||||
from core import app_cocoa, directories
|
||||
from . import data
|
||||
from .cache import string_to_colors, Cache
|
||||
from . import data, _block_osx
|
||||
from .scanner import ScannerPE
|
||||
|
||||
mainBundle = NSBundle.mainBundle()
|
||||
PictureBlocks = mainBundle.classNamed_('PictureBlocks')
|
||||
assert PictureBlocks is not None
|
||||
|
||||
class Photo(fs.File):
|
||||
INITIAL_INFO = fs.File.INITIAL_INFO.copy()
|
||||
INITIAL_INFO.update({
|
||||
@@ -44,17 +38,16 @@ class Photo(fs.File):
|
||||
def _read_info(self, field):
|
||||
fs.File._read_info(self, field)
|
||||
if field == 'dimensions':
|
||||
size = PictureBlocks.getImageSize_(unicode(self.path))
|
||||
self.dimensions = (size.width, size.height)
|
||||
self.dimensions = _block_osx.get_image_size(unicode(self.path))
|
||||
|
||||
def get_blocks(self, block_count_per_side):
|
||||
try:
|
||||
blocks = PictureBlocks.getBlocksFromImagePath_blockCount_(unicode(self.path), block_count_per_side)
|
||||
blocks = _block_osx.getblocks(unicode(self.path), block_count_per_side)
|
||||
except Exception as e:
|
||||
raise IOError('The reading of "%s" failed with "%s"' % (unicode(self.path), unicode(e)))
|
||||
if not blocks:
|
||||
raise IOError('The picture %s could not be read' % unicode(self.path))
|
||||
return string_to_colors(blocks)
|
||||
return blocks
|
||||
|
||||
|
||||
class IPhoto(Photo):
|
||||
@@ -74,7 +67,7 @@ def get_iphoto_database_path():
|
||||
|
||||
def get_iphoto_pictures(plistpath):
|
||||
if not io.exists(plistpath):
|
||||
raise InvalidPath(self)
|
||||
return []
|
||||
s = io.open(plistpath).read()
|
||||
# There was a case where a guy had 0x10 chars in his plist, causing expat errors on loading
|
||||
s = s.replace('\x10', '')
|
||||
@@ -124,9 +117,8 @@ class Directories(directories.Directories):
|
||||
|
||||
def add_path(self, path):
|
||||
if path == Path('iPhoto Library'):
|
||||
if path in self:
|
||||
raise AlreadyThereError()
|
||||
self._dirs.append(path)
|
||||
if path not in self:
|
||||
self._dirs.append(path)
|
||||
else:
|
||||
directories.Directories.add_path(self, path)
|
||||
|
||||
@@ -136,8 +128,7 @@ class DupeGuruPE(app_cocoa.DupeGuru):
|
||||
app_cocoa.DupeGuru.__init__(self, data, 'dupeGuru Picture Edition', appid=5)
|
||||
self.scanner = ScannerPE()
|
||||
self.directories = Directories()
|
||||
p = op.join(self.appdata, 'cached_pictures.db')
|
||||
self.scanner.cached_blocks = Cache(p)
|
||||
self.scanner.cache_path = op.join(self.appdata, 'cached_pictures.db')
|
||||
|
||||
def _do_delete(self, j):
|
||||
def op(dupe):
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
from _block import NoBlocksError, DifferentBlockCountError, avgdiff, getblocks2
|
||||
|
||||
# Converted to Cython
|
||||
# Converted to C
|
||||
# def getblock(image):
|
||||
# """Returns a 3 sized tuple containing the mean color of 'image'.
|
||||
#
|
||||
@@ -43,7 +43,7 @@ from _block import NoBlocksError, DifferentBlockCountError, avgdiff, getblocks2
|
||||
# result.append(getblock(crop))
|
||||
# return result
|
||||
|
||||
# Converted to Cython
|
||||
# Converted to C
|
||||
# def getblocks2(image,block_count_per_side):
|
||||
# """Returns a list of blocks (3 sized tuples).
|
||||
#
|
||||
@@ -70,7 +70,7 @@ from _block import NoBlocksError, DifferentBlockCountError, avgdiff, getblocks2
|
||||
# result.append(getblock(crop))
|
||||
# return result
|
||||
|
||||
# Converted to Cython
|
||||
# Converted to C
|
||||
# def diff(first, second):
|
||||
# """Returns the difference between the first block and the second.
|
||||
#
|
||||
@@ -80,7 +80,7 @@ from _block import NoBlocksError, DifferentBlockCountError, avgdiff, getblocks2
|
||||
# r2, g2, b2 = second
|
||||
# return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2)
|
||||
|
||||
# Converted to Cython
|
||||
# Converted to C
|
||||
# def avgdiff(first, second, limit=768, min_iterations=1):
|
||||
# """Returns the average diff between first blocks and seconds.
|
||||
#
|
||||
|
||||
@@ -10,8 +10,6 @@ import os
|
||||
import logging
|
||||
import sqlite3 as sqlite
|
||||
|
||||
import hsutil.sqlite
|
||||
|
||||
from _cache import string_to_colors
|
||||
|
||||
def colors_to_string(colors):
|
||||
@@ -22,7 +20,7 @@ def colors_to_string(colors):
|
||||
"""
|
||||
return ''.join(['%02x%02x%02x' % (r,g,b) for r,g,b in colors])
|
||||
|
||||
# This function is an important bottleneck of dupeGuru PE. It has been converted to Cython.
|
||||
# This function is an important bottleneck of dupeGuru PE. It has been converted to C.
|
||||
# def string_to_colors(s):
|
||||
# """Transform the string 's' in a list of 3 sized tuples.
|
||||
# """
|
||||
@@ -35,31 +33,10 @@ def colors_to_string(colors):
|
||||
class Cache(object):
|
||||
"""A class to cache picture blocks.
|
||||
"""
|
||||
def __init__(self, db=':memory:', threaded=True):
|
||||
def create_tables():
|
||||
sql = "create table pictures(path TEXT, blocks TEXT)"
|
||||
self.con.execute(sql);
|
||||
sql = "create index idx_path on pictures (path)"
|
||||
self.con.execute(sql)
|
||||
|
||||
def __init__(self, db=':memory:'):
|
||||
self.dbname = db
|
||||
if threaded:
|
||||
self.con = hsutil.sqlite.ThreadedConn(db, True)
|
||||
else:
|
||||
self.con = sqlite.connect(db, isolation_level=None)
|
||||
try:
|
||||
self.con.execute("select * from pictures where 1=2")
|
||||
except sqlite.OperationalError: # new db
|
||||
create_tables()
|
||||
except sqlite.DatabaseError, e: # corrupted db
|
||||
logging.warning('Could not create picture cache because of an error: %s', str(e))
|
||||
self.con.close()
|
||||
os.remove(db)
|
||||
if threaded:
|
||||
self.con = hsutil.sqlite.ThreadedConn(db, True)
|
||||
else:
|
||||
self.con = sqlite.connect(db, isolation_level=None)
|
||||
create_tables()
|
||||
self.con = None
|
||||
self._create_con()
|
||||
|
||||
def __contains__(self, key):
|
||||
sql = "select count(*) from pictures where path = ?"
|
||||
@@ -108,9 +85,36 @@ class Cache(object):
|
||||
except sqlite.DatabaseError, e:
|
||||
logging.warning('DatabaseError while setting %r for key %r: %s', value, key, str(e))
|
||||
|
||||
def _create_con(self, second_try=False):
|
||||
def create_tables():
|
||||
sql = "create table pictures(path TEXT, blocks TEXT)"
|
||||
self.con.execute(sql);
|
||||
sql = "create index idx_path on pictures (path)"
|
||||
self.con.execute(sql)
|
||||
|
||||
self.con = sqlite.connect(self.dbname, isolation_level=None)
|
||||
try:
|
||||
self.con.execute("select * from pictures where 1=2")
|
||||
except sqlite.OperationalError: # new db
|
||||
create_tables()
|
||||
except sqlite.DatabaseError, e: # corrupted db
|
||||
if second_try:
|
||||
raise # Something really strange is happening
|
||||
logging.warning('Could not create picture cache because of an error: %s', str(e))
|
||||
self.con.close()
|
||||
os.remove(self.dbname)
|
||||
self._create_con(second_try=True)
|
||||
|
||||
def clear(self):
|
||||
sql = "delete from pictures"
|
||||
self.con.execute(sql)
|
||||
self.close()
|
||||
if self.dbname != ':memory:':
|
||||
os.remove(self.dbname)
|
||||
self._create_con()
|
||||
|
||||
def close(self):
|
||||
if self.con is not None:
|
||||
self.con.close()
|
||||
self.con = None
|
||||
|
||||
def filter(self, func):
|
||||
to_delete = [key for key in self if not func(key)]
|
||||
|
||||
@@ -63,9 +63,9 @@ def GetDupeSortKey(dupe, get_group, key, delta):
|
||||
return m.percentage
|
||||
if key == 8:
|
||||
return 0
|
||||
r = cmp_value(getattr(dupe, COLUMNS[key]['attr']))
|
||||
r = cmp_value(getattr(dupe, COLUMNS[key]['attr'], ''))
|
||||
if delta and (key in (2, 5, 6)):
|
||||
r -= cmp_value(getattr(get_group().ref, COLUMNS[key]['attr']))
|
||||
r -= cmp_value(getattr(get_group().ref, COLUMNS[key]['attr'], ''))
|
||||
return r
|
||||
|
||||
def GetGroupSortKey(group, key):
|
||||
@@ -73,5 +73,5 @@ def GetGroupSortKey(group, key):
|
||||
return group.percentage
|
||||
if key == 8:
|
||||
return len(group)
|
||||
return cmp_value(getattr(group.ref, COLUMNS[key]['attr']))
|
||||
return cmp_value(getattr(group.ref, COLUMNS[key]['attr'], ''))
|
||||
|
||||
|
||||
@@ -18,14 +18,11 @@ def move(src, dst):
|
||||
print 'Moving %s --> %s' % (src, dst)
|
||||
os.rename(src, dst)
|
||||
|
||||
# The CC=gcc-4.0 thing is because, in Snow Leopard, gcc-4.2 can't compile these units.
|
||||
os.environ['CC'] = 'gcc-4.0'
|
||||
os.chdir(op.join('modules', 'block'))
|
||||
os.chdir('modules')
|
||||
os.system('python setup.py build_ext --inplace')
|
||||
os.chdir(op.join('..', 'cache'))
|
||||
os.system('python setup.py build_ext --inplace')
|
||||
os.chdir(op.join('..', '..'))
|
||||
move(op.join('modules', 'block', '_block.so'), '_block.so')
|
||||
move(op.join('modules', 'block', '_block.pyd'), '_block.pyd')
|
||||
move(op.join('modules', 'cache', '_cache.so'), '_cache.so')
|
||||
move(op.join('modules', 'cache', '_cache.pyd'), '_cache.pyd')
|
||||
os.chdir('..')
|
||||
move(op.join('modules', '_block.so'), '_block.so')
|
||||
move(op.join('modules', '_block.pyd'), '_block.pyd')
|
||||
move(op.join('modules', '_block_osx.so'), '_block_osx.so')
|
||||
move(op.join('modules', '_cache.so'), '_cache.so')
|
||||
move(op.join('modules', '_cache.pyd'), '_cache.pyd')
|
||||
|
||||
@@ -26,20 +26,21 @@ BLOCK_COUNT_PER_SIDE = 15
|
||||
# collection made by the main process.
|
||||
RESULTS_QUEUE_LIMIT = multiprocessing.cpu_count() * 2
|
||||
|
||||
def prepare_pictures(pictures, cached_blocks, j=job.nulljob):
|
||||
def prepare_pictures(pictures, cache_path, j=job.nulljob):
|
||||
# The MemoryError handlers in there use logging without first caring about whether or not
|
||||
# there is enough memory left to carry on the operation because it is assumed that the
|
||||
# MemoryError happens when trying to read an image file, which is freed from memory by the
|
||||
# time that MemoryError is raised.
|
||||
cache = Cache(cache_path)
|
||||
prepared = [] # only pictures for which there was no error getting blocks
|
||||
try:
|
||||
for picture in j.iter_with_progress(pictures, 'Analyzed %d/%d pictures'):
|
||||
picture.dimensions
|
||||
picture.unicode_path = unicode(picture.path)
|
||||
try:
|
||||
if picture.unicode_path not in cached_blocks:
|
||||
if picture.unicode_path not in cache:
|
||||
blocks = picture.get_blocks(BLOCK_COUNT_PER_SIDE)
|
||||
cached_blocks[picture.unicode_path] = blocks
|
||||
cache[picture.unicode_path] = blocks
|
||||
prepared.append(picture)
|
||||
except IOError as e:
|
||||
logging.warning(unicode(e))
|
||||
@@ -49,6 +50,7 @@ def prepare_pictures(pictures, cached_blocks, j=job.nulljob):
|
||||
raise
|
||||
except MemoryError:
|
||||
logging.warning('Ran out of memory while preparing pictures')
|
||||
cache.close()
|
||||
return prepared
|
||||
|
||||
def get_match(first, second, percentage):
|
||||
@@ -57,7 +59,7 @@ def get_match(first, second, percentage):
|
||||
return Match(first, second, percentage)
|
||||
|
||||
def async_compare(ref_id, other_ids, dbname, threshold):
|
||||
cache = Cache(dbname, threaded=False)
|
||||
cache = Cache(dbname)
|
||||
limit = 100 - threshold
|
||||
ref_blocks = cache[ref_id]
|
||||
pairs = cache.get_multiple(other_ids)
|
||||
@@ -70,10 +72,10 @@ def async_compare(ref_id, other_ids, dbname, threshold):
|
||||
percentage = 0
|
||||
if percentage >= threshold:
|
||||
results.append((ref_id, other_id, percentage))
|
||||
cache.con.close()
|
||||
cache.close()
|
||||
return results
|
||||
|
||||
def getmatches(pictures, cached_blocks, threshold=75, match_scaled=False, j=job.nulljob):
|
||||
def getmatches(pictures, cache_path, threshold=75, match_scaled=False, j=job.nulljob):
|
||||
def empty_out_queue(queue, into):
|
||||
try:
|
||||
while True:
|
||||
@@ -82,9 +84,9 @@ def getmatches(pictures, cached_blocks, threshold=75, match_scaled=False, j=job.
|
||||
pass
|
||||
|
||||
j = j.start_subjob([3, 7])
|
||||
pictures = prepare_pictures(pictures, cached_blocks, j)
|
||||
pictures = prepare_pictures(pictures, cache_path, j)
|
||||
j = j.start_subjob([9, 1], 'Preparing for matching')
|
||||
cache = cached_blocks
|
||||
cache = Cache(cache_path)
|
||||
id2picture = {}
|
||||
dimensions2pictures = defaultdict(set)
|
||||
for picture in pictures:
|
||||
@@ -95,6 +97,7 @@ def getmatches(pictures, cached_blocks, threshold=75, match_scaled=False, j=job.
|
||||
dimensions2pictures[picture.dimensions].add(picture)
|
||||
except ValueError:
|
||||
pass
|
||||
cache.close()
|
||||
pictures = [p for p in pictures if hasattr(p, 'cache_id')]
|
||||
pool = multiprocessing.Pool()
|
||||
async_results = []
|
||||
@@ -108,7 +111,7 @@ def getmatches(pictures, cached_blocks, threshold=75, match_scaled=False, j=job.
|
||||
others = [pic for pic in others if not pic.is_ref]
|
||||
if others:
|
||||
cache_ids = [f.cache_id for f in others]
|
||||
args = (ref.cache_id, cache_ids, cached_blocks.dbname, threshold)
|
||||
args = (ref.cache_id, cache_ids, cache_path, threshold)
|
||||
async_results.append(pool.apply_async(async_compare, args))
|
||||
if len(async_results) > RESULTS_QUEUE_LIMIT:
|
||||
result = async_results.pop(0)
|
||||
|
||||
239
core_pe/modules/block.c
Normal file
239
core_pe/modules/block.c
Normal file
@@ -0,0 +1,239 @@
|
||||
/* Created By: Virgil Dupras
|
||||
* Created On: 2010-01-30
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/* avgdiff/maxdiff has been called with empty lists */
|
||||
static PyObject *NoBlocksError;
|
||||
/* avgdiff/maxdiff has been called with 2 block lists of different size. */
|
||||
static PyObject *DifferentBlockCountError;
|
||||
|
||||
/* Returns a 3 sized tuple containing the mean color of 'image'.
|
||||
* image: a PIL image or crop.
|
||||
*/
|
||||
static PyObject* getblock(PyObject *image)
|
||||
{
|
||||
int i, totr, totg, totb;
|
||||
Py_ssize_t pixel_count;
|
||||
PyObject *ppixels;
|
||||
|
||||
totr = totg = totb = 0;
|
||||
ppixels = PyObject_CallMethod(image, "getdata", NULL);
|
||||
if (ppixels == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pixel_count = PySequence_Length(ppixels);
|
||||
for (i=0; i<pixel_count; i++) {
|
||||
PyObject *ppixel, *pr, *pg, *pb;
|
||||
int r, g, b;
|
||||
|
||||
ppixel = PySequence_ITEM(ppixels, i);
|
||||
pr = PySequence_ITEM(ppixel, 0);
|
||||
pg = PySequence_ITEM(ppixel, 1);
|
||||
pb = PySequence_ITEM(ppixel, 2);
|
||||
Py_DECREF(ppixel);
|
||||
r = PyInt_AsSsize_t(pr);
|
||||
g = PyInt_AsSsize_t(pg);
|
||||
b = PyInt_AsSsize_t(pb);
|
||||
Py_DECREF(pr);
|
||||
Py_DECREF(pg);
|
||||
Py_DECREF(pb);
|
||||
|
||||
totr += r;
|
||||
totg += g;
|
||||
totb += b;
|
||||
}
|
||||
|
||||
Py_DECREF(ppixels);
|
||||
|
||||
if (pixel_count) {
|
||||
totr /= pixel_count;
|
||||
totg /= pixel_count;
|
||||
totb /= pixel_count;
|
||||
}
|
||||
|
||||
return inttuple(3, totr, totg, totb);
|
||||
}
|
||||
|
||||
/* Returns the difference between the first block and the second.
|
||||
* It returns an absolute sum of the 3 differences (RGB).
|
||||
*/
|
||||
static int diff(PyObject *first, PyObject *second)
|
||||
{
|
||||
Py_ssize_t r1, g1, b1, r2, b2, g2;
|
||||
PyObject *pr, *pg, *pb;
|
||||
pr = PySequence_ITEM(first, 0);
|
||||
pg = PySequence_ITEM(first, 1);
|
||||
pb = PySequence_ITEM(first, 2);
|
||||
r1 = PyInt_AsSsize_t(pr);
|
||||
g1 = PyInt_AsSsize_t(pg);
|
||||
b1 = PyInt_AsSsize_t(pb);
|
||||
Py_DECREF(pr);
|
||||
Py_DECREF(pg);
|
||||
Py_DECREF(pb);
|
||||
|
||||
pr = PySequence_ITEM(second, 0);
|
||||
pg = PySequence_ITEM(second, 1);
|
||||
pb = PySequence_ITEM(second, 2);
|
||||
r2 = PyInt_AsSsize_t(pr);
|
||||
g2 = PyInt_AsSsize_t(pg);
|
||||
b2 = PyInt_AsSsize_t(pb);
|
||||
Py_DECREF(pr);
|
||||
Py_DECREF(pg);
|
||||
Py_DECREF(pb);
|
||||
|
||||
return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(block_getblocks2_doc,
|
||||
"Returns a list of blocks (3 sized tuples).\n\
|
||||
\n\
|
||||
image: A PIL image to base the blocks on.\n\
|
||||
block_count_per_side: This integer determine the number of blocks the function will return.\n\
|
||||
If it is 10, for example, 100 blocks will be returns (10 width, 10 height). The blocks will not\n\
|
||||
necessarely cover square areas. The area covered by each block will be proportional to the image\n\
|
||||
itself.\n");
|
||||
|
||||
static PyObject* block_getblocks2(PyObject *self, PyObject *args)
|
||||
{
|
||||
int block_count_per_side, width, height, block_width, block_height, ih;
|
||||
PyObject *image;
|
||||
PyObject *pimage_size, *pwidth, *pheight;
|
||||
PyObject *result;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "Oi", &image, &block_count_per_side)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pimage_size = PyObject_GetAttrString(image, "size");
|
||||
pwidth = PySequence_ITEM(pimage_size, 0);
|
||||
pheight = PySequence_ITEM(pimage_size, 1);
|
||||
width = PyInt_AsSsize_t(pwidth);
|
||||
height = PyInt_AsSsize_t(pheight);
|
||||
Py_DECREF(pimage_size);
|
||||
Py_DECREF(pwidth);
|
||||
Py_DECREF(pheight);
|
||||
|
||||
if (!(width && height)) {
|
||||
return PyList_New(0);
|
||||
}
|
||||
|
||||
block_width = max(width / block_count_per_side, 1);
|
||||
block_height = max(height / block_count_per_side, 1);
|
||||
|
||||
result = PyList_New(block_count_per_side * block_count_per_side);
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (ih=0; ih<block_count_per_side; ih++) {
|
||||
int top, bottom, iw;
|
||||
top = min(ih*block_height, height-block_height);
|
||||
bottom = top + block_height;
|
||||
for (iw=0; iw<block_count_per_side; iw++) {
|
||||
int left, right;
|
||||
PyObject *pbox;
|
||||
PyObject *pmethodname;
|
||||
PyObject *pcrop;
|
||||
PyObject *pblock;
|
||||
|
||||
left = min(iw*block_width, width-block_width);
|
||||
right = left + block_width;
|
||||
pbox = inttuple(4, left, top, right, bottom);
|
||||
pmethodname = PyString_FromString("crop");
|
||||
pcrop = PyObject_CallMethodObjArgs(image, pmethodname, pbox);
|
||||
Py_DECREF(pmethodname);
|
||||
Py_DECREF(pbox);
|
||||
if (pcrop == NULL) {
|
||||
Py_DECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
pblock = getblock(pcrop);
|
||||
Py_DECREF(pcrop);
|
||||
if (pblock == NULL) {
|
||||
Py_DECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
PyList_SET_ITEM(result, ih*block_count_per_side+iw, pblock);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(block_avgdiff_doc,
|
||||
"Returns the average diff between first blocks and seconds.\n\
|
||||
\n\
|
||||
If the result surpasses limit, limit + 1 is returned, except if less than min_iterations\n\
|
||||
iterations have been made in the blocks.\n");
|
||||
|
||||
static PyObject* block_avgdiff(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *first, *second;
|
||||
int limit, min_iterations;
|
||||
Py_ssize_t count;
|
||||
int sum, i, result;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "OOii", &first, &second, &limit, &min_iterations)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
count = PySequence_Length(first);
|
||||
if (count != PySequence_Length(second)) {
|
||||
PyErr_SetString(DifferentBlockCountError, "");
|
||||
return NULL;
|
||||
}
|
||||
if (!count) {
|
||||
PyErr_SetString(NoBlocksError, "");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sum = 0;
|
||||
for (i=0; i<count; i++) {
|
||||
int iteration_count;
|
||||
PyObject *item1, *item2;
|
||||
|
||||
iteration_count = i + 1;
|
||||
item1 = PySequence_ITEM(first, i);
|
||||
item2 = PySequence_ITEM(second, i);
|
||||
sum += diff(item1, item2);
|
||||
Py_DECREF(item1);
|
||||
Py_DECREF(item2);
|
||||
if ((sum > limit*iteration_count) && (iteration_count >= min_iterations)) {
|
||||
return PyInt_FromSsize_t(limit + 1);
|
||||
}
|
||||
}
|
||||
|
||||
result = sum / count;
|
||||
if (!result && sum) {
|
||||
result = 1;
|
||||
}
|
||||
return PyInt_FromSsize_t(result);
|
||||
}
|
||||
|
||||
static PyMethodDef BlockMethods[] = {
|
||||
{"getblocks2", block_getblocks2, METH_VARARGS, block_getblocks2_doc},
|
||||
{"avgdiff", block_avgdiff, METH_VARARGS, block_avgdiff_doc},
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
init_block(void)
|
||||
{
|
||||
PyObject *m = Py_InitModule("_block", BlockMethods);
|
||||
if (m == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
NoBlocksError = PyErr_NewException("_block.NoBlocksError", NULL, NULL);
|
||||
PyModule_AddObject(m, "NoBlocksError", NoBlocksError);
|
||||
DifferentBlockCountError = PyErr_NewException("_block.DifferentBlockCountError", NULL, NULL);
|
||||
PyModule_AddObject(m, "DifferentBlockCountError", DifferentBlockCountError);
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-04-23
|
||||
# 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
|
||||
|
||||
cdef extern from "stdlib.h":
|
||||
int abs(int n) # required so that abs() is applied on ints, not python objects
|
||||
|
||||
class NoBlocksError(Exception):
|
||||
"""avgdiff/maxdiff has been called with empty lists"""
|
||||
|
||||
class DifferentBlockCountError(Exception):
|
||||
"""avgdiff/maxdiff has been called with 2 block lists of different size."""
|
||||
|
||||
|
||||
cdef object getblock(object image):
|
||||
"""Returns a 3 sized tuple containing the mean color of 'image'.
|
||||
|
||||
image: a PIL image or crop.
|
||||
"""
|
||||
cdef int pixel_count, red, green, blue, r, g, b
|
||||
if image.size[0]:
|
||||
pixel_count = image.size[0] * image.size[1]
|
||||
red = green = blue = 0
|
||||
for r, g, b in image.getdata():
|
||||
red += r
|
||||
green += g
|
||||
blue += b
|
||||
return (red // pixel_count, green // pixel_count, blue // pixel_count)
|
||||
else:
|
||||
return (0, 0, 0)
|
||||
|
||||
def getblocks2(image, int block_count_per_side):
|
||||
"""Returns a list of blocks (3 sized tuples).
|
||||
|
||||
image: A PIL image to base the blocks on.
|
||||
block_count_per_side: This integer determine the number of blocks the function will return.
|
||||
If it is 10, for example, 100 blocks will be returns (10 width, 10 height). The blocks will not
|
||||
necessarely cover square areas. The area covered by each block will be proportional to the image
|
||||
itself.
|
||||
"""
|
||||
if not image.size[0]:
|
||||
return []
|
||||
cdef int width, height, block_width, block_height, ih, iw, top, bottom, left, right
|
||||
width, height = image.size
|
||||
block_width = max(width // block_count_per_side, 1)
|
||||
block_height = max(height // block_count_per_side, 1)
|
||||
result = []
|
||||
for ih in range(block_count_per_side):
|
||||
top = min(ih * block_height, height - block_height)
|
||||
bottom = top + block_height
|
||||
for iw in range(block_count_per_side):
|
||||
left = min(iw * block_width, width - block_width)
|
||||
right = left + block_width
|
||||
box = (left, top, right, bottom)
|
||||
crop = image.crop(box)
|
||||
result.append(getblock(crop))
|
||||
return result
|
||||
|
||||
cdef int diff(first, second):
|
||||
"""Returns the difference between the first block and the second.
|
||||
|
||||
It returns an absolute sum of the 3 differences (RGB).
|
||||
"""
|
||||
cdef int r1, g1, b1, r2, g2, b2
|
||||
r1, g1, b1 = first
|
||||
r2, g2, b2 = second
|
||||
return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2)
|
||||
|
||||
def avgdiff(first, second, int limit, int min_iterations):
|
||||
"""Returns the average diff between first blocks and seconds.
|
||||
|
||||
If the result surpasses limit, limit + 1 is returned, except if less than min_iterations
|
||||
iterations have been made in the blocks.
|
||||
"""
|
||||
cdef int count, sum, i, iteration_count
|
||||
count = len(first)
|
||||
if count != len(second):
|
||||
raise DifferentBlockCountError()
|
||||
if not count:
|
||||
raise NoBlocksError()
|
||||
sum = 0
|
||||
for i in range(count):
|
||||
iteration_count = i + 1
|
||||
item1 = first[i]
|
||||
item2 = second[i]
|
||||
sum += diff(item1, item2)
|
||||
if sum > limit * iteration_count and iteration_count >= min_iterations:
|
||||
return limit + 1
|
||||
result = sum // count
|
||||
if (not result) and sum:
|
||||
result = 1
|
||||
return result
|
||||
@@ -1,16 +0,0 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-04-23
|
||||
# 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 distutils.core import setup
|
||||
from distutils.extension import Extension
|
||||
from Cython.Distutils import build_ext
|
||||
|
||||
setup(
|
||||
cmdclass = {'build_ext': build_ext},
|
||||
ext_modules = [Extension("_block", ["block.pyx"])]
|
||||
)
|
||||
229
core_pe/modules/block_osx.m
Normal file
229
core_pe/modules/block_osx.m
Normal file
@@ -0,0 +1,229 @@
|
||||
/* Created By: Virgil Dupras
|
||||
* Created On: 2010-02-04
|
||||
* 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
|
||||
**/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
static CFStringRef
|
||||
pystring2cfstring(PyObject *pystring)
|
||||
{
|
||||
PyObject *encoded;
|
||||
UInt8 *s;
|
||||
CFIndex size;
|
||||
CFStringRef result;
|
||||
|
||||
if (PyUnicode_Check(pystring)) {
|
||||
encoded = PyUnicode_AsUTF8String(pystring);
|
||||
if (encoded == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
encoded = pystring;
|
||||
Py_INCREF(encoded);
|
||||
}
|
||||
|
||||
s = (UInt8*)PyString_AS_STRING(encoded);
|
||||
size = PyString_GET_SIZE(encoded);
|
||||
result = CFStringCreateWithBytes(NULL, s, size, kCFStringEncodingUTF8, FALSE);
|
||||
Py_DECREF(encoded);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject* block_osx_get_image_size(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *path;
|
||||
CFStringRef image_path;
|
||||
CFURLRef image_url;
|
||||
CGImageSourceRef source;
|
||||
CGImageRef image;
|
||||
size_t width, height;
|
||||
PyObject *pwidth, *pheight;
|
||||
PyObject *result;
|
||||
|
||||
width = 0;
|
||||
height = 0;
|
||||
if (!PyArg_ParseTuple(args, "O", &path)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image_path = pystring2cfstring(path);
|
||||
if (image_path == NULL) {
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
image_url = CFURLCreateWithFileSystemPath(NULL, image_path, kCFURLPOSIXPathStyle, FALSE);
|
||||
CFRelease(image_path);
|
||||
|
||||
source = CGImageSourceCreateWithURL(image_url, NULL);
|
||||
CFRelease(image_url);
|
||||
if (source != NULL) {
|
||||
image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
|
||||
if (image != NULL) {
|
||||
width = CGImageGetWidth(image);
|
||||
height = CGImageGetHeight(image);
|
||||
CGImageRelease(image);
|
||||
}
|
||||
CFRelease(source);
|
||||
}
|
||||
|
||||
pwidth = PyInt_FromSsize_t(width);
|
||||
if (pwidth == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
pheight = PyInt_FromSsize_t(height);
|
||||
if (pheight == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
result = PyTuple_Pack(2, pwidth, pheight);
|
||||
Py_DECREF(pwidth);
|
||||
Py_DECREF(pheight);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CGContextRef
|
||||
MyCreateBitmapContext(int width, int height)
|
||||
{
|
||||
CGContextRef context = NULL;
|
||||
CGColorSpaceRef colorSpace;
|
||||
void *bitmapData;
|
||||
int bitmapByteCount;
|
||||
int bitmapBytesPerRow;
|
||||
|
||||
bitmapBytesPerRow = (width * 4);
|
||||
bitmapByteCount = (bitmapBytesPerRow * height);
|
||||
|
||||
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
|
||||
|
||||
// calloc() must be used to allocate bitmapData here because the buffer has to be zeroed.
|
||||
// If it's not zeroes, when images with transparency are drawn in the context, this buffer
|
||||
// will stay with undefined pixels, which means that two pictures with the same pixels will
|
||||
// most likely have different blocks (which is not supposed to happen).
|
||||
bitmapData = calloc(bitmapByteCount, 1);
|
||||
if (bitmapData == NULL) {
|
||||
fprintf(stderr, "Memory not allocated!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context = CGBitmapContextCreate(bitmapData, width, height, 8, bitmapBytesPerRow, colorSpace,
|
||||
kCGImageAlphaNoneSkipLast);
|
||||
if (context== NULL) {
|
||||
free(bitmapData);
|
||||
fprintf(stderr, "Context not created!");
|
||||
return NULL;
|
||||
}
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
return context;
|
||||
}
|
||||
|
||||
static PyObject* getblock(unsigned char *imageData, int imageWidth, int imageHeight, int boxX, int boxY, int boxW, int boxH)
|
||||
{
|
||||
int i,j, totalR, totalG, totalB;
|
||||
|
||||
totalR = totalG = totalB = 0;
|
||||
for(i=boxY; i<boxY+boxH; i++) {
|
||||
for(j=boxX; j<boxX+boxW; j++) {
|
||||
int offset = (i * imageWidth * 4) + (j * 4);
|
||||
totalR += *(imageData + offset);
|
||||
totalG += *(imageData + offset + 1);
|
||||
totalB += *(imageData + offset + 2);
|
||||
}
|
||||
}
|
||||
int pixelCount = boxH * boxW;
|
||||
totalR /= pixelCount;
|
||||
totalG /= pixelCount;
|
||||
totalB /= pixelCount;
|
||||
|
||||
return inttuple(3, totalR, totalG, totalB);
|
||||
}
|
||||
|
||||
static PyObject* block_osx_getblocks(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *path, *result;
|
||||
CFStringRef image_path;
|
||||
CFURLRef image_url;
|
||||
CGImageSourceRef source;
|
||||
CGImageRef image;
|
||||
size_t width, height;
|
||||
int block_count, block_width, block_height, block_xcount, block_ycount, i;
|
||||
|
||||
width = 0;
|
||||
height = 0;
|
||||
if (!PyArg_ParseTuple(args, "Oi", &path, &block_count)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image_path = pystring2cfstring(path);
|
||||
if (image_path == NULL) {
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
image_url = CFURLCreateWithFileSystemPath(NULL, image_path, kCFURLPOSIXPathStyle, FALSE);
|
||||
CFRelease(image_path);
|
||||
|
||||
source = CGImageSourceCreateWithURL(image_url, NULL);
|
||||
CFRelease(image_url);
|
||||
if (source == NULL) {
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
|
||||
image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
|
||||
if (image == NULL) {
|
||||
CFRelease(source);
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
|
||||
width = CGImageGetWidth(image);
|
||||
height = CGImageGetHeight(image);
|
||||
CGContextRef myContext = MyCreateBitmapContext(width, height);
|
||||
CGRect myBoundingBox = CGRectMake(0, 0, width, height);
|
||||
CGContextDrawImage(myContext, myBoundingBox, image);
|
||||
unsigned char *bitmapData = CGBitmapContextGetData(myContext);
|
||||
CGContextRelease(myContext);
|
||||
CGImageRelease(image);
|
||||
CFRelease(source);
|
||||
if (bitmapData == NULL) {
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
|
||||
block_width = max(width/block_count, 1);
|
||||
block_height = max(height/block_count, 1);
|
||||
/* block_count might have changed */
|
||||
block_xcount = (width / block_width);
|
||||
block_ycount = (height / block_height);
|
||||
|
||||
result = PyList_New(block_xcount * block_ycount);
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(i=0; i<block_ycount; i++)
|
||||
{
|
||||
int j;
|
||||
for(j=0; j<block_xcount; j++)
|
||||
{
|
||||
PyObject *block = getblock(bitmapData, width, height, j*block_width, i*block_height,
|
||||
block_width, block_height);
|
||||
PyList_SET_ITEM(result, i*block_ycount+j, block);
|
||||
}
|
||||
}
|
||||
|
||||
free(bitmapData);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyMethodDef BlockOsxMethods[] = {
|
||||
{"get_image_size", block_osx_get_image_size, METH_VARARGS, ""},
|
||||
{"getblocks", block_osx_getblocks, METH_VARARGS, ""},
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
init_block_osx(void)
|
||||
{
|
||||
Py_InitModule("_block_osx", BlockOsxMethods);
|
||||
}
|
||||
79
core_pe/modules/cache.c
Normal file
79
core_pe/modules/cache.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/* Created By: Virgil Dupras
|
||||
* Created On: 2010-01-30
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/* I know that there strtol out there, but it requires a pointer to
|
||||
* a char, which would in turn require me to buffer my chars around,
|
||||
* making the whole process slower.
|
||||
*/
|
||||
static long
|
||||
xchar_to_long(char c)
|
||||
{
|
||||
if ((c >= 48) && (c <= 57)) { /* 0-9 */
|
||||
return c - 48;
|
||||
}
|
||||
else if ((c >= 65) && (c <= 70)) { /* A-F */
|
||||
return c - 55;
|
||||
}
|
||||
else if ((c >= 97) && (c <= 102)) { /* a-f */
|
||||
return c - 87;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cache_string_to_colors(PyObject *self, PyObject *args)
|
||||
{
|
||||
char *s;
|
||||
Py_ssize_t char_count, color_count, i;
|
||||
PyObject *result;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s#", &s, &char_count)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
color_count = (char_count / 6);
|
||||
result = PyList_New(color_count);
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i=0; i<color_count; i++) {
|
||||
long r, g, b;
|
||||
Py_ssize_t ci;
|
||||
PyObject *color_tuple;
|
||||
|
||||
ci = i * 6;
|
||||
r = (xchar_to_long(s[ci]) << 4) + xchar_to_long(s[ci+1]);
|
||||
g = (xchar_to_long(s[ci+2]) << 4) + xchar_to_long(s[ci+3]);
|
||||
b = (xchar_to_long(s[ci+4]) << 4) + xchar_to_long(s[ci+5]);
|
||||
|
||||
color_tuple = inttuple(3, r, g, b);
|
||||
if (color_tuple == NULL) {
|
||||
Py_DECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
PyList_SET_ITEM(result, i, color_tuple);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyMethodDef CacheMethods[] = {
|
||||
{"string_to_colors", cache_string_to_colors, METH_VARARGS,
|
||||
"Transform the string 's' in a list of 3 sized tuples."},
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
init_cache(void)
|
||||
{
|
||||
(void)Py_InitModule("_cache", CacheMethods);
|
||||
}
|
||||
36
core_pe/modules/cache/cache.pyx
vendored
36
core_pe/modules/cache/cache.pyx
vendored
@@ -1,36 +0,0 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-04-23
|
||||
# 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
|
||||
|
||||
# ok, this is hacky and stuff, but I don't know C well enough to play with char buffers, copy
|
||||
# them around and stuff
|
||||
cdef int xchar_to_int(char c):
|
||||
if 48 <= c <= 57: # 0-9
|
||||
return c - 48
|
||||
elif 65 <= c <= 70: # A-F
|
||||
return c - 55
|
||||
elif 97 <= c <= 102: # a-f
|
||||
return c - 87
|
||||
|
||||
def string_to_colors(s):
|
||||
"""Transform the string 's' in a list of 3 sized tuples.
|
||||
"""
|
||||
result = []
|
||||
cdef int i, char_count, r, g, b
|
||||
cdef char* cs
|
||||
char_count = len(s)
|
||||
char_count = (char_count // 6) * 6
|
||||
cs = s
|
||||
for i in range(0, char_count, 6):
|
||||
r = xchar_to_int(cs[i]) << 4
|
||||
r += xchar_to_int(cs[i+1])
|
||||
g = xchar_to_int(cs[i+2]) << 4
|
||||
g += xchar_to_int(cs[i+3])
|
||||
b = xchar_to_int(cs[i+4]) << 4
|
||||
b += xchar_to_int(cs[i+5])
|
||||
result.append((r, g, b))
|
||||
return result
|
||||
16
core_pe/modules/cache/setup.py
vendored
16
core_pe/modules/cache/setup.py
vendored
@@ -1,16 +0,0 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-04-23
|
||||
# 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 distutils.core import setup
|
||||
from distutils.extension import Extension
|
||||
from Cython.Distutils import build_ext
|
||||
|
||||
setup(
|
||||
cmdclass = {'build_ext': build_ext},
|
||||
ext_modules = [Extension("_cache", ["cache.pyx"])]
|
||||
)
|
||||
45
core_pe/modules/common.c
Normal file
45
core_pe/modules/common.c
Normal file
@@ -0,0 +1,45 @@
|
||||
/* Created By: Virgil Dupras
|
||||
* Created On: 2010-02-04
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifndef _MSC_VER
|
||||
int max(int a, int b)
|
||||
{
|
||||
return b > a ? b : a;
|
||||
}
|
||||
|
||||
int min(int a, int b)
|
||||
{
|
||||
return b < a ? b : a;
|
||||
}
|
||||
#endif
|
||||
|
||||
PyObject* inttuple(int n, ...)
|
||||
{
|
||||
int i;
|
||||
PyObject *pnumber;
|
||||
PyObject *result;
|
||||
va_list numbers;
|
||||
|
||||
va_start(numbers, n);
|
||||
result = PyTuple_New(n);
|
||||
|
||||
for (i=0; i<n; i++) {
|
||||
pnumber = PyInt_FromLong(va_arg(numbers, int));
|
||||
if (pnumber == NULL) {
|
||||
Py_DECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SET_ITEM(result, i, pnumber);
|
||||
}
|
||||
|
||||
va_end(numbers);
|
||||
return result;
|
||||
}
|
||||
20
core_pe/modules/common.h
Normal file
20
core_pe/modules/common.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* Created By: Virgil Dupras
|
||||
* Created On: 2010-02-04
|
||||
* 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
|
||||
*/
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "Python.h"
|
||||
|
||||
/* It seems like MS VC defines min/max already */
|
||||
#ifndef _MSC_VER
|
||||
int max(int a, int b);
|
||||
int min(int a, int b);
|
||||
#endif
|
||||
|
||||
/* Create a tuple out of an array of integers. */
|
||||
PyObject* inttuple(int n, ...);
|
||||
30
core_pe/modules/setup.py
Normal file
30
core_pe/modules/setup.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-04-23
|
||||
# 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 sys
|
||||
|
||||
from distutils.core import setup
|
||||
from distutils.extension import Extension
|
||||
|
||||
exts = []
|
||||
|
||||
exts.append(Extension("_block", ["block.c", "common.c"]))
|
||||
exts.append(Extension("_cache", ["cache.c", "common.c"]))
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
exts.append(Extension(
|
||||
"_block_osx", ["block_osx.m", "common.c"],
|
||||
extra_link_args=[
|
||||
"-framework", "CoreFoundation",
|
||||
"-framework", "Foundation",
|
||||
"-framework", "ApplicationServices",
|
||||
]))
|
||||
|
||||
setup(
|
||||
ext_modules = exts,
|
||||
)
|
||||
@@ -10,12 +10,18 @@
|
||||
from core.scanner import Scanner
|
||||
|
||||
from . import matchbase
|
||||
from .cache import Cache
|
||||
|
||||
class ScannerPE(Scanner):
|
||||
cached_blocks = None
|
||||
cache_path = None
|
||||
match_scaled = False
|
||||
threshold = 75
|
||||
|
||||
def _getmatches(self, files, j):
|
||||
return matchbase.getmatches(files, self.cached_blocks, self.threshold, self.match_scaled, j)
|
||||
return matchbase.getmatches(files, self.cache_path, self.threshold, self.match_scaled, j)
|
||||
|
||||
def clear_picture_cache(self):
|
||||
cache = Cache(self.cache_path)
|
||||
cache.clear()
|
||||
cache.close()
|
||||
|
||||
|
||||
@@ -137,18 +137,3 @@ class TCCacheSQLEscape(TestCase):
|
||||
except KeyError:
|
||||
self.fail()
|
||||
|
||||
|
||||
class TCCacheThreaded(TestCase):
|
||||
def test_access_cache(self):
|
||||
def thread_run():
|
||||
try:
|
||||
c['foo'] = [(1,2,3)]
|
||||
except sqlite.ProgrammingError:
|
||||
self.fail()
|
||||
|
||||
c = Cache()
|
||||
t = threading.Thread(target=thread_run)
|
||||
t.start()
|
||||
t.join()
|
||||
self.assertEqual([(1,2,3)], c['foo'])
|
||||
|
||||
|
||||
@@ -10,12 +10,9 @@ from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
import objc
|
||||
from AppKit import *
|
||||
|
||||
from hsutil import io
|
||||
from hsutil.path import Path
|
||||
from hsutil.str import get_file_ext
|
||||
from hsutil.cocoa.objcmin import NSWorkspace
|
||||
|
||||
from core import fs
|
||||
from core.app_cocoa import DupeGuru as DupeGuruBase
|
||||
@@ -25,10 +22,7 @@ from .fs import Bundle as BundleBase
|
||||
|
||||
def is_bundle(str_path):
|
||||
sw = NSWorkspace.sharedWorkspace()
|
||||
if objc.__version__ == '1.4': # For a while, we have to support this.
|
||||
uti, error = sw.typeOfFile_error_(str_path)
|
||||
else:
|
||||
uti, error = sw.typeOfFile_error_(str_path, None)
|
||||
uti, error = sw.typeOfFile_error_(str_path, None)
|
||||
if error is not None:
|
||||
logging.warning(u'There was an error trying to detect the UTI of %s', str_path)
|
||||
return sw.type_conformsToType_(uti, 'com.apple.bundle') or sw.type_conformsToType_(uti, 'com.apple.package')
|
||||
|
||||
@@ -58,9 +58,9 @@ def GetDupeSortKey(dupe, get_group, key, delta):
|
||||
return m.percentage
|
||||
if key == 8:
|
||||
return 0
|
||||
r = cmp_value(getattr(dupe, COLUMNS[key]['attr']))
|
||||
r = cmp_value(getattr(dupe, COLUMNS[key]['attr'], ''))
|
||||
if delta and (key in (2, 4, 5)):
|
||||
r -= cmp_value(getattr(get_group().ref, COLUMNS[key]['attr']))
|
||||
r -= cmp_value(getattr(get_group().ref, COLUMNS[key]['attr'], ''))
|
||||
return r
|
||||
|
||||
def GetGroupSortKey(group, key):
|
||||
@@ -68,4 +68,4 @@ def GetGroupSortKey(group, key):
|
||||
return group.percentage
|
||||
if key == 8:
|
||||
return len(group)
|
||||
return cmp_value(getattr(group.ref, COLUMNS[key]['attr']))
|
||||
return cmp_value(getattr(group.ref, COLUMNS[key]['attr'], ''))
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
- date: 2010-01-19
|
||||
version: 5.7.1
|
||||
description: |
|
||||
* The Mac OS X version of dupeGuru ME is now 64-bit!
|
||||
* Improved memory usage for Contents scans. (#75)
|
||||
* Improved scanning speed when ref directories are involved. (#77)
|
||||
* Show a message dialog at the end of the scan if no duplicates are found. (#81)
|
||||
* Re-added the "Remove Dead Tracks in iTunes" menu item which got lost in 5.7.0.
|
||||
- date: 2009-12-18
|
||||
version: 5.7.0
|
||||
description: |
|
||||
|
||||
@@ -31,6 +31,6 @@
|
||||
|
||||
* **Right in destination:** All files will be sent directly in the selected destination, without trying to recreate the source path at all.
|
||||
* **Recreate relative path:** The source file's path will be re-created in the destination directory up to the root selection in the Directories panel. For example, if you added "/Users/foobar/Music" to your Directories panel and you move "/Users/foobar/Music/Artist/Album/the_song.mp3" to the destination "/Users/foobar/MyDestination", the final destination for the file will be "/Users/foobar/MyDestination/Artist/Album" ("/Users/foobar/Music" has been trimmed from source's path in the final destination.).
|
||||
* **Recreate absolute path:** The source file's path will be re-created in the destination directory in it's entirety. For example, if you move "/Users/foobar/Music/Artist/Album/the_song.mp3" to the destination "/Users/foobar/MyDestination", the final destination for the file will be "/Users/foobar/MyDestination/Users/foobar/Music/Artist/Album".</li>
|
||||
* **Recreate absolute path:** The source file's path will be re-created in the destination directory in it's entirety. For example, if you move "/Users/foobar/Music/Artist/Album/the_song.mp3" to the destination "/Users/foobar/MyDestination", the final destination for the file will be "/Users/foobar/MyDestination/Users/foobar/Music/Artist/Album".
|
||||
|
||||
In all cases, dupeGuru nicely handles naming conflicts by prepending a number to the destination filename if the filename already exists in the destination.
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
- date: 2010-02-06
|
||||
version: 1.8.2
|
||||
description: |
|
||||
* dupeGuru Picture Edition is now 64-bit on Mac OS X!
|
||||
* Improved scanning speed.
|
||||
* Fixed a crash upon quitting when support folder is not present. (#83)
|
||||
- date: 2010-01-15
|
||||
version: 1.8.1
|
||||
description: |
|
||||
* Improved scanning speed when ref directories are involved. (#77)
|
||||
* Show a message dialog at the end of the scan if no duplicates are found. (#81)
|
||||
* Fixed a crash when adding the iPhoto library twice. [Mac OS X] (#80)
|
||||
- date: 2009-12-16
|
||||
version: 1.8.0
|
||||
description: |
|
||||
|
||||
@@ -18,6 +18,6 @@
|
||||
|
||||
* **Right in destination:** All files will be sent directly in the selected destination, without trying to recreate the source path at all.
|
||||
* **Recreate relative path:** The source file's path will be re-created in the destination directory up to the root selection in the Directories panel. For example, if you added "/Users/foobar/Picture" to your Directories panel and you move "/Users/foobar/Picture/2006/06/photo.jpg" to the destination "/Users/foobar/MyDestination", the final destination for the file will be "/Users/foobar/MyDestination/2006/06" ("/Users/foobar/Picture" has been trimmed from source's path in the final destination.).
|
||||
* **Recreate absolute path:** The source file's path will be re-created in the destination directory in it's entirety. For example, if you move "/Users/foobar/Picture/2006/06/photo.jpg" to the destination "/Users/foobar/MyDestination", the final destination for the file will be "/Users/foobar/MyDestination/Users/foobar/Picture/2006/06".</li>
|
||||
* **Recreate absolute path:** The source file's path will be re-created in the destination directory in it's entirety. For example, if you move "/Users/foobar/Picture/2006/06/photo.jpg" to the destination "/Users/foobar/MyDestination", the final destination for the file will be "/Users/foobar/MyDestination/Users/foobar/Picture/2006/06".
|
||||
|
||||
In all cases, dupeGuru PE nicely handles naming conflicts by prepending a number to the destination filename if the filename already exists in the destination.
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -22,6 +22,6 @@
|
||||
|
||||
* **Right in destination:** All files will be sent directly in the selected destination, without trying to recreate the source path at all.
|
||||
* **Recreate relative path:** The source file's path will be re-created in the destination directory up to the root selection in the Directories panel. For example, if you added "/Users/foobar/Music" to your Directories panel and you move "/Users/foobar/Music/Artist/Album/the_song.mp3" to the destination "/Users/foobar/MyDestination", the final destination for the file will be "/Users/foobar/MyDestination/Artist/Album" ("/Users/foobar/Music" has been trimmed from source's path in the final destination.).
|
||||
* **Recreate absolute path:** The source file's path will be re-created in the destination directory in it's entirety. For example, if you move "/Users/foobar/Music/Artist/Album/the_song.mp3" to the destination "/Users/foobar/MyDestination", the final destination for the file will be "/Users/foobar/MyDestination/Users/foobar/Music/Artist/Album".</li>
|
||||
* **Recreate absolute path:** The source file's path will be re-created in the destination directory in it's entirety. For example, if you move "/Users/foobar/Music/Artist/Album/the_song.mp3" to the destination "/Users/foobar/MyDestination", the final destination for the file will be "/Users/foobar/MyDestination/Users/foobar/Music/Artist/Album".
|
||||
|
||||
In all cases, dupeGuru nicely handles naming conflicts by prepending a number to the destination filename if the filename already exists in the destination.
|
||||
|
||||
67
package.py
67
package.py
@@ -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()
|
||||
@@ -65,7 +65,6 @@ class DupeGuru(DupeGuruBase, QObject):
|
||||
|
||||
#--- Private
|
||||
def _setup(self):
|
||||
self.selected_dupe = None
|
||||
self.prefs = self._create_preferences()
|
||||
self.prefs.load()
|
||||
self._update_options()
|
||||
@@ -118,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:
|
||||
@@ -131,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'
|
||||
@@ -157,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()'))
|
||||
@@ -175,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 self.selected_dupe is None:
|
||||
return
|
||||
url = QUrl.fromLocalFile(unicode(self.selected_dupe.path))
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
def remove_duplicates(self, duplicates):
|
||||
self.results.remove_duplicates(duplicates)
|
||||
self.emit(SIGNAL('resultsChanged()'))
|
||||
self._open_path(debugLogPath)
|
||||
|
||||
def remove_marked_duplicates(self):
|
||||
marked = [d for d in self.results.dupes if self.results.is_marked(d)]
|
||||
@@ -200,15 +214,8 @@ class DupeGuru(DupeGuruBase, QObject):
|
||||
logging.warning("dupeGuru Warning: %s" % unicode(e))
|
||||
return False
|
||||
|
||||
def reveal_selected(self):
|
||||
if self.selected_dupe is None:
|
||||
return
|
||||
url = QUrl.fromLocalFile(unicode(self.selected_dupe.path[:-1]))
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
def select_duplicate(self, dupe):
|
||||
self.selected_dupe = dupe
|
||||
self.emit(SIGNAL('duplicateSelected()'))
|
||||
def select_dupes(self, dupes):
|
||||
self._select_dupes(dupes)
|
||||
|
||||
def show_about_box(self):
|
||||
self.about_box.show()
|
||||
@@ -247,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:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user