mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-25 16:11:39 +00:00
Compare commits
66 Commits
pe1.9.1
...
before-fai
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05b79f81af | ||
|
|
96ef2f2dd3 | ||
|
|
2542af17b6 | ||
|
|
c86bc649ff | ||
|
|
4b8e48ed88 | ||
|
|
a1addfd416 | ||
|
|
a1a57d8933 | ||
|
|
864970b860 | ||
|
|
a056be0842 | ||
|
|
c672e75739 | ||
|
|
7b5dd3f964 | ||
|
|
a6072f608b | ||
|
|
06462c65a5 | ||
|
|
359f9c0680 | ||
|
|
01db7c4948 | ||
|
|
f67f14a78d | ||
|
|
0a64d653e1 | ||
|
|
456a835285 | ||
|
|
0d8ed92a68 | ||
|
|
9bd093a03c | ||
|
|
361d4698a9 | ||
|
|
b342b15011 | ||
|
|
95638a3a80 | ||
|
|
2204fe3355 | ||
|
|
abcd774c9d | ||
|
|
ee209f8f88 | ||
|
|
b1f2e1c191 | ||
|
|
33f372f6c6 | ||
|
|
8e5c2a8875 | ||
|
|
36f3638ae4 | ||
|
|
d10210011f | ||
|
|
e867840d81 | ||
|
|
fb7e3189a8 | ||
|
|
5733c0143b | ||
|
|
ac4881f231 | ||
|
|
939efd7dab | ||
|
|
a93d96d742 | ||
|
|
f21804c769 | ||
|
|
4bc05a8d46 | ||
|
|
eebe2b0e80 | ||
|
|
250a496a78 | ||
|
|
29163ed053 | ||
|
|
cc05661f9e | ||
|
|
89409c22d1 | ||
|
|
e2f240ebc9 | ||
|
|
8d56f4c33b | ||
|
|
36eccb7122 | ||
|
|
c8827769b4 | ||
|
|
12e6c400b9 | ||
|
|
4c273a7910 | ||
|
|
58da335b17 | ||
|
|
5b2d506462 | ||
|
|
531430d44a | ||
|
|
7450eec7eb | ||
|
|
3a5802435f | ||
|
|
1b6b058097 | ||
|
|
a5797a2350 | ||
|
|
e81a5147c5 | ||
|
|
565c990687 | ||
|
|
0ccdfe0e26 | ||
|
|
f8a558e3a7 | ||
|
|
c5fa195cc6 | ||
|
|
3a821edd45 | ||
|
|
854d194f88 | ||
|
|
fb79daad6a | ||
|
|
b2ae0e8759 |
10
.hgtags
10
.hgtags
@@ -22,3 +22,13 @@ cb0a860430bacd712820bce426bcf47a4135efe1 se2.10.1
|
|||||||
cb0a860430bacd712820bce426bcf47a4135efe1 se2.10.1
|
cb0a860430bacd712820bce426bcf47a4135efe1 se2.10.1
|
||||||
f71d405e62badcfdc1b037facaac043cece40ee5 se2.10.1
|
f71d405e62badcfdc1b037facaac043cece40ee5 se2.10.1
|
||||||
3742e83edd9eadf44e1a501859f5e2462b1ef6fd me5.8.1
|
3742e83edd9eadf44e1a501859f5e2462b1ef6fd me5.8.1
|
||||||
|
724ff565dd785fb739774588c6ee652cfc0612d5 pe1.9.1
|
||||||
|
634b66415c6529f46ae4f837318027cc9d70c3b5 before-py3k
|
||||||
|
2b67955db2b0580a8b0854dc918b6ab0d1fa3b88 se2.11.0
|
||||||
|
b56fe4dd8c95bca270b078a09e86848df77e2b2d me5.9.0
|
||||||
|
618a7365457d56fdc6920c70843a244762e2ea00 pe1.10.0
|
||||||
|
95b3a4b564c6222b414f2b40182dde2bd6d0e8a4 me5.9.1
|
||||||
|
9735a5218d2b5b3b1e1dfe17f2f874177cf8f61c se2.11.1
|
||||||
|
dbfee3ee2fa5cbb9e7ab36570659c17cd5b8561f se2.12.0
|
||||||
|
d3fe0d0dcda1e0bf1100d02f117503d3bf6baacf me5.10.0
|
||||||
|
b07ac1398703dd358912c1f3d20bd995633db9fe pe1.11.0
|
||||||
|
|||||||
24
README
24
README
@@ -13,7 +13,6 @@ There are also other sub-folder that comes from external repositories (automatic
|
|||||||
with svn:externals):
|
with svn:externals):
|
||||||
|
|
||||||
- hscommon: A collection of helpers used across HS applications.
|
- hscommon: A collection of helpers used across HS applications.
|
||||||
- hsdocgen: An ad-hoc document generation used across HS project (used for help files)
|
|
||||||
- cocoalib: A collection of helpers used across Cocoa UI codebases of HS applications.
|
- cocoalib: A collection of helpers used across Cocoa UI codebases of HS applications.
|
||||||
- qtlib: A collection of helpers used across Qt UI codebases of HS applications.
|
- qtlib: A collection of helpers used across Qt UI codebases of HS applications.
|
||||||
|
|
||||||
@@ -25,12 +24,11 @@ Before being able to build dupeGuru, a few dependencies have to be installed:
|
|||||||
General dependencies
|
General dependencies
|
||||||
-----
|
-----
|
||||||
|
|
||||||
- Python 2.6 (http://www.python.org)
|
- Python 3.1 (http://www.python.org)
|
||||||
- Send2Trash (http://hg.hardcoded.net/send2trash)
|
- Send2Trash3k (http://hg.hardcoded.net/send2trash3k)
|
||||||
- hsutil (http://hg.hardcoded.net/hsutil)
|
- hsutil3k (http://hg.hardcoded.net/hsutil3k)
|
||||||
- hsaudiotag (for ME) (http://hg.hardcoded.net/hsaudiotag)
|
- hsaudiotag3k (for ME) (http://hg.hardcoded.net/hsaudiotag3k)
|
||||||
- lxml, to read and write XML files. (http://codespeak.net/lxml/)
|
- Markdown, to generate help files. (http://pypi.python.org/pypi/Markdown)
|
||||||
- Mako, to generate help files. (http://www.makotemplates.org/)
|
|
||||||
- PyYaml, for help files and the build system. (http://pyyaml.org/)
|
- PyYaml, for help files and the build system. (http://pyyaml.org/)
|
||||||
- py.test, to run unit tests. (http://codespeak.net/py/dist/test/)
|
- py.test, to run unit tests. (http://codespeak.net/py/dist/test/)
|
||||||
|
|
||||||
@@ -39,15 +37,14 @@ OS X prerequisites
|
|||||||
|
|
||||||
- XCode 3.1 (http://developer.apple.com/TOOLS/xcode/)
|
- XCode 3.1 (http://developer.apple.com/TOOLS/xcode/)
|
||||||
- Sparkle (http://sparkle.andymatuschak.org/)
|
- Sparkle (http://sparkle.andymatuschak.org/)
|
||||||
- PyObjC 2.2. (http://pyobjc.sourceforge.net/)
|
- PyObjC 2.3. (http://pyobjc.sourceforge.net/)
|
||||||
- py2app (http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html)
|
- py2app 0.5.4 (http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html)
|
||||||
|
|
||||||
Windows prerequisites
|
Windows prerequisites
|
||||||
---
|
---
|
||||||
|
|
||||||
- Visual Studio 2008 (Express is enough) is needed to build C 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)
|
- PyQt 4.7.5 (http://www.riverbankcomputing.co.uk/news)
|
||||||
- Python Imaging Library for dupeGuru PE. (http://www.pythonware.com/products/pil/)
|
|
||||||
- cx_Freeze, if you want to build a exe. You don't need it if you just want to run dupeGuru. (http://cx-freeze.sourceforge.net/)
|
- cx_Freeze, if you want to build a exe. You don't need it if you just want to run dupeGuru. (http://cx-freeze.sourceforge.net/)
|
||||||
- Advanced Installer, if you want to build the installer file. (http://www.advancedinstaller.com/)
|
- Advanced Installer, if you want to build the installer file. (http://www.advancedinstaller.com/)
|
||||||
|
|
||||||
@@ -68,8 +65,3 @@ Then, just build the thing and then run it with:
|
|||||||
If you want to create ready-to-upload package, run:
|
If you want to create ready-to-upload package, run:
|
||||||
|
|
||||||
python package.py
|
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 .
|
|
||||||
62
build.py
62
build.py
@@ -13,6 +13,7 @@ import os.path as op
|
|||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
from distutils.extension import Extension
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from hscommon import helpgen
|
from hscommon import helpgen
|
||||||
@@ -20,10 +21,10 @@ from hscommon.build import add_to_pythonpath, print_and_do, build_all_qt_ui, cop
|
|||||||
|
|
||||||
def build_cocoa(edition, dev, help_destpath):
|
def build_cocoa(edition, dev, help_destpath):
|
||||||
if not dev:
|
if not dev:
|
||||||
print "Building help index"
|
print("Building help index")
|
||||||
os.system('open -a /Developer/Applications/Utilities/Help\\ Indexer.app {0}'.format(help_destpath))
|
os.system('open -a /Developer/Applications/Utilities/Help\\ Indexer.app {0}'.format(help_destpath))
|
||||||
|
|
||||||
print "Building dg_cocoa.plugin"
|
print("Building dg_cocoa.plugin")
|
||||||
if op.exists('build'):
|
if op.exists('build'):
|
||||||
shutil.rmtree('build')
|
shutil.rmtree('build')
|
||||||
os.mkdir('build')
|
os.mkdir('build')
|
||||||
@@ -54,7 +55,7 @@ def build_cocoa(edition, dev, help_destpath):
|
|||||||
pthpath = op.join(pluginpath, 'Contents/Resources/dev.pth')
|
pthpath = op.join(pluginpath, 'Contents/Resources/dev.pth')
|
||||||
open(pthpath, 'w').write(op.abspath('.'))
|
open(pthpath, 'w').write(op.abspath('.'))
|
||||||
os.chdir(cocoa_project_path)
|
os.chdir(cocoa_project_path)
|
||||||
print "Building the XCode project"
|
print("Building the XCode project")
|
||||||
args = []
|
args = []
|
||||||
if dev:
|
if dev:
|
||||||
args.append('-configuration dev')
|
args.append('-configuration dev')
|
||||||
@@ -65,25 +66,58 @@ def build_cocoa(edition, dev, help_destpath):
|
|||||||
os.chdir('..')
|
os.chdir('..')
|
||||||
|
|
||||||
def build_qt(edition, dev):
|
def build_qt(edition, dev):
|
||||||
|
print("Building Qt stuff")
|
||||||
build_all_qt_ui(op.join('qtlib', 'ui'))
|
build_all_qt_ui(op.join('qtlib', 'ui'))
|
||||||
build_all_qt_ui(op.join('qt', 'base'))
|
build_all_qt_ui(op.join('qt', 'base'))
|
||||||
build_all_qt_ui(op.join('qt', edition))
|
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')))
|
print_and_do("pyrcc4 -py3 {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))
|
def build_pe_modules(ui):
|
||||||
os.system('python gen.py')
|
def move(src, dst):
|
||||||
os.chdir(op.join('..', '..'))
|
if not op.exists(src):
|
||||||
|
return
|
||||||
|
if op.exists(dst):
|
||||||
|
os.remove(dst)
|
||||||
|
print('Moving %s --> %s' % (src, dst))
|
||||||
|
os.rename(src, dst)
|
||||||
|
|
||||||
|
print("Building PE Modules")
|
||||||
|
exts = [
|
||||||
|
Extension("_block", [op.join('core_pe', 'modules', 'block.c'), op.join('core_pe', 'modules', 'common.c')]),
|
||||||
|
Extension("_cache", [op.join('core_pe', 'modules', 'cache.c'), op.join('core_pe', 'modules', 'common.c')]),
|
||||||
|
]
|
||||||
|
if ui == 'qt':
|
||||||
|
exts.append(Extension("_block_qt", [op.join('qt', 'pe', 'modules', 'block.c')]))
|
||||||
|
elif ui == 'cocoa':
|
||||||
|
exts.append(Extension(
|
||||||
|
"_block_osx", [op.join('core_pe', 'modules', 'block_osx.m'), op.join('core_pe', 'modules', 'common.c')],
|
||||||
|
extra_link_args=[
|
||||||
|
"-framework", "CoreFoundation",
|
||||||
|
"-framework", "Foundation",
|
||||||
|
"-framework", "ApplicationServices",]
|
||||||
|
))
|
||||||
|
setup(
|
||||||
|
script_args = ['build_ext', '--inplace'],
|
||||||
|
ext_modules = exts,
|
||||||
|
)
|
||||||
|
move('_block.so', op.join('core_pe', '_block.so'))
|
||||||
|
move('_block.pyd', op.join('core_pe', '_block.pyd'))
|
||||||
|
move('_block_osx.so', op.join('core_pe', '_block_osx.so'))
|
||||||
|
move('_cache.so', op.join('core_pe', '_cache.so'))
|
||||||
|
move('_cache.pyd', op.join('core_pe', '_cache.pyd'))
|
||||||
|
move('_block_qt.so', op.join('qt', 'pe', '_block_qt.so'))
|
||||||
|
move('_block_qt.pyd', op.join('qt', 'pe', '_block_qt.pyd'))
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
conf = yaml.load(open('conf.yaml'))
|
conf = yaml.load(open('conf.yaml'))
|
||||||
edition = conf['edition']
|
edition = conf['edition']
|
||||||
ui = conf['ui']
|
ui = conf['ui']
|
||||||
dev = conf['dev']
|
dev = conf['dev']
|
||||||
print "Building dupeGuru {0} with UI {1}".format(edition.upper(), ui)
|
print("Building dupeGuru {0} with UI {1}".format(edition.upper(), ui))
|
||||||
if dev:
|
if dev:
|
||||||
print "Building in Dev mode"
|
print("Building in Dev mode")
|
||||||
add_to_pythonpath('.')
|
add_to_pythonpath('.')
|
||||||
print "Generating Help"
|
print("Generating Help")
|
||||||
windows = sys.platform == 'win32'
|
windows = sys.platform == 'win32'
|
||||||
profile = 'win_en' if windows else 'osx_en'
|
profile = 'win_en' if windows else 'osx_en'
|
||||||
help_dir = 'help_{0}'.format(edition)
|
help_dir = 'help_{0}'.format(edition)
|
||||||
@@ -91,11 +125,9 @@ def main():
|
|||||||
help_basepath = op.abspath(help_dir)
|
help_basepath = op.abspath(help_dir)
|
||||||
help_destpath = op.abspath(op.join(help_dir, dest_dir))
|
help_destpath = op.abspath(op.join(help_dir, dest_dir))
|
||||||
helpgen.gen(help_basepath, help_destpath, profile=profile)
|
helpgen.gen(help_basepath, help_destpath, profile=profile)
|
||||||
print "Building dupeGuru"
|
print("Building dupeGuru")
|
||||||
if edition == 'pe':
|
if edition == 'pe':
|
||||||
os.chdir('core_pe')
|
build_pe_modules(ui)
|
||||||
os.system('python gen.py')
|
|
||||||
os.chdir('..')
|
|
||||||
if ui == 'cocoa':
|
if ui == 'cocoa':
|
||||||
build_cocoa(edition, dev, help_destpath)
|
build_cocoa(edition, dev, help_destpath)
|
||||||
elif ui == 'qt':
|
elif ui == 'qt':
|
||||||
|
|||||||
@@ -39,13 +39,13 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
NSOpenPanel *op = [NSOpenPanel openPanel];
|
NSOpenPanel *op = [NSOpenPanel openPanel];
|
||||||
[op setCanChooseFiles:YES];
|
[op setCanChooseFiles:YES];
|
||||||
[op setCanChooseDirectories:YES];
|
[op setCanChooseDirectories:YES];
|
||||||
[op setAllowsMultipleSelection:NO];
|
[op setAllowsMultipleSelection:YES];
|
||||||
[op setTitle:@"Select a directory to add to the scanning list"];
|
[op setTitle:@"Select a directory to add to the scanning list"];
|
||||||
[op setDelegate:self];
|
[op setDelegate:self];
|
||||||
if ([op runModalForTypes:nil] == NSOKButton)
|
if ([op runModal] == NSOKButton) {
|
||||||
{
|
for (NSString *directory in [op filenames]) {
|
||||||
NSString *directory = [[op filenames] objectAtIndex:0];
|
[self addDirectory:directory];
|
||||||
[self addDirectory:directory];
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,18 +95,14 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
NSInteger r = [[_py addDirectory:directory] intValue];
|
NSInteger r = [[_py addDirectory:directory] intValue];
|
||||||
if (r) {
|
if (r) {
|
||||||
NSString *m;
|
NSString *m = @"";
|
||||||
switch (r) {
|
if (r == 1) {
|
||||||
case 1: {
|
m = @"'%@' already is in the list.";
|
||||||
m = @"This directory already is in the list.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
m = @"This directory does not exist.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
[Dialogs showMessage:m];
|
else if (r == 2) {
|
||||||
|
m = @"'%@' does not exist.";
|
||||||
|
}
|
||||||
|
[Dialogs showMessage:[NSString stringWithFormat:m,directory]];
|
||||||
}
|
}
|
||||||
[_recentDirectories addDirectory:directory];
|
[_recentDirectories addDirectory:directory];
|
||||||
[[self window] makeKeyAndOrderFront:nil];
|
[[self window] makeKeyAndOrderFront:nil];
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
- (NSNumber *)addDirectory:(NSString *)name;
|
- (NSNumber *)addDirectory:(NSString *)name;
|
||||||
- (void)removeDirectory:(NSNumber *)index;
|
- (void)removeDirectory:(NSNumber *)index;
|
||||||
- (void)loadResults;
|
- (void)loadResults;
|
||||||
|
- (void)loadResultsFrom:(NSString *)filename;
|
||||||
- (void)saveResults;
|
- (void)saveResults;
|
||||||
|
- (void)saveResultsAs:(NSString *)filename;
|
||||||
- (void)loadIgnoreList;
|
- (void)loadIgnoreList;
|
||||||
- (void)saveIgnoreList;
|
- (void)saveIgnoreList;
|
||||||
- (void)clearIgnoreList;
|
- (void)clearIgnoreList;
|
||||||
@@ -37,6 +39,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
- (void)copyOrMove:(NSNumber *)aCopy markedTo:(NSString *)destination recreatePath:(NSNumber *)aRecreateType;
|
- (void)copyOrMove:(NSNumber *)aCopy markedTo:(NSString *)destination recreatePath:(NSNumber *)aRecreateType;
|
||||||
- (void)deleteMarked;
|
- (void)deleteMarked;
|
||||||
|
- (void)hardlinkMarked;
|
||||||
- (void)removeMarked;
|
- (void)removeMarked;
|
||||||
|
|
||||||
//Data
|
//Data
|
||||||
@@ -46,8 +49,9 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
//Scanning options
|
//Scanning options
|
||||||
- (void)setMinMatchPercentage:(NSNumber *)percentage;
|
- (void)setMinMatchPercentage:(NSNumber *)percentage;
|
||||||
- (void)setMixFileKind:(NSNumber *)mix_file_kind;
|
- (void)setMixFileKind:(BOOL)mix_file_kind;
|
||||||
- (void)setEscapeFilterRegexp:(NSNumber *)escape_filter_regexp;
|
- (void)setEscapeFilterRegexp:(BOOL)escape_filter_regexp;
|
||||||
- (void)setRemoveEmptyFolders:(NSNumber *)remove_empty_folders;
|
- (void)setRemoveEmptyFolders:(BOOL)remove_empty_folders;
|
||||||
|
- (void)setIgnoreHardlinkMatches:(BOOL)ignore_hardlink_matches;
|
||||||
- (void)setSizeThreshold:(NSInteger)size_threshold;
|
- (void)setSizeThreshold:(NSInteger)size_threshold;
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -7,18 +7,18 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "PyOutline.h"
|
#import "PyTable.h"
|
||||||
|
|
||||||
@interface PyResultTree : PyOutline
|
@interface PyResultTable : PyTable
|
||||||
- (BOOL)powerMarkerMode;
|
- (BOOL)powerMarkerMode;
|
||||||
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode;
|
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode;
|
||||||
- (BOOL)deltaValuesMode;
|
- (BOOL)deltaValuesMode;
|
||||||
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode;
|
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode;
|
||||||
|
|
||||||
- (NSString *)valueForPath:(NSArray *)aPath column:(NSInteger)aColumn;
|
- (NSString *)valueForRow:(NSInteger)rowIndex column:(NSInteger)aColumn;
|
||||||
- (BOOL)renameSelected:(NSString *)aNewName;
|
- (BOOL)renameSelected:(NSString *)aNewName;
|
||||||
- (void)sortBy:(NSInteger)aIdentifier ascending:(BOOL)aAscending;
|
- (void)sortBy:(NSInteger)aIdentifier ascending:(BOOL)aAscending;
|
||||||
- (void)markSelected;
|
- (void)markSelected;
|
||||||
- (void)removeSelected;
|
- (void)removeSelected;
|
||||||
- (NSArray *)rootChildrenCounts;
|
- (NSInteger)selectedDupeCount;
|
||||||
@end
|
@end
|
||||||
@@ -1,207 +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 "ResultOutline.h"
|
|
||||||
#import "Dialogs.h"
|
|
||||||
#import "Utils.h"
|
|
||||||
#import "Consts.h"
|
|
||||||
|
|
||||||
@implementation ResultOutline
|
|
||||||
- (id)initWithPyParent:(id)aPyParent view:(HSOutlineView *)aOutlineView
|
|
||||||
{
|
|
||||||
self = [super initWithPyClassName:@"PyResultOutline" pyParent:aPyParent view:aOutlineView];
|
|
||||||
_rootChildrenCounts = nil;
|
|
||||||
[self connect];
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
[self disconnect];
|
|
||||||
[_deltaColumns release];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (PyResultTree *)py
|
|
||||||
{
|
|
||||||
return (PyResultTree *)py;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Public */
|
|
||||||
- (BOOL)powerMarkerMode
|
|
||||||
{
|
|
||||||
return [[self py] powerMarkerMode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode
|
|
||||||
{
|
|
||||||
[[self py] setPowerMarkerMode:aPowerMarkerMode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)deltaValuesMode
|
|
||||||
{
|
|
||||||
return [[self py] deltaValuesMode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode
|
|
||||||
{
|
|
||||||
[[self py] setDeltaValuesMode:aDeltaValuesMode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDeltaColumns:(NSIndexSet *)aDeltaColumns
|
|
||||||
{
|
|
||||||
[_deltaColumns release];
|
|
||||||
_deltaColumns = [aDeltaColumns retain];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)selectedDupeCount
|
|
||||||
{
|
|
||||||
NSArray *selected = [self selectedIndexPaths];
|
|
||||||
if ([self powerMarkerMode]) {
|
|
||||||
return [selected count];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
NSInteger r = 0;
|
|
||||||
for (NSIndexPath *path in selected) {
|
|
||||||
if ([path length] == 2) {
|
|
||||||
r++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeSelected
|
|
||||||
{
|
|
||||||
NSInteger selectedDupeCount = [self selectedDupeCount];
|
|
||||||
if (!selectedDupeCount)
|
|
||||||
return;
|
|
||||||
NSString *msg = [NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",selectedDupeCount];
|
|
||||||
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) // NO
|
|
||||||
return;
|
|
||||||
[[self py] removeSelected];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Datasource */
|
|
||||||
- (NSInteger)outlineView:(NSOutlineView *)aOutlineView numberOfChildrenOfItem:(id)item
|
|
||||||
{
|
|
||||||
NSIndexPath *path = item;
|
|
||||||
if ((path != nil) && ([path length] == 1)) {
|
|
||||||
if (_rootChildrenCounts == nil) {
|
|
||||||
_rootChildrenCounts = [[[self py] rootChildrenCounts] retain];
|
|
||||||
}
|
|
||||||
NSInteger index = [path indexAtPosition:0];
|
|
||||||
return n2i([_rootChildrenCounts objectAtIndex:index]);
|
|
||||||
}
|
|
||||||
return [super outlineView:aOutlineView numberOfChildrenOfItem:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)column byItem:(id)item
|
|
||||||
{
|
|
||||||
NSIndexPath *path = item;
|
|
||||||
NSString *identifier = [column identifier];
|
|
||||||
if ([identifier isEqual:@"marked"]) {
|
|
||||||
return b2n([self boolProperty:@"marked" valueAtPath:path]);
|
|
||||||
}
|
|
||||||
NSInteger columnId = [identifier integerValue];
|
|
||||||
return [[self py] valueForPath:p2a(path) column:columnId];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)outlineView:(NSOutlineView *)aOutlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
|
|
||||||
{
|
|
||||||
if ([[tableColumn identifier] isEqual:@"0"]) {
|
|
||||||
NSIndexPath *path = item;
|
|
||||||
NSString *oldName = [[self py] valueForPath:p2a(path) column:0];
|
|
||||||
NSString *newName = object;
|
|
||||||
if (![newName isEqual:oldName]) {
|
|
||||||
BOOL renamed = [[self py] renameSelected:newName];
|
|
||||||
if (!renamed) {
|
|
||||||
[Dialogs showMessage:[NSString stringWithFormat:@"The name '%@' already exists.", newName]];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[self refreshItemAtPath:path];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[super outlineView:aOutlineView setObjectValue:object forTableColumn:tableColumn byItem:item];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Delegate */
|
|
||||||
- (void)outlineView:(NSOutlineView *)aOutlineView didClickTableColumn:(NSTableColumn *)tableColumn
|
|
||||||
{
|
|
||||||
if ([[outlineView sortDescriptors] count] < 1)
|
|
||||||
return;
|
|
||||||
NSSortDescriptor *sd = [[outlineView sortDescriptors] objectAtIndex:0];
|
|
||||||
[[self py] sortBy:[[sd key] integerValue] ascending:[sd ascending]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
|
|
||||||
{
|
|
||||||
NSIndexPath *path = item;
|
|
||||||
BOOL isMarkable = [self boolProperty:@"markable" valueAtPath:path];
|
|
||||||
if ([[tableColumn identifier] isEqual:@"marked"]) {
|
|
||||||
[cell setEnabled:isMarkable];
|
|
||||||
}
|
|
||||||
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
|
|
||||||
// Determine if the text color will be blue due to directory being reference.
|
|
||||||
NSTextFieldCell *textCell = cell;
|
|
||||||
if (isMarkable) {
|
|
||||||
[textCell setTextColor:[NSColor blackColor]];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[textCell setTextColor:[NSColor blueColor]];
|
|
||||||
}
|
|
||||||
if (([self deltaValuesMode]) && ([self powerMarkerMode] || ([path length] > 1))) {
|
|
||||||
NSInteger i = [[tableColumn identifier] integerValue];
|
|
||||||
if ([_deltaColumns containsIndex:i]) {
|
|
||||||
[textCell setTextColor:[NSColor orangeColor]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tableViewHadDeletePressed:(NSTableView *)tableView
|
|
||||||
{
|
|
||||||
[self removeSelected];
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tableViewHadSpacePressed:(NSTableView *)tableView
|
|
||||||
{
|
|
||||||
[[self py] markSelected];
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* don't calls saveEdits and cancelEdits */
|
|
||||||
- (void)outlineViewDidEndEditing:(HSOutlineView *)outlineView
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)outlineViewCancelsEdition:(HSOutlineView *)outlineView
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Python --> Cocoa */
|
|
||||||
- (void)refresh /* Override */
|
|
||||||
{
|
|
||||||
[_rootChildrenCounts release];
|
|
||||||
_rootChildrenCounts = nil;
|
|
||||||
[super refresh];
|
|
||||||
[outlineView expandItem:nil expandChildren:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateMarkings
|
|
||||||
{
|
|
||||||
for (NSMutableDictionary *props in [itemData objectEnumerator]) {
|
|
||||||
[props removeObjectForKey:@"marked"];
|
|
||||||
}
|
|
||||||
[outlineView setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
@@ -7,15 +7,15 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "HSOutline.h"
|
#import "HSTable.h"
|
||||||
#import "PyResultTree.h"
|
#import "PyResultTable.h"
|
||||||
|
|
||||||
@interface ResultOutline : HSOutline
|
@interface ResultTable : HSTable
|
||||||
{
|
{
|
||||||
NSIndexSet *_deltaColumns;
|
NSIndexSet *_deltaColumns;
|
||||||
NSArray *_rootChildrenCounts;
|
|
||||||
}
|
}
|
||||||
- (PyResultTree *)py;
|
- (id)initWithPyParent:(id)aPyParent view:(NSTableView *)aTableView;
|
||||||
|
- (PyResultTable *)py;
|
||||||
- (BOOL)powerMarkerMode;
|
- (BOOL)powerMarkerMode;
|
||||||
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode;
|
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode;
|
||||||
- (BOOL)deltaValuesMode;
|
- (BOOL)deltaValuesMode;
|
||||||
162
cocoa/base/ResultTable.m
Normal file
162
cocoa/base/ResultTable.m
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
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 "ResultTable.h"
|
||||||
|
#import "Dialogs.h"
|
||||||
|
#import "Utils.h"
|
||||||
|
#import "Consts.h"
|
||||||
|
|
||||||
|
@implementation ResultTable
|
||||||
|
- (id)initWithPyParent:(id)aPyParent view:(NSTableView *)aTableView
|
||||||
|
{
|
||||||
|
self = [super initWithPyClassName:@"PyResultTable" pyParent:aPyParent view:aTableView];
|
||||||
|
[self connect];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[self disconnect];
|
||||||
|
[_deltaColumns release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (PyResultTable *)py
|
||||||
|
{
|
||||||
|
return (PyResultTable *)py;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Public */
|
||||||
|
- (BOOL)powerMarkerMode
|
||||||
|
{
|
||||||
|
return [[self py] powerMarkerMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode
|
||||||
|
{
|
||||||
|
[[self py] setPowerMarkerMode:aPowerMarkerMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)deltaValuesMode
|
||||||
|
{
|
||||||
|
return [[self py] deltaValuesMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode
|
||||||
|
{
|
||||||
|
[[self py] setDeltaValuesMode:aDeltaValuesMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDeltaColumns:(NSIndexSet *)aDeltaColumns
|
||||||
|
{
|
||||||
|
[_deltaColumns release];
|
||||||
|
_deltaColumns = [aDeltaColumns retain];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)selectedDupeCount
|
||||||
|
{
|
||||||
|
return [[self py] selectedDupeCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeSelected
|
||||||
|
{
|
||||||
|
NSInteger selectedDupeCount = [self selectedDupeCount];
|
||||||
|
if (!selectedDupeCount)
|
||||||
|
return;
|
||||||
|
NSString *msg = [NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",selectedDupeCount];
|
||||||
|
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) // NO
|
||||||
|
return;
|
||||||
|
[[self py] removeSelected];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Datasource */
|
||||||
|
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)row
|
||||||
|
{
|
||||||
|
NSString *identifier = [column identifier];
|
||||||
|
if ([identifier isEqual:@"marked"]) {
|
||||||
|
return [[self py] valueForColumn:@"marked" row:row];
|
||||||
|
}
|
||||||
|
NSInteger columnId = [identifier integerValue];
|
||||||
|
return [[self py] valueForRow:row column:columnId];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)column row:(NSInteger)row
|
||||||
|
{
|
||||||
|
NSString *identifier = [column identifier];
|
||||||
|
if ([identifier isEqual:@"marked"]) {
|
||||||
|
[[self py] setValue:object forColumn:identifier row:row];
|
||||||
|
}
|
||||||
|
else if ([identifier isEqual:@"0"]) {
|
||||||
|
NSString *oldName = [[self py] valueForRow:row column:0];
|
||||||
|
NSString *newName = object;
|
||||||
|
if (![newName isEqual:oldName]) {
|
||||||
|
BOOL renamed = [[self py] renameSelected:newName];
|
||||||
|
if (!renamed) {
|
||||||
|
[Dialogs showMessage:[NSString stringWithFormat:@"The name '%@' already exists.", newName]];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[tableView setNeedsDisplay:YES];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delegate */
|
||||||
|
- (void)tableView:(NSTableView *)aTableView didClickTableColumn:(NSTableColumn *)tableColumn
|
||||||
|
{
|
||||||
|
if ([[tableView sortDescriptors] count] < 1)
|
||||||
|
return;
|
||||||
|
NSSortDescriptor *sd = [[tableView sortDescriptors] objectAtIndex:0];
|
||||||
|
[[self py] sortBy:[[sd key] integerValue] ascending:[sd ascending]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)column row:(NSInteger)row
|
||||||
|
{
|
||||||
|
BOOL isMarkable = n2b([[self py] valueForColumn:@"markable" row:row]);
|
||||||
|
if ([[column identifier] isEqual:@"marked"]) {
|
||||||
|
[cell setEnabled:isMarkable];
|
||||||
|
// Low-tech solution, for indentation, but it works...
|
||||||
|
NSCellImagePosition pos = isMarkable ? NSImageRight : NSImageLeft;
|
||||||
|
[cell setImagePosition:pos];
|
||||||
|
}
|
||||||
|
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
|
||||||
|
// Determine if the text color will be blue due to directory being reference.
|
||||||
|
NSTextFieldCell *textCell = cell;
|
||||||
|
if (isMarkable) {
|
||||||
|
[textCell setTextColor:[NSColor blackColor]];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[textCell setTextColor:[NSColor blueColor]];
|
||||||
|
if ([self deltaValuesMode]) {
|
||||||
|
NSInteger i = [[column identifier] integerValue];
|
||||||
|
if ([_deltaColumns containsIndex:i]) {
|
||||||
|
[textCell setTextColor:[NSColor orangeColor]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)tableViewHadDeletePressed:(NSTableView *)tableView
|
||||||
|
{
|
||||||
|
[self removeSelected];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)tableViewHadSpacePressed:(NSTableView *)tableView
|
||||||
|
{
|
||||||
|
[[self py] markSelected];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Python --> Cocoa */
|
||||||
|
- (void)invalidateMarkings
|
||||||
|
{
|
||||||
|
[tableView setNeedsDisplay:YES];
|
||||||
|
}
|
||||||
|
@end
|
||||||
@@ -7,10 +7,10 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "HSOutlineView.h"
|
|
||||||
#import "StatsLabel.h"
|
#import "StatsLabel.h"
|
||||||
#import "ResultOutline.h"
|
#import "ResultTable.h"
|
||||||
#import "ProblemDialog.h"
|
#import "ProblemDialog.h"
|
||||||
|
#import "HSTableView.h"
|
||||||
#import "PyDupeGuru.h"
|
#import "PyDupeGuru.h"
|
||||||
|
|
||||||
@interface ResultWindowBase : NSWindowController
|
@interface ResultWindowBase : NSWindowController
|
||||||
@@ -19,7 +19,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
IBOutlet PyDupeGuruBase *py;
|
IBOutlet PyDupeGuruBase *py;
|
||||||
IBOutlet id app;
|
IBOutlet id app;
|
||||||
IBOutlet NSSegmentedControl *deltaSwitch;
|
IBOutlet NSSegmentedControl *deltaSwitch;
|
||||||
IBOutlet HSOutlineView *matches;
|
IBOutlet HSTableView *matches;
|
||||||
IBOutlet NSSegmentedControl *pmSwitch;
|
IBOutlet NSSegmentedControl *pmSwitch;
|
||||||
IBOutlet NSTextField *stats;
|
IBOutlet NSTextField *stats;
|
||||||
IBOutlet NSMenu *columnsMenu;
|
IBOutlet NSMenu *columnsMenu;
|
||||||
@@ -27,7 +27,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
NSMutableArray *_resultColumns;
|
NSMutableArray *_resultColumns;
|
||||||
NSWindowController *preferencesPanel;
|
NSWindowController *preferencesPanel;
|
||||||
ResultOutline *outline;
|
ResultTable *table;
|
||||||
StatsLabel *statsLabel;
|
StatsLabel *statsLabel;
|
||||||
ProblemDialog *problemDialog;
|
ProblemDialog *problemDialog;
|
||||||
}
|
}
|
||||||
@@ -38,6 +38,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
- (NSDictionary *)getColumnsWidth;
|
- (NSDictionary *)getColumnsWidth;
|
||||||
- (void)initResultColumns;
|
- (void)initResultColumns;
|
||||||
- (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth;
|
- (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth;
|
||||||
|
- (void)sendMarkedToTrash:(BOOL)hardlinkDeleted;
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
- (IBAction)clearIgnoreList:(id)sender;
|
- (IBAction)clearIgnoreList:(id)sender;
|
||||||
@@ -45,10 +46,12 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
- (IBAction)changePowerMarker:(id)sender;
|
- (IBAction)changePowerMarker:(id)sender;
|
||||||
- (IBAction)copyMarked:(id)sender;
|
- (IBAction)copyMarked:(id)sender;
|
||||||
- (IBAction)deleteMarked:(id)sender;
|
- (IBAction)deleteMarked:(id)sender;
|
||||||
|
- (IBAction)hardlinkMarked:(id)sender;
|
||||||
- (IBAction)exportToXHTML:(id)sender;
|
- (IBAction)exportToXHTML:(id)sender;
|
||||||
- (IBAction)filter:(id)sender;
|
- (IBAction)filter:(id)sender;
|
||||||
- (IBAction)ignoreSelected:(id)sender;
|
- (IBAction)ignoreSelected:(id)sender;
|
||||||
- (IBAction)invokeCustomCommand:(id)sender;
|
- (IBAction)invokeCustomCommand:(id)sender;
|
||||||
|
- (IBAction)loadResults:(id)sender;
|
||||||
- (IBAction)markAll:(id)sender;
|
- (IBAction)markAll:(id)sender;
|
||||||
- (IBAction)markInvert:(id)sender;
|
- (IBAction)markInvert:(id)sender;
|
||||||
- (IBAction)markNone:(id)sender;
|
- (IBAction)markNone:(id)sender;
|
||||||
@@ -61,6 +64,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
- (IBAction)renameSelected:(id)sender;
|
- (IBAction)renameSelected:(id)sender;
|
||||||
- (IBAction)resetColumnsToDefault:(id)sender;
|
- (IBAction)resetColumnsToDefault:(id)sender;
|
||||||
- (IBAction)revealSelected:(id)sender;
|
- (IBAction)revealSelected:(id)sender;
|
||||||
|
- (IBAction)saveResults:(id)sender;
|
||||||
- (IBAction)showPreferencesPanel:(id)sender;
|
- (IBAction)showPreferencesPanel:(id)sender;
|
||||||
- (IBAction)startDuplicateScan:(id)sender;
|
- (IBAction)startDuplicateScan:(id)sender;
|
||||||
- (IBAction)switchSelected:(id)sender;
|
- (IBAction)switchSelected:(id)sender;
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
- (void)awakeFromNib
|
- (void)awakeFromNib
|
||||||
{
|
{
|
||||||
[self window];
|
[self window];
|
||||||
|
/* Put a cute iTunes-like bottom bar */
|
||||||
|
[[self window] setContentBorderThickness:28 forEdge:NSMinYEdge];
|
||||||
preferencesPanel = [[NSWindowController alloc] initWithWindowNibName:@"Preferences"];
|
preferencesPanel = [[NSWindowController alloc] initWithWindowNibName:@"Preferences"];
|
||||||
outline = [[ResultOutline alloc] initWithPyParent:py view:matches];
|
table = [[ResultTable alloc] initWithPyParent:py view:matches];
|
||||||
statsLabel = [[StatsLabel alloc] initWithPyParent:py labelView:stats];
|
statsLabel = [[StatsLabel alloc] initWithPyParent:py labelView:stats];
|
||||||
problemDialog = [[ProblemDialog alloc] initWithPy:py];
|
problemDialog = [[ProblemDialog alloc] initWithPy:py];
|
||||||
[self initResultColumns];
|
[self initResultColumns];
|
||||||
@@ -37,7 +39,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
[outline release];
|
[table release];
|
||||||
[preferencesPanel release];
|
[preferencesPanel release];
|
||||||
[statsLabel release];
|
[statsLabel release];
|
||||||
[problemDialog release];
|
[problemDialog release];
|
||||||
@@ -124,6 +126,29 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)sendMarkedToTrash:(BOOL)hardlinkDeleted
|
||||||
|
{
|
||||||
|
NSInteger mark_count = [[py getMarkCount] intValue];
|
||||||
|
if (!mark_count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSString *msg = @"You are about to send %d files to Trash. Continue?";
|
||||||
|
if (hardlinkDeleted) {
|
||||||
|
msg = @"You are about to send %d files to Trash (and hardlink them afterwards). Continue?";
|
||||||
|
}
|
||||||
|
if ([Dialogs askYesNo:[NSString stringWithFormat:msg,mark_count]] == NSAlertSecondButtonReturn) { // NO
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
|
[py setRemoveEmptyFolders:n2b([ud objectForKey:@"removeEmptyFolders"])];
|
||||||
|
if (hardlinkDeleted) {
|
||||||
|
[py hardlinkMarked];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[py deleteMarked];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
- (IBAction)clearIgnoreList:(id)sender
|
- (IBAction)clearIgnoreList:(id)sender
|
||||||
{
|
{
|
||||||
@@ -137,12 +162,12 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
- (IBAction)changeDelta:(id)sender
|
- (IBAction)changeDelta:(id)sender
|
||||||
{
|
{
|
||||||
[outline setDeltaValuesMode:[deltaSwitch selectedSegment] == 1];
|
[table setDeltaValuesMode:[deltaSwitch selectedSegment] == 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)changePowerMarker:(id)sender
|
- (IBAction)changePowerMarker:(id)sender
|
||||||
{
|
{
|
||||||
[outline setPowerMarkerMode:[pmSwitch selectedSegment] == 1];
|
[table setPowerMarkerMode:[pmSwitch selectedSegment] == 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)copyMarked:(id)sender
|
- (IBAction)copyMarked:(id)sender
|
||||||
@@ -166,14 +191,12 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
- (IBAction)deleteMarked:(id)sender
|
- (IBAction)deleteMarked:(id)sender
|
||||||
{
|
{
|
||||||
NSInteger mark_count = [[py getMarkCount] intValue];
|
[self sendMarkedToTrash:NO];
|
||||||
if (!mark_count)
|
}
|
||||||
return;
|
|
||||||
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to send %d files to Trash. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
|
- (IBAction)hardlinkMarked:(id)sender
|
||||||
return;
|
{
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
[self sendMarkedToTrash:YES];
|
||||||
[py setRemoveEmptyFolders:[ud objectForKey:@"removeEmptyFolders"]];
|
|
||||||
[py deleteMarked];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)exportToXHTML:(id)sender
|
- (IBAction)exportToXHTML:(id)sender
|
||||||
@@ -185,13 +208,13 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
- (IBAction)filter:(id)sender
|
- (IBAction)filter:(id)sender
|
||||||
{
|
{
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
[py setEscapeFilterRegexp:b2n(!n2b([ud objectForKey:@"useRegexpFilter"]))];
|
[py setEscapeFilterRegexp:!n2b([ud objectForKey:@"useRegexpFilter"])];
|
||||||
[py applyFilter:[filterField stringValue]];
|
[py applyFilter:[filterField stringValue]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)ignoreSelected:(id)sender
|
- (IBAction)ignoreSelected:(id)sender
|
||||||
{
|
{
|
||||||
NSInteger selectedDupeCount = [outline selectedDupeCount];
|
NSInteger selectedDupeCount = [table selectedDupeCount];
|
||||||
if (!selectedDupeCount)
|
if (!selectedDupeCount)
|
||||||
return;
|
return;
|
||||||
NSString *msg = [NSString stringWithFormat:@"All selected %d matches are going to be ignored in all subsequent scans. Continue?",selectedDupeCount];
|
NSString *msg = [NSString stringWithFormat:@"All selected %d matches are going to be ignored in all subsequent scans. Continue?",selectedDupeCount];
|
||||||
@@ -212,6 +235,21 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (IBAction)loadResults:(id)sender
|
||||||
|
{
|
||||||
|
NSOpenPanel *op = [NSOpenPanel openPanel];
|
||||||
|
[op setCanChooseFiles:YES];
|
||||||
|
[op setCanChooseDirectories:NO];
|
||||||
|
[op setCanCreateDirectories:NO];
|
||||||
|
[op setAllowsMultipleSelection:NO];
|
||||||
|
[op setAllowedFileTypes:[NSArray arrayWithObject:@"dupeguru"]];
|
||||||
|
[op setTitle:@"Select a results file to load"];
|
||||||
|
if ([op runModal] == NSOKButton) {
|
||||||
|
NSString *filename = [[op filenames] objectAtIndex:0];
|
||||||
|
[py loadResultsFrom:filename];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (IBAction)markAll:(id)sender
|
- (IBAction)markAll:(id)sender
|
||||||
{
|
{
|
||||||
[py markAll];
|
[py markAll];
|
||||||
@@ -247,7 +285,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
NSString *directory = [[op filenames] objectAtIndex:0];
|
NSString *directory = [[op filenames] objectAtIndex:0];
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
[py setRemoveEmptyFolders:[ud objectForKey:@"removeEmptyFolders"]];
|
[py setRemoveEmptyFolders:n2b([ud objectForKey:@"removeEmptyFolders"])];
|
||||||
[py copyOrMove:b2n(NO) markedTo:directory recreatePath:[ud objectForKey:@"recreatePathType"]];
|
[py copyOrMove:b2n(NO) markedTo:directory recreatePath:[ud objectForKey:@"recreatePathType"]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,7 +316,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
|
|
||||||
- (IBAction)removeSelected:(id)sender
|
- (IBAction)removeSelected:(id)sender
|
||||||
{
|
{
|
||||||
[outline removeSelected];
|
[table removeSelected];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)renameSelected:(id)sender
|
- (IBAction)renameSelected:(id)sender
|
||||||
@@ -303,6 +341,17 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[preferencesPanel showWindow:sender];
|
[preferencesPanel showWindow:sender];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (IBAction)saveResults:(id)sender
|
||||||
|
{
|
||||||
|
NSSavePanel *sp = [NSSavePanel savePanel];
|
||||||
|
[sp setCanCreateDirectories:YES];
|
||||||
|
[sp setAllowedFileTypes:[NSArray arrayWithObject:@"dupeguru"]];
|
||||||
|
[sp setTitle:@"Select a file to save your results to"];
|
||||||
|
if ([sp runModal] == NSOKButton) {
|
||||||
|
[py saveResultsAs:[sp filename]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (IBAction)startDuplicateScan:(id)sender
|
- (IBAction)startDuplicateScan:(id)sender
|
||||||
{
|
{
|
||||||
// Virtual
|
// Virtual
|
||||||
@@ -390,8 +439,8 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ([lastAction isEqualTo:jobScan]) {
|
else if ([lastAction isEqualTo:jobScan]) {
|
||||||
NSInteger groupCount = [outline intProperty:@"children_count" valueAtPath:nil];
|
NSInteger rowCount = [[table py] numberOfRows];
|
||||||
if (groupCount == 0)
|
if (rowCount == 0)
|
||||||
[Dialogs showMessage:@"No duplicates found."];
|
[Dialogs showMessage:@"No duplicates found."];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
||||||
<data>
|
<data>
|
||||||
<int key="IBDocument.SystemTarget">1050</int>
|
<int key="IBDocument.SystemTarget">1050</int>
|
||||||
<string key="IBDocument.SystemVersion">10C540</string>
|
<string key="IBDocument.SystemVersion">10F569</string>
|
||||||
<string key="IBDocument.InterfaceBuilderVersion">740</string>
|
<string key="IBDocument.InterfaceBuilderVersion">788</string>
|
||||||
<string key="IBDocument.AppKitVersion">1038.25</string>
|
<string key="IBDocument.AppKitVersion">1038.29</string>
|
||||||
<string key="IBDocument.HIToolboxVersion">458.00</string>
|
<string key="IBDocument.HIToolboxVersion">461.00</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<string key="NS.object.0">740</string>
|
<string key="NS.object.0">788</string>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<object class="NSWindowTemplate" id="476890502">
|
<object class="NSWindowTemplate" id="476890502">
|
||||||
<int key="NSWindowStyleMask">155</int>
|
<int key="NSWindowStyleMask">155</int>
|
||||||
<int key="NSWindowBacking">2</int>
|
<int key="NSWindowBacking">2</int>
|
||||||
<string key="NSWindowRect">{{33, 261}, {451, 161}}</string>
|
<string key="NSWindowRect">{{33, 261}, {451, 146}}</string>
|
||||||
<int key="NSWTFlags">-260571136</int>
|
<int key="NSWTFlags">-260571136</int>
|
||||||
<string key="NSWindowTitle">Details of Selected File</string>
|
<string key="NSWindowTitle">Details of Selected File</string>
|
||||||
<object class="NSMutableString" key="NSWindowClass">
|
<object class="NSMutableString" key="NSWindowClass">
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
<characters key="NS.bytes">View</characters>
|
<characters key="NS.bytes">View</characters>
|
||||||
</object>
|
</object>
|
||||||
<string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
|
<string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
|
||||||
<string key="NSWindowContentMinSize">{451, 161}</string>
|
<string key="NSWindowContentMinSize">{451, 146}</string>
|
||||||
<object class="NSView" key="NSWindowView" id="1027711962">
|
<object class="NSView" key="NSWindowView" id="1027711962">
|
||||||
<reference key="NSNextResponder"/>
|
<reference key="NSNextResponder"/>
|
||||||
<int key="NSvFlags">256</int>
|
<int key="NSvFlags">256</int>
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<object class="NSTableView" id="251969872">
|
<object class="NSTableView" id="251969872">
|
||||||
<reference key="NSNextResponder" ref="488480549"/>
|
<reference key="NSNextResponder" ref="488480549"/>
|
||||||
<int key="NSvFlags">256</int>
|
<int key="NSvFlags">256</int>
|
||||||
<string key="NSFrameSize">{449, 143}</string>
|
<string key="NSFrameSize">{449, 128}</string>
|
||||||
<reference key="NSSuperview" ref="488480549"/>
|
<reference key="NSSuperview" ref="488480549"/>
|
||||||
<int key="NSTag">2</int>
|
<int key="NSTag">2</int>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
@@ -224,7 +224,7 @@
|
|||||||
<int key="NSTableViewDraggingDestinationStyle">0</int>
|
<int key="NSTableViewDraggingDestinationStyle">0</int>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<string key="NSFrame">{{1, 17}, {449, 143}}</string>
|
<string key="NSFrame">{{1, 17}, {449, 128}}</string>
|
||||||
<reference key="NSSuperview" ref="362108788"/>
|
<reference key="NSSuperview" ref="362108788"/>
|
||||||
<reference key="NSNextKeyView" ref="251969872"/>
|
<reference key="NSNextKeyView" ref="251969872"/>
|
||||||
<reference key="NSDocView" ref="251969872"/>
|
<reference key="NSDocView" ref="251969872"/>
|
||||||
@@ -266,7 +266,7 @@
|
|||||||
</object>
|
</object>
|
||||||
<reference ref="717380566"/>
|
<reference ref="717380566"/>
|
||||||
</object>
|
</object>
|
||||||
<string key="NSFrameSize">{451, 161}</string>
|
<string key="NSFrameSize">{451, 146}</string>
|
||||||
<reference key="NSSuperview" ref="1027711962"/>
|
<reference key="NSSuperview" ref="1027711962"/>
|
||||||
<reference key="NSNextKeyView" ref="488480549"/>
|
<reference key="NSNextKeyView" ref="488480549"/>
|
||||||
<int key="NSsFlags">530</int>
|
<int key="NSsFlags">530</int>
|
||||||
@@ -278,12 +278,13 @@
|
|||||||
<bytes key="NSScrollAmts">QSAAAEEgAABBgAAAQYAAAA</bytes>
|
<bytes key="NSScrollAmts">QSAAAEEgAABBgAAAQYAAAA</bytes>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<string key="NSFrameSize">{451, 161}</string>
|
<string key="NSFrameSize">{451, 146}</string>
|
||||||
<reference key="NSSuperview"/>
|
<reference key="NSSuperview"/>
|
||||||
</object>
|
</object>
|
||||||
<string key="NSScreenRect">{{0, 0}, {1024, 746}}</string>
|
<string key="NSScreenRect">{{0, 0}, {1024, 746}}</string>
|
||||||
<string key="NSMinSize">{451, 177}</string>
|
<string key="NSMinSize">{451, 162}</string>
|
||||||
<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
|
<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
|
||||||
|
<string key="NSFrameAutosaveName">DetailsPanel</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBObjectContainer" key="IBDocument.Objects">
|
<object class="IBObjectContainer" key="IBDocument.Objects">
|
||||||
@@ -497,12 +498,12 @@
|
|||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<string>{{109, 656}, {451, 161}}</string>
|
<string>{{109, 671}, {451, 146}}</string>
|
||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<string>{{109, 656}, {451, 161}}</string>
|
<string>{{109, 671}, {451, 146}}</string>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<string>{451, 161}</string>
|
<string>{451, 146}</string>
|
||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
@@ -536,11 +537,18 @@
|
|||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
<string key="className">DetailsPanel</string>
|
<string key="className">DetailsPanel</string>
|
||||||
<string key="superclassName">NSWindowController</string>
|
<string key="superclassName">HSWindowController</string>
|
||||||
<object class="NSMutableDictionary" key="outlets">
|
<object class="NSMutableDictionary" key="outlets">
|
||||||
<string key="NS.key.0">detailsTable</string>
|
<string key="NS.key.0">detailsTable</string>
|
||||||
<string key="NS.object.0">NSTableView</string>
|
<string key="NS.object.0">NSTableView</string>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
|
||||||
|
<string key="NS.key.0">detailsTable</string>
|
||||||
|
<object class="IBToOneOutletInfo" key="NS.object.0">
|
||||||
|
<string key="name">detailsTable</string>
|
||||||
|
<string key="candidateClassName">NSTableView</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBProjectSource</string>
|
<string key="majorKey">IBProjectSource</string>
|
||||||
<string key="minorKey">../base/DetailsPanel.h</string>
|
<string key="minorKey">../base/DetailsPanel.h</string>
|
||||||
@@ -548,7 +556,7 @@
|
|||||||
</object>
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
<string key="className">DetailsPanel</string>
|
<string key="className">DetailsPanel</string>
|
||||||
<string key="superclassName">NSWindowController</string>
|
<string key="superclassName">HSWindowController</string>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBUserSource</string>
|
<string key="majorKey">IBUserSource</string>
|
||||||
<string key="minorKey"/>
|
<string key="minorKey"/>
|
||||||
@@ -562,6 +570,32 @@
|
|||||||
<string key="minorKey"/>
|
<string key="minorKey"/>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBPartialClassDescription">
|
||||||
|
<string key="className">HSWindowController</string>
|
||||||
|
<string key="superclassName">NSWindowController</string>
|
||||||
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
|
<string key="majorKey">IBProjectSource</string>
|
||||||
|
<string key="minorKey">../controllers/HSWindowController.h</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
|
<object class="IBPartialClassDescription">
|
||||||
|
<string key="className">NSObject</string>
|
||||||
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
|
<string key="majorKey">IBProjectSource</string>
|
||||||
|
<string key="minorKey">../views/HSOutlineView.h</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
|
<object class="IBPartialClassDescription">
|
||||||
|
<string key="className">NSObject</string>
|
||||||
|
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="111130406">
|
||||||
|
<string key="majorKey">IBProjectSource</string>
|
||||||
|
<string key="minorKey">../views/NSTableViewAdditions.h</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
|
<object class="IBPartialClassDescription">
|
||||||
|
<string key="className">NSTableView</string>
|
||||||
|
<reference key="sourceIdentifier" ref="111130406"/>
|
||||||
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
|
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
@@ -1029,6 +1063,13 @@
|
|||||||
<string key="NS.key.0">showWindow:</string>
|
<string key="NS.key.0">showWindow:</string>
|
||||||
<string key="NS.object.0">id</string>
|
<string key="NS.object.0">id</string>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="actionInfosByName">
|
||||||
|
<string key="NS.key.0">showWindow:</string>
|
||||||
|
<object class="IBActionInfo" key="NS.object.0">
|
||||||
|
<string key="name">showWindow:</string>
|
||||||
|
<string key="candidateClassName">id</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBFrameworkSource</string>
|
<string key="majorKey">IBFrameworkSource</string>
|
||||||
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
||||||
@@ -1037,6 +1078,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<int key="IBDocument.localizationMode">0</int>
|
<int key="IBDocument.localizationMode">0</int>
|
||||||
|
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
|
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
||||||
<integer value="1050" key="NS.object.0"/>
|
<integer value="1050" key="NS.object.0"/>
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
||||||
<data>
|
<data>
|
||||||
<int key="IBDocument.SystemTarget">1050</int>
|
<int key="IBDocument.SystemTarget">1050</int>
|
||||||
<string key="IBDocument.SystemVersion">10C540</string>
|
<string key="IBDocument.SystemVersion">10F569</string>
|
||||||
<string key="IBDocument.InterfaceBuilderVersion">740</string>
|
<string key="IBDocument.InterfaceBuilderVersion">788</string>
|
||||||
<string key="IBDocument.AppKitVersion">1038.25</string>
|
<string key="IBDocument.AppKitVersion">1038.29</string>
|
||||||
<string key="IBDocument.HIToolboxVersion">458.00</string>
|
<string key="IBDocument.HIToolboxVersion">461.00</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<string key="NS.object.0">740</string>
|
<string key="NS.object.0">788</string>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
@@ -428,6 +428,7 @@
|
|||||||
<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
|
<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
|
||||||
<string key="NSMinSize">{369, 291}</string>
|
<string key="NSMinSize">{369, 291}</string>
|
||||||
<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
|
<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
|
||||||
|
<string key="NSFrameAutosaveName">DirectoryPanel</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBObjectContainer" key="IBDocument.Objects">
|
<object class="IBObjectContainer" key="IBDocument.Objects">
|
||||||
@@ -869,6 +870,35 @@
|
|||||||
<string>id</string>
|
<string>id</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="actionInfosByName">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<object class="NSArray" key="dict.sortedKeys">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<string>askForDirectory:</string>
|
||||||
|
<string>popupAddDirectoryMenu:</string>
|
||||||
|
<string>removeSelectedDirectory:</string>
|
||||||
|
<string>toggleVisible:</string>
|
||||||
|
</object>
|
||||||
|
<object class="NSMutableArray" key="dict.values">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<object class="IBActionInfo">
|
||||||
|
<string key="name">askForDirectory:</string>
|
||||||
|
<string key="candidateClassName">id</string>
|
||||||
|
</object>
|
||||||
|
<object class="IBActionInfo">
|
||||||
|
<string key="name">popupAddDirectoryMenu:</string>
|
||||||
|
<string key="candidateClassName">id</string>
|
||||||
|
</object>
|
||||||
|
<object class="IBActionInfo">
|
||||||
|
<string key="name">removeSelectedDirectory:</string>
|
||||||
|
<string key="candidateClassName">id</string>
|
||||||
|
</object>
|
||||||
|
<object class="IBActionInfo">
|
||||||
|
<string key="name">toggleVisible:</string>
|
||||||
|
<string key="candidateClassName">id</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="NSMutableDictionary" key="outlets">
|
<object class="NSMutableDictionary" key="outlets">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
<object class="NSArray" key="dict.sortedKeys">
|
<object class="NSArray" key="dict.sortedKeys">
|
||||||
@@ -884,6 +914,30 @@
|
|||||||
<string>NSButton</string>
|
<string>NSButton</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<object class="NSArray" key="dict.sortedKeys">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<string>addButtonPopUp</string>
|
||||||
|
<string>outlineView</string>
|
||||||
|
<string>removeButton</string>
|
||||||
|
</object>
|
||||||
|
<object class="NSMutableArray" key="dict.values">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<object class="IBToOneOutletInfo">
|
||||||
|
<string key="name">addButtonPopUp</string>
|
||||||
|
<string key="candidateClassName">NSPopUpButton</string>
|
||||||
|
</object>
|
||||||
|
<object class="IBToOneOutletInfo">
|
||||||
|
<string key="name">outlineView</string>
|
||||||
|
<string key="candidateClassName">HSOutlineView</string>
|
||||||
|
</object>
|
||||||
|
<object class="IBToOneOutletInfo">
|
||||||
|
<string key="name">removeButton</string>
|
||||||
|
<string key="candidateClassName">NSButton</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBProjectSource</string>
|
<string key="majorKey">IBProjectSource</string>
|
||||||
<string key="minorKey">../base/DirectoryPanel.h</string>
|
<string key="minorKey">../base/DirectoryPanel.h</string>
|
||||||
@@ -1437,6 +1491,13 @@
|
|||||||
<string key="NS.key.0">showWindow:</string>
|
<string key="NS.key.0">showWindow:</string>
|
||||||
<string key="NS.object.0">id</string>
|
<string key="NS.object.0">id</string>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="actionInfosByName">
|
||||||
|
<string key="NS.key.0">showWindow:</string>
|
||||||
|
<object class="IBActionInfo" key="NS.object.0">
|
||||||
|
<string key="name">showWindow:</string>
|
||||||
|
<string key="candidateClassName">id</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBFrameworkSource</string>
|
<string key="majorKey">IBFrameworkSource</string>
|
||||||
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
||||||
@@ -1445,6 +1506,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<int key="IBDocument.localizationMode">0</int>
|
<int key="IBDocument.localizationMode">0</int>
|
||||||
|
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
|
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
||||||
<integer value="1050" key="NS.object.0"/>
|
<integer value="1050" key="NS.object.0"/>
|
||||||
@@ -1460,5 +1522,18 @@
|
|||||||
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
||||||
<string key="IBDocument.LastKnownRelativeProjectPath">../../se/dupeguru.xcodeproj</string>
|
<string key="IBDocument.LastKnownRelativeProjectPath">../../se/dupeguru.xcodeproj</string>
|
||||||
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
||||||
|
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<object class="NSArray" key="dict.sortedKeys">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<string>NSMenuCheckmark</string>
|
||||||
|
<string>NSMenuMixedState</string>
|
||||||
|
</object>
|
||||||
|
<object class="NSMutableArray" key="dict.values">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<string>{9, 8}</string>
|
||||||
|
<string>{7, 2}</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
</data>
|
</data>
|
||||||
</archive>
|
</archive>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -28,6 +28,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[d setObject:b2n(NO) forKey:@"matchSimilarWords"];
|
[d setObject:b2n(NO) forKey:@"matchSimilarWords"];
|
||||||
[d setObject:b2n(YES) forKey:@"mixFileKind"];
|
[d setObject:b2n(YES) forKey:@"mixFileKind"];
|
||||||
[d setObject:b2n(NO) forKey:@"useRegexpFilter"];
|
[d setObject:b2n(NO) forKey:@"useRegexpFilter"];
|
||||||
|
[d setObject:b2n(NO) forKey:@"ignoreHardlinkMatches"];
|
||||||
[d setObject:b2n(NO) forKey:@"removeEmptyFolders"];
|
[d setObject:b2n(NO) forKey:@"removeEmptyFolders"];
|
||||||
[d setObject:b2n(NO) forKey:@"debug"];
|
[d setObject:b2n(NO) forKey:@"debug"];
|
||||||
[d setObject:b2n(NO) forKey:@"scanTagTrack"];
|
[d setObject:b2n(NO) forKey:@"scanTagTrack"];
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>hsft</string>
|
<string>hsft</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>5.8.1</string>
|
<string>5.10.0</string>
|
||||||
<key>NSMainNibFile</key>
|
<key>NSMainNibFile</key>
|
||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
[super awakeFromNib];
|
[super awakeFromNib];
|
||||||
[[self window] setTitle:@"dupeGuru Music Edition"];
|
[[self window] setTitle:@"dupeGuru Music Edition"];
|
||||||
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,7)];
|
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,6)];
|
||||||
[deltaColumns removeIndex:6];
|
[deltaColumns removeIndex:6];
|
||||||
[outline setDeltaColumns:deltaColumns];
|
[table setDeltaColumns:deltaColumns];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
@@ -38,13 +38,15 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[columnsOrder addObject:@"2"];
|
[columnsOrder addObject:@"2"];
|
||||||
[columnsOrder addObject:@"3"];
|
[columnsOrder addObject:@"3"];
|
||||||
[columnsOrder addObject:@"4"];
|
[columnsOrder addObject:@"4"];
|
||||||
[columnsOrder addObject:@"16"];
|
[columnsOrder addObject:@"6"];
|
||||||
|
[columnsOrder addObject:@"15"];
|
||||||
NSMutableDictionary *columnsWidth = [NSMutableDictionary dictionary];
|
NSMutableDictionary *columnsWidth = [NSMutableDictionary dictionary];
|
||||||
[columnsWidth setObject:i2n(214) forKey:@"0"];
|
[columnsWidth setObject:i2n(235) forKey:@"0"];
|
||||||
[columnsWidth setObject:i2n(63) forKey:@"2"];
|
[columnsWidth setObject:i2n(63) forKey:@"2"];
|
||||||
[columnsWidth setObject:i2n(50) forKey:@"3"];
|
[columnsWidth setObject:i2n(50) forKey:@"3"];
|
||||||
[columnsWidth setObject:i2n(50) forKey:@"4"];
|
[columnsWidth setObject:i2n(50) forKey:@"4"];
|
||||||
[columnsWidth setObject:i2n(57) forKey:@"16"];
|
[columnsWidth setObject:i2n(40) forKey:@"6"];
|
||||||
|
[columnsWidth setObject:i2n(57) forKey:@"15"];
|
||||||
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
|
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,11 +68,10 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[_py enable:[ud objectForKey:@"scanTagYear"] scanForTag:@"year"];
|
[_py enable:[ud objectForKey:@"scanTagYear"] scanForTag:@"year"];
|
||||||
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
|
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
|
||||||
[_py setWordWeighting:[ud objectForKey:@"wordWeighting"]];
|
[_py setWordWeighting:[ud objectForKey:@"wordWeighting"]];
|
||||||
[_py setMixFileKind:[ud objectForKey:@"mixFileKind"]];
|
[_py setMixFileKind:n2b([ud objectForKey:@"mixFileKind"])];
|
||||||
|
[_py setIgnoreHardlinkMatches:n2b([ud objectForKey:@"ignoreHardlinkMatches"])];
|
||||||
[_py setMatchSimilarWords:[ud objectForKey:@"matchSimilarWords"]];
|
[_py setMatchSimilarWords:[ud objectForKey:@"matchSimilarWords"]];
|
||||||
NSInteger r = n2i([py doScan]);
|
NSInteger r = n2i([py doScan]);
|
||||||
if (r == 1)
|
|
||||||
[Dialogs showMessage:@"You cannot make a duplicate scan with only reference directories."];
|
|
||||||
if (r == 3)
|
if (r == 3)
|
||||||
{
|
{
|
||||||
[Dialogs showMessage:@"The selected directories contain no scannable file."];
|
[Dialogs showMessage:@"The selected directories contain no scannable file."];
|
||||||
@@ -96,18 +97,17 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[_resultColumns addObject:brCol];
|
[_resultColumns addObject:brCol];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Sample Rate" width:60 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Sample Rate" width:60 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Kind" width:40 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Kind" width:40 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Creation" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Modification" width:120 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Modification" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Title" width:120 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:9 title:@"Title" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:9 title:@"Artist" width:120 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:10 title:@"Artist" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:10 title:@"Album" width:120 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:11 title:@"Album" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:11 title:@"Genre" width:80 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:12 title:@"Genre" width:80 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:12 title:@"Year" width:40 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:13 title:@"Year" width:40 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:13 title:@"Track Number" width:40 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:14 title:@"Track Number" width:40 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:14 title:@"Comment" width:120 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:15 title:@"Comment" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:15 title:@"Match %" width:57 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:16 title:@"Match %" width:57 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:16 title:@"Words Used" width:120 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:17 title:@"Words Used" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:17 title:@"Dupe Count" width:80 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:18 title:@"Dupe Count" width:80 refCol:refCol]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Notifications */
|
/* Notifications */
|
||||||
|
|||||||
@@ -8,14 +8,19 @@ from hscommon.cocoa import signature
|
|||||||
|
|
||||||
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
|
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
|
||||||
from core_me.app_cocoa import DupeGuruME
|
from core_me.app_cocoa import DupeGuruME
|
||||||
from core.scanner import (SCAN_TYPE_FILENAME, SCAN_TYPE_FIELDS, SCAN_TYPE_FIELDS_NO_ORDER,
|
from core.scanner import ScanType
|
||||||
SCAN_TYPE_TAG, SCAN_TYPE_CONTENT, SCAN_TYPE_CONTENT_AUDIO)
|
|
||||||
|
|
||||||
# Fix py2app imports which chokes on relative imports and other stuff
|
# Fix py2app imports which chokes on relative imports and other stuff
|
||||||
from core_me import app_cocoa, data, fs, scanner
|
import core_me.app_cocoa, core_me.data, core_me.fs, core_me.scanner
|
||||||
|
import hsaudiotag.aiff, hsaudiotag.flac, hsaudiotag.genres, hsaudiotag.id3v1,\
|
||||||
|
hsaudiotag.id3v2, hsaudiotag.mp4, hsaudiotag.mpeg, hsaudiotag.ogg, hsaudiotag.wma
|
||||||
from hsaudiotag import aiff, flac, genres, id3v1, id3v2, mp4, mpeg, ogg, wma
|
from hsaudiotag import aiff, flac, genres, id3v1, id3v2, mp4, mpeg, ogg, wma
|
||||||
from lxml import etree, _elementpath
|
import hsutil.conflict
|
||||||
|
import core.engine, core.fs, core.app
|
||||||
|
import xml.etree.ElementPath
|
||||||
import gzip
|
import gzip
|
||||||
|
import aem.kae
|
||||||
|
import appscript.defaultterminology
|
||||||
|
|
||||||
class PyDupeGuru(PyDupeGuruBase):
|
class PyDupeGuru(PyDupeGuruBase):
|
||||||
def init(self):
|
def init(self):
|
||||||
@@ -41,12 +46,12 @@ class PyDupeGuru(PyDupeGuruBase):
|
|||||||
def setScanType_(self, scan_type):
|
def setScanType_(self, scan_type):
|
||||||
try:
|
try:
|
||||||
self.py.scanner.scan_type = [
|
self.py.scanner.scan_type = [
|
||||||
SCAN_TYPE_FILENAME,
|
ScanType.Filename,
|
||||||
SCAN_TYPE_FIELDS,
|
ScanType.Fields,
|
||||||
SCAN_TYPE_FIELDS_NO_ORDER,
|
ScanType.FieldsNoOrder,
|
||||||
SCAN_TYPE_TAG,
|
ScanType.Tag,
|
||||||
SCAN_TYPE_CONTENT,
|
ScanType.Contents,
|
||||||
SCAN_TYPE_CONTENT_AUDIO
|
ScanType.ContentsAudio,
|
||||||
][scan_type]
|
][scan_type]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
CE0A0C001175A1C000DCA3C6 /* HSTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0A0BFF1175A1C000DCA3C6 /* HSTable.m */; };
|
CE0A0C001175A1C000DCA3C6 /* HSTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0A0BFF1175A1C000DCA3C6 /* HSTable.m */; };
|
||||||
CE0A0C041175A1DE00DCA3C6 /* ProblemDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0A0C021175A1DE00DCA3C6 /* ProblemDialog.m */; };
|
CE0A0C041175A1DE00DCA3C6 /* ProblemDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0A0C021175A1DE00DCA3C6 /* ProblemDialog.m */; };
|
||||||
CE0A0C061175A24800DCA3C6 /* ProblemDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE0A0C051175A24800DCA3C6 /* ProblemDialog.xib */; };
|
CE0A0C061175A24800DCA3C6 /* ProblemDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE0A0C051175A24800DCA3C6 /* ProblemDialog.xib */; };
|
||||||
CE0B3D6711243F83009A7A30 /* ResultOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0B3D6611243F83009A7A30 /* ResultOutline.m */; };
|
|
||||||
CE1425890AFB718500BD5167 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
|
CE1425890AFB718500BD5167 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
|
||||||
CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
|
CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
|
||||||
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
|
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
|
||||||
@@ -44,7 +43,6 @@
|
|||||||
CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */; };
|
CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */; };
|
||||||
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */; };
|
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */; };
|
||||||
CE4B59C91119919700C06C9E /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C61119919700C06C9E /* progress.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 */; };
|
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE10FC6C12E00EC695D /* Dialogs.m */; };
|
||||||
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */; };
|
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */; };
|
||||||
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE70FC6C12E00EC695D /* ProgressController.m */; };
|
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE70FC6C12E00EC695D /* ProgressController.m */; };
|
||||||
@@ -55,12 +53,15 @@
|
|||||||
CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E160FC6C19300EC695D /* AppDelegate.m */; };
|
CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E160FC6C19300EC695D /* AppDelegate.m */; };
|
||||||
CE515E1E0FC6C19300EC695D /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E190FC6C19300EC695D /* DirectoryPanel.m */; };
|
CE515E1E0FC6C19300EC695D /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E190FC6C19300EC695D /* DirectoryPanel.m */; };
|
||||||
CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E1C0FC6C19300EC695D /* ResultWindow.m */; };
|
CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E1C0FC6C19300EC695D /* ResultWindow.m */; };
|
||||||
|
CE578303124DFC660004769C /* HSTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE578302124DFC660004769C /* HSTableView.m */; };
|
||||||
CE6032C00FE6784C007E33FF /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE6032BF0FE6784C007E33FF /* DetailsPanel.m */; };
|
CE6032C00FE6784C007E33FF /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE6032BF0FE6784C007E33FF /* DetailsPanel.m */; };
|
||||||
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE68EE6609ABC48000971085 /* DirectoryPanel.m */; };
|
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE68EE6609ABC48000971085 /* DirectoryPanel.m */; };
|
||||||
CE6E0E9F1054EB97008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0E9E1054EB97008D9390 /* dsa_pub.pem */; };
|
CE6E0E9F1054EB97008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0E9E1054EB97008D9390 /* dsa_pub.pem */; };
|
||||||
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
|
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
|
||||||
CE900AD2109B238600754048 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD1109B238600754048 /* Preferences.xib */; };
|
CE900AD2109B238600754048 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD1109B238600754048 /* Preferences.xib */; };
|
||||||
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD6109B2A9B00754048 /* MainMenu.xib */; };
|
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD6109B2A9B00754048 /* MainMenu.xib */; };
|
||||||
|
CEB14D29124DFC2800FA7481 /* ResultTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CEB14D28124DFC2800FA7481 /* ResultTable.m */; };
|
||||||
|
CECC563B12144A9000ABF262 /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CECC563912144A9000ABF262 /* registration.xib */; };
|
||||||
CEDF07A3112493B200EE5BC0 /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDF07A2112493B200EE5BC0 /* StatsLabel.m */; };
|
CEDF07A3112493B200EE5BC0 /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDF07A2112493B200EE5BC0 /* StatsLabel.m */; };
|
||||||
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
|
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
|
||||||
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; };
|
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; };
|
||||||
@@ -118,9 +119,6 @@
|
|||||||
CE0A0C031175A1DE00DCA3C6 /* PyProblemDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyProblemDialog.h; path = ../base/PyProblemDialog.h; sourceTree = SOURCE_ROOT; };
|
CE0A0C031175A1DE00DCA3C6 /* PyProblemDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyProblemDialog.h; path = ../base/PyProblemDialog.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE0A0C051175A24800DCA3C6 /* ProblemDialog.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ProblemDialog.xib; path = ../base/xib/ProblemDialog.xib; sourceTree = SOURCE_ROOT; };
|
CE0A0C051175A24800DCA3C6 /* ProblemDialog.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ProblemDialog.xib; path = ../base/xib/ProblemDialog.xib; sourceTree = SOURCE_ROOT; };
|
||||||
CE0A0C131175A28100DCA3C6 /* PyTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyTable.h; sourceTree = "<group>"; };
|
CE0A0C131175A28100DCA3C6 /* PyTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyTable.h; sourceTree = "<group>"; };
|
||||||
CE0B3D6411243F83009A7A30 /* PyResultTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTree.h; path = ../base/PyResultTree.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE0B3D6511243F83009A7A30 /* ResultOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultOutline.h; path = ../base/ResultOutline.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE0B3D6611243F83009A7A30 /* ResultOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultOutline.m; path = ../base/ResultOutline.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE1425880AFB718500BD5167 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
|
CE1425880AFB718500BD5167 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
|
||||||
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; 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; };
|
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -133,7 +131,6 @@
|
|||||||
CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; 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>"; };
|
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>"; };
|
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; };
|
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; };
|
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; };
|
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -157,6 +154,8 @@
|
|||||||
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDupeGuru.h; path = ../base/PyDupeGuru.h; sourceTree = SOURCE_ROOT; };
|
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDupeGuru.h; path = ../base/PyDupeGuru.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE515E1B0FC6C19300EC695D /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultWindow.h; path = ../base/ResultWindow.h; sourceTree = SOURCE_ROOT; };
|
CE515E1B0FC6C19300EC695D /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultWindow.h; path = ../base/ResultWindow.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE515E1C0FC6C19300EC695D /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultWindow.m; path = ../base/ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
CE515E1C0FC6C19300EC695D /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultWindow.m; path = ../base/ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE578301124DFC660004769C /* HSTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSTableView.h; path = ../../cocoalib/views/HSTableView.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE578302124DFC660004769C /* HSTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSTableView.m; path = ../../cocoalib/views/HSTableView.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE6032BE0FE6784C007E33FF /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DetailsPanel.h; path = ../base/DetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
CE6032BE0FE6784C007E33FF /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DetailsPanel.h; path = ../base/DetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE6032BF0FE6784C007E33FF /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DetailsPanel.m; path = ../base/DetailsPanel.m; sourceTree = SOURCE_ROOT; };
|
CE6032BF0FE6784C007E33FF /* 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; };
|
CE68EE6509ABC48000971085 /* DirectoryPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DirectoryPanel.h; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -165,6 +164,10 @@
|
|||||||
CE848A1809DD85810004CB44 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
|
CE848A1809DD85810004CB44 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
|
||||||
CE900AD1109B238600754048 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; };
|
CE900AD1109B238600754048 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; };
|
||||||
CE900AD6109B2A9B00754048 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; };
|
CE900AD6109B2A9B00754048 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; };
|
||||||
|
CEB14D26124DFC2800FA7481 /* PyResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTable.h; path = ../base/PyResultTable.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CEB14D27124DFC2800FA7481 /* ResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultTable.h; path = ../base/ResultTable.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CEB14D28124DFC2800FA7481 /* ResultTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultTable.m; path = ../base/ResultTable.m; sourceTree = SOURCE_ROOT; };
|
||||||
|
CECC563A12144A9000ABF262 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = ../../cocoalib/en.lproj/registration.xib; sourceTree = SOURCE_ROOT; };
|
||||||
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
||||||
CEDF07A0112493B200EE5BC0 /* PyStatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyStatsLabel.h; path = ../base/PyStatsLabel.h; sourceTree = SOURCE_ROOT; };
|
CEDF07A0112493B200EE5BC0 /* PyStatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyStatsLabel.h; path = ../base/PyStatsLabel.h; sourceTree = SOURCE_ROOT; };
|
||||||
CEDF07A1112493B200EE5BC0 /* StatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StatsLabel.h; path = ../base/StatsLabel.h; sourceTree = SOURCE_ROOT; };
|
CEDF07A1112493B200EE5BC0 /* StatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StatsLabel.h; path = ../base/StatsLabel.h; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -306,6 +309,8 @@
|
|||||||
CE003CBF11242D00004B0AA7 /* views */ = {
|
CE003CBF11242D00004B0AA7 /* views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CE578301124DFC660004769C /* HSTableView.h */,
|
||||||
|
CE578302124DFC660004769C /* HSTableView.m */,
|
||||||
CE003CC011242D00004B0AA7 /* HSOutlineView.h */,
|
CE003CC011242D00004B0AA7 /* HSOutlineView.h */,
|
||||||
CE003CC111242D00004B0AA7 /* HSOutlineView.m */,
|
CE003CC111242D00004B0AA7 /* HSOutlineView.m */,
|
||||||
CE003CC211242D00004B0AA7 /* NSIndexPathAdditions.h */,
|
CE003CC211242D00004B0AA7 /* NSIndexPathAdditions.h */,
|
||||||
@@ -342,9 +347,9 @@
|
|||||||
CE4B59C41119919700C06C9E /* xib */ = {
|
CE4B59C41119919700C06C9E /* xib */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CECC563912144A9000ABF262 /* registration.xib */,
|
||||||
CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */,
|
CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */,
|
||||||
CE4B59C61119919700C06C9E /* progress.xib */,
|
CE4B59C61119919700C06C9E /* progress.xib */,
|
||||||
CE4B59C71119919700C06C9E /* registration.xib */,
|
|
||||||
);
|
);
|
||||||
name = xib;
|
name = xib;
|
||||||
path = ../../cocoalib/xib;
|
path = ../../cocoalib/xib;
|
||||||
@@ -383,6 +388,9 @@
|
|||||||
CE515E140FC6C17900EC695D /* dgbase */ = {
|
CE515E140FC6C17900EC695D /* dgbase */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CEB14D26124DFC2800FA7481 /* PyResultTable.h */,
|
||||||
|
CEB14D27124DFC2800FA7481 /* ResultTable.h */,
|
||||||
|
CEB14D28124DFC2800FA7481 /* ResultTable.m */,
|
||||||
CE003CCD11242D2C004B0AA7 /* DirectoryOutline.h */,
|
CE003CCD11242D2C004B0AA7 /* DirectoryOutline.h */,
|
||||||
CE003CCE11242D2C004B0AA7 /* DirectoryOutline.m */,
|
CE003CCE11242D2C004B0AA7 /* DirectoryOutline.m */,
|
||||||
CE003CCF11242D2C004B0AA7 /* PyDirectoryOutline.h */,
|
CE003CCF11242D2C004B0AA7 /* PyDirectoryOutline.h */,
|
||||||
@@ -397,14 +405,11 @@
|
|||||||
CE0A0C021175A1DE00DCA3C6 /* ProblemDialog.m */,
|
CE0A0C021175A1DE00DCA3C6 /* ProblemDialog.m */,
|
||||||
CE515E1B0FC6C19300EC695D /* ResultWindow.h */,
|
CE515E1B0FC6C19300EC695D /* ResultWindow.h */,
|
||||||
CE515E1C0FC6C19300EC695D /* ResultWindow.m */,
|
CE515E1C0FC6C19300EC695D /* ResultWindow.m */,
|
||||||
CE0B3D6511243F83009A7A30 /* ResultOutline.h */,
|
|
||||||
CE0B3D6611243F83009A7A30 /* ResultOutline.m */,
|
|
||||||
CEDF07A1112493B200EE5BC0 /* StatsLabel.h */,
|
CEDF07A1112493B200EE5BC0 /* StatsLabel.h */,
|
||||||
CEDF07A2112493B200EE5BC0 /* StatsLabel.m */,
|
CEDF07A2112493B200EE5BC0 /* StatsLabel.m */,
|
||||||
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */,
|
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */,
|
||||||
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */,
|
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */,
|
||||||
CE0A0C031175A1DE00DCA3C6 /* PyProblemDialog.h */,
|
CE0A0C031175A1DE00DCA3C6 /* PyProblemDialog.h */,
|
||||||
CE0B3D6411243F83009A7A30 /* PyResultTree.h */,
|
|
||||||
CEDF07A0112493B200EE5BC0 /* PyStatsLabel.h */,
|
CEDF07A0112493B200EE5BC0 /* PyStatsLabel.h */,
|
||||||
);
|
);
|
||||||
name = dgbase;
|
name = dgbase;
|
||||||
@@ -451,6 +456,13 @@
|
|||||||
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */;
|
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */;
|
||||||
compatibilityVersion = "Xcode 3.0";
|
compatibilityVersion = "Xcode 3.0";
|
||||||
hasScannedForEncodings = 1;
|
hasScannedForEncodings = 1;
|
||||||
|
knownRegions = (
|
||||||
|
English,
|
||||||
|
Japanese,
|
||||||
|
French,
|
||||||
|
German,
|
||||||
|
en,
|
||||||
|
);
|
||||||
mainGroup = 29B97314FDCFA39411CA2CEA /* dupeguru */;
|
mainGroup = 29B97314FDCFA39411CA2CEA /* dupeguru */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
@@ -478,8 +490,8 @@
|
|||||||
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */,
|
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */,
|
||||||
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */,
|
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */,
|
||||||
CE4B59C91119919700C06C9E /* progress.xib in Resources */,
|
CE4B59C91119919700C06C9E /* progress.xib in Resources */,
|
||||||
CE4B59CA1119919700C06C9E /* registration.xib in Resources */,
|
|
||||||
CE0A0C061175A24800DCA3C6 /* ProblemDialog.xib in Resources */,
|
CE0A0C061175A24800DCA3C6 /* ProblemDialog.xib in Resources */,
|
||||||
|
CECC563B12144A9000ABF262 /* registration.xib in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -514,15 +526,28 @@
|
|||||||
CE003CCB11242D00004B0AA7 /* NSIndexPathAdditions.m in Sources */,
|
CE003CCB11242D00004B0AA7 /* NSIndexPathAdditions.m in Sources */,
|
||||||
CE003CCC11242D00004B0AA7 /* NSTableViewAdditions.m in Sources */,
|
CE003CCC11242D00004B0AA7 /* NSTableViewAdditions.m in Sources */,
|
||||||
CE003CD011242D2C004B0AA7 /* DirectoryOutline.m in Sources */,
|
CE003CD011242D2C004B0AA7 /* DirectoryOutline.m in Sources */,
|
||||||
CE0B3D6711243F83009A7A30 /* ResultOutline.m in Sources */,
|
|
||||||
CEDF07A3112493B200EE5BC0 /* StatsLabel.m in Sources */,
|
CEDF07A3112493B200EE5BC0 /* StatsLabel.m in Sources */,
|
||||||
CE0A0C001175A1C000DCA3C6 /* HSTable.m in Sources */,
|
CE0A0C001175A1C000DCA3C6 /* HSTable.m in Sources */,
|
||||||
CE0A0C041175A1DE00DCA3C6 /* ProblemDialog.m in Sources */,
|
CE0A0C041175A1DE00DCA3C6 /* ProblemDialog.m in Sources */,
|
||||||
|
CEB14D29124DFC2800FA7481 /* ResultTable.m in Sources */,
|
||||||
|
CE578303124DFC660004769C /* HSTableView.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
CECC563912144A9000ABF262 /* registration.xib */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
CECC563A12144A9000ABF262 /* en */,
|
||||||
|
);
|
||||||
|
name = registration.xib;
|
||||||
|
path = ../../cocoalib/xib;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
C01FCF4C08A954540054247B /* release */ = {
|
C01FCF4C08A954540054247B /* release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
||||||
<data>
|
<data>
|
||||||
<int key="IBDocument.SystemTarget">1050</int>
|
<int key="IBDocument.SystemTarget">1050</int>
|
||||||
<string key="IBDocument.SystemVersion">10D573</string>
|
<string key="IBDocument.SystemVersion">10F569</string>
|
||||||
<string key="IBDocument.InterfaceBuilderVersion">740</string>
|
<string key="IBDocument.InterfaceBuilderVersion">788</string>
|
||||||
<string key="IBDocument.AppKitVersion">1038.29</string>
|
<string key="IBDocument.AppKitVersion">1038.29</string>
|
||||||
<string key="IBDocument.HIToolboxVersion">460.00</string>
|
<string key="IBDocument.HIToolboxVersion">461.00</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<string key="NS.object.0">740</string>
|
<string key="NS.object.0">788</string>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<object class="NSUserDefaultsController" id="579641073">
|
<object class="NSUserDefaultsController" id="579641073">
|
||||||
<object class="NSMutableArray" key="NSDeclaredKeys">
|
<object class="NSMutableArray" key="NSDeclaredKeys">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
<string>CustomCommand</string>
|
<string>ignoreHardlinkMatches</string>
|
||||||
</object>
|
</object>
|
||||||
<bool key="NSSharedInstance">YES</bool>
|
<bool key="NSSharedInstance">YES</bool>
|
||||||
</object>
|
</object>
|
||||||
@@ -698,7 +698,7 @@
|
|||||||
<object class="NSTextField" id="519483808">
|
<object class="NSTextField" id="519483808">
|
||||||
<reference key="NSNextResponder" ref="76055040"/>
|
<reference key="NSNextResponder" ref="76055040"/>
|
||||||
<int key="NSvFlags">292</int>
|
<int key="NSvFlags">292</int>
|
||||||
<string key="NSFrame">{{14, 192}, {85, 13}}</string>
|
<string key="NSFrame">{{14, 172}, {85, 13}}</string>
|
||||||
<reference key="NSSuperview" ref="76055040"/>
|
<reference key="NSSuperview" ref="76055040"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSTextFieldCell" key="NSCell" id="142188233">
|
<object class="NSTextFieldCell" key="NSCell" id="142188233">
|
||||||
@@ -714,7 +714,7 @@
|
|||||||
<object class="NSTextField" id="839713145">
|
<object class="NSTextField" id="839713145">
|
||||||
<reference key="NSNextResponder" ref="76055040"/>
|
<reference key="NSNextResponder" ref="76055040"/>
|
||||||
<int key="NSvFlags">292</int>
|
<int key="NSvFlags">292</int>
|
||||||
<string key="NSFrame">{{14, 164}, {324, 17}}</string>
|
<string key="NSFrame">{{14, 144}, {324, 17}}</string>
|
||||||
<reference key="NSSuperview" ref="76055040"/>
|
<reference key="NSSuperview" ref="76055040"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSTextFieldCell" key="NSCell" id="380716547">
|
<object class="NSTextFieldCell" key="NSCell" id="380716547">
|
||||||
@@ -730,7 +730,7 @@
|
|||||||
<object class="NSPopUpButton" id="1046542754">
|
<object class="NSPopUpButton" id="1046542754">
|
||||||
<reference key="NSNextResponder" ref="76055040"/>
|
<reference key="NSNextResponder" ref="76055040"/>
|
||||||
<int key="NSvFlags">292</int>
|
<int key="NSvFlags">292</int>
|
||||||
<string key="NSFrame">{{104, 185}, {234, 26}}</string>
|
<string key="NSFrame">{{104, 165}, {234, 26}}</string>
|
||||||
<reference key="NSSuperview" ref="76055040"/>
|
<reference key="NSSuperview" ref="76055040"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSPopUpButtonCell" key="NSCell" id="923770094">
|
<object class="NSPopUpButtonCell" key="NSCell" id="923770094">
|
||||||
@@ -800,7 +800,7 @@
|
|||||||
<object class="NSTextField" id="330569030">
|
<object class="NSTextField" id="330569030">
|
||||||
<reference key="NSNextResponder" ref="76055040"/>
|
<reference key="NSNextResponder" ref="76055040"/>
|
||||||
<int key="NSvFlags">266</int>
|
<int key="NSvFlags">266</int>
|
||||||
<string key="NSFrame">{{17, 142}, {326, 22}}</string>
|
<string key="NSFrame">{{17, 122}, {326, 22}}</string>
|
||||||
<reference key="NSSuperview" ref="76055040"/>
|
<reference key="NSSuperview" ref="76055040"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSTextFieldCell" key="NSCell" id="573680954">
|
<object class="NSTextFieldCell" key="NSCell" id="573680954">
|
||||||
@@ -827,6 +827,27 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSButton" id="1065764374">
|
||||||
|
<reference key="NSNextResponder" ref="76055040"/>
|
||||||
|
<int key="NSvFlags">256</int>
|
||||||
|
<string key="NSFrame">{{15, 195}, {265, 18}}</string>
|
||||||
|
<reference key="NSSuperview" ref="76055040"/>
|
||||||
|
<bool key="NSEnabled">YES</bool>
|
||||||
|
<object class="NSButtonCell" key="NSCell" id="621426332">
|
||||||
|
<int key="NSCellFlags">67239424</int>
|
||||||
|
<int key="NSCellFlags2">0</int>
|
||||||
|
<string key="NSContents">Ignore duplicates hardlinking to the same file</string>
|
||||||
|
<reference key="NSSupport" ref="26"/>
|
||||||
|
<reference key="NSControlView" ref="1065764374"/>
|
||||||
|
<int key="NSButtonFlags">1211912703</int>
|
||||||
|
<int key="NSButtonFlags2">2</int>
|
||||||
|
<reference key="NSAlternateImage" ref="150447483"/>
|
||||||
|
<string key="NSAlternateContents"/>
|
||||||
|
<string key="NSKeyEquivalent"/>
|
||||||
|
<int key="NSPeriodicDelay">200</int>
|
||||||
|
<int key="NSPeriodicInterval">25</int>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<string key="NSFrame">{{10, 33}, {360, 234}}</string>
|
<string key="NSFrame">{{10, 33}, {360, 234}}</string>
|
||||||
</object>
|
</object>
|
||||||
@@ -1461,6 +1482,22 @@
|
|||||||
</object>
|
</object>
|
||||||
<int key="connectionID">124</int>
|
<int key="connectionID">124</int>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBConnectionRecord">
|
||||||
|
<object class="IBBindingConnection" key="connection">
|
||||||
|
<string key="label">value: values.ignoreHardlinkMatches</string>
|
||||||
|
<reference key="source" ref="1065764374"/>
|
||||||
|
<reference key="destination" ref="579641073"/>
|
||||||
|
<object class="NSNibBindingConnector" key="connector">
|
||||||
|
<reference key="NSSource" ref="1065764374"/>
|
||||||
|
<reference key="NSDestination" ref="579641073"/>
|
||||||
|
<string key="NSLabel">value: values.ignoreHardlinkMatches</string>
|
||||||
|
<string key="NSBinding">value</string>
|
||||||
|
<string key="NSKeyPath">values.ignoreHardlinkMatches</string>
|
||||||
|
<int key="NSNibBindingConnectorVersion">2</int>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
|
<int key="connectionID">128</int>
|
||||||
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||||
<object class="NSArray" key="orderedObjects">
|
<object class="NSArray" key="orderedObjects">
|
||||||
@@ -1563,9 +1600,10 @@
|
|||||||
<object class="NSMutableArray" key="children">
|
<object class="NSMutableArray" key="children">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
<reference ref="836095588"/>
|
<reference ref="836095588"/>
|
||||||
<reference ref="1046542754"/>
|
<reference ref="1065764374"/>
|
||||||
<reference ref="519483808"/>
|
<reference ref="519483808"/>
|
||||||
<reference ref="839713145"/>
|
<reference ref="839713145"/>
|
||||||
|
<reference ref="1046542754"/>
|
||||||
<reference ref="330569030"/>
|
<reference ref="330569030"/>
|
||||||
</object>
|
</object>
|
||||||
<reference key="parent" ref="963172262"/>
|
<reference key="parent" ref="963172262"/>
|
||||||
@@ -2020,6 +2058,20 @@
|
|||||||
<reference key="object" ref="573680954"/>
|
<reference key="object" ref="573680954"/>
|
||||||
<reference key="parent" ref="330569030"/>
|
<reference key="parent" ref="330569030"/>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBObjectRecord">
|
||||||
|
<int key="objectID">125</int>
|
||||||
|
<reference key="object" ref="1065764374"/>
|
||||||
|
<object class="NSMutableArray" key="children">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<reference ref="621426332"/>
|
||||||
|
</object>
|
||||||
|
<reference key="parent" ref="76055040"/>
|
||||||
|
</object>
|
||||||
|
<object class="IBObjectRecord">
|
||||||
|
<int key="objectID">126</int>
|
||||||
|
<reference key="object" ref="621426332"/>
|
||||||
|
<reference key="parent" ref="1065764374"/>
|
||||||
|
</object>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSMutableDictionary" key="flattenedProperties">
|
<object class="NSMutableDictionary" key="flattenedProperties">
|
||||||
@@ -2045,6 +2097,9 @@
|
|||||||
<string>121.IBPluginDependency</string>
|
<string>121.IBPluginDependency</string>
|
||||||
<string>122.IBPluginDependency</string>
|
<string>122.IBPluginDependency</string>
|
||||||
<string>123.IBPluginDependency</string>
|
<string>123.IBPluginDependency</string>
|
||||||
|
<string>125.IBPluginDependency</string>
|
||||||
|
<string>125.ImportedFromIB2</string>
|
||||||
|
<string>126.IBPluginDependency</string>
|
||||||
<string>13.IBPluginDependency</string>
|
<string>13.IBPluginDependency</string>
|
||||||
<string>13.ImportedFromIB2</string>
|
<string>13.ImportedFromIB2</string>
|
||||||
<string>14.IBPluginDependency</string>
|
<string>14.IBPluginDependency</string>
|
||||||
@@ -2165,6 +2220,9 @@
|
|||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<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"/>
|
<boolean value="YES"/>
|
||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
@@ -2174,9 +2232,9 @@
|
|||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<string>{{318, 425}, {406, 326}}</string>
|
<string>{{555, 271}, {406, 326}}</string>
|
||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<string>{{318, 425}, {406, 326}}</string>
|
<string>{{555, 271}, {406, 326}}</string>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<string>{213, 107}</string>
|
<string>{213, 107}</string>
|
||||||
@@ -2275,7 +2333,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<nil key="sourceID"/>
|
<nil key="sourceID"/>
|
||||||
<int key="maxID">124</int>
|
<int key="maxID">128</int>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
<object class="IBClassDescriber" key="IBDocument.Classes">
|
||||||
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
||||||
@@ -2825,6 +2883,13 @@
|
|||||||
<string key="NS.key.0">showWindow:</string>
|
<string key="NS.key.0">showWindow:</string>
|
||||||
<string key="NS.object.0">id</string>
|
<string key="NS.object.0">id</string>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="actionInfosByName">
|
||||||
|
<string key="NS.key.0">showWindow:</string>
|
||||||
|
<object class="IBActionInfo" key="NS.object.0">
|
||||||
|
<string key="name">showWindow:</string>
|
||||||
|
<string key="candidateClassName">id</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBFrameworkSource</string>
|
<string key="majorKey">IBFrameworkSource</string>
|
||||||
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
||||||
@@ -2833,6 +2898,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<int key="IBDocument.localizationMode">0</int>
|
<int key="IBDocument.localizationMode">0</int>
|
||||||
|
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
|
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
||||||
<integer value="1050" key="NS.object.0"/>
|
<integer value="1050" key="NS.object.0"/>
|
||||||
@@ -2844,5 +2910,18 @@
|
|||||||
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
||||||
<string key="IBDocument.LastKnownRelativeProjectPath">../dupeguru.xcodeproj</string>
|
<string key="IBDocument.LastKnownRelativeProjectPath">../dupeguru.xcodeproj</string>
|
||||||
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
||||||
|
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<object class="NSArray" key="dict.sortedKeys">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<string>NSMenuCheckmark</string>
|
||||||
|
<string>NSMenuMixedState</string>
|
||||||
|
</object>
|
||||||
|
<object class="NSMutableArray" key="dict.values">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<string>{9, 8}</string>
|
||||||
|
<string>{7, 2}</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
</data>
|
</data>
|
||||||
</archive>
|
</archive>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[d setObject:[NSNumber numberWithBool:NO] forKey:@"matchScaled"];
|
[d setObject:[NSNumber numberWithBool:NO] forKey:@"matchScaled"];
|
||||||
[d setObject:[NSNumber numberWithBool:YES] forKey:@"mixFileKind"];
|
[d setObject:[NSNumber numberWithBool:YES] forKey:@"mixFileKind"];
|
||||||
[d setObject:[NSNumber numberWithBool:NO] forKey:@"useRegexpFilter"];
|
[d setObject:[NSNumber numberWithBool:NO] forKey:@"useRegexpFilter"];
|
||||||
|
[d setObject:[NSNumber numberWithBool:NO] forKey:@"ignoreHardlinkMatches"];
|
||||||
[d setObject:[NSNumber numberWithBool:NO] forKey:@"removeEmptyFolders"];
|
[d setObject:[NSNumber numberWithBool:NO] forKey:@"removeEmptyFolders"];
|
||||||
[d setObject:[NSNumber numberWithBool:NO] forKey:@"debug"];
|
[d setObject:[NSNumber numberWithBool:NO] forKey:@"debug"];
|
||||||
[d setObject:[NSArray array] forKey:@"recentDirectories"];
|
[d setObject:[NSArray array] forKey:@"recentDirectories"];
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>hsft</string>
|
<string>hsft</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.9.1</string>
|
<string>1.11.0</string>
|
||||||
<key>NSMainNibFile</key>
|
<key>NSMainNibFile</key>
|
||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
|
|||||||
@@ -20,10 +20,9 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
[super awakeFromNib];
|
[super awakeFromNib];
|
||||||
[[self window] setTitle:@"dupeGuru Picture Edition"];
|
[[self window] setTitle:@"dupeGuru Picture Edition"];
|
||||||
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,5)];
|
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndex:2];
|
||||||
[deltaColumns removeIndex:3];
|
[deltaColumns addIndex:5];
|
||||||
[deltaColumns removeIndex:4];
|
[table setDeltaColumns:deltaColumns];
|
||||||
[outline setDeltaColumns:deltaColumns];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
@@ -41,13 +40,13 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[columnsOrder addObject:@"1"];
|
[columnsOrder addObject:@"1"];
|
||||||
[columnsOrder addObject:@"2"];
|
[columnsOrder addObject:@"2"];
|
||||||
[columnsOrder addObject:@"4"];
|
[columnsOrder addObject:@"4"];
|
||||||
[columnsOrder addObject:@"7"];
|
[columnsOrder addObject:@"6"];
|
||||||
NSMutableDictionary *columnsWidth = [NSMutableDictionary dictionary];
|
NSMutableDictionary *columnsWidth = [NSMutableDictionary dictionary];
|
||||||
[columnsWidth setObject:i2n(121) forKey:@"0"];
|
[columnsWidth setObject:i2n(162) forKey:@"0"];
|
||||||
[columnsWidth setObject:i2n(120) forKey:@"1"];
|
[columnsWidth setObject:i2n(142) forKey:@"1"];
|
||||||
[columnsWidth setObject:i2n(63) forKey:@"2"];
|
[columnsWidth setObject:i2n(63) forKey:@"2"];
|
||||||
[columnsWidth setObject:i2n(73) forKey:@"4"];
|
[columnsWidth setObject:i2n(73) forKey:@"4"];
|
||||||
[columnsWidth setObject:i2n(58) forKey:@"7"];
|
[columnsWidth setObject:i2n(58) forKey:@"6"];
|
||||||
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
|
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,13 +60,12 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
PyDupeGuru *_py = (PyDupeGuru *)py;
|
PyDupeGuru *_py = (PyDupeGuru *)py;
|
||||||
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
|
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
|
||||||
[_py setMixFileKind:[ud objectForKey:@"mixFileKind"]];
|
[_py setMixFileKind:n2b([ud objectForKey:@"mixFileKind"])];
|
||||||
|
[_py setIgnoreHardlinkMatches:n2b([ud objectForKey:@"ignoreHardlinkMatches"])];
|
||||||
[_py setMatchScaled:[ud objectForKey:@"matchScaled"]];
|
[_py setMatchScaled:[ud objectForKey:@"matchScaled"]];
|
||||||
int r = n2i([py doScan]);
|
int r = n2i([py doScan]);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
[[ProgressController mainProgressController] hide];
|
[[ProgressController mainProgressController] hide];
|
||||||
if (r == 1)
|
|
||||||
[Dialogs showMessage:@"You cannot make a duplicate scan with only reference directories."];
|
|
||||||
if (r == 3)
|
if (r == 3)
|
||||||
{
|
{
|
||||||
[Dialogs showMessage:@"The selected directories contain no scannable file."];
|
[Dialogs showMessage:@"The selected directories contain no scannable file."];
|
||||||
@@ -92,9 +90,8 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[_resultColumns addObject:sizeCol];
|
[_resultColumns addObject:sizeCol];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:3 title:@"Kind" width:40 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:3 title:@"Kind" width:40 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:4 title:@"Dimensions" width:80 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:4 title:@"Dimensions" width:80 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Creation" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Modification" width:120 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Modification" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Match %" width:58 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Match %" width:58 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Dupe Count" width:80 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Dupe Count" width:80 refCol:refCol]];
|
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
@@ -8,9 +8,13 @@ from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
|
|||||||
from core_pe import app_cocoa as app_pe_cocoa
|
from core_pe import app_cocoa as app_pe_cocoa
|
||||||
|
|
||||||
# Fix py2app imports which chokes on relative imports and other stuff
|
# Fix py2app imports which chokes on relative imports and other stuff
|
||||||
from core_pe import block, cache, matchbase, data, _block_osx
|
import hsutil.conflict
|
||||||
from lxml import etree, _elementpath
|
import core.engine, core.fs, core.app
|
||||||
|
import core_pe.block, core_pe.cache, core_pe.matchbase, core_pe.data, core_pe._block_osx
|
||||||
|
import xml.etree.ElementPath
|
||||||
import gzip
|
import gzip
|
||||||
|
import aem.kae
|
||||||
|
import appscript.defaultterminology
|
||||||
|
|
||||||
class PyDupeGuru(PyDupeGuruBase):
|
class PyDupeGuru(PyDupeGuruBase):
|
||||||
def init(self):
|
def init(self):
|
||||||
@@ -23,10 +27,10 @@ class PyDupeGuru(PyDupeGuruBase):
|
|||||||
|
|
||||||
#---Information
|
#---Information
|
||||||
def getSelectedDupePath(self):
|
def getSelectedDupePath(self):
|
||||||
return unicode(self.py.selected_dupe_path())
|
return str(self.py.selected_dupe_path())
|
||||||
|
|
||||||
def getSelectedDupeRefPath(self):
|
def getSelectedDupeRefPath(self):
|
||||||
return unicode(self.py.selected_dupe_ref_path())
|
return str(self.py.selected_dupe_ref_path())
|
||||||
|
|
||||||
#---Properties
|
#---Properties
|
||||||
def setMatchScaled_(self,match_scaled):
|
def setMatchScaled_(self,match_scaled):
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
CE77C8A810946CE20078B0DB /* DetailsPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE77C8A710946CE20078B0DB /* DetailsPanel.xib */; };
|
CE77C8A810946CE20078B0DB /* DetailsPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE77C8A710946CE20078B0DB /* DetailsPanel.xib */; };
|
||||||
CE7AC9181119911200D02F6C /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */; };
|
CE7AC9181119911200D02F6C /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */; };
|
||||||
CE7AC9191119911200D02F6C /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9161119911200D02F6C /* progress.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 */; };
|
CE80DB2E0FC192D60086DCA6 /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB1C0FC192D60086DCA6 /* Dialogs.m */; };
|
||||||
CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB1E0FC192D60086DCA6 /* HSErrorReportWindow.m */; };
|
CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB1E0FC192D60086DCA6 /* HSErrorReportWindow.m */; };
|
||||||
CE80DB310FC192D60086DCA6 /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB220FC192D60086DCA6 /* ProgressController.m */; };
|
CE80DB310FC192D60086DCA6 /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB220FC192D60086DCA6 /* ProgressController.m */; };
|
||||||
@@ -41,7 +40,7 @@
|
|||||||
CE80DB8B0FC1951C0086DCA6 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */; };
|
CE80DB8B0FC1951C0086DCA6 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */; };
|
||||||
CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB890FC1951C0086DCA6 /* ResultWindow.m */; };
|
CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB890FC1951C0086DCA6 /* ResultWindow.m */; };
|
||||||
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
|
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
|
||||||
CE95865E112C516400F95FD2 /* ResultOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE95865B112C516400F95FD2 /* ResultOutline.m */; };
|
CE895D7B12144A7800E74705 /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE895D7912144A7800E74705 /* registration.xib */; };
|
||||||
CE95865F112C516400F95FD2 /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE95865D112C516400F95FD2 /* StatsLabel.m */; };
|
CE95865F112C516400F95FD2 /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE95865D112C516400F95FD2 /* StatsLabel.m */; };
|
||||||
CE9EA7561122C96C008CD2BC /* HSGUIController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7441122C96C008CD2BC /* HSGUIController.m */; };
|
CE9EA7561122C96C008CD2BC /* HSGUIController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7441122C96C008CD2BC /* HSGUIController.m */; };
|
||||||
CE9EA7571122C96C008CD2BC /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7461122C96C008CD2BC /* HSOutline.m */; };
|
CE9EA7571122C96C008CD2BC /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7461122C96C008CD2BC /* HSOutline.m */; };
|
||||||
@@ -55,6 +54,8 @@
|
|||||||
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECA899A09DB132E00A3D774 /* DetailsPanel.h */; };
|
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECA899A09DB132E00A3D774 /* DetailsPanel.h */; };
|
||||||
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CECA899B09DB132E00A3D774 /* DetailsPanel.m */; };
|
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CECA899B09DB132E00A3D774 /* DetailsPanel.m */; };
|
||||||
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
|
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
|
||||||
|
CEF12A7E124DFD400087B51D /* HSTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = CEF12A7D124DFD400087B51D /* HSTableView.m */; };
|
||||||
|
CEF12A84124DFD620087B51D /* ResultTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CEF12A83124DFD620087B51D /* ResultTable.m */; };
|
||||||
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; };
|
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; };
|
||||||
CEFC295509C89FF200D9F998 /* details32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295309C89FF200D9F998 /* details32.png */; };
|
CEFC295509C89FF200D9F998 /* details32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295309C89FF200D9F998 /* details32.png */; };
|
||||||
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295409C89FF200D9F998 /* preferences32.png */; };
|
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295409C89FF200D9F998 /* preferences32.png */; };
|
||||||
@@ -110,7 +111,6 @@
|
|||||||
CE77C8A710946CE20078B0DB /* DetailsPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DetailsPanel.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>"; };
|
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>"; };
|
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; };
|
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; };
|
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; };
|
CE80DB1D0FC192D60086DCA6 /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -139,10 +139,8 @@
|
|||||||
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultWindow.h; path = ../base/ResultWindow.h; sourceTree = SOURCE_ROOT; };
|
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultWindow.h; path = ../base/ResultWindow.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE80DB890FC1951C0086DCA6 /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultWindow.m; path = ../base/ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
CE80DB890FC1951C0086DCA6 /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultWindow.m; path = ../base/ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE848A1809DD85810004CB44 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
|
CE848A1809DD85810004CB44 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
|
||||||
CE958658112C516400F95FD2 /* PyResultTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTree.h; path = ../base/PyResultTree.h; sourceTree = SOURCE_ROOT; };
|
CE895D7A12144A7800E74705 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = ../../cocoalib/en.lproj/registration.xib; sourceTree = SOURCE_ROOT; };
|
||||||
CE958659112C516400F95FD2 /* PyStatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyStatsLabel.h; path = ../base/PyStatsLabel.h; sourceTree = SOURCE_ROOT; };
|
CE958659112C516400F95FD2 /* PyStatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyStatsLabel.h; path = ../base/PyStatsLabel.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE95865A112C516400F95FD2 /* ResultOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultOutline.h; path = ../base/ResultOutline.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE95865B112C516400F95FD2 /* ResultOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultOutline.m; path = ../base/ResultOutline.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE95865C112C516400F95FD2 /* StatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StatsLabel.h; path = ../base/StatsLabel.h; sourceTree = SOURCE_ROOT; };
|
CE95865C112C516400F95FD2 /* StatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StatsLabel.h; path = ../base/StatsLabel.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE95865D112C516400F95FD2 /* StatsLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StatsLabel.m; path = ../base/StatsLabel.m; sourceTree = SOURCE_ROOT; };
|
CE95865D112C516400F95FD2 /* StatsLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StatsLabel.m; path = ../base/StatsLabel.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE9EA7431122C96C008CD2BC /* HSGUIController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSGUIController.h; sourceTree = "<group>"; };
|
CE9EA7431122C96C008CD2BC /* HSGUIController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSGUIController.h; sourceTree = "<group>"; };
|
||||||
@@ -170,6 +168,11 @@
|
|||||||
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; 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>"; };
|
CECA899B09DB132E00A3D774 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DetailsPanel.m; sourceTree = "<group>"; };
|
||||||
CEEB135109C837A2004D2330 /* dupeguru.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = dupeguru.icns; sourceTree = "<group>"; };
|
CEEB135109C837A2004D2330 /* dupeguru.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = dupeguru.icns; sourceTree = "<group>"; };
|
||||||
|
CEF12A7C124DFD400087B51D /* HSTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSTableView.h; path = ../../cocoalib/views/HSTableView.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CEF12A7D124DFD400087B51D /* HSTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSTableView.m; path = ../../cocoalib/views/HSTableView.m; sourceTree = SOURCE_ROOT; };
|
||||||
|
CEF12A81124DFD620087B51D /* PyResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTable.h; path = ../base/PyResultTable.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CEF12A82124DFD620087B51D /* ResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultTable.h; path = ../base/ResultTable.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CEF12A83124DFD620087B51D /* ResultTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultTable.m; path = ../base/ResultTable.m; sourceTree = SOURCE_ROOT; };
|
||||||
CEFC294509C89E3D00D9F998 /* folder32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = folder32.png; path = ../../images/folder32.png; sourceTree = SOURCE_ROOT; };
|
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; };
|
CEFC295309C89FF200D9F998 /* details32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = details32.png; path = ../../images/details32.png; sourceTree = SOURCE_ROOT; };
|
||||||
CEFC295409C89FF200D9F998 /* preferences32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = preferences32.png; path = ../../images/preferences32.png; sourceTree = SOURCE_ROOT; };
|
CEFC295409C89FF200D9F998 /* preferences32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = preferences32.png; path = ../../images/preferences32.png; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -294,9 +297,9 @@
|
|||||||
CE7AC9141119911200D02F6C /* xib */ = {
|
CE7AC9141119911200D02F6C /* xib */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CE895D7912144A7800E74705 /* registration.xib */,
|
||||||
CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */,
|
CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */,
|
||||||
CE7AC9161119911200D02F6C /* progress.xib */,
|
CE7AC9161119911200D02F6C /* progress.xib */,
|
||||||
CE7AC9171119911200D02F6C /* registration.xib */,
|
|
||||||
);
|
);
|
||||||
name = xib;
|
name = xib;
|
||||||
path = ../../cocoalib/xib;
|
path = ../../cocoalib/xib;
|
||||||
@@ -339,6 +342,9 @@
|
|||||||
CE80DB810FC194BD0086DCA6 /* dgbase */ = {
|
CE80DB810FC194BD0086DCA6 /* dgbase */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CEF12A81124DFD620087B51D /* PyResultTable.h */,
|
||||||
|
CEF12A82124DFD620087B51D /* ResultTable.h */,
|
||||||
|
CEF12A83124DFD620087B51D /* ResultTable.m */,
|
||||||
CE80DB820FC1951C0086DCA6 /* AppDelegate.h */,
|
CE80DB820FC1951C0086DCA6 /* AppDelegate.h */,
|
||||||
CE80DB830FC1951C0086DCA6 /* AppDelegate.m */,
|
CE80DB830FC1951C0086DCA6 /* AppDelegate.m */,
|
||||||
CE80DB840FC1951C0086DCA6 /* Consts.h */,
|
CE80DB840FC1951C0086DCA6 /* Consts.h */,
|
||||||
@@ -352,9 +358,6 @@
|
|||||||
CE0C2ABB1177014200BC749F /* ProblemDialog.m */,
|
CE0C2ABB1177014200BC749F /* ProblemDialog.m */,
|
||||||
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */,
|
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */,
|
||||||
CE80DB890FC1951C0086DCA6 /* ResultWindow.m */,
|
CE80DB890FC1951C0086DCA6 /* ResultWindow.m */,
|
||||||
CE958658112C516400F95FD2 /* PyResultTree.h */,
|
|
||||||
CE95865A112C516400F95FD2 /* ResultOutline.h */,
|
|
||||||
CE95865B112C516400F95FD2 /* ResultOutline.m */,
|
|
||||||
CE95865C112C516400F95FD2 /* StatsLabel.h */,
|
CE95865C112C516400F95FD2 /* StatsLabel.h */,
|
||||||
CE95865D112C516400F95FD2 /* StatsLabel.m */,
|
CE95865D112C516400F95FD2 /* StatsLabel.m */,
|
||||||
CE80DB870FC1951C0086DCA6 /* PyDupeGuru.h */,
|
CE80DB870FC1951C0086DCA6 /* PyDupeGuru.h */,
|
||||||
@@ -396,6 +399,8 @@
|
|||||||
CE9EA74F1122C96C008CD2BC /* views */ = {
|
CE9EA74F1122C96C008CD2BC /* views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CEF12A7C124DFD400087B51D /* HSTableView.h */,
|
||||||
|
CEF12A7D124DFD400087B51D /* HSTableView.m */,
|
||||||
CE9EA7501122C96C008CD2BC /* HSOutlineView.h */,
|
CE9EA7501122C96C008CD2BC /* HSOutlineView.h */,
|
||||||
CE9EA7511122C96C008CD2BC /* HSOutlineView.m */,
|
CE9EA7511122C96C008CD2BC /* HSOutlineView.m */,
|
||||||
CE9EA7521122C96C008CD2BC /* NSIndexPathAdditions.h */,
|
CE9EA7521122C96C008CD2BC /* NSIndexPathAdditions.h */,
|
||||||
@@ -458,6 +463,13 @@
|
|||||||
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */;
|
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */;
|
||||||
compatibilityVersion = "Xcode 3.0";
|
compatibilityVersion = "Xcode 3.0";
|
||||||
hasScannedForEncodings = 1;
|
hasScannedForEncodings = 1;
|
||||||
|
knownRegions = (
|
||||||
|
English,
|
||||||
|
Japanese,
|
||||||
|
French,
|
||||||
|
German,
|
||||||
|
en,
|
||||||
|
);
|
||||||
mainGroup = 29B97314FDCFA39411CA2CEA /* dupeguru */;
|
mainGroup = 29B97314FDCFA39411CA2CEA /* dupeguru */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
@@ -486,8 +498,8 @@
|
|||||||
CE031754109B345200517EE6 /* MainMenu.xib in Resources */,
|
CE031754109B345200517EE6 /* MainMenu.xib in Resources */,
|
||||||
CE7AC9181119911200D02F6C /* ErrorReportWindow.xib in Resources */,
|
CE7AC9181119911200D02F6C /* ErrorReportWindow.xib in Resources */,
|
||||||
CE7AC9191119911200D02F6C /* progress.xib in Resources */,
|
CE7AC9191119911200D02F6C /* progress.xib in Resources */,
|
||||||
CE7AC91A1119911200D02F6C /* registration.xib in Resources */,
|
|
||||||
CE0C2AC81177021600BC749F /* ProblemDialog.xib in Resources */,
|
CE0C2AC81177021600BC749F /* ProblemDialog.xib in Resources */,
|
||||||
|
CE895D7B12144A7800E74705 /* registration.xib in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -525,15 +537,28 @@
|
|||||||
CE9EA75B1122C96C008CD2BC /* NSIndexPathAdditions.m in Sources */,
|
CE9EA75B1122C96C008CD2BC /* NSIndexPathAdditions.m in Sources */,
|
||||||
CE9EA75C1122C96C008CD2BC /* NSTableViewAdditions.m in Sources */,
|
CE9EA75C1122C96C008CD2BC /* NSTableViewAdditions.m in Sources */,
|
||||||
CE9EA7721122CA0B008CD2BC /* DirectoryOutline.m in Sources */,
|
CE9EA7721122CA0B008CD2BC /* DirectoryOutline.m in Sources */,
|
||||||
CE95865E112C516400F95FD2 /* ResultOutline.m in Sources */,
|
|
||||||
CE95865F112C516400F95FD2 /* StatsLabel.m in Sources */,
|
CE95865F112C516400F95FD2 /* StatsLabel.m in Sources */,
|
||||||
CE0C2AB61177011000BC749F /* HSTable.m in Sources */,
|
CE0C2AB61177011000BC749F /* HSTable.m in Sources */,
|
||||||
CE0C2ABD1177014200BC749F /* ProblemDialog.m in Sources */,
|
CE0C2ABD1177014200BC749F /* ProblemDialog.m in Sources */,
|
||||||
|
CEF12A7E124DFD400087B51D /* HSTableView.m in Sources */,
|
||||||
|
CEF12A84124DFD620087B51D /* ResultTable.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
CE895D7912144A7800E74705 /* registration.xib */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
CE895D7A12144A7800E74705 /* en */,
|
||||||
|
);
|
||||||
|
name = registration.xib;
|
||||||
|
path = ../../cocoalib/xib;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
C01FCF4C08A954540054247B /* release */ = {
|
C01FCF4C08A954540054247B /* release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
||||||
<data>
|
<data>
|
||||||
<int key="IBDocument.SystemTarget">1050</int>
|
<int key="IBDocument.SystemTarget">1050</int>
|
||||||
<string key="IBDocument.SystemVersion">10C540</string>
|
<string key="IBDocument.SystemVersion">10F569</string>
|
||||||
<string key="IBDocument.InterfaceBuilderVersion">740</string>
|
<string key="IBDocument.InterfaceBuilderVersion">788</string>
|
||||||
<string key="IBDocument.AppKitVersion">1038.25</string>
|
<string key="IBDocument.AppKitVersion">1038.29</string>
|
||||||
<string key="IBDocument.HIToolboxVersion">458.00</string>
|
<string key="IBDocument.HIToolboxVersion">461.00</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<string key="NS.object.0">740</string>
|
<string key="NS.object.0">788</string>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
@@ -434,6 +434,7 @@
|
|||||||
<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
|
<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
|
||||||
<string key="NSMinSize">{451, 177}</string>
|
<string key="NSMinSize">{451, 177}</string>
|
||||||
<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
|
<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
|
||||||
|
<string key="NSFrameAutosaveName">DetailsPanel</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBObjectContainer" key="IBDocument.Objects">
|
<object class="IBObjectContainer" key="IBDocument.Objects">
|
||||||
@@ -866,6 +867,13 @@
|
|||||||
<string key="NS.key.0">detailsTable</string>
|
<string key="NS.key.0">detailsTable</string>
|
||||||
<string key="NS.object.0">NSTableView</string>
|
<string key="NS.object.0">NSTableView</string>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
|
||||||
|
<string key="NS.key.0">detailsTable</string>
|
||||||
|
<object class="IBToOneOutletInfo" key="NS.object.0">
|
||||||
|
<string key="name">detailsTable</string>
|
||||||
|
<string key="candidateClassName">NSTableView</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBProjectSource</string>
|
<string key="majorKey">IBProjectSource</string>
|
||||||
<string key="minorKey">../base/DetailsPanel.h</string>
|
<string key="minorKey">../base/DetailsPanel.h</string>
|
||||||
@@ -891,6 +899,35 @@
|
|||||||
<string>NSProgressIndicator</string>
|
<string>NSProgressIndicator</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<object class="NSArray" key="dict.sortedKeys">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<string>dupeImage</string>
|
||||||
|
<string>dupeProgressIndicator</string>
|
||||||
|
<string>refImage</string>
|
||||||
|
<string>refProgressIndicator</string>
|
||||||
|
</object>
|
||||||
|
<object class="NSMutableArray" key="dict.values">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<object class="IBToOneOutletInfo">
|
||||||
|
<string key="name">dupeImage</string>
|
||||||
|
<string key="candidateClassName">NSImageView</string>
|
||||||
|
</object>
|
||||||
|
<object class="IBToOneOutletInfo">
|
||||||
|
<string key="name">dupeProgressIndicator</string>
|
||||||
|
<string key="candidateClassName">NSProgressIndicator</string>
|
||||||
|
</object>
|
||||||
|
<object class="IBToOneOutletInfo">
|
||||||
|
<string key="name">refImage</string>
|
||||||
|
<string key="candidateClassName">NSImageView</string>
|
||||||
|
</object>
|
||||||
|
<object class="IBToOneOutletInfo">
|
||||||
|
<string key="name">refProgressIndicator</string>
|
||||||
|
<string key="candidateClassName">NSProgressIndicator</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBProjectSource</string>
|
<string key="majorKey">IBProjectSource</string>
|
||||||
<string key="minorKey">DetailsPanel.h</string>
|
<string key="minorKey">DetailsPanel.h</string>
|
||||||
@@ -903,6 +940,13 @@
|
|||||||
<string key="NS.key.0">detailsTable</string>
|
<string key="NS.key.0">detailsTable</string>
|
||||||
<string key="NS.object.0">NSTableView</string>
|
<string key="NS.object.0">NSTableView</string>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
|
||||||
|
<string key="NS.key.0">detailsTable</string>
|
||||||
|
<object class="IBToOneOutletInfo" key="NS.object.0">
|
||||||
|
<string key="name">detailsTable</string>
|
||||||
|
<string key="candidateClassName">NSTableView</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBUserSource</string>
|
<string key="majorKey">IBUserSource</string>
|
||||||
<string key="minorKey"/>
|
<string key="minorKey"/>
|
||||||
@@ -1423,6 +1467,13 @@
|
|||||||
<string key="NS.key.0">showWindow:</string>
|
<string key="NS.key.0">showWindow:</string>
|
||||||
<string key="NS.object.0">id</string>
|
<string key="NS.object.0">id</string>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="actionInfosByName">
|
||||||
|
<string key="NS.key.0">showWindow:</string>
|
||||||
|
<object class="IBActionInfo" key="NS.object.0">
|
||||||
|
<string key="name">showWindow:</string>
|
||||||
|
<string key="candidateClassName">id</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBFrameworkSource</string>
|
<string key="majorKey">IBFrameworkSource</string>
|
||||||
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
||||||
@@ -1431,6 +1482,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<int key="IBDocument.localizationMode">0</int>
|
<int key="IBDocument.localizationMode">0</int>
|
||||||
|
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
|
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
||||||
<integer value="1050" key="NS.object.0"/>
|
<integer value="1050" key="NS.object.0"/>
|
||||||
@@ -1446,5 +1498,9 @@
|
|||||||
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
||||||
<nil key="IBDocument.LastKnownRelativeProjectPath"/>
|
<nil key="IBDocument.LastKnownRelativeProjectPath"/>
|
||||||
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
||||||
|
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
|
||||||
|
<string key="NS.key.0">NSApplicationIcon</string>
|
||||||
|
<string key="NS.object.0">{128, 128}</string>
|
||||||
|
</object>
|
||||||
</data>
|
</data>
|
||||||
</archive>
|
</archive>
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
||||||
<data>
|
<data>
|
||||||
<int key="IBDocument.SystemTarget">1050</int>
|
<int key="IBDocument.SystemTarget">1050</int>
|
||||||
<string key="IBDocument.SystemVersion">10D573</string>
|
<string key="IBDocument.SystemVersion">10F569</string>
|
||||||
<string key="IBDocument.InterfaceBuilderVersion">740</string>
|
<string key="IBDocument.InterfaceBuilderVersion">788</string>
|
||||||
<string key="IBDocument.AppKitVersion">1038.29</string>
|
<string key="IBDocument.AppKitVersion">1038.29</string>
|
||||||
<string key="IBDocument.HIToolboxVersion">460.00</string>
|
<string key="IBDocument.HIToolboxVersion">461.00</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<string key="NS.object.0">740</string>
|
<string key="NS.object.0">788</string>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<object class="NSUserDefaultsController" id="455472712">
|
<object class="NSUserDefaultsController" id="455472712">
|
||||||
<object class="NSMutableArray" key="NSDeclaredKeys">
|
<object class="NSMutableArray" key="NSDeclaredKeys">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
<string>CustomCommand</string>
|
<string>ignoreHardlinkMatches</string>
|
||||||
</object>
|
</object>
|
||||||
<bool key="NSSharedInstance">YES</bool>
|
<bool key="NSSharedInstance">YES</bool>
|
||||||
</object>
|
</object>
|
||||||
@@ -406,10 +406,31 @@
|
|||||||
<int key="NSPeriodicInterval">25</int>
|
<int key="NSPeriodicInterval">25</int>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSButton" id="519470955">
|
||||||
|
<reference key="NSNextResponder" ref="581039403"/>
|
||||||
|
<int key="NSvFlags">256</int>
|
||||||
|
<string key="NSFrame">{{15, 100}, {265, 18}}</string>
|
||||||
|
<reference key="NSSuperview" ref="581039403"/>
|
||||||
|
<bool key="NSEnabled">YES</bool>
|
||||||
|
<object class="NSButtonCell" key="NSCell" id="595497720">
|
||||||
|
<int key="NSCellFlags">67239424</int>
|
||||||
|
<int key="NSCellFlags2">0</int>
|
||||||
|
<string key="NSContents">Ignore duplicates hardlinking to the same file</string>
|
||||||
|
<reference key="NSSupport" ref="26"/>
|
||||||
|
<reference key="NSControlView" ref="519470955"/>
|
||||||
|
<int key="NSButtonFlags">1211912703</int>
|
||||||
|
<int key="NSButtonFlags2">2</int>
|
||||||
|
<reference key="NSAlternateImage" ref="990345653"/>
|
||||||
|
<string key="NSAlternateContents"/>
|
||||||
|
<string key="NSKeyEquivalent"/>
|
||||||
|
<int key="NSPeriodicDelay">200</int>
|
||||||
|
<int key="NSPeriodicInterval">25</int>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="NSTextField" id="748076392">
|
<object class="NSTextField" id="748076392">
|
||||||
<reference key="NSNextResponder" ref="581039403"/>
|
<reference key="NSNextResponder" ref="581039403"/>
|
||||||
<int key="NSvFlags">292</int>
|
<int key="NSvFlags">292</int>
|
||||||
<string key="NSFrame">{{14, 97}, {85, 13}}</string>
|
<string key="NSFrame">{{14, 77}, {85, 13}}</string>
|
||||||
<reference key="NSSuperview" ref="581039403"/>
|
<reference key="NSSuperview" ref="581039403"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSTextFieldCell" key="NSCell" id="936873031">
|
<object class="NSTextFieldCell" key="NSCell" id="936873031">
|
||||||
@@ -425,7 +446,7 @@
|
|||||||
<object class="NSTextField" id="526155835">
|
<object class="NSTextField" id="526155835">
|
||||||
<reference key="NSNextResponder" ref="581039403"/>
|
<reference key="NSNextResponder" ref="581039403"/>
|
||||||
<int key="NSvFlags">292</int>
|
<int key="NSvFlags">292</int>
|
||||||
<string key="NSFrame">{{14, 69}, {306, 17}}</string>
|
<string key="NSFrame">{{14, 49}, {306, 17}}</string>
|
||||||
<reference key="NSSuperview" ref="581039403"/>
|
<reference key="NSSuperview" ref="581039403"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSTextFieldCell" key="NSCell" id="765798142">
|
<object class="NSTextFieldCell" key="NSCell" id="765798142">
|
||||||
@@ -441,7 +462,7 @@
|
|||||||
<object class="NSPopUpButton" id="724953200">
|
<object class="NSPopUpButton" id="724953200">
|
||||||
<reference key="NSNextResponder" ref="581039403"/>
|
<reference key="NSNextResponder" ref="581039403"/>
|
||||||
<int key="NSvFlags">292</int>
|
<int key="NSvFlags">292</int>
|
||||||
<string key="NSFrame">{{104, 90}, {216, 26}}</string>
|
<string key="NSFrame">{{104, 70}, {216, 26}}</string>
|
||||||
<reference key="NSSuperview" ref="581039403"/>
|
<reference key="NSSuperview" ref="581039403"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSPopUpButtonCell" key="NSCell" id="601288025">
|
<object class="NSPopUpButtonCell" key="NSCell" id="601288025">
|
||||||
@@ -517,7 +538,7 @@
|
|||||||
<object class="NSTextField" id="590530357">
|
<object class="NSTextField" id="590530357">
|
||||||
<reference key="NSNextResponder" ref="581039403"/>
|
<reference key="NSNextResponder" ref="581039403"/>
|
||||||
<int key="NSvFlags">266</int>
|
<int key="NSvFlags">266</int>
|
||||||
<string key="NSFrame">{{17, 47}, {312, 22}}</string>
|
<string key="NSFrame">{{17, 27}, {312, 22}}</string>
|
||||||
<reference key="NSSuperview" ref="581039403"/>
|
<reference key="NSSuperview" ref="581039403"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSTextFieldCell" key="NSCell" id="922246764">
|
<object class="NSTextFieldCell" key="NSCell" id="922246764">
|
||||||
@@ -790,6 +811,30 @@
|
|||||||
</object>
|
</object>
|
||||||
<int key="connectionID">68</int>
|
<int key="connectionID">68</int>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBConnectionRecord">
|
||||||
|
<object class="IBOutletConnection" key="connection">
|
||||||
|
<string key="label">nextKeyView</string>
|
||||||
|
<reference key="source" ref="519470955"/>
|
||||||
|
<reference key="destination" ref="279087998"/>
|
||||||
|
</object>
|
||||||
|
<int key="connectionID">72</int>
|
||||||
|
</object>
|
||||||
|
<object class="IBConnectionRecord">
|
||||||
|
<object class="IBBindingConnection" key="connection">
|
||||||
|
<string key="label">value: values.ignoreHardlinkMatches</string>
|
||||||
|
<reference key="source" ref="519470955"/>
|
||||||
|
<reference key="destination" ref="455472712"/>
|
||||||
|
<object class="NSNibBindingConnector" key="connector">
|
||||||
|
<reference key="NSSource" ref="519470955"/>
|
||||||
|
<reference key="NSDestination" ref="455472712"/>
|
||||||
|
<string key="NSLabel">value: values.ignoreHardlinkMatches</string>
|
||||||
|
<string key="NSBinding">value</string>
|
||||||
|
<string key="NSKeyPath">values.ignoreHardlinkMatches</string>
|
||||||
|
<int key="NSNibBindingConnectorVersion">2</int>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
|
<int key="connectionID">73</int>
|
||||||
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||||
<object class="NSArray" key="orderedObjects">
|
<object class="NSArray" key="orderedObjects">
|
||||||
@@ -892,9 +937,10 @@
|
|||||||
<object class="NSMutableArray" key="children">
|
<object class="NSMutableArray" key="children">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
<reference ref="1018598123"/>
|
<reference ref="1018598123"/>
|
||||||
<reference ref="724953200"/>
|
<reference ref="519470955"/>
|
||||||
<reference ref="748076392"/>
|
<reference ref="748076392"/>
|
||||||
<reference ref="526155835"/>
|
<reference ref="526155835"/>
|
||||||
|
<reference ref="724953200"/>
|
||||||
<reference ref="590530357"/>
|
<reference ref="590530357"/>
|
||||||
</object>
|
</object>
|
||||||
<reference key="parent" ref="1045400351"/>
|
<reference key="parent" ref="1045400351"/>
|
||||||
@@ -1151,6 +1197,20 @@
|
|||||||
<reference key="object" ref="922246764"/>
|
<reference key="object" ref="922246764"/>
|
||||||
<reference key="parent" ref="590530357"/>
|
<reference key="parent" ref="590530357"/>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBObjectRecord">
|
||||||
|
<int key="objectID">69</int>
|
||||||
|
<reference key="object" ref="519470955"/>
|
||||||
|
<object class="NSMutableArray" key="children">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<reference ref="595497720"/>
|
||||||
|
</object>
|
||||||
|
<reference key="parent" ref="581039403"/>
|
||||||
|
</object>
|
||||||
|
<object class="IBObjectRecord">
|
||||||
|
<int key="objectID">70</int>
|
||||||
|
<reference key="object" ref="595497720"/>
|
||||||
|
<reference key="parent" ref="519470955"/>
|
||||||
|
</object>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSMutableDictionary" key="flattenedProperties">
|
<object class="NSMutableDictionary" key="flattenedProperties">
|
||||||
@@ -1221,8 +1281,11 @@
|
|||||||
<string>65.IBPluginDependency</string>
|
<string>65.IBPluginDependency</string>
|
||||||
<string>66.IBPluginDependency</string>
|
<string>66.IBPluginDependency</string>
|
||||||
<string>67.IBPluginDependency</string>
|
<string>67.IBPluginDependency</string>
|
||||||
|
<string>69.IBPluginDependency</string>
|
||||||
|
<string>69.ImportedFromIB2</string>
|
||||||
<string>7.IBPluginDependency</string>
|
<string>7.IBPluginDependency</string>
|
||||||
<string>7.ImportedFromIB2</string>
|
<string>7.ImportedFromIB2</string>
|
||||||
|
<string>70.IBPluginDependency</string>
|
||||||
<string>8.IBPluginDependency</string>
|
<string>8.IBPluginDependency</string>
|
||||||
<string>8.ImportedFromIB2</string>
|
<string>8.ImportedFromIB2</string>
|
||||||
<string>9.IBPluginDependency</string>
|
<string>9.IBPluginDependency</string>
|
||||||
@@ -1299,6 +1362,9 @@
|
|||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<boolean value="YES"/>
|
<boolean value="YES"/>
|
||||||
<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"/>
|
<boolean value="YES"/>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
@@ -1318,7 +1384,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<nil key="sourceID"/>
|
<nil key="sourceID"/>
|
||||||
<int key="maxID">68</int>
|
<int key="maxID">73</int>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
<object class="IBClassDescriber" key="IBDocument.Classes">
|
||||||
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
||||||
@@ -1330,6 +1396,13 @@
|
|||||||
<string key="minorKey">../views/HSOutlineView.h</string>
|
<string key="minorKey">../views/HSOutlineView.h</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBPartialClassDescription">
|
||||||
|
<string key="className">NSObject</string>
|
||||||
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
|
<string key="majorKey">IBProjectSource</string>
|
||||||
|
<string key="minorKey">../views/HSTableView.h</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
<string key="className">NSObject</string>
|
<string key="className">NSObject</string>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
@@ -1868,6 +1941,13 @@
|
|||||||
<string key="NS.key.0">showWindow:</string>
|
<string key="NS.key.0">showWindow:</string>
|
||||||
<string key="NS.object.0">id</string>
|
<string key="NS.object.0">id</string>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="actionInfosByName">
|
||||||
|
<string key="NS.key.0">showWindow:</string>
|
||||||
|
<object class="IBActionInfo" key="NS.object.0">
|
||||||
|
<string key="name">showWindow:</string>
|
||||||
|
<string key="candidateClassName">id</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBFrameworkSource</string>
|
<string key="majorKey">IBFrameworkSource</string>
|
||||||
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
||||||
@@ -1876,6 +1956,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<int key="IBDocument.localizationMode">0</int>
|
<int key="IBDocument.localizationMode">0</int>
|
||||||
|
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
|
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
||||||
<integer value="1050" key="NS.object.0"/>
|
<integer value="1050" key="NS.object.0"/>
|
||||||
@@ -1887,5 +1968,18 @@
|
|||||||
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
||||||
<string key="IBDocument.LastKnownRelativeProjectPath">../dupeguru.xcodeproj</string>
|
<string key="IBDocument.LastKnownRelativeProjectPath">../dupeguru.xcodeproj</string>
|
||||||
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
||||||
|
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<object class="NSArray" key="dict.sortedKeys">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<string>NSMenuCheckmark</string>
|
||||||
|
<string>NSMenuMixedState</string>
|
||||||
|
</object>
|
||||||
|
<object class="NSMutableArray" key="dict.values">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<string>{9, 8}</string>
|
||||||
|
<string>{7, 2}</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
</data>
|
</data>
|
||||||
</archive>
|
</archive>
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[d setObject:b2n(NO) forKey:@"matchSimilarWords"];
|
[d setObject:b2n(NO) forKey:@"matchSimilarWords"];
|
||||||
[d setObject:b2n(YES) forKey:@"mixFileKind"];
|
[d setObject:b2n(YES) forKey:@"mixFileKind"];
|
||||||
[d setObject:b2n(NO) forKey:@"useRegexpFilter"];
|
[d setObject:b2n(NO) forKey:@"useRegexpFilter"];
|
||||||
|
[d setObject:b2n(NO) forKey:@"ignoreHardlinkMatches"];
|
||||||
[d setObject:b2n(NO) forKey:@"removeEmptyFolders"];
|
[d setObject:b2n(NO) forKey:@"removeEmptyFolders"];
|
||||||
[d setObject:b2n(YES) forKey:@"ignoreSmallFiles"];
|
[d setObject:b2n(YES) forKey:@"ignoreSmallFiles"];
|
||||||
[d setObject:b2n(NO) forKey:@"debug"];
|
[d setObject:b2n(NO) forKey:@"debug"];
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>hsft</string>
|
<string>hsft</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>2.10.1</string>
|
<string>2.12.0</string>
|
||||||
<key>NSMainNibFile</key>
|
<key>NSMainNibFile</key>
|
||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
- (void)awakeFromNib
|
- (void)awakeFromNib
|
||||||
{
|
{
|
||||||
[super awakeFromNib];
|
[super awakeFromNib];
|
||||||
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,4)];
|
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndex:2];
|
||||||
[deltaColumns removeIndex:3];
|
[deltaColumns addIndex:4];
|
||||||
[outline setDeltaColumns:deltaColumns];
|
[table setDeltaColumns:deltaColumns];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
@@ -30,12 +30,12 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[columnsOrder addObject:@"0"];
|
[columnsOrder addObject:@"0"];
|
||||||
[columnsOrder addObject:@"1"];
|
[columnsOrder addObject:@"1"];
|
||||||
[columnsOrder addObject:@"2"];
|
[columnsOrder addObject:@"2"];
|
||||||
[columnsOrder addObject:@"6"];
|
[columnsOrder addObject:@"5"];
|
||||||
NSMutableDictionary *columnsWidth = [NSMutableDictionary dictionary];
|
NSMutableDictionary *columnsWidth = [NSMutableDictionary dictionary];
|
||||||
[columnsWidth setObject:i2n(195) forKey:@"0"];
|
[columnsWidth setObject:i2n(195) forKey:@"0"];
|
||||||
[columnsWidth setObject:i2n(120) forKey:@"1"];
|
[columnsWidth setObject:i2n(183) forKey:@"1"];
|
||||||
[columnsWidth setObject:i2n(63) forKey:@"2"];
|
[columnsWidth setObject:i2n(63) forKey:@"2"];
|
||||||
[columnsWidth setObject:i2n(60) forKey:@"6"];
|
[columnsWidth setObject:i2n(60) forKey:@"5"];
|
||||||
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
|
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +51,8 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[_py setScanType:[ud objectForKey:@"scanType"]];
|
[_py setScanType:[ud objectForKey:@"scanType"]];
|
||||||
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
|
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
|
||||||
[_py setWordWeighting:[ud objectForKey:@"wordWeighting"]];
|
[_py setWordWeighting:[ud objectForKey:@"wordWeighting"]];
|
||||||
[_py setMixFileKind:[ud objectForKey:@"mixFileKind"]];
|
[_py setMixFileKind:n2b([ud objectForKey:@"mixFileKind"])];
|
||||||
|
[_py setIgnoreHardlinkMatches:n2b([ud objectForKey:@"ignoreHardlinkMatches"])];
|
||||||
[_py setMatchSimilarWords:[ud objectForKey:@"matchSimilarWords"]];
|
[_py setMatchSimilarWords:[ud objectForKey:@"matchSimilarWords"]];
|
||||||
int smallFileThreshold = [ud integerForKey:@"smallFileThreshold"]; // In KB
|
int smallFileThreshold = [ud integerForKey:@"smallFileThreshold"]; // In KB
|
||||||
int sizeThreshold = [ud boolForKey:@"ignoreSmallFiles"] ? smallFileThreshold * 1024 : 0; // The py side wants bytes
|
int sizeThreshold = [ud boolForKey:@"ignoreSmallFiles"] ? smallFileThreshold * 1024 : 0; // The py side wants bytes
|
||||||
@@ -59,8 +60,6 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
int r = n2i([py doScan]);
|
int r = n2i([py doScan]);
|
||||||
if (r != 0)
|
if (r != 0)
|
||||||
[[ProgressController mainProgressController] hide];
|
[[ProgressController mainProgressController] hide];
|
||||||
if (r == 1)
|
|
||||||
[Dialogs showMessage:@"You cannot make a duplicate scan with only reference directories."];
|
|
||||||
if (r == 3)
|
if (r == 3)
|
||||||
{
|
{
|
||||||
[Dialogs showMessage:@"The selected directories contain no scannable file."];
|
[Dialogs showMessage:@"The selected directories contain no scannable file."];
|
||||||
@@ -80,10 +79,9 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[[sizeCol dataCell] setAlignment:NSRightTextAlignment];
|
[[sizeCol dataCell] setAlignment:NSRightTextAlignment];
|
||||||
[_resultColumns addObject:sizeCol];
|
[_resultColumns addObject:sizeCol];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:3 title:@"Kind" width:40 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:3 title:@"Kind" width:40 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:4 title:@"Creation" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:4 title:@"Modification" width:120 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Modification" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Match %" width:60 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Match %" width:60 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Words Used" width:120 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Words Used" width:120 refCol:refCol]];
|
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Dupe Count" width:80 refCol:refCol]];
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Dupe Count" width:80 refCol:refCol]];
|
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -6,13 +6,15 @@
|
|||||||
|
|
||||||
from hscommon.cocoa import signature
|
from hscommon.cocoa import signature
|
||||||
|
|
||||||
from core import scanner
|
from core.scanner import ScanType
|
||||||
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
|
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
|
||||||
from core_se.app_cocoa import DupeGuru
|
from core_se.app_cocoa import DupeGuru
|
||||||
|
|
||||||
# Fix py2app imports with chokes on relative imports and other stuff
|
# Fix py2app imports with chokes on relative imports and other stuff
|
||||||
from core_se import fs, data
|
import hsutil.conflict
|
||||||
from lxml import etree, _elementpath
|
import core.engine, core.fs, core.app
|
||||||
|
import core_se.fs, core_se.data
|
||||||
|
import xml.etree.ElementPath
|
||||||
import gzip
|
import gzip
|
||||||
|
|
||||||
class PyDupeGuru(PyDupeGuruBase):
|
class PyDupeGuru(PyDupeGuruBase):
|
||||||
@@ -28,8 +30,8 @@ class PyDupeGuru(PyDupeGuruBase):
|
|||||||
def setScanType_(self,scan_type):
|
def setScanType_(self,scan_type):
|
||||||
try:
|
try:
|
||||||
self.py.scanner.scan_type = [
|
self.py.scanner.scan_type = [
|
||||||
scanner.SCAN_TYPE_FILENAME,
|
ScanType.Filename,
|
||||||
scanner.SCAN_TYPE_CONTENT
|
ScanType.Contents,
|
||||||
][scan_type]
|
][scan_type]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
CE073F6309CAE1A3005C1D2F /* dupeguru_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_help */; };
|
CE073F6309CAE1A3005C1D2F /* dupeguru_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_help */; };
|
||||||
CE19BC6311199231007CCEB0 /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */; };
|
CE19BC6311199231007CCEB0 /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */; };
|
||||||
CE19BC6411199231007CCEB0 /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6111199231007CCEB0 /* progress.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 */; };
|
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
|
||||||
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9A09914ADF003581CE /* ResultWindow.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 */; };
|
CE381D0509915304003581CE /* dg_cocoa.plugin in Resources */ = {isa = PBXBuildFile; fileRef = CE381CF509915304003581CE /* dg_cocoa.plugin */; };
|
||||||
@@ -21,6 +20,8 @@
|
|||||||
CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; };
|
CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; };
|
||||||
CE647E571173024A006D28BA /* ProblemDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = CE647E551173024A006D28BA /* ProblemDialog.m */; };
|
CE647E571173024A006D28BA /* ProblemDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = CE647E551173024A006D28BA /* ProblemDialog.m */; };
|
||||||
CE647E591173026F006D28BA /* ProblemDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE647E581173026F006D28BA /* ProblemDialog.xib */; };
|
CE647E591173026F006D28BA /* ProblemDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE647E581173026F006D28BA /* ProblemDialog.xib */; };
|
||||||
|
CE6DD4E7124CA3070089A48D /* ResultTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CE6DD4E6124CA3070089A48D /* ResultTable.m */; };
|
||||||
|
CE6DD547124CAF1F0089A48D /* HSTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE6DD546124CAF1F0089A48D /* HSTableView.m */; };
|
||||||
CE6E0DFE1054E9EF008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */; };
|
CE6E0DFE1054E9EF008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */; };
|
||||||
CE76FDC4111EE37C006618EA /* HSOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDBF111EE37C006618EA /* HSOutlineView.m */; };
|
CE76FDC4111EE37C006618EA /* HSOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDBF111EE37C006618EA /* HSOutlineView.m */; };
|
||||||
CE76FDC5111EE37C006618EA /* NSIndexPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */; };
|
CE76FDC5111EE37C006618EA /* NSIndexPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */; };
|
||||||
@@ -30,9 +31,9 @@
|
|||||||
CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDDE111EE42F006618EA /* HSOutline.m */; };
|
CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDDE111EE42F006618EA /* HSOutline.m */; };
|
||||||
CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDF6111EE561006618EA /* NSEventAdditions.m */; };
|
CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDF6111EE561006618EA /* NSEventAdditions.m */; };
|
||||||
CE8C53BC117324CE0011B41F /* HSTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CE8C53BB117324CE0011B41F /* HSTable.m */; };
|
CE8C53BC117324CE0011B41F /* HSTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CE8C53BB117324CE0011B41F /* HSTable.m */; };
|
||||||
CE91F215113BC22D0010360B /* ResultOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE91F212113BC22D0010360B /* ResultOutline.m */; };
|
|
||||||
CE91F216113BC22D0010360B /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE91F214113BC22D0010360B /* StatsLabel.m */; };
|
CE91F216113BC22D0010360B /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE91F214113BC22D0010360B /* StatsLabel.m */; };
|
||||||
CEAC6811109B0B7E00B43C85 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEAC6810109B0B7E00B43C85 /* Preferences.xib */; };
|
CEAC6811109B0B7E00B43C85 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEAC6810109B0B7E00B43C85 /* Preferences.xib */; };
|
||||||
|
CEBC6C3912144A4B007B43AE /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEBC6C3712144A4B007B43AE /* registration.xib */; };
|
||||||
CEBE4D74111F0EE1009AAC6D /* HSWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CEBE4D73111F0EE1009AAC6D /* HSWindowController.m */; };
|
CEBE4D74111F0EE1009AAC6D /* HSWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CEBE4D73111F0EE1009AAC6D /* HSWindowController.m */; };
|
||||||
CEDD92DA0FDD01640031C7B7 /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDD92D70FDD01640031C7B7 /* BRSingleLineFormatter.m */; };
|
CEDD92DA0FDD01640031C7B7 /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDD92D70FDD01640031C7B7 /* BRSingleLineFormatter.m */; };
|
||||||
CEE7EA130FE675C80004E467 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEE7EA120FE675C80004E467 /* DetailsPanel.m */; };
|
CEE7EA130FE675C80004E467 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEE7EA120FE675C80004E467 /* DetailsPanel.m */; };
|
||||||
@@ -78,7 +79,6 @@
|
|||||||
CE073F5409CAE1A3005C1D2F /* dupeguru_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_help; path = ../../help_se/dupeguru_help; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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; };
|
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; };
|
CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -90,6 +90,11 @@
|
|||||||
CE647E551173024A006D28BA /* ProblemDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProblemDialog.m; path = ../base/ProblemDialog.m; sourceTree = SOURCE_ROOT; };
|
CE647E551173024A006D28BA /* ProblemDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProblemDialog.m; path = ../base/ProblemDialog.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE647E561173024A006D28BA /* PyProblemDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyProblemDialog.h; path = ../base/PyProblemDialog.h; sourceTree = SOURCE_ROOT; };
|
CE647E561173024A006D28BA /* PyProblemDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyProblemDialog.h; path = ../base/PyProblemDialog.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE647E581173026F006D28BA /* ProblemDialog.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ProblemDialog.xib; path = ../base/xib/ProblemDialog.xib; sourceTree = SOURCE_ROOT; };
|
CE647E581173026F006D28BA /* ProblemDialog.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ProblemDialog.xib; path = ../base/xib/ProblemDialog.xib; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE6DD4E4124CA3070089A48D /* PyResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTable.h; path = ../base/PyResultTable.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE6DD4E5124CA3070089A48D /* ResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultTable.h; path = ../base/ResultTable.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE6DD4E6124CA3070089A48D /* ResultTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultTable.m; path = ../base/ResultTable.m; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE6DD545124CAF1F0089A48D /* HSTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSTableView.h; path = ../../cocoalib/views/HSTableView.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
CE6DD546124CAF1F0089A48D /* HSTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSTableView.m; path = ../../cocoalib/views/HSTableView.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>"; };
|
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; };
|
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>"; };
|
CE76FDBE111EE37C006618EA /* HSOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutlineView.h; sourceTree = "<group>"; };
|
||||||
@@ -111,13 +116,11 @@
|
|||||||
CE76FDF6111EE561006618EA /* NSEventAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSEventAdditions.m; path = ../../cocoalib/NSEventAdditions.m; sourceTree = SOURCE_ROOT; };
|
CE76FDF6111EE561006618EA /* NSEventAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSEventAdditions.m; path = ../../cocoalib/NSEventAdditions.m; sourceTree = SOURCE_ROOT; };
|
||||||
CE8C53B61173248F0011B41F /* PyTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyTable.h; sourceTree = "<group>"; };
|
CE8C53B61173248F0011B41F /* PyTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyTable.h; sourceTree = "<group>"; };
|
||||||
CE8C53BB117324CE0011B41F /* HSTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSTable.m; sourceTree = "<group>"; };
|
CE8C53BB117324CE0011B41F /* HSTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSTable.m; sourceTree = "<group>"; };
|
||||||
CE91F20F113BC22D0010360B /* PyResultTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTree.h; path = ../base/PyResultTree.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE91F210113BC22D0010360B /* PyStatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyStatsLabel.h; path = ../base/PyStatsLabel.h; sourceTree = SOURCE_ROOT; };
|
CE91F210113BC22D0010360B /* PyStatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyStatsLabel.h; path = ../base/PyStatsLabel.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE91F211113BC22D0010360B /* ResultOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultOutline.h; path = ../base/ResultOutline.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE91F212113BC22D0010360B /* ResultOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultOutline.m; path = ../base/ResultOutline.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE91F213113BC22D0010360B /* StatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StatsLabel.h; path = ../base/StatsLabel.h; sourceTree = SOURCE_ROOT; };
|
CE91F213113BC22D0010360B /* StatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StatsLabel.h; path = ../base/StatsLabel.h; sourceTree = SOURCE_ROOT; };
|
||||||
CE91F214113BC22D0010360B /* StatsLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StatsLabel.m; path = ../base/StatsLabel.m; sourceTree = SOURCE_ROOT; };
|
CE91F214113BC22D0010360B /* StatsLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StatsLabel.m; path = ../base/StatsLabel.m; sourceTree = SOURCE_ROOT; };
|
||||||
CEAC6810109B0B7E00B43C85 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Preferences.xib; path = xib/Preferences.xib; sourceTree = "<group>"; };
|
CEAC6810109B0B7E00B43C85 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Preferences.xib; path = xib/Preferences.xib; sourceTree = "<group>"; };
|
||||||
|
CEBC6C3812144A4B007B43AE /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = ../../cocoalib/en.lproj/registration.xib; sourceTree = SOURCE_ROOT; };
|
||||||
CEBE4D72111F0EE1009AAC6D /* HSWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSWindowController.h; 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>"; };
|
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; };
|
CEDD92D60FDD01640031C7B7 /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -249,9 +252,9 @@
|
|||||||
CE19BC5F11199231007CCEB0 /* xib */ = {
|
CE19BC5F11199231007CCEB0 /* xib */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CEBC6C3712144A4B007B43AE /* registration.xib */,
|
||||||
CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */,
|
CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */,
|
||||||
CE19BC6111199231007CCEB0 /* progress.xib */,
|
CE19BC6111199231007CCEB0 /* progress.xib */,
|
||||||
CE19BC6211199231007CCEB0 /* registration.xib */,
|
|
||||||
);
|
);
|
||||||
name = xib;
|
name = xib;
|
||||||
path = ../../cocoalib/xib;
|
path = ../../cocoalib/xib;
|
||||||
@@ -260,6 +263,8 @@
|
|||||||
CE76FDBD111EE37C006618EA /* views */ = {
|
CE76FDBD111EE37C006618EA /* views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
CE6DD545124CAF1F0089A48D /* HSTableView.h */,
|
||||||
|
CE6DD546124CAF1F0089A48D /* HSTableView.m */,
|
||||||
CE76FDBE111EE37C006618EA /* HSOutlineView.h */,
|
CE76FDBE111EE37C006618EA /* HSOutlineView.h */,
|
||||||
CE76FDBF111EE37C006618EA /* HSOutlineView.m */,
|
CE76FDBF111EE37C006618EA /* HSOutlineView.m */,
|
||||||
CE76FDC0111EE37C006618EA /* NSIndexPathAdditions.h */,
|
CE76FDC0111EE37C006618EA /* NSIndexPathAdditions.h */,
|
||||||
@@ -362,10 +367,10 @@
|
|||||||
CEFC7FB00FC9518F00CD5728 /* dgbase */ = {
|
CEFC7FB00FC9518F00CD5728 /* dgbase */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
CE91F20F113BC22D0010360B /* PyResultTree.h */,
|
CE6DD4E4124CA3070089A48D /* PyResultTable.h */,
|
||||||
|
CE6DD4E5124CA3070089A48D /* ResultTable.h */,
|
||||||
|
CE6DD4E6124CA3070089A48D /* ResultTable.m */,
|
||||||
CE91F210113BC22D0010360B /* PyStatsLabel.h */,
|
CE91F210113BC22D0010360B /* PyStatsLabel.h */,
|
||||||
CE91F211113BC22D0010360B /* ResultOutline.h */,
|
|
||||||
CE91F212113BC22D0010360B /* ResultOutline.m */,
|
|
||||||
CE91F213113BC22D0010360B /* StatsLabel.h */,
|
CE91F213113BC22D0010360B /* StatsLabel.h */,
|
||||||
CE91F214113BC22D0010360B /* StatsLabel.m */,
|
CE91F214113BC22D0010360B /* StatsLabel.m */,
|
||||||
CE76FDD1111EE3A7006618EA /* DirectoryOutline.h */,
|
CE76FDD1111EE3A7006618EA /* DirectoryOutline.h */,
|
||||||
@@ -422,6 +427,13 @@
|
|||||||
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */;
|
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */;
|
||||||
compatibilityVersion = "Xcode 3.0";
|
compatibilityVersion = "Xcode 3.0";
|
||||||
hasScannedForEncodings = 1;
|
hasScannedForEncodings = 1;
|
||||||
|
knownRegions = (
|
||||||
|
English,
|
||||||
|
Japanese,
|
||||||
|
French,
|
||||||
|
German,
|
||||||
|
en,
|
||||||
|
);
|
||||||
mainGroup = 29B97314FDCFA39411CA2CEA /* dupeguru */;
|
mainGroup = 29B97314FDCFA39411CA2CEA /* dupeguru */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
@@ -449,8 +461,8 @@
|
|||||||
CE3A46FA109B212E002ABFD5 /* MainMenu.xib in Resources */,
|
CE3A46FA109B212E002ABFD5 /* MainMenu.xib in Resources */,
|
||||||
CE19BC6311199231007CCEB0 /* ErrorReportWindow.xib in Resources */,
|
CE19BC6311199231007CCEB0 /* ErrorReportWindow.xib in Resources */,
|
||||||
CE19BC6411199231007CCEB0 /* progress.xib in Resources */,
|
CE19BC6411199231007CCEB0 /* progress.xib in Resources */,
|
||||||
CE19BC6511199231007CCEB0 /* registration.xib in Resources */,
|
|
||||||
CE647E591173026F006D28BA /* ProblemDialog.xib in Resources */,
|
CE647E591173026F006D28BA /* ProblemDialog.xib in Resources */,
|
||||||
|
CEBC6C3912144A4B007B43AE /* registration.xib in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -484,15 +496,28 @@
|
|||||||
CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */,
|
CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */,
|
||||||
CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */,
|
CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */,
|
||||||
CEBE4D74111F0EE1009AAC6D /* HSWindowController.m in Sources */,
|
CEBE4D74111F0EE1009AAC6D /* HSWindowController.m in Sources */,
|
||||||
CE91F215113BC22D0010360B /* ResultOutline.m in Sources */,
|
|
||||||
CE91F216113BC22D0010360B /* StatsLabel.m in Sources */,
|
CE91F216113BC22D0010360B /* StatsLabel.m in Sources */,
|
||||||
CE647E571173024A006D28BA /* ProblemDialog.m in Sources */,
|
CE647E571173024A006D28BA /* ProblemDialog.m in Sources */,
|
||||||
CE8C53BC117324CE0011B41F /* HSTable.m in Sources */,
|
CE8C53BC117324CE0011B41F /* HSTable.m in Sources */,
|
||||||
|
CE6DD4E7124CA3070089A48D /* ResultTable.m in Sources */,
|
||||||
|
CE6DD547124CAF1F0089A48D /* HSTableView.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
CEBC6C3712144A4B007B43AE /* registration.xib */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
CEBC6C3812144A4B007B43AE /* en */,
|
||||||
|
);
|
||||||
|
name = registration.xib;
|
||||||
|
path = ../../cocoalib/xib;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
C01FCF4C08A954540054247B /* release */ = {
|
C01FCF4C08A954540054247B /* release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
||||||
<data>
|
<data>
|
||||||
<int key="IBDocument.SystemTarget">1050</int>
|
<int key="IBDocument.SystemTarget">1050</int>
|
||||||
<string key="IBDocument.SystemVersion">10D573</string>
|
<string key="IBDocument.SystemVersion">10F569</string>
|
||||||
<string key="IBDocument.InterfaceBuilderVersion">740</string>
|
<string key="IBDocument.InterfaceBuilderVersion">788</string>
|
||||||
<string key="IBDocument.AppKitVersion">1038.29</string>
|
<string key="IBDocument.AppKitVersion">1038.29</string>
|
||||||
<string key="IBDocument.HIToolboxVersion">460.00</string>
|
<string key="IBDocument.HIToolboxVersion">461.00</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<string key="NS.object.0">740</string>
|
<string key="NS.object.0">788</string>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
@@ -623,10 +623,31 @@
|
|||||||
<int key="NSPeriodicInterval">25</int>
|
<int key="NSPeriodicInterval">25</int>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSButton" id="647216699">
|
||||||
|
<reference key="NSNextResponder" ref="448252432"/>
|
||||||
|
<int key="NSvFlags">256</int>
|
||||||
|
<string key="NSFrame">{{15, 171}, {265, 18}}</string>
|
||||||
|
<reference key="NSSuperview" ref="448252432"/>
|
||||||
|
<bool key="NSEnabled">YES</bool>
|
||||||
|
<object class="NSButtonCell" key="NSCell" id="118836063">
|
||||||
|
<int key="NSCellFlags">67239424</int>
|
||||||
|
<int key="NSCellFlags2">0</int>
|
||||||
|
<string key="NSContents">Ignore duplicates hardlinking to the same file</string>
|
||||||
|
<reference key="NSSupport" ref="26"/>
|
||||||
|
<reference key="NSControlView" ref="647216699"/>
|
||||||
|
<int key="NSButtonFlags">1211912703</int>
|
||||||
|
<int key="NSButtonFlags2">2</int>
|
||||||
|
<reference key="NSAlternateImage" ref="589920880"/>
|
||||||
|
<string key="NSAlternateContents"/>
|
||||||
|
<string key="NSKeyEquivalent"/>
|
||||||
|
<int key="NSPeriodicDelay">200</int>
|
||||||
|
<int key="NSPeriodicInterval">25</int>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="NSTextField" id="962804407">
|
<object class="NSTextField" id="962804407">
|
||||||
<reference key="NSNextResponder" ref="448252432"/>
|
<reference key="NSNextResponder" ref="448252432"/>
|
||||||
<int key="NSvFlags">292</int>
|
<int key="NSvFlags">292</int>
|
||||||
<string key="NSFrame">{{14, 168}, {315, 17}}</string>
|
<string key="NSFrame">{{14, 148}, {315, 17}}</string>
|
||||||
<reference key="NSSuperview" ref="448252432"/>
|
<reference key="NSSuperview" ref="448252432"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSTextFieldCell" key="NSCell" id="111092399">
|
<object class="NSTextFieldCell" key="NSCell" id="111092399">
|
||||||
@@ -642,7 +663,7 @@
|
|||||||
<object class="NSTextField" id="873032174">
|
<object class="NSTextField" id="873032174">
|
||||||
<reference key="NSNextResponder" ref="448252432"/>
|
<reference key="NSNextResponder" ref="448252432"/>
|
||||||
<int key="NSvFlags">266</int>
|
<int key="NSvFlags">266</int>
|
||||||
<string key="NSFrame">{{17, 138}, {309, 22}}</string>
|
<string key="NSFrame">{{17, 118}, {309, 22}}</string>
|
||||||
<reference key="NSSuperview" ref="448252432"/>
|
<reference key="NSSuperview" ref="448252432"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSTextFieldCell" key="NSCell" id="249250691">
|
<object class="NSTextFieldCell" key="NSCell" id="249250691">
|
||||||
@@ -659,7 +680,7 @@
|
|||||||
<object class="NSTextField" id="511043844">
|
<object class="NSTextField" id="511043844">
|
||||||
<reference key="NSNextResponder" ref="448252432"/>
|
<reference key="NSNextResponder" ref="448252432"/>
|
||||||
<int key="NSvFlags">292</int>
|
<int key="NSvFlags">292</int>
|
||||||
<string key="NSFrame">{{14, 117}, {85, 13}}</string>
|
<string key="NSFrame">{{14, 97}, {85, 13}}</string>
|
||||||
<reference key="NSSuperview" ref="448252432"/>
|
<reference key="NSSuperview" ref="448252432"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSTextFieldCell" key="NSCell" id="669919489">
|
<object class="NSTextFieldCell" key="NSCell" id="669919489">
|
||||||
@@ -675,7 +696,7 @@
|
|||||||
<object class="NSPopUpButton" id="857082145">
|
<object class="NSPopUpButton" id="857082145">
|
||||||
<reference key="NSNextResponder" ref="448252432"/>
|
<reference key="NSNextResponder" ref="448252432"/>
|
||||||
<int key="NSvFlags">292</int>
|
<int key="NSvFlags">292</int>
|
||||||
<string key="NSFrame">{{101, 106}, {216, 26}}</string>
|
<string key="NSFrame">{{101, 86}, {216, 26}}</string>
|
||||||
<reference key="NSSuperview" ref="448252432"/>
|
<reference key="NSSuperview" ref="448252432"/>
|
||||||
<bool key="NSEnabled">YES</bool>
|
<bool key="NSEnabled">YES</bool>
|
||||||
<object class="NSPopUpButtonCell" key="NSCell" id="307074513">
|
<object class="NSPopUpButtonCell" key="NSCell" id="307074513">
|
||||||
@@ -1068,6 +1089,22 @@
|
|||||||
</object>
|
</object>
|
||||||
<int key="connectionID">140</int>
|
<int key="connectionID">140</int>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBConnectionRecord">
|
||||||
|
<object class="IBBindingConnection" key="connection">
|
||||||
|
<string key="label">value: values.ignoreHardlinkMatches</string>
|
||||||
|
<reference key="source" ref="647216699"/>
|
||||||
|
<reference key="destination" ref="75941798"/>
|
||||||
|
<object class="NSNibBindingConnector" key="connector">
|
||||||
|
<reference key="NSSource" ref="647216699"/>
|
||||||
|
<reference key="NSDestination" ref="75941798"/>
|
||||||
|
<string key="NSLabel">value: values.ignoreHardlinkMatches</string>
|
||||||
|
<string key="NSBinding">value</string>
|
||||||
|
<string key="NSKeyPath">values.ignoreHardlinkMatches</string>
|
||||||
|
<int key="NSNibBindingConnectorVersion">2</int>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
|
<int key="connectionID">144</int>
|
||||||
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||||
<object class="NSArray" key="orderedObjects">
|
<object class="NSArray" key="orderedObjects">
|
||||||
@@ -1170,6 +1207,7 @@
|
|||||||
<object class="NSMutableArray" key="children">
|
<object class="NSMutableArray" key="children">
|
||||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
<reference ref="724127338"/>
|
<reference ref="724127338"/>
|
||||||
|
<reference ref="647216699"/>
|
||||||
<reference ref="962804407"/>
|
<reference ref="962804407"/>
|
||||||
<reference ref="873032174"/>
|
<reference ref="873032174"/>
|
||||||
<reference ref="511043844"/>
|
<reference ref="511043844"/>
|
||||||
@@ -1552,6 +1590,20 @@
|
|||||||
<reference key="object" ref="97487610"/>
|
<reference key="object" ref="97487610"/>
|
||||||
<reference key="parent" ref="593115874"/>
|
<reference key="parent" ref="593115874"/>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBObjectRecord">
|
||||||
|
<int key="objectID">141</int>
|
||||||
|
<reference key="object" ref="647216699"/>
|
||||||
|
<object class="NSMutableArray" key="children">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<reference ref="118836063"/>
|
||||||
|
</object>
|
||||||
|
<reference key="parent" ref="448252432"/>
|
||||||
|
</object>
|
||||||
|
<object class="IBObjectRecord">
|
||||||
|
<int key="objectID">142</int>
|
||||||
|
<reference key="object" ref="118836063"/>
|
||||||
|
<reference key="parent" ref="647216699"/>
|
||||||
|
</object>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<object class="NSMutableDictionary" key="flattenedProperties">
|
<object class="NSMutableDictionary" key="flattenedProperties">
|
||||||
@@ -1573,6 +1625,9 @@
|
|||||||
<string>134.IBPluginDependency</string>
|
<string>134.IBPluginDependency</string>
|
||||||
<string>138.IBPluginDependency</string>
|
<string>138.IBPluginDependency</string>
|
||||||
<string>139.IBPluginDependency</string>
|
<string>139.IBPluginDependency</string>
|
||||||
|
<string>141.IBPluginDependency</string>
|
||||||
|
<string>141.ImportedFromIB2</string>
|
||||||
|
<string>142.IBPluginDependency</string>
|
||||||
<string>51.IBPluginDependency</string>
|
<string>51.IBPluginDependency</string>
|
||||||
<string>51.ImportedFromIB2</string>
|
<string>51.ImportedFromIB2</string>
|
||||||
<string>52.IBEditorWindowLastContentRect</string>
|
<string>52.IBEditorWindowLastContentRect</string>
|
||||||
@@ -1675,6 +1730,9 @@
|
|||||||
<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"/>
|
<boolean value="YES"/>
|
||||||
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
|
<boolean value="YES"/>
|
||||||
<string>{{88, 543}, {389, 302}}</string>
|
<string>{{88, 543}, {389, 302}}</string>
|
||||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||||
<string>{{88, 543}, {389, 302}}</string>
|
<string>{{88, 543}, {389, 302}}</string>
|
||||||
@@ -1773,7 +1831,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<nil key="sourceID"/>
|
<nil key="sourceID"/>
|
||||||
<int key="maxID">140</int>
|
<int key="maxID">144</int>
|
||||||
</object>
|
</object>
|
||||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
<object class="IBClassDescriber" key="IBDocument.Classes">
|
||||||
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
||||||
@@ -1785,6 +1843,13 @@
|
|||||||
<string key="minorKey">../views/HSOutlineView.h</string>
|
<string key="minorKey">../views/HSOutlineView.h</string>
|
||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="IBPartialClassDescription">
|
||||||
|
<string key="className">NSObject</string>
|
||||||
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
|
<string key="majorKey">IBProjectSource</string>
|
||||||
|
<string key="minorKey">../views/HSTableView.h</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBPartialClassDescription">
|
<object class="IBPartialClassDescription">
|
||||||
<string key="className">NSObject</string>
|
<string key="className">NSObject</string>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
@@ -2323,6 +2388,13 @@
|
|||||||
<string key="NS.key.0">showWindow:</string>
|
<string key="NS.key.0">showWindow:</string>
|
||||||
<string key="NS.object.0">id</string>
|
<string key="NS.object.0">id</string>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="NSMutableDictionary" key="actionInfosByName">
|
||||||
|
<string key="NS.key.0">showWindow:</string>
|
||||||
|
<object class="IBActionInfo" key="NS.object.0">
|
||||||
|
<string key="name">showWindow:</string>
|
||||||
|
<string key="candidateClassName">id</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
<object class="IBClassDescriptionSource" key="sourceIdentifier">
|
||||||
<string key="majorKey">IBFrameworkSource</string>
|
<string key="majorKey">IBFrameworkSource</string>
|
||||||
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
|
||||||
@@ -2331,6 +2403,7 @@
|
|||||||
</object>
|
</object>
|
||||||
</object>
|
</object>
|
||||||
<int key="IBDocument.localizationMode">0</int>
|
<int key="IBDocument.localizationMode">0</int>
|
||||||
|
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
|
||||||
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
|
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
|
||||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
|
||||||
<integer value="1050" key="NS.object.0"/>
|
<integer value="1050" key="NS.object.0"/>
|
||||||
@@ -2342,5 +2415,18 @@
|
|||||||
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
|
||||||
<string key="IBDocument.LastKnownRelativeProjectPath">../dupeguru.xcodeproj</string>
|
<string key="IBDocument.LastKnownRelativeProjectPath">../dupeguru.xcodeproj</string>
|
||||||
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
<int key="IBDocument.defaultPropertyAccessControl">3</int>
|
||||||
|
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<object class="NSArray" key="dict.sortedKeys">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<string>NSMenuCheckmark</string>
|
||||||
|
<string>NSMenuMixedState</string>
|
||||||
|
</object>
|
||||||
|
<object class="NSMutableArray" key="dict.values">
|
||||||
|
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||||
|
<string>{9, 8}</string>
|
||||||
|
<string>{7, 2}</string>
|
||||||
|
</object>
|
||||||
|
</object>
|
||||||
</data>
|
</data>
|
||||||
</archive>
|
</archive>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ def main(edition, ui, dev):
|
|||||||
if ui not in ('cocoa', 'qt'):
|
if ui not in ('cocoa', 'qt'):
|
||||||
ui = 'cocoa' if sys.platform == 'darwin' else 'qt'
|
ui = 'cocoa' if sys.platform == 'darwin' else 'qt'
|
||||||
build_type = 'Dev' if dev else 'Release'
|
build_type = 'Dev' if dev else 'Release'
|
||||||
print "Configuring dupeGuru {0} for UI {1} ({2})".format(edition.upper(), ui, build_type)
|
print("Configuring dupeGuru {0} for UI {1} ({2})".format(edition.upper(), ui, build_type))
|
||||||
conf = {
|
conf = {
|
||||||
'edition': edition,
|
'edition': edition,
|
||||||
'ui': ui,
|
'ui': ui,
|
||||||
|
|||||||
89
core/app.py
89
core/app.py
@@ -6,8 +6,6 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import os.path as op
|
import os.path as op
|
||||||
import logging
|
import logging
|
||||||
@@ -33,9 +31,6 @@ JOB_DELETE = 'job_delete'
|
|||||||
class NoScannableFileError(Exception):
|
class NoScannableFileError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class AllFilesAreRefError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class DupeGuru(RegistrableApplication, Broadcaster):
|
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."
|
DEMO_LIMIT_DESC = "In the demo version, only 10 duplicates per session can be sent to the recycle bin, moved or copied."
|
||||||
|
|
||||||
@@ -53,6 +48,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
self.options = {
|
self.options = {
|
||||||
'escape_filter_regexp': True,
|
'escape_filter_regexp': True,
|
||||||
'clean_empty_dirs': False,
|
'clean_empty_dirs': False,
|
||||||
|
'ignore_hardlink_matches': False,
|
||||||
}
|
}
|
||||||
self.selected_dupes = []
|
self.selected_dupes = []
|
||||||
|
|
||||||
@@ -65,34 +61,28 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
else:
|
else:
|
||||||
self.action_count += count
|
self.action_count += count
|
||||||
|
|
||||||
def _do_delete(self, j):
|
def _do_delete(self, j, replace_with_hardlinks):
|
||||||
def op(dupe):
|
def op(dupe):
|
||||||
j.add_progress()
|
j.add_progress()
|
||||||
return self._do_delete_dupe(dupe)
|
return self._do_delete_dupe(dupe, replace_with_hardlinks)
|
||||||
|
|
||||||
j.start_job(self.results.mark_count)
|
j.start_job(self.results.mark_count)
|
||||||
self.results.perform_on_marked(op, True)
|
self.results.perform_on_marked(op, True)
|
||||||
|
|
||||||
def _do_delete_dupe(self, dupe):
|
def _do_delete_dupe(self, dupe, replace_with_hardlinks):
|
||||||
if not io.exists(dupe.path):
|
if not io.exists(dupe.path):
|
||||||
return
|
return
|
||||||
send2trash(unicode(dupe.path)) # Raises OSError when there's a problem
|
send2trash(str(dupe.path)) # Raises OSError when there's a problem
|
||||||
|
if replace_with_hardlinks:
|
||||||
|
group = self.results.get_group_of_duplicate(dupe)
|
||||||
|
ref = group.ref
|
||||||
|
os.link(str(ref.path), str(dupe.path))
|
||||||
self.clean_empty_dirs(dupe.path[:-1])
|
self.clean_empty_dirs(dupe.path[:-1])
|
||||||
|
|
||||||
def _do_load(self, j):
|
def _do_load(self, j):
|
||||||
self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml'))
|
self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml'))
|
||||||
self.notify('directories_changed')
|
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)
|
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)
|
|
||||||
try:
|
|
||||||
for file in j.iter_with_progress(files, 'Reading metadata %d/%d'):
|
|
||||||
file._read_all_info(attrnames=self.data.METADATA_TO_READ)
|
|
||||||
except (OSError, IOError):
|
|
||||||
# If this error is raised, it means that a file was deleted while we were reading
|
|
||||||
# metadata. Proper handling of this rare occurrence is complex because there's no easy
|
|
||||||
# way to remove an arbitrary file from the Results. Thus, we simply clear them.
|
|
||||||
self.results.groups = []
|
|
||||||
|
|
||||||
def _get_display_info(self, dupe, group, delta=False):
|
def _get_display_info(self, dupe, group, delta=False):
|
||||||
if (dupe is None) or (group is None):
|
if (dupe is None) or (group is None):
|
||||||
@@ -100,12 +90,19 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
try:
|
try:
|
||||||
return self.data.GetDisplayInfo(dupe, group, delta)
|
return self.data.GetDisplayInfo(dupe, group, delta)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning("Exception on GetDisplayInfo for %s: %s", unicode(dupe.path), unicode(e))
|
logging.warning("Exception on GetDisplayInfo for %s: %s", str(dupe.path), str(e))
|
||||||
return ['---'] * len(self.data.COLUMNS)
|
return ['---'] * len(self.data.COLUMNS)
|
||||||
|
|
||||||
def _get_file(self, str_path):
|
def _get_file(self, str_path):
|
||||||
path = Path(str_path)
|
path = Path(str_path)
|
||||||
return fs.get_file(path, self.directories.fileclasses)
|
f = fs.get_file(path, self.directories.fileclasses)
|
||||||
|
if f is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
f._read_all_info(attrnames=self.data.METADATA_TO_READ)
|
||||||
|
return f
|
||||||
|
except EnvironmentError:
|
||||||
|
return None
|
||||||
|
|
||||||
def _job_completed(self, jobid):
|
def _job_completed(self, jobid):
|
||||||
# Must be called by subclasses when they detect that an async job is completed.
|
# Must be called by subclasses when they detect that an async job is completed.
|
||||||
@@ -123,14 +120,25 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
def _reveal_path(path):
|
def _reveal_path(path):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _remove_hardlink_dupes(files):
|
||||||
|
seen_inodes = set()
|
||||||
|
result = []
|
||||||
|
for file in files:
|
||||||
|
inode = io.stat(file.path).st_ino
|
||||||
|
if inode not in seen_inodes:
|
||||||
|
seen_inodes.add(inode)
|
||||||
|
result.append(file)
|
||||||
|
return result
|
||||||
|
|
||||||
def _select_dupes(self, dupes):
|
def _select_dupes(self, dupes):
|
||||||
if dupes == self.selected_dupes:
|
if dupes == self.selected_dupes:
|
||||||
return
|
return
|
||||||
self.selected_dupes = dupes
|
self.selected_dupes = dupes
|
||||||
self.notify('dupes_selected')
|
self.notify('dupes_selected')
|
||||||
|
|
||||||
def _start_job(self, jobid, func):
|
def _start_job(self, jobid, func, *args):
|
||||||
# func(j)
|
# func(j, *args)
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def add_directory(self, d):
|
def add_directory(self, d):
|
||||||
@@ -149,7 +157,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
g = self.results.get_group_of_duplicate(dupe)
|
g = self.results.get_group_of_duplicate(dupe)
|
||||||
for other in g:
|
for other in g:
|
||||||
if other is not dupe:
|
if other is not dupe:
|
||||||
self.scanner.ignore_list.Ignore(unicode(other.path), unicode(dupe.path))
|
self.scanner.ignore_list.Ignore(str(other.path), str(dupe.path))
|
||||||
self.remove_duplicates(dupes)
|
self.remove_duplicates(dupes)
|
||||||
|
|
||||||
def apply_filter(self, filter):
|
def apply_filter(self, filter):
|
||||||
@@ -202,13 +210,13 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
jobid = JOB_COPY if copy else JOB_MOVE
|
jobid = JOB_COPY if copy else JOB_MOVE
|
||||||
self._start_job(jobid, do)
|
self._start_job(jobid, do)
|
||||||
|
|
||||||
def delete_marked(self):
|
def delete_marked(self, replace_with_hardlinks=False):
|
||||||
self._demo_check()
|
self._demo_check()
|
||||||
self._start_job(JOB_DELETE, self._do_delete)
|
self._start_job(JOB_DELETE, self._do_delete, replace_with_hardlinks)
|
||||||
|
|
||||||
def export_to_xhtml(self, column_ids):
|
def export_to_xhtml(self, column_ids):
|
||||||
column_ids = [colid for colid in column_ids if colid.isdigit()]
|
column_ids = [colid for colid in column_ids if colid.isdigit()]
|
||||||
column_ids = map(int, column_ids)
|
column_ids = list(map(int, column_ids))
|
||||||
column_ids.sort()
|
column_ids.sort()
|
||||||
colnames = [col['display'] for i, col in enumerate(self.data.COLUMNS) if i in column_ids]
|
colnames = [col['display'] for i, col in enumerate(self.data.COLUMNS) if i in column_ids]
|
||||||
rows = []
|
rows = []
|
||||||
@@ -232,8 +240,8 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
dupe = self.selected_dupes[0]
|
dupe = self.selected_dupes[0]
|
||||||
group = self.results.get_group_of_duplicate(dupe)
|
group = self.results.get_group_of_duplicate(dupe)
|
||||||
ref = group.ref
|
ref = group.ref
|
||||||
cmd = cmd.replace('%d', unicode(dupe.path))
|
cmd = cmd.replace('%d', str(dupe.path))
|
||||||
cmd = cmd.replace('%r', unicode(ref.path))
|
cmd = cmd.replace('%r', str(ref.path))
|
||||||
match = re.match(r'"([^"]+)"(.*)', cmd)
|
match = re.match(r'"([^"]+)"(.*)', cmd)
|
||||||
if match is not None:
|
if match is not None:
|
||||||
# This code here is because subprocess. Popen doesn't seem to accept, under Windows,
|
# This code here is because subprocess. Popen doesn't seem to accept, under Windows,
|
||||||
@@ -249,6 +257,11 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
self._start_job(JOB_LOAD, self._do_load)
|
self._start_job(JOB_LOAD, self._do_load)
|
||||||
self.load_ignore_list()
|
self.load_ignore_list()
|
||||||
|
|
||||||
|
def load_from(self, filename):
|
||||||
|
def do(j):
|
||||||
|
self.results.load_from_xml(filename, self._get_file, j)
|
||||||
|
self._start_job(JOB_LOAD, do)
|
||||||
|
|
||||||
def load_ignore_list(self):
|
def load_ignore_list(self):
|
||||||
p = op.join(self.appdata, 'ignore_list.xml')
|
p = op.join(self.appdata, 'ignore_list.xml')
|
||||||
self.scanner.ignore_list.load_from_xml(p)
|
self.scanner.ignore_list.load_from_xml(p)
|
||||||
@@ -313,7 +326,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
d.rename(newname)
|
d.rename(newname)
|
||||||
return True
|
return True
|
||||||
except (IndexError, fs.FSError) as e:
|
except (IndexError, fs.FSError) as e:
|
||||||
logging.warning("dupeGuru Warning: %s" % unicode(e))
|
logging.warning("dupeGuru Warning: %s" % str(e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def reveal_selected(self):
|
def reveal_selected(self):
|
||||||
@@ -324,7 +337,13 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
if not op.exists(self.appdata):
|
if not op.exists(self.appdata):
|
||||||
os.makedirs(self.appdata)
|
os.makedirs(self.appdata)
|
||||||
self.directories.save_to_file(op.join(self.appdata, 'last_directories.xml'))
|
self.directories.save_to_file(op.join(self.appdata, 'last_directories.xml'))
|
||||||
self.results.save_to_xml(op.join(self.appdata, 'last_results.xml'))
|
if self.results.is_modified:
|
||||||
|
self.results.save_to_xml(op.join(self.appdata, 'last_results.xml'))
|
||||||
|
|
||||||
|
def save_as(self, filename):
|
||||||
|
self.results.save_to_xml(filename)
|
||||||
|
# It's not because we saved it here that we don't want to save it in appdata when we quit
|
||||||
|
self.results.is_modified = True
|
||||||
|
|
||||||
def save_ignore_list(self):
|
def save_ignore_list(self):
|
||||||
if not op.exists(self.appdata):
|
if not op.exists(self.appdata):
|
||||||
@@ -336,15 +355,13 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
|||||||
def do(j):
|
def do(j):
|
||||||
j.set_progress(0, 'Collecting files to scan')
|
j.set_progress(0, 'Collecting files to scan')
|
||||||
files = list(self.directories.get_files())
|
files = list(self.directories.get_files())
|
||||||
|
if self.options['ignore_hardlink_matches']:
|
||||||
|
files = self._remove_hardlink_dupes(files)
|
||||||
logging.info('Scanning %d files' % len(files))
|
logging.info('Scanning %d files' % len(files))
|
||||||
self.results.groups = self.scanner.GetDupeGroups(files, j)
|
self.results.groups = self.scanner.GetDupeGroups(files, j)
|
||||||
|
|
||||||
files = self.directories.get_files()
|
if not self.directories.has_any_file():
|
||||||
first_file = first(files)
|
|
||||||
if first_file is None:
|
|
||||||
raise NoScannableFileError()
|
raise NoScannableFileError()
|
||||||
if first_file.is_ref and all(f.is_ref for f in files):
|
|
||||||
raise AllFilesAreRefError()
|
|
||||||
self.results.groups = []
|
self.results.groups = []
|
||||||
self._start_job(JOB_SCAN, do)
|
self._start_job(JOB_SCAN, do)
|
||||||
|
|
||||||
|
|||||||
@@ -49,16 +49,17 @@ class DupeGuru(app.DupeGuru):
|
|||||||
#--- Override
|
#--- Override
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _open_path(path):
|
def _open_path(path):
|
||||||
NSWorkspace.sharedWorkspace().openFile_(unicode(path))
|
NSWorkspace.sharedWorkspace().openFile_(str(path))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _reveal_path(path):
|
def _reveal_path(path):
|
||||||
NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(unicode(path), '')
|
NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(str(path), '')
|
||||||
|
|
||||||
def _start_job(self, jobid, func):
|
def _start_job(self, jobid, func, *args):
|
||||||
try:
|
try:
|
||||||
j = self.progress.create_job()
|
j = self.progress.create_job()
|
||||||
self.progress.run_threaded(func, args=(j, ))
|
args = tuple([j] + list(args))
|
||||||
|
self.progress.run_threaded(func, args=args)
|
||||||
except job.JobInProgressError:
|
except job.JobInProgressError:
|
||||||
NSNotificationCenter.defaultCenter().postNotificationName_object_('JobInProgress', self)
|
NSNotificationCenter.defaultCenter().postNotificationName_object_('JobInProgress', self)
|
||||||
else:
|
else:
|
||||||
@@ -76,6 +77,4 @@ class DupeGuru(app.DupeGuru):
|
|||||||
return 0
|
return 0
|
||||||
except app.NoScannableFileError:
|
except app.NoScannableFileError:
|
||||||
return 3
|
return 3
|
||||||
except app.AllFilesAreRefError:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from .gui.details_panel import DetailsPanel
|
|||||||
from .gui.directory_tree import DirectoryTree
|
from .gui.directory_tree import DirectoryTree
|
||||||
from .gui.problem_dialog import ProblemDialog
|
from .gui.problem_dialog import ProblemDialog
|
||||||
from .gui.problem_table import ProblemTable
|
from .gui.problem_table import ProblemTable
|
||||||
from .gui.result_tree import ResultTree
|
from .gui.result_table import ResultTable
|
||||||
from .gui.stats_label import StatsLabel
|
from .gui.stats_label import StatsLabel
|
||||||
|
|
||||||
# Fix py2app's problems on relative imports
|
# Fix py2app's problems on relative imports
|
||||||
@@ -46,6 +46,9 @@ class PyDupeGuruBase(PyRegistrable):
|
|||||||
def loadResults(self):
|
def loadResults(self):
|
||||||
self.py.load()
|
self.py.load()
|
||||||
|
|
||||||
|
def loadResultsFrom_(self, filename):
|
||||||
|
self.py.load_from(filename)
|
||||||
|
|
||||||
def markAll(self):
|
def markAll(self):
|
||||||
self.py.mark_all()
|
self.py.mark_all()
|
||||||
|
|
||||||
@@ -67,6 +70,9 @@ class PyDupeGuruBase(PyRegistrable):
|
|||||||
def saveResults(self):
|
def saveResults(self):
|
||||||
self.py.save()
|
self.py.save()
|
||||||
|
|
||||||
|
def saveResultsAs_(self, filename):
|
||||||
|
self.py.save_as(filename)
|
||||||
|
|
||||||
#---Actions
|
#---Actions
|
||||||
def addSelectedToIgnoreList(self):
|
def addSelectedToIgnoreList(self):
|
||||||
self.py.add_selected_to_ignore_list()
|
self.py.add_selected_to_ignore_list()
|
||||||
@@ -74,6 +80,9 @@ class PyDupeGuruBase(PyRegistrable):
|
|||||||
def deleteMarked(self):
|
def deleteMarked(self):
|
||||||
self.py.delete_marked()
|
self.py.delete_marked()
|
||||||
|
|
||||||
|
def hardlinkMarked(self):
|
||||||
|
self.py.delete_marked(replace_with_hardlinks=True)
|
||||||
|
|
||||||
def applyFilter_(self, filter):
|
def applyFilter_(self, filter):
|
||||||
self.py.apply_filter(filter)
|
self.py.apply_filter(filter)
|
||||||
|
|
||||||
@@ -110,18 +119,31 @@ class PyDupeGuruBase(PyRegistrable):
|
|||||||
return bool(self.py.results.problems)
|
return bool(self.py.results.problems)
|
||||||
|
|
||||||
#---Properties
|
#---Properties
|
||||||
|
@signature('v@:c')
|
||||||
def setMixFileKind_(self, mix_file_kind):
|
def setMixFileKind_(self, mix_file_kind):
|
||||||
self.py.scanner.mix_file_kind = mix_file_kind
|
self.py.scanner.mix_file_kind = mix_file_kind
|
||||||
|
|
||||||
|
@signature('v@:c')
|
||||||
def setEscapeFilterRegexp_(self, escape_filter_regexp):
|
def setEscapeFilterRegexp_(self, escape_filter_regexp):
|
||||||
self.py.options['escape_filter_regexp'] = escape_filter_regexp
|
self.py.options['escape_filter_regexp'] = escape_filter_regexp
|
||||||
|
|
||||||
|
@signature('v@:c')
|
||||||
def setRemoveEmptyFolders_(self, remove_empty_folders):
|
def setRemoveEmptyFolders_(self, remove_empty_folders):
|
||||||
self.py.options['clean_empty_dirs'] = remove_empty_folders
|
self.py.options['clean_empty_dirs'] = remove_empty_folders
|
||||||
|
|
||||||
|
@signature('v@:c')
|
||||||
|
def setIgnoreHardlinkMatches_(self, ignore_hardlink_matches):
|
||||||
|
self.py.options['ignore_hardlink_matches'] = ignore_hardlink_matches
|
||||||
|
|
||||||
#---Worker
|
#---Worker
|
||||||
def getJobProgress(self):
|
def getJobProgress(self):
|
||||||
return self.py.progress.last_progress
|
try:
|
||||||
|
return self.py.progress.last_progress
|
||||||
|
except AttributeError:
|
||||||
|
# I have *no idea* why this can possible happen (last_progress is always set by
|
||||||
|
# create_job() *before* any threaded job notification, which shows the progress panel,
|
||||||
|
# is sent), but it happens anyway, so there we go.
|
||||||
|
return -1
|
||||||
|
|
||||||
def getJobDesc(self):
|
def getJobDesc(self):
|
||||||
return self.py.progress.last_desc
|
return self.py.progress.last_desc
|
||||||
@@ -151,8 +173,8 @@ class PyDirectoryOutline(PyOutline):
|
|||||||
self.py.add_directory(path)
|
self.py.add_directory(path)
|
||||||
|
|
||||||
|
|
||||||
class PyResultOutline(PyOutline):
|
class PyResultTable(PyTable):
|
||||||
py_class = ResultTree
|
py_class = ResultTable
|
||||||
|
|
||||||
@signature('c@:')
|
@signature('c@:')
|
||||||
def powerMarkerMode(self):
|
def powerMarkerMode(self):
|
||||||
@@ -170,9 +192,9 @@ class PyResultOutline(PyOutline):
|
|||||||
def setDeltaValuesMode_(self, value):
|
def setDeltaValuesMode_(self, value):
|
||||||
self.py.delta_values = value
|
self.py.delta_values = value
|
||||||
|
|
||||||
@signature('@@:@i')
|
@signature('@@:ii')
|
||||||
def valueForPath_column_(self, path, column):
|
def valueForRow_column_(self, row_index, column):
|
||||||
return self.py.get_node_value(path, column)
|
return self.py.get_row_value(row_index, column)
|
||||||
|
|
||||||
@signature('c@:@')
|
@signature('c@:@')
|
||||||
def renameSelected_(self, newname):
|
def renameSelected_(self, newname):
|
||||||
@@ -188,8 +210,9 @@ class PyResultOutline(PyOutline):
|
|||||||
def removeSelected(self):
|
def removeSelected(self):
|
||||||
self.py.app.remove_selected()
|
self.py.app.remove_selected()
|
||||||
|
|
||||||
def rootChildrenCounts(self):
|
@signature('i@:')
|
||||||
return self.py.root_children_counts()
|
def selectedDupeCount(self):
|
||||||
|
return self.py.selected_dupe_count
|
||||||
|
|
||||||
# python --> cocoa
|
# python --> cocoa
|
||||||
def invalidate_markings(self):
|
def invalidate_markings(self):
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from hsutil.str import format_time, FT_DECIMAL, format_size
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
def format_path(p):
|
def format_path(p):
|
||||||
return unicode(p[:-1])
|
return str(p[:-1])
|
||||||
|
|
||||||
def format_timestamp(t, delta):
|
def format_timestamp(t, delta):
|
||||||
if delta:
|
if delta:
|
||||||
@@ -38,4 +38,4 @@ def format_dupe_count(c):
|
|||||||
return str(c) if c else '---'
|
return str(c) if c else '---'
|
||||||
|
|
||||||
def cmp_value(value):
|
def cmp_value(value):
|
||||||
return value.lower() if isinstance(value, basestring) else value
|
return value.lower() if isinstance(value, str) else value
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
from lxml import etree
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
from hsutil import io
|
from hsutil import io
|
||||||
from hsutil.files import FileOrPath
|
from hsutil.files import FileOrPath
|
||||||
@@ -124,12 +124,19 @@ class Directories(object):
|
|||||||
else:
|
else:
|
||||||
return STATE_NORMAL
|
return STATE_NORMAL
|
||||||
|
|
||||||
|
def has_any_file(self):
|
||||||
|
try:
|
||||||
|
next(self.get_files())
|
||||||
|
return True
|
||||||
|
except StopIteration:
|
||||||
|
return False
|
||||||
|
|
||||||
def load_from_file(self, infile):
|
def load_from_file(self, infile):
|
||||||
try:
|
try:
|
||||||
root = etree.parse(infile).getroot()
|
root = ET.parse(infile).getroot()
|
||||||
except:
|
except Exception:
|
||||||
return
|
return
|
||||||
for rdn in root.iterchildren('root_directory'):
|
for rdn in root.getiterator('root_directory'):
|
||||||
attrib = rdn.attrib
|
attrib = rdn.attrib
|
||||||
if 'path' not in attrib:
|
if 'path' not in attrib:
|
||||||
continue
|
continue
|
||||||
@@ -138,7 +145,7 @@ class Directories(object):
|
|||||||
self.add_path(Path(path))
|
self.add_path(Path(path))
|
||||||
except (AlreadyThereError, InvalidPathError):
|
except (AlreadyThereError, InvalidPathError):
|
||||||
pass
|
pass
|
||||||
for sn in root.iterchildren('state'):
|
for sn in root.getiterator('state'):
|
||||||
attrib = sn.attrib
|
attrib = sn.attrib
|
||||||
if not ('path' in attrib and 'value' in attrib):
|
if not ('path' in attrib and 'value' in attrib):
|
||||||
continue
|
continue
|
||||||
@@ -148,15 +155,15 @@ class Directories(object):
|
|||||||
|
|
||||||
def save_to_file(self, outfile):
|
def save_to_file(self, outfile):
|
||||||
with FileOrPath(outfile, 'wb') as fp:
|
with FileOrPath(outfile, 'wb') as fp:
|
||||||
root = etree.Element('directories')
|
root = ET.Element('directories')
|
||||||
for root_path in self:
|
for root_path in self:
|
||||||
root_path_node = etree.SubElement(root, 'root_directory')
|
root_path_node = ET.SubElement(root, 'root_directory')
|
||||||
root_path_node.set('path', unicode(root_path))
|
root_path_node.set('path', str(root_path))
|
||||||
for path, state in self.states.iteritems():
|
for path, state in self.states.items():
|
||||||
state_node = etree.SubElement(root, 'state')
|
state_node = ET.SubElement(root, 'state')
|
||||||
state_node.set('path', unicode(path))
|
state_node.set('path', str(path))
|
||||||
state_node.set('value', unicode(state))
|
state_node.set('value', str(state))
|
||||||
tree = etree.ElementTree(root)
|
tree = ET.ElementTree(root)
|
||||||
tree.write(fp, encoding='utf-8')
|
tree.write(fp, encoding='utf-8')
|
||||||
|
|
||||||
def set_state(self, path, state):
|
def set_state(self, path, state):
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
from __future__ import division
|
|
||||||
import difflib
|
import difflib
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
@@ -25,15 +25,15 @@ NO_FIELD_ORDER) = range(3)
|
|||||||
JOB_REFRESH_RATE = 100
|
JOB_REFRESH_RATE = 100
|
||||||
|
|
||||||
def getwords(s):
|
def getwords(s):
|
||||||
if isinstance(s, unicode):
|
if isinstance(s, str):
|
||||||
s = normalize('NFD', s)
|
s = normalize('NFD', s)
|
||||||
s = multi_replace(s, "-_&+():;\\[]{}.,<>/?~!@#$*", ' ').lower()
|
s = multi_replace(s, "-_&+():;\\[]{}.,<>/?~!@#$*", ' ').lower()
|
||||||
s = ''.join(c for c in s if c in string.ascii_letters + string.digits + string.whitespace)
|
s = ''.join(c for c in s if c in string.ascii_letters + string.digits + string.whitespace)
|
||||||
return filter(None, s.split(' ')) # filter() is to remove empty elements
|
return [_f for _f in s.split(' ') if _f] # remove empty elements
|
||||||
|
|
||||||
def getfields(s):
|
def getfields(s):
|
||||||
fields = [getwords(field) for field in s.split(' - ')]
|
fields = [getwords(field) for field in s.split(' - ')]
|
||||||
return filter(None, fields)
|
return [_f for _f in fields if _f]
|
||||||
|
|
||||||
def unpack_fields(fields):
|
def unpack_fields(fields):
|
||||||
result = []
|
result = []
|
||||||
@@ -118,7 +118,7 @@ def build_word_dict(objects, j=job.nulljob):
|
|||||||
def merge_similar_words(word_dict):
|
def merge_similar_words(word_dict):
|
||||||
"""Take all keys in word_dict that are similar, and merge them together.
|
"""Take all keys in word_dict that are similar, and merge them together.
|
||||||
"""
|
"""
|
||||||
keys = word_dict.keys()
|
keys = list(word_dict.keys())
|
||||||
keys.sort(key=len)# we want the shortest word to stay
|
keys.sort(key=len)# we want the shortest word to stay
|
||||||
while keys:
|
while keys:
|
||||||
key = keys.pop(0)
|
key = keys.pop(0)
|
||||||
@@ -138,7 +138,7 @@ def reduce_common_words(word_dict, threshold):
|
|||||||
Because if we remove them, we will miss some duplicates!
|
Because if we remove them, we will miss some duplicates!
|
||||||
"""
|
"""
|
||||||
uncommon_words = set(word for word, objects in word_dict.items() if len(objects) < threshold)
|
uncommon_words = set(word for word, objects in word_dict.items() if len(objects) < threshold)
|
||||||
for word, objects in word_dict.items():
|
for word, objects in list(word_dict.items()):
|
||||||
if len(objects) < threshold:
|
if len(objects) < threshold:
|
||||||
continue
|
continue
|
||||||
reduced = set()
|
reduced = set()
|
||||||
|
|||||||
@@ -6,14 +6,13 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
import tempfile
|
|
||||||
import os.path as op
|
import os.path as op
|
||||||
from tempfile import mkdtemp
|
from tempfile import mkdtemp
|
||||||
|
|
||||||
# Yes, this is a very low-tech solution, but at least it doesn't have all these annoying dependency
|
# Yes, this is a very low-tech solution, but at least it doesn't have all these annoying dependency
|
||||||
# and resource problems.
|
# and resource problems.
|
||||||
|
|
||||||
MAIN_TEMPLATE = u"""
|
MAIN_TEMPLATE = """
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>
|
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
@@ -104,34 +103,34 @@ $rows
|
|||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
COLHEADERS_TEMPLATE = u"<th>{name}</th>"
|
COLHEADERS_TEMPLATE = "<th>{name}</th>"
|
||||||
|
|
||||||
ROW_TEMPLATE = u"""
|
ROW_TEMPLATE = """
|
||||||
<tr>
|
<tr>
|
||||||
<td class="{indented}">{filename}</td>{cells}
|
<td class="{indented}">{filename}</td>{cells}
|
||||||
</tr>
|
</tr>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
CELL_TEMPLATE = u"""<td>{value}</td>"""
|
CELL_TEMPLATE = """<td>{value}</td>"""
|
||||||
|
|
||||||
def export_to_xhtml(colnames, rows):
|
def export_to_xhtml(colnames, rows):
|
||||||
# a row is a list of values with the first value being a flag indicating if the row should be indented
|
# a row is a list of values with the first value being a flag indicating if the row should be indented
|
||||||
if rows:
|
if rows:
|
||||||
assert len(rows[0]) == len(colnames) + 1 # + 1 is for the "indented" flag
|
assert len(rows[0]) == len(colnames) + 1 # + 1 is for the "indented" flag
|
||||||
colheaders = u''.join(COLHEADERS_TEMPLATE.format(name=name) for name in colnames)
|
colheaders = ''.join(COLHEADERS_TEMPLATE.format(name=name) for name in colnames)
|
||||||
rendered_rows = []
|
rendered_rows = []
|
||||||
for row in rows:
|
for row in rows:
|
||||||
# [2:] is to remove the indented flag + filename
|
# [2:] is to remove the indented flag + filename
|
||||||
indented = u'indented' if row[0] else u''
|
indented = 'indented' if row[0] else ''
|
||||||
filename = row[1]
|
filename = row[1]
|
||||||
cells = u''.join(CELL_TEMPLATE.format(value=value) for value in row[2:])
|
cells = ''.join(CELL_TEMPLATE.format(value=value) for value in row[2:])
|
||||||
rendered_rows.append(ROW_TEMPLATE.format(indented=indented, filename=filename, cells=cells))
|
rendered_rows.append(ROW_TEMPLATE.format(indented=indented, filename=filename, cells=cells))
|
||||||
rendered_rows = u''.join(rendered_rows)
|
rendered_rows = ''.join(rendered_rows)
|
||||||
# The main template can't use format because the css code uses {}
|
# The main template can't use format because the css code uses {}
|
||||||
content = MAIN_TEMPLATE.replace('$colheaders', colheaders).replace('$rows', rendered_rows)
|
content = MAIN_TEMPLATE.replace('$colheaders', colheaders).replace('$rows', rendered_rows)
|
||||||
folder = mkdtemp()
|
folder = mkdtemp()
|
||||||
destpath = op.join(folder, u'export.htm')
|
destpath = op.join(folder, 'export.htm')
|
||||||
fp = open(destpath, 'w')
|
fp = open(destpath, 'wt', encoding='utf-8')
|
||||||
fp.write(content.encode('utf-8'))
|
fp.write(content)
|
||||||
fp.close()
|
fp.close()
|
||||||
return destpath
|
return destpath
|
||||||
|
|||||||
12
core/fs.py
12
core/fs.py
@@ -12,7 +12,7 @@
|
|||||||
# resulting needless complexity and memory usage. It's been a while since I wanted to do that fork,
|
# resulting needless complexity and memory usage. It's been a while since I wanted to do that fork,
|
||||||
# and I'm doing it now.
|
# and I'm doing it now.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
@@ -25,13 +25,13 @@ class FSError(Exception):
|
|||||||
cls_message = "An error has occured on '{name}' in '{parent}'"
|
cls_message = "An error has occured on '{name}' in '{parent}'"
|
||||||
def __init__(self, fsobject, parent=None):
|
def __init__(self, fsobject, parent=None):
|
||||||
message = self.cls_message
|
message = self.cls_message
|
||||||
if isinstance(fsobject, basestring):
|
if isinstance(fsobject, str):
|
||||||
name = fsobject
|
name = fsobject
|
||||||
elif isinstance(fsobject, File):
|
elif isinstance(fsobject, File):
|
||||||
name = fsobject.name
|
name = fsobject.name
|
||||||
else:
|
else:
|
||||||
name = ''
|
name = ''
|
||||||
parentname = unicode(parent) if parent is not None else ''
|
parentname = str(parent) if parent is not None else ''
|
||||||
Exception.__init__(self, message.format(name=name, parent=parentname))
|
Exception.__init__(self, message.format(name=name, parent=parentname))
|
||||||
|
|
||||||
|
|
||||||
@@ -55,7 +55,6 @@ class OperationError(FSError):
|
|||||||
class File(object):
|
class File(object):
|
||||||
INITIAL_INFO = {
|
INITIAL_INFO = {
|
||||||
'size': 0,
|
'size': 0,
|
||||||
'ctime': 0,
|
|
||||||
'mtime': 0,
|
'mtime': 0,
|
||||||
'md5': '',
|
'md5': '',
|
||||||
'md5partial': '',
|
'md5partial': '',
|
||||||
@@ -82,10 +81,9 @@ class File(object):
|
|||||||
raise AttributeError()
|
raise AttributeError()
|
||||||
|
|
||||||
def _read_info(self, field):
|
def _read_info(self, field):
|
||||||
if field in ('size', 'ctime', 'mtime'):
|
if field in ('size', 'mtime'):
|
||||||
stats = io.stat(self.path)
|
stats = io.stat(self.path)
|
||||||
self.size = nonone(stats.st_size, 0)
|
self.size = nonone(stats.st_size, 0)
|
||||||
self.ctime = nonone(stats.st_ctime, 0)
|
|
||||||
self.mtime = nonone(stats.st_mtime, 0)
|
self.mtime = nonone(stats.st_mtime, 0)
|
||||||
elif field == 'md5partial':
|
elif field == 'md5partial':
|
||||||
try:
|
try:
|
||||||
@@ -119,7 +117,7 @@ class File(object):
|
|||||||
If `attrnames` is not None, caches only attrnames.
|
If `attrnames` is not None, caches only attrnames.
|
||||||
"""
|
"""
|
||||||
if attrnames is None:
|
if attrnames is None:
|
||||||
attrnames = self.INITIAL_INFO.keys()
|
attrnames = list(self.INITIAL_INFO.keys())
|
||||||
for attrname in attrnames:
|
for attrname in attrnames:
|
||||||
if attrname not in self.__dict__:
|
if attrname not in self.__dict__:
|
||||||
self._read_info(attrname)
|
self._read_info(attrname)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class DetailsPanel(GUIObject):
|
|||||||
ref = group.ref if group is not None and group.ref is not dupe else None
|
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)
|
l2 = self.app._get_display_info(ref, group, False)
|
||||||
names = [c['display'] for c in self.app.data.COLUMNS]
|
names = [c['display'] for c in self.app.data.COLUMNS]
|
||||||
self._table = zip(names, l1, l2)
|
self._table = list(zip(names, l1, l2))
|
||||||
|
|
||||||
#--- Public
|
#--- Public
|
||||||
def row_count(self):
|
def row_count(self):
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class DirectoryTree(GUIObject, Tree):
|
|||||||
def _refresh(self):
|
def _refresh(self):
|
||||||
self.clear()
|
self.clear()
|
||||||
for path in self.app.directories:
|
for path in self.app.directories:
|
||||||
self.append(DirectoryNode(self.app, path, unicode(path)))
|
self.append(DirectoryNode(self.app, path, str(path)))
|
||||||
|
|
||||||
def add_directory(self, path):
|
def add_directory(self, path):
|
||||||
self.app.add_directory(path)
|
self.app.add_directory(path)
|
||||||
|
|||||||
@@ -39,5 +39,5 @@ class ProblemRow(Row):
|
|||||||
Row.__init__(self, table)
|
Row.__init__(self, table)
|
||||||
self.dupe = dupe
|
self.dupe = dupe
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
self.path = unicode(dupe.path)
|
self.path = str(dupe.path)
|
||||||
|
|
||||||
|
|||||||
@@ -9,14 +9,14 @@
|
|||||||
|
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
from hsgui.tree import Tree, Node
|
from hsgui.table import GUITable, Row
|
||||||
|
|
||||||
from .base import GUIObject
|
from .base import GUIObject
|
||||||
|
|
||||||
class DupeNode(Node):
|
class DupeRow(Row):
|
||||||
def __init__(self, app, group, dupe):
|
def __init__(self, table, group, dupe):
|
||||||
Node.__init__(self, '')
|
Row.__init__(self, table)
|
||||||
self._app = app
|
self._app = table.app
|
||||||
self._group = group
|
self._group = group
|
||||||
self._dupe = dupe
|
self._dupe = dupe
|
||||||
self._data = None
|
self._data = None
|
||||||
@@ -34,6 +34,10 @@ class DupeNode(Node):
|
|||||||
self._data_delta = self._app._get_display_info(self._dupe, self._group, True)
|
self._data_delta = self._app._get_display_info(self._dupe, self._group, True)
|
||||||
return self._data_delta
|
return self._data_delta
|
||||||
|
|
||||||
|
@property
|
||||||
|
def isref(self):
|
||||||
|
return self._dupe is self._group.ref
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def markable(self):
|
def markable(self):
|
||||||
return self._app.results.is_markable(self._dupe)
|
return self._app.results.is_markable(self._dupe)
|
||||||
@@ -47,10 +51,10 @@ class DupeNode(Node):
|
|||||||
self._app.mark_dupe(self._dupe, value)
|
self._app.mark_dupe(self._dupe, value)
|
||||||
|
|
||||||
|
|
||||||
class ResultTree(GUIObject, Tree):
|
class ResultTable(GUIObject, GUITable):
|
||||||
def __init__(self, view, app):
|
def __init__(self, view, app):
|
||||||
GUIObject.__init__(self, view, app)
|
GUIObject.__init__(self, view, app)
|
||||||
Tree.__init__(self)
|
GUITable.__init__(self)
|
||||||
self._power_marker = False
|
self._power_marker = False
|
||||||
self._delta_values = False
|
self._delta_values = False
|
||||||
self._sort_descriptors = (0, True)
|
self._sort_descriptors = (0, True)
|
||||||
@@ -58,61 +62,58 @@ class ResultTree(GUIObject, Tree):
|
|||||||
#--- Override
|
#--- Override
|
||||||
def connect(self):
|
def connect(self):
|
||||||
GUIObject.connect(self)
|
GUIObject.connect(self)
|
||||||
self._refresh()
|
self._refresh_with_view()
|
||||||
self.view.refresh()
|
|
||||||
|
|
||||||
def _select_nodes(self, nodes):
|
def _restore_selection(self, previous_selection):
|
||||||
Tree._select_nodes(self, nodes)
|
if self.app.selected_dupes:
|
||||||
self.app._select_dupes(map(attrgetter('_dupe'), nodes))
|
to_find = set(self.app.selected_dupes)
|
||||||
|
indexes = [i for i, r in enumerate(self) if r._dupe in to_find]
|
||||||
|
self.selected_indexes = indexes
|
||||||
|
|
||||||
#--- Private
|
def _update_selection(self):
|
||||||
def _refresh(self):
|
rows = self.selected_rows
|
||||||
self.clear()
|
self.app._select_dupes(list(map(attrgetter('_dupe'), rows)))
|
||||||
|
|
||||||
|
def _fill(self):
|
||||||
if not self.power_marker:
|
if not self.power_marker:
|
||||||
for group in self.app.results.groups:
|
for group in self.app.results.groups:
|
||||||
group_node = DupeNode(self.app, group, group.ref)
|
self.append(DupeRow(self, group, group.ref))
|
||||||
self.append(group_node)
|
|
||||||
for dupe in group.dupes:
|
for dupe in group.dupes:
|
||||||
group_node.append(DupeNode(self.app, group, dupe))
|
self.append(DupeRow(self, group, dupe))
|
||||||
else:
|
else:
|
||||||
for dupe in self.app.results.dupes:
|
for dupe in self.app.results.dupes:
|
||||||
group = self.app.results.get_group_of_duplicate(dupe)
|
group = self.app.results.get_group_of_duplicate(dupe)
|
||||||
self.append(DupeNode(self.app, group, dupe))
|
self.append(DupeRow(self, group, dupe))
|
||||||
if self.app.selected_dupes:
|
|
||||||
to_find = set(self.app.selected_dupes)
|
def _refresh_with_view(self):
|
||||||
nodes = list(self.findall(lambda n: n is not self and n._dupe in to_find))
|
self.refresh()
|
||||||
self.selected_nodes = nodes
|
self.view.refresh()
|
||||||
|
self.view.show_selected_row()
|
||||||
|
|
||||||
#--- Public
|
#--- Public
|
||||||
def get_node_value(self, path, column):
|
def get_row_value(self, index, column):
|
||||||
try:
|
try:
|
||||||
node = self.get_node(path)
|
row = self[index]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return '---'
|
return '---'
|
||||||
if self.delta_values:
|
if self.delta_values:
|
||||||
return node.data_delta[column]
|
return row.data_delta[column]
|
||||||
else:
|
else:
|
||||||
return node.data[column]
|
return row.data[column]
|
||||||
|
|
||||||
def rename_selected(self, newname):
|
def rename_selected(self, newname):
|
||||||
node = self.selected_node
|
row = self.selected_row
|
||||||
node._data = None
|
row._data = None
|
||||||
node._data_delta = None
|
row._data_delta = None
|
||||||
return self.app.rename_selected(newname)
|
return self.app.rename_selected(newname)
|
||||||
|
|
||||||
def root_children_counts(self):
|
|
||||||
# This is a speed optimization for cases where there's a lot of results so that there is
|
|
||||||
# not thousands of children_count queries when expandAll is called.
|
|
||||||
return [len(node) for node in self]
|
|
||||||
|
|
||||||
def sort(self, key, asc):
|
def sort(self, key, asc):
|
||||||
if self.power_marker:
|
if self.power_marker:
|
||||||
self.app.results.sort_dupes(key, asc, self.delta_values)
|
self.app.results.sort_dupes(key, asc, self.delta_values)
|
||||||
else:
|
else:
|
||||||
self.app.results.sort_groups(key, asc)
|
self.app.results.sort_groups(key, asc)
|
||||||
self._sort_descriptors = (key, asc)
|
self._sort_descriptors = (key, asc)
|
||||||
self._refresh()
|
self._refresh_with_view()
|
||||||
self.view.refresh()
|
|
||||||
|
|
||||||
#--- Properties
|
#--- Properties
|
||||||
@property
|
@property
|
||||||
@@ -126,8 +127,7 @@ class ResultTree(GUIObject, Tree):
|
|||||||
self._power_marker = value
|
self._power_marker = value
|
||||||
key, asc = self._sort_descriptors
|
key, asc = self._sort_descriptors
|
||||||
self.sort(key, asc)
|
self.sort(key, asc)
|
||||||
self._refresh()
|
# no need to refresh, it has happened in sort()
|
||||||
self.view.refresh()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def delta_values(self):
|
def delta_values(self):
|
||||||
@@ -138,22 +138,25 @@ class ResultTree(GUIObject, Tree):
|
|||||||
if value == self._delta_values:
|
if value == self._delta_values:
|
||||||
return
|
return
|
||||||
self._delta_values = value
|
self._delta_values = value
|
||||||
self._refresh()
|
self.refresh()
|
||||||
self.view.refresh()
|
self.view.refresh()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def selected_dupe_count(self):
|
||||||
|
return sum(1 for row in self.selected_rows if not row.isref)
|
||||||
|
|
||||||
#--- Event Handlers
|
#--- Event Handlers
|
||||||
def marking_changed(self):
|
def marking_changed(self):
|
||||||
self.view.invalidate_markings()
|
self.view.invalidate_markings()
|
||||||
|
|
||||||
def results_changed(self):
|
def results_changed(self):
|
||||||
self._refresh()
|
self._refresh_with_view()
|
||||||
self.view.refresh()
|
|
||||||
|
|
||||||
def results_changed_but_keep_selection(self):
|
def results_changed_but_keep_selection(self):
|
||||||
# What we want to to here is that instead of restoring selected *dupes* after refresh, we
|
# What we want to to here is that instead of restoring selected *dupes* after refresh, we
|
||||||
# restore selected *paths*.
|
# restore selected *paths*.
|
||||||
paths = self.selected_paths
|
indexes = self.selected_indexes
|
||||||
self._refresh()
|
self.refresh()
|
||||||
self.selected_paths = paths
|
self.select(indexes)
|
||||||
self.view.refresh()
|
self.view.refresh()
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
from lxml import etree
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
from hsutil.files import FileOrPath
|
from hsutil.files import FileOrPath
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ class IgnoreList(object):
|
|||||||
self._count = 0
|
self._count = 0
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for first,seconds in self._ignored.iteritems():
|
for first,seconds in self._ignored.items():
|
||||||
for second in seconds:
|
for second in seconds:
|
||||||
yield (first,second)
|
yield (first,second)
|
||||||
|
|
||||||
@@ -77,14 +77,16 @@ class IgnoreList(object):
|
|||||||
infile can be a file object or a filename.
|
infile can be a file object or a filename.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
root = etree.parse(infile).getroot()
|
root = ET.parse(infile).getroot()
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
for fn in root.iterchildren('file'):
|
file_elems = (e for e in root if e.tag == 'file')
|
||||||
|
for fn in file_elems:
|
||||||
file_path = fn.get('path')
|
file_path = fn.get('path')
|
||||||
if not file_path:
|
if not file_path:
|
||||||
continue
|
continue
|
||||||
for sfn in fn.iterchildren('file'):
|
subfile_elems = (e for e in fn if e.tag == 'file')
|
||||||
|
for sfn in subfile_elems:
|
||||||
subfile_path = sfn.get('path')
|
subfile_path = sfn.get('path')
|
||||||
if subfile_path:
|
if subfile_path:
|
||||||
self.Ignore(file_path, subfile_path)
|
self.Ignore(file_path, subfile_path)
|
||||||
@@ -94,14 +96,14 @@ class IgnoreList(object):
|
|||||||
|
|
||||||
outfile can be a file object or a filename.
|
outfile can be a file object or a filename.
|
||||||
"""
|
"""
|
||||||
root = etree.Element('ignore_list')
|
root = ET.Element('ignore_list')
|
||||||
for filename, subfiles in self._ignored.items():
|
for filename, subfiles in self._ignored.items():
|
||||||
file_node = etree.SubElement(root, 'file')
|
file_node = ET.SubElement(root, 'file')
|
||||||
file_node.set('path', filename)
|
file_node.set('path', filename)
|
||||||
for subfilename in subfiles:
|
for subfilename in subfiles:
|
||||||
subfile_node = etree.SubElement(file_node, 'file')
|
subfile_node = ET.SubElement(file_node, 'file')
|
||||||
subfile_node.set('path', subfilename)
|
subfile_node.set('path', subfilename)
|
||||||
tree = etree.ElementTree(root)
|
tree = ET.ElementTree(root)
|
||||||
with FileOrPath(outfile, 'wb') as fp:
|
with FileOrPath(outfile, 'wb') as fp:
|
||||||
tree.write(fp, encoding='utf-8')
|
tree.write(fp, encoding='utf-8')
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from lxml import etree
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
from . import engine
|
from . import engine
|
||||||
from hscommon.job import nulljob
|
from hscommon.job import nulljob
|
||||||
@@ -33,6 +33,7 @@ class Results(Markable):
|
|||||||
self.__marked_size = 0
|
self.__marked_size = 0
|
||||||
self.data = data_module
|
self.data = data_module
|
||||||
self.problems = [] # (dupe, error_msg)
|
self.problems = [] # (dupe, error_msg)
|
||||||
|
self.is_modified = False
|
||||||
|
|
||||||
def _did_mark(self, dupe):
|
def _did_mark(self, dupe):
|
||||||
self.__marked_size += dupe.size
|
self.__marked_size += dupe.size
|
||||||
@@ -115,6 +116,7 @@ class Results(Markable):
|
|||||||
self.__group_of_duplicate[dupe] = g
|
self.__group_of_duplicate[dupe] = g
|
||||||
if not hasattr(dupe, 'is_ref'):
|
if not hasattr(dupe, 'is_ref'):
|
||||||
dupe.is_ref = False
|
dupe.is_ref = False
|
||||||
|
self.is_modified = True
|
||||||
old_filters = nonone(self.__filters, [])
|
old_filters = nonone(self.__filters, [])
|
||||||
self.apply_filter(None)
|
self.apply_filter(None)
|
||||||
for filter_str in old_filters:
|
for filter_str in old_filters:
|
||||||
@@ -147,7 +149,7 @@ class Results(Markable):
|
|||||||
self.__filters.append(filter_str)
|
self.__filters.append(filter_str)
|
||||||
if self.__filtered_dupes is None:
|
if self.__filtered_dupes is None:
|
||||||
self.__filtered_dupes = flatten(g[:] for g in self.groups)
|
self.__filtered_dupes = flatten(g[:] for g in self.groups)
|
||||||
self.__filtered_dupes = set(dupe for dupe in self.__filtered_dupes if filter_re.search(unicode(dupe.path)))
|
self.__filtered_dupes = set(dupe for dupe in self.__filtered_dupes if filter_re.search(str(dupe.path)))
|
||||||
filtered_groups = set()
|
filtered_groups = set()
|
||||||
for dupe in self.__filtered_dupes:
|
for dupe in self.__filtered_dupes:
|
||||||
filtered_groups.add(self.get_group_of_duplicate(dupe))
|
filtered_groups.add(self.get_group_of_duplicate(dupe))
|
||||||
@@ -176,16 +178,16 @@ class Results(Markable):
|
|||||||
|
|
||||||
self.apply_filter(None)
|
self.apply_filter(None)
|
||||||
try:
|
try:
|
||||||
root = etree.parse(infile).getroot()
|
root = ET.parse(infile).getroot()
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
group_elems = list(root.iterchildren('group'))
|
group_elems = list(root.getiterator('group'))
|
||||||
groups = []
|
groups = []
|
||||||
marked = set()
|
marked = set()
|
||||||
for group_elem in j.iter_with_progress(group_elems, every=100):
|
for group_elem in j.iter_with_progress(group_elems, every=100):
|
||||||
group = engine.Group()
|
group = engine.Group()
|
||||||
dupes = []
|
dupes = []
|
||||||
for file_elem in group_elem.iterchildren('file'):
|
for file_elem in group_elem.getiterator('file'):
|
||||||
path = file_elem.get('path')
|
path = file_elem.get('path')
|
||||||
words = file_elem.get('words', '')
|
words = file_elem.get('words', '')
|
||||||
if not path:
|
if not path:
|
||||||
@@ -198,7 +200,7 @@ class Results(Markable):
|
|||||||
dupes.append(file)
|
dupes.append(file)
|
||||||
if file_elem.get('marked') == 'y':
|
if file_elem.get('marked') == 'y':
|
||||||
marked.add(file)
|
marked.add(file)
|
||||||
for match_elem in group_elem.iterchildren('match'):
|
for match_elem in group_elem.getiterator('match'):
|
||||||
try:
|
try:
|
||||||
attrs = match_elem.attrib
|
attrs = match_elem.attrib
|
||||||
first_file = dupes[int(attrs['first'])]
|
first_file = dupes[int(attrs['first'])]
|
||||||
@@ -216,6 +218,7 @@ class Results(Markable):
|
|||||||
self.groups = groups
|
self.groups = groups
|
||||||
for dupe_file in marked:
|
for dupe_file in marked:
|
||||||
self.mark(dupe_file)
|
self.mark(dupe_file)
|
||||||
|
self.is_modified = False
|
||||||
|
|
||||||
def make_ref(self, dupe):
|
def make_ref(self, dupe):
|
||||||
g = self.get_group_of_duplicate(dupe)
|
g = self.get_group_of_duplicate(dupe)
|
||||||
@@ -229,6 +232,7 @@ class Results(Markable):
|
|||||||
self.__total_count -= 1
|
self.__total_count -= 1
|
||||||
self.__total_size -= dupe.size
|
self.__total_size -= dupe.size
|
||||||
self.__dupes = None
|
self.__dupes = None
|
||||||
|
self.is_modified = True
|
||||||
|
|
||||||
def perform_on_marked(self, func, remove_from_results):
|
def perform_on_marked(self, func, remove_from_results):
|
||||||
# Performs `func` on all marked dupes. If an EnvironmentError is raised during the call,
|
# Performs `func` on all marked dupes. If an EnvironmentError is raised during the call,
|
||||||
@@ -241,7 +245,7 @@ class Results(Markable):
|
|||||||
func(dupe)
|
func(dupe)
|
||||||
to_remove.append(dupe)
|
to_remove.append(dupe)
|
||||||
except EnvironmentError as e:
|
except EnvironmentError as e:
|
||||||
self.problems.append((dupe, unicode(e)))
|
self.problems.append((dupe, str(e)))
|
||||||
if remove_from_results:
|
if remove_from_results:
|
||||||
self.remove_duplicates(to_remove)
|
self.remove_duplicates(to_remove)
|
||||||
self.mark_none()
|
self.mark_none()
|
||||||
@@ -269,13 +273,14 @@ class Results(Markable):
|
|||||||
for group in affected_groups:
|
for group in affected_groups:
|
||||||
group.discard_matches()
|
group.discard_matches()
|
||||||
self.__dupes = None
|
self.__dupes = None
|
||||||
|
self.is_modified = True
|
||||||
|
|
||||||
def save_to_xml(self, outfile):
|
def save_to_xml(self, outfile):
|
||||||
self.apply_filter(None)
|
self.apply_filter(None)
|
||||||
root = etree.Element('results')
|
root = ET.Element('results')
|
||||||
# writer = XMLGenerator(outfile, 'utf-8')
|
# writer = XMLGenerator(outfile, 'utf-8')
|
||||||
for g in self.groups:
|
for g in self.groups:
|
||||||
group_elem = etree.SubElement(root, 'group')
|
group_elem = ET.SubElement(root, 'group')
|
||||||
dupe2index = {}
|
dupe2index = {}
|
||||||
for index, d in enumerate(g):
|
for index, d in enumerate(g):
|
||||||
dupe2index[d] = index
|
dupe2index[d] = index
|
||||||
@@ -283,22 +288,23 @@ class Results(Markable):
|
|||||||
words = engine.unpack_fields(d.words)
|
words = engine.unpack_fields(d.words)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
words = ()
|
words = ()
|
||||||
file_elem = etree.SubElement(group_elem, 'file')
|
file_elem = ET.SubElement(group_elem, 'file')
|
||||||
try:
|
try:
|
||||||
file_elem.set('path', unicode(d.path))
|
file_elem.set('path', str(d.path))
|
||||||
file_elem.set('words', ','.join(words))
|
file_elem.set('words', ','.join(words))
|
||||||
except ValueError: # If there's an invalid character, just skip the file
|
except ValueError: # If there's an invalid character, just skip the file
|
||||||
file_elem.set('path', '')
|
file_elem.set('path', '')
|
||||||
file_elem.set('is_ref', ('y' if d.is_ref else 'n'))
|
file_elem.set('is_ref', ('y' if d.is_ref else 'n'))
|
||||||
file_elem.set('marked', ('y' if self.is_marked(d) else 'n'))
|
file_elem.set('marked', ('y' if self.is_marked(d) else 'n'))
|
||||||
for match in g.matches:
|
for match in g.matches:
|
||||||
match_elem = etree.SubElement(group_elem, 'match')
|
match_elem = ET.SubElement(group_elem, 'match')
|
||||||
match_elem.set('first', unicode(dupe2index[match.first]))
|
match_elem.set('first', str(dupe2index[match.first]))
|
||||||
match_elem.set('second', unicode(dupe2index[match.second]))
|
match_elem.set('second', str(dupe2index[match.second]))
|
||||||
match_elem.set('percentage', unicode(int(match.percentage)))
|
match_elem.set('percentage', str(int(match.percentage)))
|
||||||
tree = etree.ElementTree(root)
|
tree = ET.ElementTree(root)
|
||||||
with FileOrPath(outfile, 'wb') as fp:
|
with FileOrPath(outfile, 'wb') as fp:
|
||||||
tree.write(fp, encoding='utf-8')
|
tree.write(fp, encoding='utf-8')
|
||||||
|
self.is_modified = False
|
||||||
|
|
||||||
def sort_dupes(self, key, asc=True, delta=False):
|
def sort_dupes(self, key, asc=True, delta=False):
|
||||||
if not self.__dupes:
|
if not self.__dupes:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
from hscommon import job
|
from hscommon import job
|
||||||
from hsutil import io
|
from hsutil import io
|
||||||
@@ -17,16 +17,26 @@ from hsutil.str import get_file_ext, rem_file_ext
|
|||||||
from . import engine
|
from . import engine
|
||||||
from .ignore import IgnoreList
|
from .ignore import IgnoreList
|
||||||
|
|
||||||
(SCAN_TYPE_FILENAME,
|
class ScanType:
|
||||||
SCAN_TYPE_FIELDS,
|
Filename = 0
|
||||||
SCAN_TYPE_FIELDS_NO_ORDER,
|
Fields = 1
|
||||||
SCAN_TYPE_TAG,
|
FieldsNoOrder = 2
|
||||||
UNUSED, # Must not be removed. Constants here are what scan_type in the prefs are.
|
Tag = 3
|
||||||
SCAN_TYPE_CONTENT,
|
# number 4 is obsolete
|
||||||
SCAN_TYPE_CONTENT_AUDIO) = range(7)
|
Contents = 5
|
||||||
|
ContentsAudio = 6
|
||||||
|
|
||||||
SCANNABLE_TAGS = ['track', 'artist', 'album', 'title', 'genre', 'year']
|
SCANNABLE_TAGS = ['track', 'artist', 'album', 'title', 'genre', 'year']
|
||||||
|
|
||||||
|
RE_DIGIT_ENDING = re.compile(r'\d+|\(\d+\)|\[\d+\]|{\d+}')
|
||||||
|
|
||||||
|
def is_same_with_digit(name, refname):
|
||||||
|
# Returns True if name is the same as refname, but with digits (with brackets or not) at the end
|
||||||
|
if not name.startswith(refname):
|
||||||
|
return False
|
||||||
|
end = name[len(refname):].strip()
|
||||||
|
return RE_DIGIT_ENDING.match(end) is not None
|
||||||
|
|
||||||
class Scanner(object):
|
class Scanner(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ignore_list = IgnoreList()
|
self.ignore_list = IgnoreList()
|
||||||
@@ -38,22 +48,22 @@ class Scanner(object):
|
|||||||
for f in j.iter_with_progress(files, 'Read size of %d/%d files'):
|
for f in j.iter_with_progress(files, 'Read size of %d/%d files'):
|
||||||
f.size # pre-read, makes a smoother progress if read here (especially for bundles)
|
f.size # pre-read, makes a smoother progress if read here (especially for bundles)
|
||||||
files = [f for f in files if f.size >= self.size_threshold]
|
files = [f for f in files if f.size >= self.size_threshold]
|
||||||
if self.scan_type in (SCAN_TYPE_CONTENT, SCAN_TYPE_CONTENT_AUDIO):
|
if self.scan_type in (ScanType.Contents, ScanType.ContentsAudio):
|
||||||
sizeattr = 'size' if self.scan_type == SCAN_TYPE_CONTENT else 'audiosize'
|
sizeattr = 'size' if self.scan_type == ScanType.Contents else 'audiosize'
|
||||||
return engine.getmatches_by_contents(files, sizeattr, partial=self.scan_type==SCAN_TYPE_CONTENT_AUDIO, j=j)
|
return engine.getmatches_by_contents(files, sizeattr, partial=self.scan_type==ScanType.ContentsAudio, j=j)
|
||||||
else:
|
else:
|
||||||
j = j.start_subjob([2, 8])
|
j = j.start_subjob([2, 8])
|
||||||
kw = {}
|
kw = {}
|
||||||
kw['match_similar_words'] = self.match_similar_words
|
kw['match_similar_words'] = self.match_similar_words
|
||||||
kw['weight_words'] = self.word_weighting
|
kw['weight_words'] = self.word_weighting
|
||||||
kw['min_match_percentage'] = self.min_match_percentage
|
kw['min_match_percentage'] = self.min_match_percentage
|
||||||
if self.scan_type == SCAN_TYPE_FIELDS_NO_ORDER:
|
if self.scan_type == ScanType.FieldsNoOrder:
|
||||||
self.scan_type = SCAN_TYPE_FIELDS
|
self.scan_type = ScanType.Fields
|
||||||
kw['no_field_order'] = True
|
kw['no_field_order'] = True
|
||||||
func = {
|
func = {
|
||||||
SCAN_TYPE_FILENAME: lambda f: engine.getwords(rem_file_ext(f.name)),
|
ScanType.Filename: lambda f: engine.getwords(rem_file_ext(f.name)),
|
||||||
SCAN_TYPE_FIELDS: lambda f: engine.getfields(rem_file_ext(f.name)),
|
ScanType.Fields: lambda f: engine.getfields(rem_file_ext(f.name)),
|
||||||
SCAN_TYPE_TAG: lambda f: [engine.getwords(unicode(getattr(f, attrname))) for attrname in SCANNABLE_TAGS if attrname in self.scanned_tags],
|
ScanType.Tag: lambda f: [engine.getwords(str(getattr(f, attrname))) for attrname in SCANNABLE_TAGS if attrname in self.scanned_tags],
|
||||||
}[self.scan_type]
|
}[self.scan_type]
|
||||||
for f in j.iter_with_progress(files, 'Read metadata of %d/%d files'):
|
for f in j.iter_with_progress(files, 'Read metadata of %d/%d files'):
|
||||||
f.words = func(f)
|
f.words = func(f)
|
||||||
@@ -67,9 +77,13 @@ class Scanner(object):
|
|||||||
def _tie_breaker(ref, dupe):
|
def _tie_breaker(ref, dupe):
|
||||||
refname = rem_file_ext(ref.name).lower()
|
refname = rem_file_ext(ref.name).lower()
|
||||||
dupename = rem_file_ext(dupe.name).lower()
|
dupename = rem_file_ext(dupe.name).lower()
|
||||||
if 'copy' in refname and 'copy' not in dupename:
|
if 'copy' in dupename:
|
||||||
|
return False
|
||||||
|
if 'copy' in refname:
|
||||||
return True
|
return True
|
||||||
if refname.startswith(dupename) and (refname[len(dupename):].strip().isdigit()):
|
if is_same_with_digit(dupename, refname):
|
||||||
|
return False
|
||||||
|
if is_same_with_digit(refname, dupename):
|
||||||
return True
|
return True
|
||||||
return len(dupe.path) > len(ref.path)
|
return len(dupe.path) > len(ref.path)
|
||||||
|
|
||||||
@@ -88,7 +102,7 @@ class Scanner(object):
|
|||||||
j = j.start_subjob(2)
|
j = j.start_subjob(2)
|
||||||
iter_matches = j.iter_with_progress(matches, 'Processed %d/%d matches against the ignore list')
|
iter_matches = j.iter_with_progress(matches, 'Processed %d/%d matches against the ignore list')
|
||||||
matches = [m for m in iter_matches
|
matches = [m for m in iter_matches
|
||||||
if not self.ignore_list.AreIgnored(unicode(m.first.path), unicode(m.second.path))]
|
if not self.ignore_list.AreIgnored(str(m.first.path), str(m.second.path))]
|
||||||
logging.info('Grouping matches')
|
logging.info('Grouping matches')
|
||||||
groups = engine.get_groups(matches, j)
|
groups = engine.get_groups(matches, j)
|
||||||
matched_files = dedupe([m.first for m in matches] + [m.second for m in matches])
|
matched_files = dedupe([m.first for m in matches] + [m.second for m in matches])
|
||||||
@@ -103,7 +117,7 @@ class Scanner(object):
|
|||||||
match_similar_words = False
|
match_similar_words = False
|
||||||
min_match_percentage = 80
|
min_match_percentage = 80
|
||||||
mix_file_kind = True
|
mix_file_kind = True
|
||||||
scan_type = SCAN_TYPE_FILENAME
|
scan_type = ScanType.Filename
|
||||||
scanned_tags = set(['artist', 'title'])
|
scanned_tags = set(['artist', 'title'])
|
||||||
size_threshold = 0
|
size_threshold = 0
|
||||||
word_weighting = False
|
word_weighting = False
|
||||||
|
|||||||
@@ -23,14 +23,15 @@ from .. import app, fs, engine
|
|||||||
from ..app import DupeGuru as DupeGuruBase
|
from ..app import DupeGuru as DupeGuruBase
|
||||||
from ..gui.details_panel import DetailsPanel
|
from ..gui.details_panel import DetailsPanel
|
||||||
from ..gui.directory_tree import DirectoryTree
|
from ..gui.directory_tree import DirectoryTree
|
||||||
from ..gui.result_tree import ResultTree
|
from ..gui.result_table import ResultTable
|
||||||
|
from ..scanner import ScanType
|
||||||
|
|
||||||
class DupeGuru(DupeGuruBase):
|
class DupeGuru(DupeGuruBase):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
DupeGuruBase.__init__(self, data, '/tmp', appid=4)
|
DupeGuruBase.__init__(self, data, '/tmp', appid=4)
|
||||||
|
|
||||||
def _start_job(self, jobid, func):
|
def _start_job(self, jobid, func, *args):
|
||||||
func(nulljob)
|
func(nulljob, *args)
|
||||||
|
|
||||||
|
|
||||||
class CallLogger(object):
|
class CallLogger(object):
|
||||||
@@ -109,7 +110,7 @@ class TCDupeGuru(TestCase):
|
|||||||
|
|
||||||
def test_Scan_with_objects_evaluating_to_false(self):
|
def test_Scan_with_objects_evaluating_to_false(self):
|
||||||
class FakeFile(fs.File):
|
class FakeFile(fs.File):
|
||||||
def __nonzero__(self):
|
def __bool__(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@@ -118,10 +119,23 @@ class TCDupeGuru(TestCase):
|
|||||||
f1, f2 = [FakeFile('foo') for i in range(2)]
|
f1, f2 = [FakeFile('foo') for i in range(2)]
|
||||||
f1.is_ref, f2.is_ref = (False, False)
|
f1.is_ref, f2.is_ref = (False, False)
|
||||||
assert not (bool(f1) and bool(f2))
|
assert not (bool(f1) and bool(f2))
|
||||||
app.directories.get_files = lambda: [f1, f2]
|
app.directories.get_files = lambda: iter([f1, f2])
|
||||||
app.directories._dirs.append('this is just so Scan() doesnt return 3')
|
app.directories._dirs.append('this is just so Scan() doesnt return 3')
|
||||||
app.start_scanning() # no exception
|
app.start_scanning() # no exception
|
||||||
|
|
||||||
|
def test_ignore_hardlink_matches(self):
|
||||||
|
# If the ignore_hardlink_matches option is set, don't match files hardlinking to the same
|
||||||
|
# inode.
|
||||||
|
tmppath = Path(self.tmpdir())
|
||||||
|
io.open(tmppath + 'myfile', 'w').write('foo')
|
||||||
|
os.link(str(tmppath + 'myfile'), str(tmppath + 'hardlink'))
|
||||||
|
app = DupeGuru()
|
||||||
|
app.directories.add_path(tmppath)
|
||||||
|
app.scanner.scan_type = ScanType.Contents
|
||||||
|
app.options['ignore_hardlink_matches'] = True
|
||||||
|
app.start_scanning()
|
||||||
|
eq_(len(app.results.groups), 0)
|
||||||
|
|
||||||
|
|
||||||
class TCDupeGuru_clean_empty_dirs(TestCase):
|
class TCDupeGuru_clean_empty_dirs(TestCase):
|
||||||
cls_tested_module = app
|
cls_tested_module = app
|
||||||
@@ -166,11 +180,11 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
self.dpanel = DetailsPanel(self.dpanel_gui, self.app)
|
self.dpanel = DetailsPanel(self.dpanel_gui, self.app)
|
||||||
self.dtree_gui = CallLogger()
|
self.dtree_gui = CallLogger()
|
||||||
self.dtree = DirectoryTree(self.dtree_gui, self.app)
|
self.dtree = DirectoryTree(self.dtree_gui, self.app)
|
||||||
self.rtree_gui = CallLogger()
|
self.rtable_gui = CallLogger()
|
||||||
self.rtree = ResultTree(self.rtree_gui, self.app)
|
self.rtable = ResultTable(self.rtable_gui, self.app)
|
||||||
self.dpanel.connect()
|
self.dpanel.connect()
|
||||||
self.dtree.connect()
|
self.dtree.connect()
|
||||||
self.rtree.connect()
|
self.rtable.connect()
|
||||||
tmppath = self.tmppath()
|
tmppath = self.tmppath()
|
||||||
io.mkdir(tmppath + 'foo')
|
io.mkdir(tmppath + 'foo')
|
||||||
io.mkdir(tmppath + 'bar')
|
io.mkdir(tmppath + 'bar')
|
||||||
@@ -200,11 +214,11 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
if expected is not None:
|
if expected is not None:
|
||||||
expected = set(expected)
|
expected = set(expected)
|
||||||
not_called = expected - calls
|
not_called = expected - calls
|
||||||
assert not not_called, u"These calls haven't been made: {0}".format(not_called)
|
assert not not_called, "These calls haven't been made: {0}".format(not_called)
|
||||||
if not_expected is not None:
|
if not_expected is not None:
|
||||||
not_expected = set(not_expected)
|
not_expected = set(not_expected)
|
||||||
called = not_expected & calls
|
called = not_expected & calls
|
||||||
assert not called, u"These calls shouldn't have been made: {0}".format(called)
|
assert not called, "These calls shouldn't have been made: {0}".format(called)
|
||||||
gui.clear_calls()
|
gui.clear_calls()
|
||||||
|
|
||||||
def clear_gui_calls(self):
|
def clear_gui_calls(self):
|
||||||
@@ -217,42 +231,35 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
def test_GetObjects(self):
|
def test_GetObjects(self):
|
||||||
objects = self.objects
|
objects = self.objects
|
||||||
groups = self.groups
|
groups = self.groups
|
||||||
n = self.rtree.get_node([0])
|
r = self.rtable[0]
|
||||||
assert n._group is groups[0]
|
assert r._group is groups[0]
|
||||||
assert n._dupe is objects[0]
|
assert r._dupe is objects[0]
|
||||||
n = self.rtree.get_node([0, 0])
|
r = self.rtable[1]
|
||||||
assert n._group is groups[0]
|
assert r._group is groups[0]
|
||||||
assert n._dupe is objects[1]
|
assert r._dupe is objects[1]
|
||||||
n = self.rtree.get_node([1, 0])
|
r = self.rtable[4]
|
||||||
assert n._group is groups[1]
|
assert r._group is groups[1]
|
||||||
assert n._dupe is objects[4]
|
assert r._dupe is objects[4]
|
||||||
|
|
||||||
def test_GetObjects_after_sort(self):
|
def test_GetObjects_after_sort(self):
|
||||||
objects = self.objects
|
objects = self.objects
|
||||||
groups = self.groups[:] # we need an un-sorted reference
|
groups = self.groups[:] # we need an un-sorted reference
|
||||||
self.rtree.sort(0, False) #0 = Filename
|
self.rtable.sort(0, False) #0 = Filename
|
||||||
n = self.rtree.get_node([0, 0])
|
r = self.rtable[1]
|
||||||
assert n._group is groups[1]
|
assert r._group is groups[1]
|
||||||
assert n._dupe is objects[4]
|
assert r._dupe is objects[4]
|
||||||
|
|
||||||
def test_selected_result_node_paths(self):
|
|
||||||
# app.selected_dupes is correctly converted into node paths
|
|
||||||
paths = [[0, 0], [0, 1], [1]]
|
|
||||||
self.rtree.selected_paths = paths
|
|
||||||
eq_(self.rtree.selected_paths, paths)
|
|
||||||
|
|
||||||
def test_selected_result_node_paths_after_deletion(self):
|
def test_selected_result_node_paths_after_deletion(self):
|
||||||
# cases where the selected dupes aren't there are correctly handled
|
# cases where the selected dupes aren't there are correctly handled
|
||||||
paths = [[0, 0], [0, 1], [1]]
|
self.rtable.select([1, 2, 3])
|
||||||
self.rtree.selected_paths = paths
|
|
||||||
self.app.remove_selected()
|
self.app.remove_selected()
|
||||||
# The first 2 dupes have been removed. The 3rd one is a ref. it stays there, in first pos.
|
# The first 2 dupes have been removed. The 3rd one is a ref. it stays there, in first pos.
|
||||||
eq_(self.rtree.selected_paths, [[0, 0]]) # no exception
|
eq_(self.rtable.selected_indexes, [1]) # no exception
|
||||||
|
|
||||||
def test_selectResultNodePaths(self):
|
def test_selectResultNodePaths(self):
|
||||||
app = self.app
|
app = self.app
|
||||||
objects = self.objects
|
objects = self.objects
|
||||||
self.rtree.selected_paths = [[0, 0], [0, 1]]
|
self.rtable.select([1, 2])
|
||||||
eq_(len(app.selected_dupes), 2)
|
eq_(len(app.selected_dupes), 2)
|
||||||
assert app.selected_dupes[0] is objects[1]
|
assert app.selected_dupes[0] is objects[1]
|
||||||
assert app.selected_dupes[1] is objects[2]
|
assert app.selected_dupes[1] is objects[2]
|
||||||
@@ -260,7 +267,7 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
def test_selectResultNodePaths_with_ref(self):
|
def test_selectResultNodePaths_with_ref(self):
|
||||||
app = self.app
|
app = self.app
|
||||||
objects = self.objects
|
objects = self.objects
|
||||||
self.rtree.selected_paths = [[0, 0], [0, 1], [1]]
|
self.rtable.select([1, 2, 3])
|
||||||
eq_(len(app.selected_dupes), 3)
|
eq_(len(app.selected_dupes), 3)
|
||||||
assert app.selected_dupes[0] is objects[1]
|
assert app.selected_dupes[0] is objects[1]
|
||||||
assert app.selected_dupes[1] is objects[2]
|
assert app.selected_dupes[1] is objects[2]
|
||||||
@@ -270,9 +277,9 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
app = self.app
|
app = self.app
|
||||||
objects = self.objects
|
objects = self.objects
|
||||||
groups = self.groups[:] #To keep the old order in memory
|
groups = self.groups[:] #To keep the old order in memory
|
||||||
self.rtree.sort(0, False) #0 = Filename
|
self.rtable.sort(0, False) #0 = Filename
|
||||||
#Now, the group order is supposed to be reversed
|
#Now, the group order is supposed to be reversed
|
||||||
self.rtree.selected_paths = [[0, 0], [1], [1, 0]]
|
self.rtable.select([1, 2, 3])
|
||||||
eq_(len(app.selected_dupes), 3)
|
eq_(len(app.selected_dupes), 3)
|
||||||
assert app.selected_dupes[0] is objects[4]
|
assert app.selected_dupes[0] is objects[4]
|
||||||
assert app.selected_dupes[1] is groups[0].ref
|
assert app.selected_dupes[1] is groups[0].ref
|
||||||
@@ -282,39 +289,26 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
# app.selected_dupes is correctly converted into paths
|
# app.selected_dupes is correctly converted into paths
|
||||||
app = self.app
|
app = self.app
|
||||||
objects = self.objects
|
objects = self.objects
|
||||||
self.rtree.power_marker = True
|
self.rtable.power_marker = True
|
||||||
self.rtree.selected_paths = [[0], [1], [2]]
|
self.rtable.select([0, 1, 2])
|
||||||
self.rtree.power_marker = False
|
self.rtable.power_marker = False
|
||||||
eq_(self.rtree.selected_paths, [[0, 0], [0, 1], [1, 0]])
|
eq_(self.rtable.selected_indexes, [1, 2, 4])
|
||||||
|
|
||||||
def test_selected_powermarker_node_paths_after_deletion(self):
|
def test_selected_powermarker_node_paths_after_deletion(self):
|
||||||
# cases where the selected dupes aren't there are correctly handled
|
# cases where the selected dupes aren't there are correctly handled
|
||||||
app = self.app
|
app = self.app
|
||||||
objects = self.objects
|
objects = self.objects
|
||||||
self.rtree.power_marker = True
|
self.rtable.power_marker = True
|
||||||
self.rtree.selected_paths = [[0], [1], [2]]
|
self.rtable.select([0, 1, 2])
|
||||||
app.remove_selected()
|
app.remove_selected()
|
||||||
eq_(self.rtree.selected_paths, []) # no exception
|
eq_(self.rtable.selected_indexes, []) # no exception
|
||||||
|
|
||||||
def test_selectPowerMarkerRows(self):
|
|
||||||
app = self.app
|
|
||||||
objects = self.objects
|
|
||||||
self.rtree.selected_paths = [[0, 0], [0, 1], [1, 0]]
|
|
||||||
eq_(len(app.selected_dupes), 3)
|
|
||||||
assert app.selected_dupes[0] is objects[1]
|
|
||||||
assert app.selected_dupes[1] is objects[2]
|
|
||||||
assert app.selected_dupes[2] is objects[4]
|
|
||||||
|
|
||||||
def test_selectPowerMarkerRows_empty(self):
|
|
||||||
self.rtree.selected_paths = []
|
|
||||||
eq_(len(self.app.selected_dupes), 0)
|
|
||||||
|
|
||||||
def test_selectPowerMarkerRows_after_sort(self):
|
def test_selectPowerMarkerRows_after_sort(self):
|
||||||
app = self.app
|
app = self.app
|
||||||
objects = self.objects
|
objects = self.objects
|
||||||
self.rtree.power_marker = True
|
self.rtable.power_marker = True
|
||||||
self.rtree.sort(0, False) #0 = Filename
|
self.rtable.sort(0, False) #0 = Filename
|
||||||
self.rtree.selected_paths = [[0], [1], [2]]
|
self.rtable.select([0, 1, 2])
|
||||||
eq_(len(app.selected_dupes), 3)
|
eq_(len(app.selected_dupes), 3)
|
||||||
assert app.selected_dupes[0] is objects[4]
|
assert app.selected_dupes[0] is objects[4]
|
||||||
assert app.selected_dupes[1] is objects[2]
|
assert app.selected_dupes[1] is objects[2]
|
||||||
@@ -325,7 +319,7 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
objects = self.objects
|
objects = self.objects
|
||||||
app.toggle_selected_mark_state()
|
app.toggle_selected_mark_state()
|
||||||
eq_(app.results.mark_count, 0)
|
eq_(app.results.mark_count, 0)
|
||||||
self.rtree.selected_paths = [[0, 0], [1, 0]]
|
self.rtable.select([1, 4])
|
||||||
app.toggle_selected_mark_state()
|
app.toggle_selected_mark_state()
|
||||||
eq_(app.results.mark_count, 2)
|
eq_(app.results.mark_count, 2)
|
||||||
assert not app.results.is_marked(objects[0])
|
assert not app.results.is_marked(objects[0])
|
||||||
@@ -335,10 +329,10 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
assert app.results.is_marked(objects[4])
|
assert app.results.is_marked(objects[4])
|
||||||
|
|
||||||
def test_refreshDetailsWithSelected(self):
|
def test_refreshDetailsWithSelected(self):
|
||||||
self.rtree.selected_paths = [[0, 0], [1, 0]]
|
self.rtable.select([1, 4])
|
||||||
eq_(self.dpanel.row(0), ('Filename', 'bar bleh', 'foo bar'))
|
eq_(self.dpanel.row(0), ('Filename', 'bar bleh', 'foo bar'))
|
||||||
self.check_gui_calls(self.dpanel_gui, ['refresh'])
|
self.check_gui_calls(self.dpanel_gui, ['refresh'])
|
||||||
self.rtree.selected_paths = []
|
self.rtable.select([])
|
||||||
eq_(self.dpanel.row(0), ('Filename', '---', '---'))
|
eq_(self.dpanel.row(0), ('Filename', '---', '---'))
|
||||||
self.check_gui_calls(self.dpanel_gui, ['refresh'])
|
self.check_gui_calls(self.dpanel_gui, ['refresh'])
|
||||||
|
|
||||||
@@ -346,7 +340,7 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
app = self.app
|
app = self.app
|
||||||
objects = self.objects
|
objects = self.objects
|
||||||
groups = self.groups
|
groups = self.groups
|
||||||
self.rtree.selected_paths = [[0, 0], [1, 0]]
|
self.rtable.select([1, 4])
|
||||||
app.make_selected_reference()
|
app.make_selected_reference()
|
||||||
assert groups[0].ref is objects[1]
|
assert groups[0].ref is objects[1]
|
||||||
assert groups[1].ref is objects[4]
|
assert groups[1].ref is objects[4]
|
||||||
@@ -355,7 +349,7 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
app = self.app
|
app = self.app
|
||||||
objects = self.objects
|
objects = self.objects
|
||||||
groups = self.groups
|
groups = self.groups
|
||||||
self.rtree.selected_paths = [[0, 0], [0, 1], [1, 0]]
|
self.rtable.select([1, 2, 4])
|
||||||
#Only [0, 0] and [1, 0] must go ref, not [0, 1] because it is a part of the same group
|
#Only [0, 0] and [1, 0] must go ref, not [0, 1] because it is a part of the same group
|
||||||
app.make_selected_reference()
|
app.make_selected_reference()
|
||||||
assert groups[0].ref is objects[1]
|
assert groups[0].ref is objects[1]
|
||||||
@@ -363,7 +357,7 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
|
|
||||||
def test_removeSelected(self):
|
def test_removeSelected(self):
|
||||||
app = self.app
|
app = self.app
|
||||||
self.rtree.selected_paths = [[0, 0], [1, 0]]
|
self.rtable.select([1, 4])
|
||||||
app.remove_selected()
|
app.remove_selected()
|
||||||
eq_(len(app.results.dupes), 1) # the first path is now selected
|
eq_(len(app.results.dupes), 1) # the first path is now selected
|
||||||
app.remove_selected()
|
app.remove_selected()
|
||||||
@@ -386,10 +380,10 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
|
|
||||||
def test_ignore(self):
|
def test_ignore(self):
|
||||||
app = self.app
|
app = self.app
|
||||||
self.rtree.selected_path = [1, 0] #The dupe of the second, 2 sized group
|
self.rtable.select([4]) #The dupe of the second, 2 sized group
|
||||||
app.add_selected_to_ignore_list()
|
app.add_selected_to_ignore_list()
|
||||||
eq_(len(app.scanner.ignore_list), 1)
|
eq_(len(app.scanner.ignore_list), 1)
|
||||||
self.rtree.selected_path = [0, 0] #first dupe of the 3 dupes group
|
self.rtable.select([1]) #first dupe of the 3 dupes group
|
||||||
app.add_selected_to_ignore_list()
|
app.add_selected_to_ignore_list()
|
||||||
#BOTH the ref and the other dupe should have been added
|
#BOTH the ref and the other dupe should have been added
|
||||||
eq_(len(app.scanner.ignore_list), 3)
|
eq_(len(app.scanner.ignore_list), 3)
|
||||||
@@ -409,25 +403,25 @@ class TCDupeGuruWithResults(TestCase):
|
|||||||
|
|
||||||
def test_only_unicode_is_added_to_ignore_list(self):
|
def test_only_unicode_is_added_to_ignore_list(self):
|
||||||
def FakeIgnore(first,second):
|
def FakeIgnore(first,second):
|
||||||
if not isinstance(first,unicode):
|
if not isinstance(first,str):
|
||||||
self.fail()
|
self.fail()
|
||||||
if not isinstance(second,unicode):
|
if not isinstance(second,str):
|
||||||
self.fail()
|
self.fail()
|
||||||
|
|
||||||
app = self.app
|
app = self.app
|
||||||
app.scanner.ignore_list.Ignore = FakeIgnore
|
app.scanner.ignore_list.Ignore = FakeIgnore
|
||||||
self.rtree.selected_path = [1, 0]
|
self.rtable.select([4])
|
||||||
app.add_selected_to_ignore_list()
|
app.add_selected_to_ignore_list()
|
||||||
|
|
||||||
|
|
||||||
class TCDupeGuru_renameSelected(TestCase):
|
class TCDupeGuru_renameSelected(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
p = self.tmppath()
|
p = self.tmppath()
|
||||||
fp = open(unicode(p + 'foo bar 1'),mode='w')
|
fp = open(str(p + 'foo bar 1'),mode='w')
|
||||||
fp.close()
|
fp.close()
|
||||||
fp = open(unicode(p + 'foo bar 2'),mode='w')
|
fp = open(str(p + 'foo bar 2'),mode='w')
|
||||||
fp.close()
|
fp.close()
|
||||||
fp = open(unicode(p + 'foo bar 3'),mode='w')
|
fp = open(str(p + 'foo bar 3'),mode='w')
|
||||||
fp.close()
|
fp.close()
|
||||||
files = fs.get_files(p)
|
files = fs.get_files(p)
|
||||||
matches = engine.getmatches(files)
|
matches = engine.getmatches(files)
|
||||||
@@ -440,14 +434,14 @@ class TCDupeGuru_renameSelected(TestCase):
|
|||||||
self.groups = groups
|
self.groups = groups
|
||||||
self.p = p
|
self.p = p
|
||||||
self.files = files
|
self.files = files
|
||||||
self.rtree_gui = CallLogger()
|
self.rtable_gui = CallLogger()
|
||||||
self.rtree = ResultTree(self.rtree_gui, self.app)
|
self.rtable = ResultTable(self.rtable_gui, self.app)
|
||||||
self.rtree.connect()
|
self.rtable.connect()
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
app = self.app
|
app = self.app
|
||||||
g = self.groups[0]
|
g = self.groups[0]
|
||||||
self.rtree.selected_path = [0, 0]
|
self.rtable.select([1])
|
||||||
assert app.rename_selected('renamed')
|
assert app.rename_selected('renamed')
|
||||||
names = io.listdir(self.p)
|
names = io.listdir(self.p)
|
||||||
assert 'renamed' in names
|
assert 'renamed' in names
|
||||||
@@ -457,7 +451,7 @@ class TCDupeGuru_renameSelected(TestCase):
|
|||||||
def test_none_selected(self):
|
def test_none_selected(self):
|
||||||
app = self.app
|
app = self.app
|
||||||
g = self.groups[0]
|
g = self.groups[0]
|
||||||
self.rtree.selected_paths = []
|
self.rtable.select([])
|
||||||
self.mock(logging, 'warning', log_calls(lambda msg: None))
|
self.mock(logging, 'warning', log_calls(lambda msg: None))
|
||||||
assert not app.rename_selected('renamed')
|
assert not app.rename_selected('renamed')
|
||||||
msg = logging.warning.calls[0]['msg']
|
msg = logging.warning.calls[0]['msg']
|
||||||
@@ -470,7 +464,7 @@ class TCDupeGuru_renameSelected(TestCase):
|
|||||||
def test_name_already_exists(self):
|
def test_name_already_exists(self):
|
||||||
app = self.app
|
app = self.app
|
||||||
g = self.groups[0]
|
g = self.groups[0]
|
||||||
self.rtree.selected_path = [0, 0]
|
self.rtable.select([1])
|
||||||
self.mock(logging, 'warning', log_calls(lambda msg: None))
|
self.mock(logging, 'warning', log_calls(lambda msg: None))
|
||||||
assert not app.rename_selected('foo bar 1')
|
assert not app.rename_selected('foo bar 1')
|
||||||
msg = logging.warning.calls[0]['msg']
|
msg = logging.warning.calls[0]['msg']
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ class TCDirectories(TestCase):
|
|||||||
|
|
||||||
def test_AddPath_non_latin(self):
|
def test_AddPath_non_latin(self):
|
||||||
p = Path(self.tmpdir())
|
p = Path(self.tmpdir())
|
||||||
to_add = p + u'unicode\u201a'
|
to_add = p + 'unicode\u201a'
|
||||||
os.mkdir(unicode(to_add))
|
os.mkdir(str(to_add))
|
||||||
d = Directories()
|
d = Directories()
|
||||||
try:
|
try:
|
||||||
d.add_path(to_add)
|
d.add_path(to_add)
|
||||||
@@ -111,7 +111,7 @@ class TCDirectories(TestCase):
|
|||||||
self.assertEqual(STATE_REFERENCE,d.get_state(p))
|
self.assertEqual(STATE_REFERENCE,d.get_state(p))
|
||||||
self.assertEqual(STATE_REFERENCE,d.get_state(p + 'dir1'))
|
self.assertEqual(STATE_REFERENCE,d.get_state(p + 'dir1'))
|
||||||
self.assertEqual(1,len(d.states))
|
self.assertEqual(1,len(d.states))
|
||||||
self.assertEqual(p,d.states.keys()[0])
|
self.assertEqual(p,list(d.states.keys())[0])
|
||||||
self.assertEqual(STATE_REFERENCE,d.states[p])
|
self.assertEqual(STATE_REFERENCE,d.states[p])
|
||||||
|
|
||||||
def test_get_state_with_path_not_there(self):
|
def test_get_state_with_path_not_there(self):
|
||||||
@@ -213,11 +213,11 @@ class TCDirectories(TestCase):
|
|||||||
|
|
||||||
def test_unicode_save(self):
|
def test_unicode_save(self):
|
||||||
d = Directories()
|
d = Directories()
|
||||||
p1 = self.tmppath() + u'hello\xe9'
|
p1 = self.tmppath() + 'hello\xe9'
|
||||||
io.mkdir(p1)
|
io.mkdir(p1)
|
||||||
io.mkdir(p1 + u'foo\xe9')
|
io.mkdir(p1 + 'foo\xe9')
|
||||||
d.add_path(p1)
|
d.add_path(p1)
|
||||||
d.set_state(p1 + u'foo\xe9', STATE_EXCLUDED)
|
d.set_state(p1 + 'foo\xe9', STATE_EXCLUDED)
|
||||||
tmpxml = op.join(self.tmpdir(), 'directories_testunit.xml')
|
tmpxml = op.join(self.tmpdir(), 'directories_testunit.xml')
|
||||||
try:
|
try:
|
||||||
d.save_to_file(tmpxml)
|
d.save_to_file(tmpxml)
|
||||||
|
|||||||
@@ -62,12 +62,12 @@ class TCgetwords(TestCase):
|
|||||||
|
|
||||||
def test_splitter_chars(self):
|
def test_splitter_chars(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[chr(i) for i in xrange(ord('a'),ord('z')+1)],
|
[chr(i) for i in range(ord('a'),ord('z')+1)],
|
||||||
getwords("a-b_c&d+e(f)g;h\\i[j]k{l}m:n.o,p<q>r/s?t~u!v@w#x$y*z")
|
getwords("a-b_c&d+e(f)g;h\\i[j]k{l}m:n.o,p<q>r/s?t~u!v@w#x$y*z")
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_joiner_chars(self):
|
def test_joiner_chars(self):
|
||||||
self.assertEqual(["aec"], getwords(u"a'e\u0301c"))
|
self.assertEqual(["aec"], getwords("a'e\u0301c"))
|
||||||
|
|
||||||
def test_empty(self):
|
def test_empty(self):
|
||||||
self.assertEqual([], getwords(''))
|
self.assertEqual([], getwords(''))
|
||||||
@@ -76,7 +76,7 @@ class TCgetwords(TestCase):
|
|||||||
self.assertEqual(['foo', 'bar'], getwords('FOO BAR'))
|
self.assertEqual(['foo', 'bar'], getwords('FOO BAR'))
|
||||||
|
|
||||||
def test_decompose_unicode(self):
|
def test_decompose_unicode(self):
|
||||||
self.assertEqual(getwords(u'foo\xe9bar'), ['fooebar'])
|
self.assertEqual(getwords('foo\xe9bar'), ['fooebar'])
|
||||||
|
|
||||||
|
|
||||||
class TCgetfields(TestCase):
|
class TCgetfields(TestCase):
|
||||||
@@ -768,7 +768,7 @@ class TCget_groups(TestCase):
|
|||||||
self.assert_(o3 in g)
|
self.assert_(o3 in g)
|
||||||
|
|
||||||
def test_four_sized_group(self):
|
def test_four_sized_group(self):
|
||||||
l = [NamedObject("foobar") for i in xrange(4)]
|
l = [NamedObject("foobar") for i in range(4)]
|
||||||
m = getmatches(l)
|
m = getmatches(l)
|
||||||
r = get_groups(m)
|
r = get_groups(m)
|
||||||
self.assertEqual(1,len(r))
|
self.assertEqual(1,len(r))
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
import cStringIO
|
import io
|
||||||
from lxml import etree
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
from hsutil.testutil import eq_
|
from hsutil.testutil import eq_
|
||||||
|
|
||||||
@@ -59,10 +59,10 @@ def test_save_to_xml():
|
|||||||
il.Ignore('foo','bar')
|
il.Ignore('foo','bar')
|
||||||
il.Ignore('foo','bleh')
|
il.Ignore('foo','bleh')
|
||||||
il.Ignore('bleh','bar')
|
il.Ignore('bleh','bar')
|
||||||
f = cStringIO.StringIO()
|
f = io.BytesIO()
|
||||||
il.save_to_xml(f)
|
il.save_to_xml(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
doc = etree.parse(f)
|
doc = ET.parse(f)
|
||||||
root = doc.getroot()
|
root = doc.getroot()
|
||||||
eq_(root.tag, 'ignore_list')
|
eq_(root.tag, 'ignore_list')
|
||||||
eq_(len(root), 2)
|
eq_(len(root), 2)
|
||||||
@@ -76,19 +76,18 @@ def test_SaveThenLoad():
|
|||||||
il.Ignore('foo', 'bar')
|
il.Ignore('foo', 'bar')
|
||||||
il.Ignore('foo', 'bleh')
|
il.Ignore('foo', 'bleh')
|
||||||
il.Ignore('bleh', 'bar')
|
il.Ignore('bleh', 'bar')
|
||||||
il.Ignore(u'\u00e9', 'bar')
|
il.Ignore('\u00e9', 'bar')
|
||||||
f = cStringIO.StringIO()
|
f = io.BytesIO()
|
||||||
il.save_to_xml(f)
|
il.save_to_xml(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
f.seek(0)
|
|
||||||
il = IgnoreList()
|
il = IgnoreList()
|
||||||
il.load_from_xml(f)
|
il.load_from_xml(f)
|
||||||
eq_(4,len(il))
|
eq_(4,len(il))
|
||||||
assert il.AreIgnored(u'\u00e9','bar')
|
assert il.AreIgnored('\u00e9','bar')
|
||||||
|
|
||||||
def test_LoadXML_with_empty_file_tags():
|
def test_LoadXML_with_empty_file_tags():
|
||||||
f = cStringIO.StringIO()
|
f = io.BytesIO()
|
||||||
f.write('<?xml version="1.0" encoding="utf-8"?><ignore_list><file><file/></file></ignore_list>')
|
f.write(b'<?xml version="1.0" encoding="utf-8"?><ignore_list><file><file/></file></ignore_list>')
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
il = IgnoreList()
|
il = IgnoreList()
|
||||||
il.load_from_xml(f)
|
il.load_from_xml(f)
|
||||||
@@ -130,12 +129,12 @@ def test_filter():
|
|||||||
|
|
||||||
def test_save_with_non_ascii_items():
|
def test_save_with_non_ascii_items():
|
||||||
il = IgnoreList()
|
il = IgnoreList()
|
||||||
il.Ignore(u'\xac', u'\xbf')
|
il.Ignore('\xac', '\xbf')
|
||||||
f = cStringIO.StringIO()
|
f = io.BytesIO()
|
||||||
try:
|
try:
|
||||||
il.save_to_xml(f)
|
il.save_to_xml(f)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise AssertionError(unicode(e))
|
raise AssertionError(str(e))
|
||||||
|
|
||||||
def test_len():
|
def test_len():
|
||||||
il = IgnoreList()
|
il = IgnoreList()
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
import StringIO
|
import io
|
||||||
import os.path as op
|
import os.path as op
|
||||||
|
|
||||||
from lxml import etree
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
from hsutil.path import Path
|
from hsutil.path import Path
|
||||||
from hsutil.testutil import eq_
|
from hsutil.testutil import eq_
|
||||||
@@ -25,7 +25,7 @@ class NamedObject(engine_test.NamedObject):
|
|||||||
path = property(lambda x:Path('basepath') + x.name)
|
path = property(lambda x:Path('basepath') + x.name)
|
||||||
is_ref = False
|
is_ref = False
|
||||||
|
|
||||||
def __nonzero__(self):
|
def __bool__(self):
|
||||||
return False #Make sure that operations are made correctly when the bool value of files is false.
|
return False #Make sure that operations are made correctly when the bool value of files is false.
|
||||||
|
|
||||||
# Returns a group set that looks like that:
|
# Returns a group set that looks like that:
|
||||||
@@ -54,21 +54,24 @@ class TCResultsEmpty(TestCase):
|
|||||||
self.test_stat_line() # make sure that the stats line isn't saying we applied a '[' filter
|
self.test_stat_line() # make sure that the stats line isn't saying we applied a '[' filter
|
||||||
|
|
||||||
def test_stat_line(self):
|
def test_stat_line(self):
|
||||||
self.assertEqual("0 / 0 (0.00 B / 0.00 B) duplicates marked.",self.results.stat_line)
|
eq_("0 / 0 (0.00 B / 0.00 B) duplicates marked.",self.results.stat_line)
|
||||||
|
|
||||||
def test_groups(self):
|
def test_groups(self):
|
||||||
self.assertEqual(0,len(self.results.groups))
|
eq_(0,len(self.results.groups))
|
||||||
|
|
||||||
def test_get_group_of_duplicate(self):
|
def test_get_group_of_duplicate(self):
|
||||||
self.assert_(self.results.get_group_of_duplicate('foo') is None)
|
assert self.results.get_group_of_duplicate('foo') is None
|
||||||
|
|
||||||
def test_save_to_xml(self):
|
def test_save_to_xml(self):
|
||||||
f = StringIO.StringIO()
|
f = io.BytesIO()
|
||||||
self.results.save_to_xml(f)
|
self.results.save_to_xml(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
doc = etree.parse(f)
|
doc = ET.parse(f)
|
||||||
root = doc.getroot()
|
root = doc.getroot()
|
||||||
self.assertEqual('results', root.tag)
|
eq_('results', root.tag)
|
||||||
|
|
||||||
|
def test_is_modified(self):
|
||||||
|
assert not self.results.is_modified
|
||||||
|
|
||||||
|
|
||||||
class TCResultsWithSomeGroups(TestCase):
|
class TCResultsWithSomeGroups(TestCase):
|
||||||
@@ -78,57 +81,57 @@ class TCResultsWithSomeGroups(TestCase):
|
|||||||
self.results.groups = self.groups
|
self.results.groups = self.groups
|
||||||
|
|
||||||
def test_stat_line(self):
|
def test_stat_line(self):
|
||||||
self.assertEqual("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
eq_("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
||||||
|
|
||||||
def test_groups(self):
|
def test_groups(self):
|
||||||
self.assertEqual(2,len(self.results.groups))
|
eq_(2,len(self.results.groups))
|
||||||
|
|
||||||
def test_get_group_of_duplicate(self):
|
def test_get_group_of_duplicate(self):
|
||||||
for o in self.objects:
|
for o in self.objects:
|
||||||
g = self.results.get_group_of_duplicate(o)
|
g = self.results.get_group_of_duplicate(o)
|
||||||
self.assert_(isinstance(g, engine.Group))
|
assert isinstance(g, engine.Group)
|
||||||
self.assert_(o in g)
|
assert o in g
|
||||||
self.assert_(self.results.get_group_of_duplicate(self.groups[0]) is None)
|
assert self.results.get_group_of_duplicate(self.groups[0]) is None
|
||||||
|
|
||||||
def test_remove_duplicates(self):
|
def test_remove_duplicates(self):
|
||||||
g1,g2 = self.results.groups
|
g1,g2 = self.results.groups
|
||||||
self.results.remove_duplicates([g1.dupes[0]])
|
self.results.remove_duplicates([g1.dupes[0]])
|
||||||
self.assertEqual(2,len(g1))
|
eq_(2,len(g1))
|
||||||
self.assert_(g1 in self.results.groups)
|
assert g1 in self.results.groups
|
||||||
self.results.remove_duplicates([g1.ref])
|
self.results.remove_duplicates([g1.ref])
|
||||||
self.assertEqual(2,len(g1))
|
eq_(2,len(g1))
|
||||||
self.assert_(g1 in self.results.groups)
|
assert g1 in self.results.groups
|
||||||
self.results.remove_duplicates([g1.dupes[0]])
|
self.results.remove_duplicates([g1.dupes[0]])
|
||||||
self.assertEqual(0,len(g1))
|
eq_(0,len(g1))
|
||||||
self.assert_(g1 not in self.results.groups)
|
assert g1 not in self.results.groups
|
||||||
self.results.remove_duplicates([g2.dupes[0]])
|
self.results.remove_duplicates([g2.dupes[0]])
|
||||||
self.assertEqual(0,len(g2))
|
eq_(0,len(g2))
|
||||||
self.assert_(g2 not in self.results.groups)
|
assert g2 not in self.results.groups
|
||||||
self.assertEqual(0,len(self.results.groups))
|
eq_(0,len(self.results.groups))
|
||||||
|
|
||||||
def test_remove_duplicates_with_ref_files(self):
|
def test_remove_duplicates_with_ref_files(self):
|
||||||
g1,g2 = self.results.groups
|
g1,g2 = self.results.groups
|
||||||
self.objects[0].is_ref = True
|
self.objects[0].is_ref = True
|
||||||
self.objects[1].is_ref = True
|
self.objects[1].is_ref = True
|
||||||
self.results.remove_duplicates([self.objects[2]])
|
self.results.remove_duplicates([self.objects[2]])
|
||||||
self.assertEqual(0,len(g1))
|
eq_(0,len(g1))
|
||||||
self.assert_(g1 not in self.results.groups)
|
assert g1 not in self.results.groups
|
||||||
|
|
||||||
def test_make_ref(self):
|
def test_make_ref(self):
|
||||||
g = self.results.groups[0]
|
g = self.results.groups[0]
|
||||||
d = g.dupes[0]
|
d = g.dupes[0]
|
||||||
self.results.make_ref(d)
|
self.results.make_ref(d)
|
||||||
self.assert_(d is g.ref)
|
assert d is g.ref
|
||||||
|
|
||||||
def test_sort_groups(self):
|
def test_sort_groups(self):
|
||||||
self.results.make_ref(self.objects[1]) #We want to make the 1024 sized object to go ref.
|
self.results.make_ref(self.objects[1]) #We want to make the 1024 sized object to go ref.
|
||||||
g1,g2 = self.groups
|
g1,g2 = self.groups
|
||||||
self.results.sort_groups(2) #2 is the key for size
|
self.results.sort_groups(2) #2 is the key for size
|
||||||
self.assert_(self.results.groups[0] is g2)
|
assert self.results.groups[0] is g2
|
||||||
self.assert_(self.results.groups[1] is g1)
|
assert self.results.groups[1] is g1
|
||||||
self.results.sort_groups(2,False)
|
self.results.sort_groups(2,False)
|
||||||
self.assert_(self.results.groups[0] is g1)
|
assert self.results.groups[0] is g1
|
||||||
self.assert_(self.results.groups[1] is g2)
|
assert self.results.groups[1] is g2
|
||||||
|
|
||||||
def test_set_groups_when_sorted(self):
|
def test_set_groups_when_sorted(self):
|
||||||
self.results.make_ref(self.objects[1]) #We want to make the 1024 sized object to go ref.
|
self.results.make_ref(self.objects[1]) #We want to make the 1024 sized object to go ref.
|
||||||
@@ -137,24 +140,24 @@ class TCResultsWithSomeGroups(TestCase):
|
|||||||
g1,g2 = groups
|
g1,g2 = groups
|
||||||
g1.switch_ref(objects[1])
|
g1.switch_ref(objects[1])
|
||||||
self.results.groups = groups
|
self.results.groups = groups
|
||||||
self.assert_(self.results.groups[0] is g2)
|
assert self.results.groups[0] is g2
|
||||||
self.assert_(self.results.groups[1] is g1)
|
assert self.results.groups[1] is g1
|
||||||
|
|
||||||
def test_get_dupe_list(self):
|
def test_get_dupe_list(self):
|
||||||
self.assertEqual([self.objects[1],self.objects[2],self.objects[4]],self.results.dupes)
|
eq_([self.objects[1],self.objects[2],self.objects[4]],self.results.dupes)
|
||||||
|
|
||||||
def test_dupe_list_is_cached(self):
|
def test_dupe_list_is_cached(self):
|
||||||
self.assert_(self.results.dupes is self.results.dupes)
|
assert self.results.dupes is self.results.dupes
|
||||||
|
|
||||||
def test_dupe_list_cache_is_invalidated_when_needed(self):
|
def test_dupe_list_cache_is_invalidated_when_needed(self):
|
||||||
o1,o2,o3,o4,o5 = self.objects
|
o1,o2,o3,o4,o5 = self.objects
|
||||||
self.assertEqual([o2,o3,o5],self.results.dupes)
|
eq_([o2,o3,o5],self.results.dupes)
|
||||||
self.results.make_ref(o2)
|
self.results.make_ref(o2)
|
||||||
self.assertEqual([o1,o3,o5],self.results.dupes)
|
eq_([o1,o3,o5],self.results.dupes)
|
||||||
objects,matches,groups = GetTestGroups()
|
objects,matches,groups = GetTestGroups()
|
||||||
o1,o2,o3,o4,o5 = objects
|
o1,o2,o3,o4,o5 = objects
|
||||||
self.results.groups = groups
|
self.results.groups = groups
|
||||||
self.assertEqual([o2,o3,o5],self.results.dupes)
|
eq_([o2,o3,o5],self.results.dupes)
|
||||||
|
|
||||||
def test_dupe_list_sort(self):
|
def test_dupe_list_sort(self):
|
||||||
o1,o2,o3,o4,o5 = self.objects
|
o1,o2,o3,o4,o5 = self.objects
|
||||||
@@ -164,9 +167,9 @@ class TCResultsWithSomeGroups(TestCase):
|
|||||||
o4.size = 2
|
o4.size = 2
|
||||||
o5.size = 1
|
o5.size = 1
|
||||||
self.results.sort_dupes(2)
|
self.results.sort_dupes(2)
|
||||||
self.assertEqual([o5,o3,o2],self.results.dupes)
|
eq_([o5,o3,o2],self.results.dupes)
|
||||||
self.results.sort_dupes(2,False)
|
self.results.sort_dupes(2,False)
|
||||||
self.assertEqual([o2,o3,o5],self.results.dupes)
|
eq_([o2,o3,o5],self.results.dupes)
|
||||||
|
|
||||||
def test_dupe_list_remember_sort(self):
|
def test_dupe_list_remember_sort(self):
|
||||||
o1,o2,o3,o4,o5 = self.objects
|
o1,o2,o3,o4,o5 = self.objects
|
||||||
@@ -177,7 +180,7 @@ class TCResultsWithSomeGroups(TestCase):
|
|||||||
o5.size = 1
|
o5.size = 1
|
||||||
self.results.sort_dupes(2)
|
self.results.sort_dupes(2)
|
||||||
self.results.make_ref(o2)
|
self.results.make_ref(o2)
|
||||||
self.assertEqual([o5,o3,o1],self.results.dupes)
|
eq_([o5,o3,o1],self.results.dupes)
|
||||||
|
|
||||||
def test_dupe_list_sort_delta_values(self):
|
def test_dupe_list_sort_delta_values(self):
|
||||||
o1,o2,o3,o4,o5 = self.objects
|
o1,o2,o3,o4,o5 = self.objects
|
||||||
@@ -187,19 +190,69 @@ class TCResultsWithSomeGroups(TestCase):
|
|||||||
o4.size = 20
|
o4.size = 20
|
||||||
o5.size = 1 #-19
|
o5.size = 1 #-19
|
||||||
self.results.sort_dupes(2,delta=True)
|
self.results.sort_dupes(2,delta=True)
|
||||||
self.assertEqual([o5,o2,o3],self.results.dupes)
|
eq_([o5,o2,o3],self.results.dupes)
|
||||||
|
|
||||||
def test_sort_empty_list(self):
|
def test_sort_empty_list(self):
|
||||||
#There was an infinite loop when sorting an empty list.
|
#There was an infinite loop when sorting an empty list.
|
||||||
r = Results(data)
|
r = Results(data)
|
||||||
r.sort_dupes(0)
|
r.sort_dupes(0)
|
||||||
self.assertEqual([],r.dupes)
|
eq_([],r.dupes)
|
||||||
|
|
||||||
def test_dupe_list_update_on_remove_duplicates(self):
|
def test_dupe_list_update_on_remove_duplicates(self):
|
||||||
o1,o2,o3,o4,o5 = self.objects
|
o1,o2,o3,o4,o5 = self.objects
|
||||||
self.assertEqual(3,len(self.results.dupes))
|
eq_(3,len(self.results.dupes))
|
||||||
self.results.remove_duplicates([o2])
|
self.results.remove_duplicates([o2])
|
||||||
self.assertEqual(2,len(self.results.dupes))
|
eq_(2,len(self.results.dupes))
|
||||||
|
|
||||||
|
def test_is_modified(self):
|
||||||
|
# Changing the groups sets the modified flag
|
||||||
|
assert self.results.is_modified
|
||||||
|
|
||||||
|
def test_is_modified_after_save_and_load(self):
|
||||||
|
# Saving/Loading a file sets the modified flag back to False
|
||||||
|
def get_file(path):
|
||||||
|
return [f for f in self.objects if str(f.path) == path][0]
|
||||||
|
|
||||||
|
f = io.BytesIO()
|
||||||
|
self.results.save_to_xml(f)
|
||||||
|
assert not self.results.is_modified
|
||||||
|
self.results.groups = self.groups # sets the flag back
|
||||||
|
f.seek(0)
|
||||||
|
self.results.load_from_xml(f, get_file)
|
||||||
|
assert not self.results.is_modified
|
||||||
|
|
||||||
|
|
||||||
|
class ResultsWithSavedResults(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.results = Results(data)
|
||||||
|
self.objects,self.matches,self.groups = GetTestGroups()
|
||||||
|
self.results.groups = self.groups
|
||||||
|
self.f = io.BytesIO()
|
||||||
|
self.results.save_to_xml(self.f)
|
||||||
|
self.f.seek(0)
|
||||||
|
|
||||||
|
def test_is_modified(self):
|
||||||
|
# Saving a file sets the modified flag back to False
|
||||||
|
assert not self.results.is_modified
|
||||||
|
|
||||||
|
def test_is_modified_after_load(self):
|
||||||
|
# Loading a file sets the modified flag back to False
|
||||||
|
def get_file(path):
|
||||||
|
return [f for f in self.objects if str(f.path) == path][0]
|
||||||
|
|
||||||
|
self.results.groups = self.groups # sets the flag back
|
||||||
|
self.results.load_from_xml(self.f, get_file)
|
||||||
|
assert not self.results.is_modified
|
||||||
|
|
||||||
|
def test_is_modified_after_remove(self):
|
||||||
|
# Removing dupes sets the modified flag
|
||||||
|
self.results.remove_duplicates([self.results.groups[0].dupes[0]])
|
||||||
|
assert self.results.is_modified
|
||||||
|
|
||||||
|
def test_is_modified_after_make_ref(self):
|
||||||
|
# Making a dupe ref sets the modified flag
|
||||||
|
self.results.make_ref(self.results.groups[0].dupes[0])
|
||||||
|
assert self.results.is_modified
|
||||||
|
|
||||||
|
|
||||||
class TCResultsMarkings(TestCase):
|
class TCResultsMarkings(TestCase):
|
||||||
@@ -209,27 +262,27 @@ class TCResultsMarkings(TestCase):
|
|||||||
self.results.groups = self.groups
|
self.results.groups = self.groups
|
||||||
|
|
||||||
def test_stat_line(self):
|
def test_stat_line(self):
|
||||||
self.assertEqual("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
eq_("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
||||||
self.results.mark(self.objects[1])
|
self.results.mark(self.objects[1])
|
||||||
self.assertEqual("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
|
eq_("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
|
||||||
self.results.mark_invert()
|
self.results.mark_invert()
|
||||||
self.assertEqual("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
eq_("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
||||||
self.results.mark_invert()
|
self.results.mark_invert()
|
||||||
self.results.unmark(self.objects[1])
|
self.results.unmark(self.objects[1])
|
||||||
self.results.mark(self.objects[2])
|
self.results.mark(self.objects[2])
|
||||||
self.results.mark(self.objects[4])
|
self.results.mark(self.objects[4])
|
||||||
self.assertEqual("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
eq_("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
||||||
self.results.mark(self.objects[0]) #this is a ref, it can't be counted
|
self.results.mark(self.objects[0]) #this is a ref, it can't be counted
|
||||||
self.assertEqual("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
eq_("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
||||||
self.results.groups = self.groups
|
self.results.groups = self.groups
|
||||||
self.assertEqual("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
eq_("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
|
||||||
|
|
||||||
def test_with_ref_duplicate(self):
|
def test_with_ref_duplicate(self):
|
||||||
self.objects[1].is_ref = True
|
self.objects[1].is_ref = True
|
||||||
self.results.groups = self.groups
|
self.results.groups = self.groups
|
||||||
self.assert_(not self.results.mark(self.objects[1]))
|
assert not self.results.mark(self.objects[1])
|
||||||
self.results.mark(self.objects[2])
|
self.results.mark(self.objects[2])
|
||||||
self.assertEqual("1 / 2 (1.00 B / 2.00 B) duplicates marked.",self.results.stat_line)
|
eq_("1 / 2 (1.00 B / 2.00 B) duplicates marked.",self.results.stat_line)
|
||||||
|
|
||||||
def test_perform_on_marked(self):
|
def test_perform_on_marked(self):
|
||||||
def log_object(o):
|
def log_object(o):
|
||||||
@@ -239,17 +292,17 @@ class TCResultsMarkings(TestCase):
|
|||||||
log = []
|
log = []
|
||||||
self.results.mark_all()
|
self.results.mark_all()
|
||||||
self.results.perform_on_marked(log_object,False)
|
self.results.perform_on_marked(log_object,False)
|
||||||
self.assert_(self.objects[1] in log)
|
assert self.objects[1] in log
|
||||||
self.assert_(self.objects[2] in log)
|
assert self.objects[2] in log
|
||||||
self.assert_(self.objects[4] in log)
|
assert self.objects[4] in log
|
||||||
self.assertEqual(3,len(log))
|
eq_(3,len(log))
|
||||||
log = []
|
log = []
|
||||||
self.results.mark_none()
|
self.results.mark_none()
|
||||||
self.results.mark(self.objects[4])
|
self.results.mark(self.objects[4])
|
||||||
self.results.perform_on_marked(log_object,True)
|
self.results.perform_on_marked(log_object,True)
|
||||||
self.assertEqual(1,len(log))
|
eq_(1,len(log))
|
||||||
self.assert_(self.objects[4] in log)
|
assert self.objects[4] in log
|
||||||
self.assertEqual(1,len(self.results.groups))
|
eq_(1,len(self.results.groups))
|
||||||
|
|
||||||
def test_perform_on_marked_with_problems(self):
|
def test_perform_on_marked_with_problems(self):
|
||||||
def log_object(o):
|
def log_object(o):
|
||||||
@@ -282,61 +335,61 @@ class TCResultsMarkings(TestCase):
|
|||||||
self.objects[1].is_ref = True
|
self.objects[1].is_ref = True
|
||||||
self.results.mark_all()
|
self.results.mark_all()
|
||||||
self.results.perform_on_marked(log_object,True)
|
self.results.perform_on_marked(log_object,True)
|
||||||
self.assert_(self.objects[1] not in log)
|
assert self.objects[1] not in log
|
||||||
self.assert_(self.objects[2] in log)
|
assert self.objects[2] in log
|
||||||
self.assert_(self.objects[4] in log)
|
assert self.objects[4] in log
|
||||||
self.assertEqual(2,len(log))
|
eq_(2,len(log))
|
||||||
self.assertEqual(0,len(self.results.groups))
|
eq_(0,len(self.results.groups))
|
||||||
|
|
||||||
def test_perform_on_marked_remove_objects_only_at_the_end(self):
|
def test_perform_on_marked_remove_objects_only_at_the_end(self):
|
||||||
def check_groups(o):
|
def check_groups(o):
|
||||||
self.assertEqual(3,len(g1))
|
eq_(3,len(g1))
|
||||||
self.assertEqual(2,len(g2))
|
eq_(2,len(g2))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
g1,g2 = self.results.groups
|
g1,g2 = self.results.groups
|
||||||
self.results.mark_all()
|
self.results.mark_all()
|
||||||
self.results.perform_on_marked(check_groups,True)
|
self.results.perform_on_marked(check_groups,True)
|
||||||
self.assertEqual(0,len(g1))
|
eq_(0,len(g1))
|
||||||
self.assertEqual(0,len(g2))
|
eq_(0,len(g2))
|
||||||
self.assertEqual(0,len(self.results.groups))
|
eq_(0,len(self.results.groups))
|
||||||
|
|
||||||
def test_remove_duplicates(self):
|
def test_remove_duplicates(self):
|
||||||
g1 = self.results.groups[0]
|
g1 = self.results.groups[0]
|
||||||
g2 = self.results.groups[1]
|
g2 = self.results.groups[1]
|
||||||
self.results.mark(g1.dupes[0])
|
self.results.mark(g1.dupes[0])
|
||||||
self.assertEqual("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
|
eq_("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
|
||||||
self.results.remove_duplicates([g1.dupes[1]])
|
self.results.remove_duplicates([g1.dupes[1]])
|
||||||
self.assertEqual("1 / 2 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
|
eq_("1 / 2 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
|
||||||
self.results.remove_duplicates([g1.dupes[0]])
|
self.results.remove_duplicates([g1.dupes[0]])
|
||||||
self.assertEqual("0 / 1 (0.00 B / 1.00 B) duplicates marked.",self.results.stat_line)
|
eq_("0 / 1 (0.00 B / 1.00 B) duplicates marked.",self.results.stat_line)
|
||||||
|
|
||||||
def test_make_ref(self):
|
def test_make_ref(self):
|
||||||
g = self.results.groups[0]
|
g = self.results.groups[0]
|
||||||
d = g.dupes[0]
|
d = g.dupes[0]
|
||||||
self.results.mark(d)
|
self.results.mark(d)
|
||||||
self.assertEqual("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
|
eq_("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
|
||||||
self.results.make_ref(d)
|
self.results.make_ref(d)
|
||||||
self.assertEqual("0 / 3 (0.00 B / 3.00 B) duplicates marked.",self.results.stat_line)
|
eq_("0 / 3 (0.00 B / 3.00 B) duplicates marked.",self.results.stat_line)
|
||||||
self.results.make_ref(d)
|
self.results.make_ref(d)
|
||||||
self.assertEqual("0 / 3 (0.00 B / 3.00 B) duplicates marked.",self.results.stat_line)
|
eq_("0 / 3 (0.00 B / 3.00 B) duplicates marked.",self.results.stat_line)
|
||||||
|
|
||||||
def test_SaveXML(self):
|
def test_SaveXML(self):
|
||||||
self.results.mark(self.objects[1])
|
self.results.mark(self.objects[1])
|
||||||
self.results.mark_invert()
|
self.results.mark_invert()
|
||||||
f = StringIO.StringIO()
|
f = io.BytesIO()
|
||||||
self.results.save_to_xml(f)
|
self.results.save_to_xml(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
doc = etree.parse(f)
|
doc = ET.parse(f)
|
||||||
root = doc.getroot()
|
root = doc.getroot()
|
||||||
g1, g2 = root.iterchildren('group')
|
g1, g2 = root.getiterator('group')
|
||||||
d1, d2, d3 = g1.iterchildren('file')
|
d1, d2, d3 = g1.getiterator('file')
|
||||||
self.assertEqual('n', d1.get('marked'))
|
eq_('n', d1.get('marked'))
|
||||||
self.assertEqual('n', d2.get('marked'))
|
eq_('n', d2.get('marked'))
|
||||||
self.assertEqual('y', d3.get('marked'))
|
eq_('y', d3.get('marked'))
|
||||||
d1, d2 = g2.iterchildren('file')
|
d1, d2 = g2.getiterator('file')
|
||||||
self.assertEqual('n', d1.get('marked'))
|
eq_('n', d1.get('marked'))
|
||||||
self.assertEqual('y', d2.get('marked'))
|
eq_('y', d2.get('marked'))
|
||||||
|
|
||||||
def test_LoadXML(self):
|
def test_LoadXML(self):
|
||||||
def get_file(path):
|
def get_file(path):
|
||||||
@@ -345,16 +398,16 @@ class TCResultsMarkings(TestCase):
|
|||||||
self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path
|
self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path
|
||||||
self.results.mark(self.objects[1])
|
self.results.mark(self.objects[1])
|
||||||
self.results.mark_invert()
|
self.results.mark_invert()
|
||||||
f = StringIO.StringIO()
|
f = io.BytesIO()
|
||||||
self.results.save_to_xml(f)
|
self.results.save_to_xml(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
r = Results(data)
|
r = Results(data)
|
||||||
r.load_from_xml(f,get_file)
|
r.load_from_xml(f,get_file)
|
||||||
self.assert_(not r.is_marked(self.objects[0]))
|
assert not r.is_marked(self.objects[0])
|
||||||
self.assert_(not r.is_marked(self.objects[1]))
|
assert not r.is_marked(self.objects[1])
|
||||||
self.assert_(r.is_marked(self.objects[2]))
|
assert r.is_marked(self.objects[2])
|
||||||
self.assert_(not r.is_marked(self.objects[3]))
|
assert not r.is_marked(self.objects[3])
|
||||||
self.assert_(r.is_marked(self.objects[4]))
|
assert r.is_marked(self.objects[4])
|
||||||
|
|
||||||
|
|
||||||
class TCResultsXML(TestCase):
|
class TCResultsXML(TestCase):
|
||||||
@@ -369,38 +422,38 @@ class TCResultsXML(TestCase):
|
|||||||
def test_save_to_xml(self):
|
def test_save_to_xml(self):
|
||||||
self.objects[0].is_ref = True
|
self.objects[0].is_ref = True
|
||||||
self.objects[0].words = [['foo','bar']]
|
self.objects[0].words = [['foo','bar']]
|
||||||
f = StringIO.StringIO()
|
f = io.BytesIO()
|
||||||
self.results.save_to_xml(f)
|
self.results.save_to_xml(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
doc = etree.parse(f)
|
doc = ET.parse(f)
|
||||||
root = doc.getroot()
|
root = doc.getroot()
|
||||||
self.assertEqual('results', root.tag)
|
eq_('results', root.tag)
|
||||||
self.assertEqual(2, len(root))
|
eq_(2, len(root))
|
||||||
self.assertEqual(2, len([c for c in root if c.tag == 'group']))
|
eq_(2, len([c for c in root if c.tag == 'group']))
|
||||||
g1, g2 = root
|
g1, g2 = root
|
||||||
self.assertEqual(6,len(g1))
|
eq_(6,len(g1))
|
||||||
self.assertEqual(3,len([c for c in g1 if c.tag == 'file']))
|
eq_(3,len([c for c in g1 if c.tag == 'file']))
|
||||||
self.assertEqual(3,len([c for c in g1 if c.tag == 'match']))
|
eq_(3,len([c for c in g1 if c.tag == 'match']))
|
||||||
d1, d2, d3 = [c for c in g1 if c.tag == 'file']
|
d1, d2, d3 = [c for c in g1 if c.tag == 'file']
|
||||||
self.assertEqual(op.join('basepath','foo bar'),d1.get('path'))
|
eq_(op.join('basepath','foo bar'),d1.get('path'))
|
||||||
self.assertEqual(op.join('basepath','bar bleh'),d2.get('path'))
|
eq_(op.join('basepath','bar bleh'),d2.get('path'))
|
||||||
self.assertEqual(op.join('basepath','foo bleh'),d3.get('path'))
|
eq_(op.join('basepath','foo bleh'),d3.get('path'))
|
||||||
self.assertEqual('y',d1.get('is_ref'))
|
eq_('y',d1.get('is_ref'))
|
||||||
self.assertEqual('n',d2.get('is_ref'))
|
eq_('n',d2.get('is_ref'))
|
||||||
self.assertEqual('n',d3.get('is_ref'))
|
eq_('n',d3.get('is_ref'))
|
||||||
self.assertEqual('foo,bar',d1.get('words'))
|
eq_('foo,bar',d1.get('words'))
|
||||||
self.assertEqual('bar,bleh',d2.get('words'))
|
eq_('bar,bleh',d2.get('words'))
|
||||||
self.assertEqual('foo,bleh',d3.get('words'))
|
eq_('foo,bleh',d3.get('words'))
|
||||||
self.assertEqual(3,len(g2))
|
eq_(3,len(g2))
|
||||||
self.assertEqual(2,len([c for c in g2 if c.tag == 'file']))
|
eq_(2,len([c for c in g2 if c.tag == 'file']))
|
||||||
self.assertEqual(1,len([c for c in g2 if c.tag == 'match']))
|
eq_(1,len([c for c in g2 if c.tag == 'match']))
|
||||||
d1, d2 = [c for c in g2 if c.tag == 'file']
|
d1, d2 = [c for c in g2 if c.tag == 'file']
|
||||||
self.assertEqual(op.join('basepath','ibabtu'),d1.get('path'))
|
eq_(op.join('basepath','ibabtu'),d1.get('path'))
|
||||||
self.assertEqual(op.join('basepath','ibabtu'),d2.get('path'))
|
eq_(op.join('basepath','ibabtu'),d2.get('path'))
|
||||||
self.assertEqual('n',d1.get('is_ref'))
|
eq_('n',d1.get('is_ref'))
|
||||||
self.assertEqual('n',d2.get('is_ref'))
|
eq_('n',d2.get('is_ref'))
|
||||||
self.assertEqual('ibabtu',d1.get('words'))
|
eq_('ibabtu',d1.get('words'))
|
||||||
self.assertEqual('ibabtu',d2.get('words'))
|
eq_('ibabtu',d2.get('words'))
|
||||||
|
|
||||||
def test_LoadXML(self):
|
def test_LoadXML(self):
|
||||||
def get_file(path):
|
def get_file(path):
|
||||||
@@ -408,30 +461,30 @@ class TCResultsXML(TestCase):
|
|||||||
|
|
||||||
self.objects[0].is_ref = True
|
self.objects[0].is_ref = True
|
||||||
self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path
|
self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path
|
||||||
f = StringIO.StringIO()
|
f = io.BytesIO()
|
||||||
self.results.save_to_xml(f)
|
self.results.save_to_xml(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
r = Results(data)
|
r = Results(data)
|
||||||
r.load_from_xml(f,get_file)
|
r.load_from_xml(f,get_file)
|
||||||
self.assertEqual(2,len(r.groups))
|
eq_(2,len(r.groups))
|
||||||
g1,g2 = r.groups
|
g1,g2 = r.groups
|
||||||
self.assertEqual(3,len(g1))
|
eq_(3,len(g1))
|
||||||
self.assert_(g1[0].is_ref)
|
assert g1[0].is_ref
|
||||||
self.assert_(not g1[1].is_ref)
|
assert not g1[1].is_ref
|
||||||
self.assert_(not g1[2].is_ref)
|
assert not g1[2].is_ref
|
||||||
self.assert_(g1[0] is self.objects[0])
|
assert g1[0] is self.objects[0]
|
||||||
self.assert_(g1[1] is self.objects[1])
|
assert g1[1] is self.objects[1]
|
||||||
self.assert_(g1[2] is self.objects[2])
|
assert g1[2] is self.objects[2]
|
||||||
self.assertEqual(['foo','bar'],g1[0].words)
|
eq_(['foo','bar'],g1[0].words)
|
||||||
self.assertEqual(['bar','bleh'],g1[1].words)
|
eq_(['bar','bleh'],g1[1].words)
|
||||||
self.assertEqual(['foo','bleh'],g1[2].words)
|
eq_(['foo','bleh'],g1[2].words)
|
||||||
self.assertEqual(2,len(g2))
|
eq_(2,len(g2))
|
||||||
self.assert_(not g2[0].is_ref)
|
assert not g2[0].is_ref
|
||||||
self.assert_(not g2[1].is_ref)
|
assert not g2[1].is_ref
|
||||||
self.assert_(g2[0] is self.objects[3])
|
assert g2[0] is self.objects[3]
|
||||||
self.assert_(g2[1] is self.objects[4])
|
assert g2[1] is self.objects[4]
|
||||||
self.assertEqual(['ibabtu'],g2[0].words)
|
eq_(['ibabtu'],g2[0].words)
|
||||||
self.assertEqual(['ibabtu'],g2[1].words)
|
eq_(['ibabtu'],g2[1].words)
|
||||||
|
|
||||||
def test_LoadXML_with_filename(self):
|
def test_LoadXML_with_filename(self):
|
||||||
def get_file(path):
|
def get_file(path):
|
||||||
@@ -442,7 +495,7 @@ class TCResultsXML(TestCase):
|
|||||||
self.results.save_to_xml(filename)
|
self.results.save_to_xml(filename)
|
||||||
r = Results(data)
|
r = Results(data)
|
||||||
r.load_from_xml(filename,get_file)
|
r.load_from_xml(filename,get_file)
|
||||||
self.assertEqual(2,len(r.groups))
|
eq_(2,len(r.groups))
|
||||||
|
|
||||||
def test_LoadXML_with_some_files_that_dont_exist_anymore(self):
|
def test_LoadXML_with_some_files_that_dont_exist_anymore(self):
|
||||||
def get_file(path):
|
def get_file(path):
|
||||||
@@ -451,84 +504,84 @@ class TCResultsXML(TestCase):
|
|||||||
return [f for f in self.objects if str(f.path) == path][0]
|
return [f for f in self.objects if str(f.path) == path][0]
|
||||||
|
|
||||||
self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path
|
self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path
|
||||||
f = StringIO.StringIO()
|
f = io.BytesIO()
|
||||||
self.results.save_to_xml(f)
|
self.results.save_to_xml(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
r = Results(data)
|
r = Results(data)
|
||||||
r.load_from_xml(f,get_file)
|
r.load_from_xml(f,get_file)
|
||||||
self.assertEqual(1,len(r.groups))
|
eq_(1,len(r.groups))
|
||||||
self.assertEqual(3,len(r.groups[0]))
|
eq_(3,len(r.groups[0]))
|
||||||
|
|
||||||
def test_LoadXML_missing_attributes_and_bogus_elements(self):
|
def test_LoadXML_missing_attributes_and_bogus_elements(self):
|
||||||
def get_file(path):
|
def get_file(path):
|
||||||
return [f for f in self.objects if str(f.path) == path][0]
|
return [f for f in self.objects if str(f.path) == path][0]
|
||||||
|
|
||||||
root = etree.Element('foobar') #The root element shouldn't matter, really.
|
root = ET.Element('foobar') #The root element shouldn't matter, really.
|
||||||
group_node = etree.SubElement(root, 'group')
|
group_node = ET.SubElement(root, 'group')
|
||||||
dupe_node = etree.SubElement(group_node, 'file') #Perfectly correct file
|
dupe_node = ET.SubElement(group_node, 'file') #Perfectly correct file
|
||||||
dupe_node.set('path', op.join('basepath','foo bar'))
|
dupe_node.set('path', op.join('basepath','foo bar'))
|
||||||
dupe_node.set('is_ref', 'y')
|
dupe_node.set('is_ref', 'y')
|
||||||
dupe_node.set('words', 'foo,bar')
|
dupe_node.set('words', 'foo,bar')
|
||||||
dupe_node = etree.SubElement(group_node, 'file') #is_ref missing, default to 'n'
|
dupe_node = ET.SubElement(group_node, 'file') #is_ref missing, default to 'n'
|
||||||
dupe_node.set('path',op.join('basepath','foo bleh'))
|
dupe_node.set('path',op.join('basepath','foo bleh'))
|
||||||
dupe_node.set('words','foo,bleh')
|
dupe_node.set('words','foo,bleh')
|
||||||
dupe_node = etree.SubElement(group_node, 'file') #words are missing, valid.
|
dupe_node = ET.SubElement(group_node, 'file') #words are missing, valid.
|
||||||
dupe_node.set('path',op.join('basepath','bar bleh'))
|
dupe_node.set('path',op.join('basepath','bar bleh'))
|
||||||
dupe_node = etree.SubElement(group_node, 'file') #path is missing, invalid.
|
dupe_node = ET.SubElement(group_node, 'file') #path is missing, invalid.
|
||||||
dupe_node.set('words','foo,bleh')
|
dupe_node.set('words','foo,bleh')
|
||||||
dupe_node = etree.SubElement(group_node, 'foobar') #Invalid element name
|
dupe_node = ET.SubElement(group_node, 'foobar') #Invalid element name
|
||||||
dupe_node.set('path',op.join('basepath','bar bleh'))
|
dupe_node.set('path',op.join('basepath','bar bleh'))
|
||||||
dupe_node.set('is_ref','y')
|
dupe_node.set('is_ref','y')
|
||||||
dupe_node.set('words','bar,bleh')
|
dupe_node.set('words','bar,bleh')
|
||||||
match_node = etree.SubElement(group_node, 'match') # match pointing to a bad index
|
match_node = ET.SubElement(group_node, 'match') # match pointing to a bad index
|
||||||
match_node.set('first', '42')
|
match_node.set('first', '42')
|
||||||
match_node.set('second', '45')
|
match_node.set('second', '45')
|
||||||
match_node = etree.SubElement(group_node, 'match') # match with missing attrs
|
match_node = ET.SubElement(group_node, 'match') # match with missing attrs
|
||||||
match_node = etree.SubElement(group_node, 'match') # match with non-int values
|
match_node = ET.SubElement(group_node, 'match') # match with non-int values
|
||||||
match_node.set('first', 'foo')
|
match_node.set('first', 'foo')
|
||||||
match_node.set('second', 'bar')
|
match_node.set('second', 'bar')
|
||||||
match_node.set('percentage', 'baz')
|
match_node.set('percentage', 'baz')
|
||||||
group_node = etree.SubElement(root, 'foobar') #invalid group
|
group_node = ET.SubElement(root, 'foobar') #invalid group
|
||||||
group_node = etree.SubElement(root, 'group') #empty group
|
group_node = ET.SubElement(root, 'group') #empty group
|
||||||
f = StringIO.StringIO()
|
f = io.BytesIO()
|
||||||
tree = etree.ElementTree(root)
|
tree = ET.ElementTree(root)
|
||||||
tree.write(f, encoding='utf-8')
|
tree.write(f, encoding='utf-8')
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
r = Results(data)
|
r = Results(data)
|
||||||
r.load_from_xml(f, get_file)
|
r.load_from_xml(f, get_file)
|
||||||
self.assertEqual(1,len(r.groups))
|
eq_(1,len(r.groups))
|
||||||
self.assertEqual(3,len(r.groups[0]))
|
eq_(3,len(r.groups[0]))
|
||||||
|
|
||||||
def test_xml_non_ascii(self):
|
def test_xml_non_ascii(self):
|
||||||
def get_file(path):
|
def get_file(path):
|
||||||
if path == op.join('basepath',u'\xe9foo bar'):
|
if path == op.join('basepath','\xe9foo bar'):
|
||||||
return objects[0]
|
return objects[0]
|
||||||
if path == op.join('basepath',u'bar bleh'):
|
if path == op.join('basepath','bar bleh'):
|
||||||
return objects[1]
|
return objects[1]
|
||||||
|
|
||||||
objects = [NamedObject(u"\xe9foo bar",True),NamedObject("bar bleh",True)]
|
objects = [NamedObject("\xe9foo bar",True),NamedObject("bar bleh",True)]
|
||||||
matches = engine.getmatches(objects) #we should have 5 matches
|
matches = engine.getmatches(objects) #we should have 5 matches
|
||||||
groups = engine.get_groups(matches) #We should have 2 groups
|
groups = engine.get_groups(matches) #We should have 2 groups
|
||||||
for g in groups:
|
for g in groups:
|
||||||
g.prioritize(lambda x:objects.index(x)) #We want the dupes to be in the same order as the list is
|
g.prioritize(lambda x:objects.index(x)) #We want the dupes to be in the same order as the list is
|
||||||
results = Results(data)
|
results = Results(data)
|
||||||
results.groups = groups
|
results.groups = groups
|
||||||
f = StringIO.StringIO()
|
f = io.BytesIO()
|
||||||
results.save_to_xml(f)
|
results.save_to_xml(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
r = Results(data)
|
r = Results(data)
|
||||||
r.load_from_xml(f,get_file)
|
r.load_from_xml(f,get_file)
|
||||||
g = r.groups[0]
|
g = r.groups[0]
|
||||||
self.assertEqual(u"\xe9foo bar",g[0].name)
|
eq_("\xe9foo bar",g[0].name)
|
||||||
self.assertEqual(['efoo','bar'],g[0].words)
|
eq_(['efoo','bar'],g[0].words)
|
||||||
|
|
||||||
def test_load_invalid_xml(self):
|
def test_load_invalid_xml(self):
|
||||||
f = StringIO.StringIO()
|
f = io.BytesIO()
|
||||||
f.write('<this is invalid')
|
f.write(b'<this is invalid')
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
r = Results(data)
|
r = Results(data)
|
||||||
r.load_from_xml(f,None)
|
r.load_from_xml(f,None)
|
||||||
self.assertEqual(0,len(r.groups))
|
eq_(0,len(r.groups))
|
||||||
|
|
||||||
def test_load_non_existant_xml(self):
|
def test_load_non_existant_xml(self):
|
||||||
r = Results(data)
|
r = Results(data)
|
||||||
@@ -536,7 +589,7 @@ class TCResultsXML(TestCase):
|
|||||||
r.load_from_xml('does_not_exist.xml', None)
|
r.load_from_xml('does_not_exist.xml', None)
|
||||||
except IOError:
|
except IOError:
|
||||||
self.fail()
|
self.fail()
|
||||||
self.assertEqual(0,len(r.groups))
|
eq_(0,len(r.groups))
|
||||||
|
|
||||||
def test_remember_match_percentage(self):
|
def test_remember_match_percentage(self):
|
||||||
group = self.groups[0]
|
group = self.groups[0]
|
||||||
@@ -546,7 +599,7 @@ class TCResultsXML(TestCase):
|
|||||||
fake_matches.add(engine.Match(d1, d3, 43))
|
fake_matches.add(engine.Match(d1, d3, 43))
|
||||||
fake_matches.add(engine.Match(d2, d3, 46))
|
fake_matches.add(engine.Match(d2, d3, 46))
|
||||||
group.matches = fake_matches
|
group.matches = fake_matches
|
||||||
f = StringIO.StringIO()
|
f = io.BytesIO()
|
||||||
results = self.results
|
results = self.results
|
||||||
results.save_to_xml(f)
|
results.save_to_xml(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
@@ -555,16 +608,16 @@ class TCResultsXML(TestCase):
|
|||||||
group = results.groups[0]
|
group = results.groups[0]
|
||||||
d1, d2, d3 = group
|
d1, d2, d3 = group
|
||||||
match = group.get_match_of(d2) #d1 - d2
|
match = group.get_match_of(d2) #d1 - d2
|
||||||
self.assertEqual(42, match[2])
|
eq_(42, match[2])
|
||||||
match = group.get_match_of(d3) #d1 - d3
|
match = group.get_match_of(d3) #d1 - d3
|
||||||
self.assertEqual(43, match[2])
|
eq_(43, match[2])
|
||||||
group.switch_ref(d2)
|
group.switch_ref(d2)
|
||||||
match = group.get_match_of(d3) #d2 - d3
|
match = group.get_match_of(d3) #d2 - d3
|
||||||
self.assertEqual(46, match[2])
|
eq_(46, match[2])
|
||||||
|
|
||||||
def test_save_and_load(self):
|
def test_save_and_load(self):
|
||||||
# previously, when reloading matches, they wouldn't be reloaded as namedtuples
|
# previously, when reloading matches, they wouldn't be reloaded as namedtuples
|
||||||
f = StringIO.StringIO()
|
f = io.BytesIO()
|
||||||
self.results.save_to_xml(f)
|
self.results.save_to_xml(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
self.results.load_from_xml(f, self.get_file)
|
self.results.load_from_xml(f, self.get_file)
|
||||||
@@ -572,13 +625,13 @@ class TCResultsXML(TestCase):
|
|||||||
|
|
||||||
def test_apply_filter_works_on_paths(self):
|
def test_apply_filter_works_on_paths(self):
|
||||||
# apply_filter() searches on the whole path, not just on the filename.
|
# apply_filter() searches on the whole path, not just on the filename.
|
||||||
self.results.apply_filter(u'basepath')
|
self.results.apply_filter('basepath')
|
||||||
eq_(len(self.results.groups), 2)
|
eq_(len(self.results.groups), 2)
|
||||||
|
|
||||||
def test_save_xml_with_invalid_characters(self):
|
def test_save_xml_with_invalid_characters(self):
|
||||||
# Don't crash when saving files that have invalid xml characters in their path
|
# Don't crash when saving files that have invalid xml characters in their path
|
||||||
self.objects[0].name = u'foo\x19'
|
self.objects[0].name = 'foo\x19'
|
||||||
self.results.save_to_xml(StringIO.StringIO()) # don't crash
|
self.results.save_to_xml(io.BytesIO()) # don't crash
|
||||||
|
|
||||||
|
|
||||||
class TCResultsFilter(TestCase):
|
class TCResultsFilter(TestCase):
|
||||||
@@ -589,47 +642,47 @@ class TCResultsFilter(TestCase):
|
|||||||
self.results.apply_filter(r'foo')
|
self.results.apply_filter(r'foo')
|
||||||
|
|
||||||
def test_groups(self):
|
def test_groups(self):
|
||||||
self.assertEqual(1, len(self.results.groups))
|
eq_(1, len(self.results.groups))
|
||||||
self.assert_(self.results.groups[0] is self.groups[0])
|
assert self.results.groups[0] is self.groups[0]
|
||||||
|
|
||||||
def test_dupes(self):
|
def test_dupes(self):
|
||||||
# There are 2 objects matching. The first one is ref. Only the 3rd one is supposed to be in dupes.
|
# There are 2 objects matching. The first one is ref. Only the 3rd one is supposed to be in dupes.
|
||||||
self.assertEqual(1, len(self.results.dupes))
|
eq_(1, len(self.results.dupes))
|
||||||
self.assert_(self.results.dupes[0] is self.objects[2])
|
assert self.results.dupes[0] is self.objects[2]
|
||||||
|
|
||||||
def test_cancel_filter(self):
|
def test_cancel_filter(self):
|
||||||
self.results.apply_filter(None)
|
self.results.apply_filter(None)
|
||||||
self.assertEqual(3, len(self.results.dupes))
|
eq_(3, len(self.results.dupes))
|
||||||
self.assertEqual(2, len(self.results.groups))
|
eq_(2, len(self.results.groups))
|
||||||
|
|
||||||
def test_dupes_reconstructed_filtered(self):
|
def test_dupes_reconstructed_filtered(self):
|
||||||
# make_ref resets self.__dupes to None. When it's reconstructed, we want it filtered
|
# make_ref resets self.__dupes to None. When it's reconstructed, we want it filtered
|
||||||
dupe = self.results.dupes[0] #3rd object
|
dupe = self.results.dupes[0] #3rd object
|
||||||
self.results.make_ref(dupe)
|
self.results.make_ref(dupe)
|
||||||
self.assertEqual(1, len(self.results.dupes))
|
eq_(1, len(self.results.dupes))
|
||||||
self.assert_(self.results.dupes[0] is self.objects[0])
|
assert self.results.dupes[0] is self.objects[0]
|
||||||
|
|
||||||
def test_include_ref_dupes_in_filter(self):
|
def test_include_ref_dupes_in_filter(self):
|
||||||
# When only the ref of a group match the filter, include it in the group
|
# When only the ref of a group match the filter, include it in the group
|
||||||
self.results.apply_filter(None)
|
self.results.apply_filter(None)
|
||||||
self.results.apply_filter(r'foo bar')
|
self.results.apply_filter(r'foo bar')
|
||||||
self.assertEqual(1, len(self.results.groups))
|
eq_(1, len(self.results.groups))
|
||||||
self.assertEqual(0, len(self.results.dupes))
|
eq_(0, len(self.results.dupes))
|
||||||
|
|
||||||
def test_filters_build_on_one_another(self):
|
def test_filters_build_on_one_another(self):
|
||||||
self.results.apply_filter(r'bar')
|
self.results.apply_filter(r'bar')
|
||||||
self.assertEqual(1, len(self.results.groups))
|
eq_(1, len(self.results.groups))
|
||||||
self.assertEqual(0, len(self.results.dupes))
|
eq_(0, len(self.results.dupes))
|
||||||
|
|
||||||
def test_stat_line(self):
|
def test_stat_line(self):
|
||||||
expected = '0 / 1 (0.00 B / 1.00 B) duplicates marked. filter: foo'
|
expected = '0 / 1 (0.00 B / 1.00 B) duplicates marked. filter: foo'
|
||||||
self.assertEqual(expected, self.results.stat_line)
|
eq_(expected, self.results.stat_line)
|
||||||
self.results.apply_filter(r'bar')
|
self.results.apply_filter(r'bar')
|
||||||
expected = '0 / 0 (0.00 B / 0.00 B) duplicates marked. filter: foo --> bar'
|
expected = '0 / 0 (0.00 B / 0.00 B) duplicates marked. filter: foo --> bar'
|
||||||
self.assertEqual(expected, self.results.stat_line)
|
eq_(expected, self.results.stat_line)
|
||||||
self.results.apply_filter(None)
|
self.results.apply_filter(None)
|
||||||
expected = '0 / 3 (0.00 B / 1.01 KB) duplicates marked.'
|
expected = '0 / 3 (0.00 B / 1.01 KB) duplicates marked.'
|
||||||
self.assertEqual(expected, self.results.stat_line)
|
eq_(expected, self.results.stat_line)
|
||||||
|
|
||||||
def test_mark_count_is_filtered_as_well(self):
|
def test_mark_count_is_filtered_as_well(self):
|
||||||
self.results.apply_filter(None)
|
self.results.apply_filter(None)
|
||||||
@@ -638,7 +691,7 @@ class TCResultsFilter(TestCase):
|
|||||||
self.results.mark(dupe)
|
self.results.mark(dupe)
|
||||||
self.results.apply_filter(r'foo')
|
self.results.apply_filter(r'foo')
|
||||||
expected = '1 / 1 (1.00 B / 1.00 B) duplicates marked. filter: foo'
|
expected = '1 / 1 (1.00 B / 1.00 B) duplicates marked. filter: foo'
|
||||||
self.assertEqual(expected, self.results.stat_line)
|
eq_(expected, self.results.stat_line)
|
||||||
|
|
||||||
def test_sort_groups(self):
|
def test_sort_groups(self):
|
||||||
self.results.apply_filter(None)
|
self.results.apply_filter(None)
|
||||||
@@ -646,22 +699,22 @@ class TCResultsFilter(TestCase):
|
|||||||
g1,g2 = self.groups
|
g1,g2 = self.groups
|
||||||
self.results.apply_filter('a') # Matches both group
|
self.results.apply_filter('a') # Matches both group
|
||||||
self.results.sort_groups(2) #2 is the key for size
|
self.results.sort_groups(2) #2 is the key for size
|
||||||
self.assert_(self.results.groups[0] is g2)
|
assert self.results.groups[0] is g2
|
||||||
self.assert_(self.results.groups[1] is g1)
|
assert self.results.groups[1] is g1
|
||||||
self.results.apply_filter(None)
|
self.results.apply_filter(None)
|
||||||
self.assert_(self.results.groups[0] is g2)
|
assert self.results.groups[0] is g2
|
||||||
self.assert_(self.results.groups[1] is g1)
|
assert self.results.groups[1] is g1
|
||||||
self.results.sort_groups(2, False)
|
self.results.sort_groups(2, False)
|
||||||
self.results.apply_filter('a')
|
self.results.apply_filter('a')
|
||||||
self.assert_(self.results.groups[1] is g2)
|
assert self.results.groups[1] is g2
|
||||||
self.assert_(self.results.groups[0] is g1)
|
assert self.results.groups[0] is g1
|
||||||
|
|
||||||
def test_set_group(self):
|
def test_set_group(self):
|
||||||
#We want the new group to be filtered
|
#We want the new group to be filtered
|
||||||
self.objects, self.matches, self.groups = GetTestGroups()
|
self.objects, self.matches, self.groups = GetTestGroups()
|
||||||
self.results.groups = self.groups
|
self.results.groups = self.groups
|
||||||
self.assertEqual(1, len(self.results.groups))
|
eq_(1, len(self.results.groups))
|
||||||
self.assert_(self.results.groups[0] is self.groups[0])
|
assert self.results.groups[0] is self.groups[0]
|
||||||
|
|
||||||
def test_load_cancels_filter(self):
|
def test_load_cancels_filter(self):
|
||||||
def get_file(path):
|
def get_file(path):
|
||||||
@@ -673,23 +726,23 @@ class TCResultsFilter(TestCase):
|
|||||||
r = Results(data)
|
r = Results(data)
|
||||||
r.apply_filter('foo')
|
r.apply_filter('foo')
|
||||||
r.load_from_xml(filename,get_file)
|
r.load_from_xml(filename,get_file)
|
||||||
self.assertEqual(2,len(r.groups))
|
eq_(2,len(r.groups))
|
||||||
|
|
||||||
def test_remove_dupe(self):
|
def test_remove_dupe(self):
|
||||||
self.results.remove_duplicates([self.results.dupes[0]])
|
self.results.remove_duplicates([self.results.dupes[0]])
|
||||||
self.results.apply_filter(None)
|
self.results.apply_filter(None)
|
||||||
self.assertEqual(2,len(self.results.groups))
|
eq_(2,len(self.results.groups))
|
||||||
self.assertEqual(2,len(self.results.dupes))
|
eq_(2,len(self.results.dupes))
|
||||||
self.results.apply_filter('ibabtu')
|
self.results.apply_filter('ibabtu')
|
||||||
self.results.remove_duplicates([self.results.dupes[0]])
|
self.results.remove_duplicates([self.results.dupes[0]])
|
||||||
self.results.apply_filter(None)
|
self.results.apply_filter(None)
|
||||||
self.assertEqual(1,len(self.results.groups))
|
eq_(1,len(self.results.groups))
|
||||||
self.assertEqual(1,len(self.results.dupes))
|
eq_(1,len(self.results.dupes))
|
||||||
|
|
||||||
def test_filter_is_case_insensitive(self):
|
def test_filter_is_case_insensitive(self):
|
||||||
self.results.apply_filter(None)
|
self.results.apply_filter(None)
|
||||||
self.results.apply_filter('FOO')
|
self.results.apply_filter('FOO')
|
||||||
self.assertEqual(1, len(self.results.dupes))
|
eq_(1, len(self.results.dupes))
|
||||||
|
|
||||||
def test_make_ref_on_filtered_out_doesnt_mess_stats(self):
|
def test_make_ref_on_filtered_out_doesnt_mess_stats(self):
|
||||||
# When filtered, a group containing filtered out dupes will display them as being reference.
|
# When filtered, a group containing filtered out dupes will display them as being reference.
|
||||||
@@ -700,10 +753,10 @@ class TCResultsFilter(TestCase):
|
|||||||
self.results.make_ref(bar_bleh)
|
self.results.make_ref(bar_bleh)
|
||||||
# Now the stats should display *2* markable dupes (instead of 1)
|
# Now the stats should display *2* markable dupes (instead of 1)
|
||||||
expected = '0 / 2 (0.00 B / 2.00 B) duplicates marked. filter: foo'
|
expected = '0 / 2 (0.00 B / 2.00 B) duplicates marked. filter: foo'
|
||||||
self.assertEqual(expected, self.results.stat_line)
|
eq_(expected, self.results.stat_line)
|
||||||
self.results.apply_filter(None) # Now let's make sure our unfiltered results aren't fucked up
|
self.results.apply_filter(None) # Now let's make sure our unfiltered results aren't fucked up
|
||||||
expected = '0 / 3 (0.00 B / 3.00 B) duplicates marked.'
|
expected = '0 / 3 (0.00 B / 3.00 B) duplicates marked.'
|
||||||
self.assertEqual(expected, self.results.stat_line)
|
eq_(expected, self.results.stat_line)
|
||||||
|
|
||||||
|
|
||||||
class TCResultsRefFile(TestCase):
|
class TCResultsRefFile(TestCase):
|
||||||
@@ -716,15 +769,15 @@ class TCResultsRefFile(TestCase):
|
|||||||
|
|
||||||
def test_stat_line(self):
|
def test_stat_line(self):
|
||||||
expected = '0 / 2 (0.00 B / 2.00 B) duplicates marked.'
|
expected = '0 / 2 (0.00 B / 2.00 B) duplicates marked.'
|
||||||
self.assertEqual(expected, self.results.stat_line)
|
eq_(expected, self.results.stat_line)
|
||||||
|
|
||||||
def test_make_ref(self):
|
def test_make_ref(self):
|
||||||
d = self.results.groups[0].dupes[1] #non-ref
|
d = self.results.groups[0].dupes[1] #non-ref
|
||||||
r = self.results.groups[0].ref
|
r = self.results.groups[0].ref
|
||||||
self.results.make_ref(d)
|
self.results.make_ref(d)
|
||||||
expected = '0 / 1 (0.00 B / 1.00 B) duplicates marked.'
|
expected = '0 / 1 (0.00 B / 1.00 B) duplicates marked.'
|
||||||
self.assertEqual(expected, self.results.stat_line)
|
eq_(expected, self.results.stat_line)
|
||||||
self.results.make_ref(r)
|
self.results.make_ref(r)
|
||||||
expected = '0 / 2 (0.00 B / 2.00 B) duplicates marked.'
|
expected = '0 / 2 (0.00 B / 2.00 B) duplicates marked.'
|
||||||
self.assertEqual(expected, self.results.stat_line)
|
eq_(expected, self.results.stat_line)
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ class NamedObject(object):
|
|||||||
self.path = Path('')
|
self.path = Path('')
|
||||||
self.words = getwords(name)
|
self.words = getwords(name)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<NamedObject %r>' % self.name
|
||||||
|
|
||||||
|
|
||||||
no = NamedObject
|
no = NamedObject
|
||||||
|
|
||||||
@@ -43,7 +46,7 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
def test_default_settings(self):
|
def test_default_settings(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
eq_(s.min_match_percentage, 80)
|
eq_(s.min_match_percentage, 80)
|
||||||
eq_(s.scan_type, SCAN_TYPE_FILENAME)
|
eq_(s.scan_type, ScanType.Filename)
|
||||||
eq_(s.mix_file_kind, True)
|
eq_(s.mix_file_kind, True)
|
||||||
eq_(s.word_weighting, False)
|
eq_(s.word_weighting, False)
|
||||||
eq_(s.match_similar_words, False)
|
eq_(s.match_similar_words, False)
|
||||||
@@ -95,7 +98,7 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
|
|
||||||
def test_content_scan(self):
|
def test_content_scan(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_CONTENT
|
s.scan_type = ScanType.Contents
|
||||||
f = [no('foo'), no('bar'), no('bleh')]
|
f = [no('foo'), no('bar'), no('bleh')]
|
||||||
f[0].md5 = f[0].md5partial = 'foobar'
|
f[0].md5 = f[0].md5partial = 'foobar'
|
||||||
f[1].md5 = f[1].md5partial = 'foobar'
|
f[1].md5 = f[1].md5partial = 'foobar'
|
||||||
@@ -112,13 +115,13 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
raise AssertionError()
|
raise AssertionError()
|
||||||
|
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_CONTENT
|
s.scan_type = ScanType.Contents
|
||||||
f = [MyFile('foo', 1), MyFile('bar', 2)]
|
f = [MyFile('foo', 1), MyFile('bar', 2)]
|
||||||
eq_(len(s.GetDupeGroups(f)), 0)
|
eq_(len(s.GetDupeGroups(f)), 0)
|
||||||
|
|
||||||
def test_min_match_perc_doesnt_matter_for_content_scan(self):
|
def test_min_match_perc_doesnt_matter_for_content_scan(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_CONTENT
|
s.scan_type = ScanType.Contents
|
||||||
f = [no('foo'), no('bar'), no('bleh')]
|
f = [no('foo'), no('bar'), no('bleh')]
|
||||||
f[0].md5 = f[0].md5partial = 'foobar'
|
f[0].md5 = f[0].md5partial = 'foobar'
|
||||||
f[1].md5 = f[1].md5partial = 'foobar'
|
f[1].md5 = f[1].md5partial = 'foobar'
|
||||||
@@ -134,7 +137,7 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
|
|
||||||
def test_content_scan_doesnt_put_md5_in_words_at_the_end(self):
|
def test_content_scan_doesnt_put_md5_in_words_at_the_end(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_CONTENT
|
s.scan_type = ScanType.Contents
|
||||||
f = [no('foo'),no('bar')]
|
f = [no('foo'),no('bar')]
|
||||||
f[0].md5 = f[0].md5partial = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
|
f[0].md5 = f[0].md5partial = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
|
||||||
f[1].md5 = f[1].md5partial = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
|
f[1].md5 = f[1].md5partial = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
|
||||||
@@ -188,21 +191,21 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
|
|
||||||
def test_fields(self):
|
def test_fields(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_FIELDS
|
s.scan_type = ScanType.Fields
|
||||||
f = [no('The White Stripes - Little Ghost'), no('The White Stripes - Little Acorn')]
|
f = [no('The White Stripes - Little Ghost'), no('The White Stripes - Little Acorn')]
|
||||||
r = s.GetDupeGroups(f)
|
r = s.GetDupeGroups(f)
|
||||||
eq_(len(r), 0)
|
eq_(len(r), 0)
|
||||||
|
|
||||||
def test_fields_no_order(self):
|
def test_fields_no_order(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_FIELDS_NO_ORDER
|
s.scan_type = ScanType.FieldsNoOrder
|
||||||
f = [no('The White Stripes - Little Ghost'), no('Little Ghost - The White Stripes')]
|
f = [no('The White Stripes - Little Ghost'), no('Little Ghost - The White Stripes')]
|
||||||
r = s.GetDupeGroups(f)
|
r = s.GetDupeGroups(f)
|
||||||
eq_(len(r), 1)
|
eq_(len(r), 1)
|
||||||
|
|
||||||
def test_tag_scan(self):
|
def test_tag_scan(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_TAG
|
s.scan_type = ScanType.Tag
|
||||||
o1 = no('foo')
|
o1 = no('foo')
|
||||||
o2 = no('bar')
|
o2 = no('bar')
|
||||||
o1.artist = 'The White Stripes'
|
o1.artist = 'The White Stripes'
|
||||||
@@ -214,7 +217,7 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
|
|
||||||
def test_tag_with_album_scan(self):
|
def test_tag_with_album_scan(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_TAG
|
s.scan_type = ScanType.Tag
|
||||||
s.scanned_tags = set(['artist', 'album', 'title'])
|
s.scanned_tags = set(['artist', 'album', 'title'])
|
||||||
o1 = no('foo')
|
o1 = no('foo')
|
||||||
o2 = no('bar')
|
o2 = no('bar')
|
||||||
@@ -233,7 +236,7 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
|
|
||||||
def test_that_dash_in_tags_dont_create_new_fields(self):
|
def test_that_dash_in_tags_dont_create_new_fields(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_TAG
|
s.scan_type = ScanType.Tag
|
||||||
s.scanned_tags = set(['artist', 'album', 'title'])
|
s.scanned_tags = set(['artist', 'album', 'title'])
|
||||||
s.min_match_percentage = 50
|
s.min_match_percentage = 50
|
||||||
o1 = no('foo')
|
o1 = no('foo')
|
||||||
@@ -249,7 +252,7 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
|
|
||||||
def test_tag_scan_with_different_scanned(self):
|
def test_tag_scan_with_different_scanned(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_TAG
|
s.scan_type = ScanType.Tag
|
||||||
s.scanned_tags = set(['track', 'year'])
|
s.scanned_tags = set(['track', 'year'])
|
||||||
o1 = no('foo')
|
o1 = no('foo')
|
||||||
o2 = no('bar')
|
o2 = no('bar')
|
||||||
@@ -266,7 +269,7 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
|
|
||||||
def test_tag_scan_only_scans_existing_tags(self):
|
def test_tag_scan_only_scans_existing_tags(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_TAG
|
s.scan_type = ScanType.Tag
|
||||||
s.scanned_tags = set(['artist', 'foo'])
|
s.scanned_tags = set(['artist', 'foo'])
|
||||||
o1 = no('foo')
|
o1 = no('foo')
|
||||||
o2 = no('bar')
|
o2 = no('bar')
|
||||||
@@ -279,7 +282,7 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
|
|
||||||
def test_tag_scan_converts_to_str(self):
|
def test_tag_scan_converts_to_str(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_TAG
|
s.scan_type = ScanType.Tag
|
||||||
s.scanned_tags = set(['track'])
|
s.scanned_tags = set(['track'])
|
||||||
o1 = no('foo')
|
o1 = no('foo')
|
||||||
o2 = no('bar')
|
o2 = no('bar')
|
||||||
@@ -293,12 +296,12 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
|
|
||||||
def test_tag_scan_non_ascii(self):
|
def test_tag_scan_non_ascii(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_TAG
|
s.scan_type = ScanType.Tag
|
||||||
s.scanned_tags = set(['title'])
|
s.scanned_tags = set(['title'])
|
||||||
o1 = no('foo')
|
o1 = no('foo')
|
||||||
o2 = no('bar')
|
o2 = no('bar')
|
||||||
o1.title = u'foobar\u00e9'
|
o1.title = 'foobar\u00e9'
|
||||||
o2.title = u'foobar\u00e9'
|
o2.title = 'foobar\u00e9'
|
||||||
try:
|
try:
|
||||||
r = s.GetDupeGroups([o1, o2])
|
r = s.GetDupeGroups([o1, o2])
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
@@ -307,7 +310,7 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
|
|
||||||
def test_audio_content_scan(self):
|
def test_audio_content_scan(self):
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_CONTENT_AUDIO
|
s.scan_type = ScanType.ContentsAudio
|
||||||
f = [no('foo'), no('bar'), no('bleh')]
|
f = [no('foo'), no('bar'), no('bleh')]
|
||||||
f[0].md5 = 'foo'
|
f[0].md5 = 'foo'
|
||||||
f[1].md5 = 'bar'
|
f[1].md5 = 'bar'
|
||||||
@@ -329,7 +332,7 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
raise AssertionError()
|
raise AssertionError()
|
||||||
|
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_CONTENT_AUDIO
|
s.scan_type = ScanType.ContentsAudio
|
||||||
f = [MyFile('foo'), MyFile('bar')]
|
f = [MyFile('foo'), MyFile('bar')]
|
||||||
f[0].audiosize = 1
|
f[0].audiosize = 1
|
||||||
f[1].audiosize = 2
|
f[1].audiosize = 2
|
||||||
@@ -362,11 +365,11 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
f1 = no('foobar')
|
f1 = no('foobar')
|
||||||
f2 = no('foobar')
|
f2 = no('foobar')
|
||||||
f3 = no('foobar')
|
f3 = no('foobar')
|
||||||
f1.path = Path(u'foo1\u00e9')
|
f1.path = Path('foo1\u00e9')
|
||||||
f2.path = Path(u'foo2\u00e9')
|
f2.path = Path('foo2\u00e9')
|
||||||
f3.path = Path(u'foo3\u00e9')
|
f3.path = Path('foo3\u00e9')
|
||||||
s.ignore_list.Ignore(unicode(f1.path),unicode(f2.path))
|
s.ignore_list.Ignore(str(f1.path),str(f2.path))
|
||||||
s.ignore_list.Ignore(unicode(f1.path),unicode(f3.path))
|
s.ignore_list.Ignore(str(f1.path),str(f3.path))
|
||||||
r = s.GetDupeGroups([f1,f2,f3])
|
r = s.GetDupeGroups([f1,f2,f3])
|
||||||
eq_(len(r), 1)
|
eq_(len(r), 1)
|
||||||
g = r[0]
|
g = r[0]
|
||||||
@@ -379,7 +382,7 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
# A very wrong way to use any() was added at some point, causing resulting group list
|
# A very wrong way to use any() was added at some point, causing resulting group list
|
||||||
# to be empty.
|
# to be empty.
|
||||||
class FalseNamedObject(NamedObject):
|
class FalseNamedObject(NamedObject):
|
||||||
def __nonzero__(self):
|
def __bool__(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@@ -426,11 +429,20 @@ class ScannerTestFakeFiles(TestCase):
|
|||||||
# if ref has the same words as dupe, but has some just one extra word which is a digit, it
|
# if ref has the same words as dupe, but has some just one extra word which is a digit, it
|
||||||
# becomes a dupe
|
# becomes a dupe
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
o1, o2 = no('foo bar 42'), no('foo bar')
|
o1 = no('foo bar 42')
|
||||||
|
o2 = no('foo bar [42]')
|
||||||
|
o3 = no('foo bar (42)')
|
||||||
|
o4 = no('foo bar {42}')
|
||||||
|
o5 = no('foo bar')
|
||||||
|
# all numbered names have deeper paths, so they'll end up ref if the digits aren't correctly
|
||||||
|
# used as tie breakers
|
||||||
o1.path = Path('deeper/path')
|
o1.path = Path('deeper/path')
|
||||||
o2.path = Path('foo')
|
o2.path = Path('deeper/path')
|
||||||
[group] = s.GetDupeGroups([o1, o2])
|
o3.path = Path('deeper/path')
|
||||||
assert group.ref is o2
|
o4.path = Path('deeper/path')
|
||||||
|
o5.path = Path('foo')
|
||||||
|
[group] = s.GetDupeGroups([o1, o2, o3, o4, o5])
|
||||||
|
assert group.ref is o5
|
||||||
|
|
||||||
def test_partial_group_match(self):
|
def test_partial_group_match(self):
|
||||||
# Count the number od discarded matches (when a file doesn't match all other dupes of the
|
# Count the number od discarded matches (when a file doesn't match all other dupes of the
|
||||||
@@ -453,7 +465,7 @@ class ScannerTest(TestCase):
|
|||||||
# In this test, we have to delete one of the files between the get_matches() part and the
|
# In this test, we have to delete one of the files between the get_matches() part and the
|
||||||
# get_groups() part.
|
# get_groups() part.
|
||||||
s = Scanner()
|
s = Scanner()
|
||||||
s.scan_type = SCAN_TYPE_CONTENT
|
s.scan_type = ScanType.Contents
|
||||||
p = self.tmppath()
|
p = self.tmppath()
|
||||||
io.open(p + 'file1', 'w').write('foo')
|
io.open(p + 'file1', 'w').write('foo')
|
||||||
io.open(p + 'file2', 'w').write('foo')
|
io.open(p + 'file2', 'w').write('foo')
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class DupeGuruME(DupeGuruBase):
|
|||||||
try:
|
try:
|
||||||
track.delete(timeout=0)
|
track.delete(timeout=0)
|
||||||
except CommandError as e:
|
except CommandError as e:
|
||||||
logging.warning('Error while trying to remove a track from iTunes: %s' % unicode(e))
|
logging.warning('Error while trying to remove a track from iTunes: %s' % str(e))
|
||||||
|
|
||||||
self._start_job(JOB_REMOVE_DEAD_TRACKS, do)
|
self._start_job(JOB_REMOVE_DEAD_TRACKS, do)
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ COLUMNS = [
|
|||||||
{'attr':'bitrate','display':'Bitrate'},
|
{'attr':'bitrate','display':'Bitrate'},
|
||||||
{'attr':'samplerate','display':'Sample Rate'},
|
{'attr':'samplerate','display':'Sample Rate'},
|
||||||
{'attr':'extension','display':'Kind'},
|
{'attr':'extension','display':'Kind'},
|
||||||
{'attr':'ctime','display':'Creation'},
|
|
||||||
{'attr':'mtime','display':'Modification'},
|
{'attr':'mtime','display':'Modification'},
|
||||||
{'attr':'title','display':'Title'},
|
{'attr':'title','display':'Title'},
|
||||||
{'attr':'artist','display':'Artist'},
|
{'attr':'artist','display':'Artist'},
|
||||||
@@ -32,7 +31,7 @@ COLUMNS = [
|
|||||||
{'attr':'dupe_count','display':'Dupe Count'},
|
{'attr':'dupe_count','display':'Dupe Count'},
|
||||||
]
|
]
|
||||||
|
|
||||||
METADATA_TO_READ = ['size', 'ctime', 'mtime', 'duration', 'bitrate', 'samplerate', 'title', 'artist',
|
METADATA_TO_READ = ['size', 'mtime', 'duration', 'bitrate', 'samplerate', 'title', 'artist',
|
||||||
'album', 'genre', 'year', 'track', 'comment']
|
'album', 'genre', 'year', 'track', 'comment']
|
||||||
|
|
||||||
def GetDisplayInfo(dupe, group, delta):
|
def GetDisplayInfo(dupe, group, delta):
|
||||||
@@ -40,7 +39,6 @@ def GetDisplayInfo(dupe, group, delta):
|
|||||||
duration = dupe.duration
|
duration = dupe.duration
|
||||||
bitrate = dupe.bitrate
|
bitrate = dupe.bitrate
|
||||||
samplerate = dupe.samplerate
|
samplerate = dupe.samplerate
|
||||||
ctime = dupe.ctime
|
|
||||||
mtime = dupe.mtime
|
mtime = dupe.mtime
|
||||||
m = group.get_match_of(dupe)
|
m = group.get_match_of(dupe)
|
||||||
if m:
|
if m:
|
||||||
@@ -52,7 +50,6 @@ def GetDisplayInfo(dupe, group, delta):
|
|||||||
duration -= r.duration
|
duration -= r.duration
|
||||||
bitrate -= r.bitrate
|
bitrate -= r.bitrate
|
||||||
samplerate -= r.samplerate
|
samplerate -= r.samplerate
|
||||||
ctime -= r.ctime
|
|
||||||
mtime -= r.mtime
|
mtime -= r.mtime
|
||||||
else:
|
else:
|
||||||
percentage = group.percentage
|
percentage = group.percentage
|
||||||
@@ -65,7 +62,6 @@ def GetDisplayInfo(dupe, group, delta):
|
|||||||
str(bitrate),
|
str(bitrate),
|
||||||
str(samplerate),
|
str(samplerate),
|
||||||
dupe.extension,
|
dupe.extension,
|
||||||
format_timestamp(ctime,delta and m),
|
|
||||||
format_timestamp(mtime,delta and m),
|
format_timestamp(mtime,delta and m),
|
||||||
dupe.title,
|
dupe.title,
|
||||||
dupe.artist,
|
dupe.artist,
|
||||||
|
|||||||
@@ -42,12 +42,12 @@ class Mp3File(MusicFile):
|
|||||||
HANDLED_EXTS = set(['mp3'])
|
HANDLED_EXTS = set(['mp3'])
|
||||||
def _read_info(self, field):
|
def _read_info(self, field):
|
||||||
if field == 'md5partial':
|
if field == 'md5partial':
|
||||||
fileinfo = mpeg.Mpeg(unicode(self.path))
|
fileinfo = mpeg.Mpeg(str(self.path))
|
||||||
self._md5partial_offset = fileinfo.audio_offset
|
self._md5partial_offset = fileinfo.audio_offset
|
||||||
self._md5partial_size = fileinfo.audio_size
|
self._md5partial_size = fileinfo.audio_size
|
||||||
MusicFile._read_info(self, field)
|
MusicFile._read_info(self, field)
|
||||||
if field in TAG_FIELDS:
|
if field in TAG_FIELDS:
|
||||||
fileinfo = mpeg.Mpeg(unicode(self.path))
|
fileinfo = mpeg.Mpeg(str(self.path))
|
||||||
self.audiosize = fileinfo.audio_size
|
self.audiosize = fileinfo.audio_size
|
||||||
self.bitrate = fileinfo.bitrate
|
self.bitrate = fileinfo.bitrate
|
||||||
self.duration = fileinfo.duration
|
self.duration = fileinfo.duration
|
||||||
@@ -70,12 +70,12 @@ class WmaFile(MusicFile):
|
|||||||
HANDLED_EXTS = set(['wma'])
|
HANDLED_EXTS = set(['wma'])
|
||||||
def _read_info(self, field):
|
def _read_info(self, field):
|
||||||
if field == 'md5partial':
|
if field == 'md5partial':
|
||||||
dec = wma.WMADecoder(unicode(self.path))
|
dec = wma.WMADecoder(str(self.path))
|
||||||
self._md5partial_offset = dec.audio_offset
|
self._md5partial_offset = dec.audio_offset
|
||||||
self._md5partial_size = dec.audio_size
|
self._md5partial_size = dec.audio_size
|
||||||
MusicFile._read_info(self, field)
|
MusicFile._read_info(self, field)
|
||||||
if field in TAG_FIELDS:
|
if field in TAG_FIELDS:
|
||||||
dec = wma.WMADecoder(unicode(self.path))
|
dec = wma.WMADecoder(str(self.path))
|
||||||
self.audiosize = dec.audio_size
|
self.audiosize = dec.audio_size
|
||||||
self.bitrate = dec.bitrate
|
self.bitrate = dec.bitrate
|
||||||
self.duration = dec.duration
|
self.duration = dec.duration
|
||||||
@@ -92,13 +92,13 @@ class Mp4File(MusicFile):
|
|||||||
HANDLED_EXTS = set(['m4a', 'm4p'])
|
HANDLED_EXTS = set(['m4a', 'm4p'])
|
||||||
def _read_info(self, field):
|
def _read_info(self, field):
|
||||||
if field == 'md5partial':
|
if field == 'md5partial':
|
||||||
dec = mp4.File(unicode(self.path))
|
dec = mp4.File(str(self.path))
|
||||||
self._md5partial_offset = dec.audio_offset
|
self._md5partial_offset = dec.audio_offset
|
||||||
self._md5partial_size = dec.audio_size
|
self._md5partial_size = dec.audio_size
|
||||||
dec.close()
|
dec.close()
|
||||||
MusicFile._read_info(self, field)
|
MusicFile._read_info(self, field)
|
||||||
if field in TAG_FIELDS:
|
if field in TAG_FIELDS:
|
||||||
dec = mp4.File(unicode(self.path))
|
dec = mp4.File(str(self.path))
|
||||||
self.audiosize = dec.audio_size
|
self.audiosize = dec.audio_size
|
||||||
self.bitrate = dec.bitrate
|
self.bitrate = dec.bitrate
|
||||||
self.duration = dec.duration
|
self.duration = dec.duration
|
||||||
@@ -116,12 +116,12 @@ class OggFile(MusicFile):
|
|||||||
HANDLED_EXTS = set(['ogg'])
|
HANDLED_EXTS = set(['ogg'])
|
||||||
def _read_info(self, field):
|
def _read_info(self, field):
|
||||||
if field == 'md5partial':
|
if field == 'md5partial':
|
||||||
dec = ogg.Vorbis(unicode(self.path))
|
dec = ogg.Vorbis(str(self.path))
|
||||||
self._md5partial_offset = dec.audio_offset
|
self._md5partial_offset = dec.audio_offset
|
||||||
self._md5partial_size = dec.audio_size
|
self._md5partial_size = dec.audio_size
|
||||||
MusicFile._read_info(self, field)
|
MusicFile._read_info(self, field)
|
||||||
if field in TAG_FIELDS:
|
if field in TAG_FIELDS:
|
||||||
dec = ogg.Vorbis(unicode(self.path))
|
dec = ogg.Vorbis(str(self.path))
|
||||||
self.audiosize = dec.audio_size
|
self.audiosize = dec.audio_size
|
||||||
self.bitrate = dec.bitrate
|
self.bitrate = dec.bitrate
|
||||||
self.duration = dec.duration
|
self.duration = dec.duration
|
||||||
@@ -138,12 +138,12 @@ class FlacFile(MusicFile):
|
|||||||
HANDLED_EXTS = set(['flac'])
|
HANDLED_EXTS = set(['flac'])
|
||||||
def _read_info(self, field):
|
def _read_info(self, field):
|
||||||
if field == 'md5partial':
|
if field == 'md5partial':
|
||||||
dec = flac.FLAC(unicode(self.path))
|
dec = flac.FLAC(str(self.path))
|
||||||
self._md5partial_offset = dec.audio_offset
|
self._md5partial_offset = dec.audio_offset
|
||||||
self._md5partial_size = dec.audio_size
|
self._md5partial_size = dec.audio_size
|
||||||
MusicFile._read_info(self, field)
|
MusicFile._read_info(self, field)
|
||||||
if field in TAG_FIELDS:
|
if field in TAG_FIELDS:
|
||||||
dec = flac.FLAC(unicode(self.path))
|
dec = flac.FLAC(str(self.path))
|
||||||
self.audiosize = dec.audio_size
|
self.audiosize = dec.audio_size
|
||||||
self.bitrate = dec.bitrate
|
self.bitrate = dec.bitrate
|
||||||
self.duration = dec.duration
|
self.duration = dec.duration
|
||||||
@@ -160,12 +160,12 @@ class AiffFile(MusicFile):
|
|||||||
HANDLED_EXTS = set(['aif', 'aiff', 'aifc'])
|
HANDLED_EXTS = set(['aif', 'aiff', 'aifc'])
|
||||||
def _read_info(self, field):
|
def _read_info(self, field):
|
||||||
if field == 'md5partial':
|
if field == 'md5partial':
|
||||||
dec = aiff.File(unicode(self.path))
|
dec = aiff.File(str(self.path))
|
||||||
self._md5partial_offset = dec.audio_offset
|
self._md5partial_offset = dec.audio_offset
|
||||||
self._md5partial_size = dec.audio_size
|
self._md5partial_size = dec.audio_size
|
||||||
MusicFile._read_info(self, field)
|
MusicFile._read_info(self, field)
|
||||||
if field in TAG_FIELDS:
|
if field in TAG_FIELDS:
|
||||||
dec = aiff.File(unicode(self.path))
|
dec = aiff.File(str(self.path))
|
||||||
self.audiosize = dec.audio_size
|
self.audiosize = dec.audio_size
|
||||||
self.bitrate = dec.bitrate
|
self.bitrate = dec.bitrate
|
||||||
self.duration = dec.duration
|
self.duration = dec.duration
|
||||||
|
|||||||
@@ -8,12 +8,13 @@
|
|||||||
|
|
||||||
import os.path as op
|
import os.path as op
|
||||||
import plistlib
|
import plistlib
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
from appscript import app, k, CommandError
|
from appscript import app, k, CommandError
|
||||||
|
|
||||||
from hsutil import io
|
from hsutil import io
|
||||||
from hsutil.str import get_file_ext
|
from hsutil.str import get_file_ext, remove_invalid_xml
|
||||||
from hsutil.path import Path
|
from hsutil.path import Path
|
||||||
from hscommon.cocoa import as_fetch
|
from hscommon.cocoa import as_fetch
|
||||||
from hscommon.cocoa.objcmin import NSUserDefaults, NSURL
|
from hscommon.cocoa.objcmin import NSUserDefaults, NSURL
|
||||||
@@ -37,15 +38,15 @@ class Photo(fs.File):
|
|||||||
def _read_info(self, field):
|
def _read_info(self, field):
|
||||||
fs.File._read_info(self, field)
|
fs.File._read_info(self, field)
|
||||||
if field == 'dimensions':
|
if field == 'dimensions':
|
||||||
self.dimensions = _block_osx.get_image_size(unicode(self.path))
|
self.dimensions = _block_osx.get_image_size(str(self.path))
|
||||||
|
|
||||||
def get_blocks(self, block_count_per_side):
|
def get_blocks(self, block_count_per_side):
|
||||||
try:
|
try:
|
||||||
blocks = _block_osx.getblocks(unicode(self.path), block_count_per_side)
|
blocks = _block_osx.getblocks(str(self.path), block_count_per_side)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise IOError('The reading of "%s" failed with "%s"' % (unicode(self.path), unicode(e)))
|
raise IOError('The reading of "%s" failed with "%s"' % (str(self.path), str(e)))
|
||||||
if not blocks:
|
if not blocks:
|
||||||
raise IOError('The picture %s could not be read' % unicode(self.path))
|
raise IOError('The picture %s could not be read' % str(self.path))
|
||||||
return blocks
|
return blocks
|
||||||
|
|
||||||
|
|
||||||
@@ -67,11 +68,16 @@ def get_iphoto_database_path():
|
|||||||
def get_iphoto_pictures(plistpath):
|
def get_iphoto_pictures(plistpath):
|
||||||
if not io.exists(plistpath):
|
if not io.exists(plistpath):
|
||||||
return []
|
return []
|
||||||
# We make the xml go through lxml so that it can fix broken xml which iPhoto sometimes produces.
|
s = io.open(plistpath, 'rt', encoding='utf-8').read()
|
||||||
parser = etree.XMLParser(recover=True)
|
# There was a case where a guy had 0x10 chars in his plist, causing expat errors on loading
|
||||||
root = etree.parse(io.open(plistpath), parser=parser).getroot()
|
s = remove_invalid_xml(s, replace_with='')
|
||||||
s = etree.tostring(root)
|
# It seems that iPhoto sometimes doesn't properly escape & chars. The regexp below is to find
|
||||||
plist = plistlib.readPlistFromString(s)
|
# any & char that is not a &-based entity (&, ", etc.). based on TextMate's XML
|
||||||
|
# bundle's regexp
|
||||||
|
s, count = re.subn(r'&(?![a-zA-Z0-9_-]+|#[0-9]+|#x[0-9a-fA-F]+;)', '', s)
|
||||||
|
if count:
|
||||||
|
logging.warning("%d invalid XML entities replacement made", count)
|
||||||
|
plist = plistlib.readPlistFromBytes(s.encode('utf-8'))
|
||||||
result = []
|
result = []
|
||||||
for photo_data in plist['Master Image List'].values():
|
for photo_data in plist['Master Image List'].values():
|
||||||
if photo_data['MediaType'] != 'Image':
|
if photo_data['MediaType'] != 'Image':
|
||||||
@@ -116,6 +122,15 @@ class Directories(directories.Directories):
|
|||||||
else:
|
else:
|
||||||
directories.Directories.add_path(self, path)
|
directories.Directories.add_path(self, path)
|
||||||
|
|
||||||
|
def has_any_file(self):
|
||||||
|
# If we don't do that, it causes a hangup in the GUI when we click Start Scanning because
|
||||||
|
# checking if there's any file to scan involves reading the whole library. If we have the
|
||||||
|
# iPhoto library, we assume we have at least one file.
|
||||||
|
if any(path == Path('iPhoto Library') for path in self._dirs):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return directories.Directories.has_any_file(self)
|
||||||
|
|
||||||
|
|
||||||
class DupeGuruPE(app_cocoa.DupeGuru):
|
class DupeGuruPE(app_cocoa.DupeGuru):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -124,10 +139,10 @@ class DupeGuruPE(app_cocoa.DupeGuru):
|
|||||||
self.directories = Directories()
|
self.directories = Directories()
|
||||||
self.scanner.cache_path = op.join(self.appdata, 'cached_pictures.db')
|
self.scanner.cache_path = op.join(self.appdata, 'cached_pictures.db')
|
||||||
|
|
||||||
def _do_delete(self, j):
|
def _do_delete(self, j, replace_with_hardlinks):
|
||||||
def op(dupe):
|
def op(dupe):
|
||||||
j.add_progress()
|
j.add_progress()
|
||||||
return self._do_delete_dupe(dupe)
|
return self._do_delete_dupe(dupe, replace_with_hardlinks)
|
||||||
|
|
||||||
marked = [dupe for dupe in self.results.dupes if self.results.is_marked(dupe)]
|
marked = [dupe for dupe in self.results.dupes if self.results.is_marked(dupe)]
|
||||||
self.path2iphoto = {}
|
self.path2iphoto = {}
|
||||||
@@ -140,7 +155,7 @@ class DupeGuruPE(app_cocoa.DupeGuru):
|
|||||||
photos = as_fetch(a.photo_library_album().photos, k.item)
|
photos = as_fetch(a.photo_library_album().photos, k.item)
|
||||||
for photo in j.iter_with_progress(photos):
|
for photo in j.iter_with_progress(photos):
|
||||||
try:
|
try:
|
||||||
self.path2iphoto[unicode(photo.image_path(timeout=0))] = photo
|
self.path2iphoto[str(photo.image_path(timeout=0))] = photo
|
||||||
except CommandError:
|
except CommandError:
|
||||||
pass
|
pass
|
||||||
except (CommandError, RuntimeError):
|
except (CommandError, RuntimeError):
|
||||||
@@ -149,24 +164,20 @@ class DupeGuruPE(app_cocoa.DupeGuru):
|
|||||||
self.results.perform_on_marked(op, True)
|
self.results.perform_on_marked(op, True)
|
||||||
del self.path2iphoto
|
del self.path2iphoto
|
||||||
|
|
||||||
def _do_delete_dupe(self, dupe):
|
def _do_delete_dupe(self, dupe, replace_with_hardlinks):
|
||||||
if isinstance(dupe, IPhoto):
|
if isinstance(dupe, IPhoto):
|
||||||
if unicode(dupe.path) in self.path2iphoto:
|
if str(dupe.path) in self.path2iphoto:
|
||||||
photo = self.path2iphoto[unicode(dupe.path)]
|
photo = self.path2iphoto[str(dupe.path)]
|
||||||
try:
|
try:
|
||||||
a = app('iPhoto')
|
a = app('iPhoto')
|
||||||
a.remove(photo, timeout=0)
|
a.remove(photo, timeout=0)
|
||||||
except (CommandError, RuntimeError) as e:
|
except (CommandError, RuntimeError) as e:
|
||||||
raise EnvironmentError(unicode(e))
|
raise EnvironmentError(str(e))
|
||||||
else:
|
else:
|
||||||
msg = u"Could not find photo %s in iPhoto Library" % unicode(dupe.path)
|
msg = "Could not find photo %s in iPhoto Library" % str(dupe.path)
|
||||||
raise EnvironmentError(msg)
|
raise EnvironmentError(msg)
|
||||||
else:
|
else:
|
||||||
app_cocoa.DupeGuru._do_delete_dupe(self, dupe)
|
app_cocoa.DupeGuru._do_delete_dupe(self, dupe, replace_with_hardlinks)
|
||||||
|
|
||||||
def _do_load(self, j):
|
|
||||||
self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml'))
|
|
||||||
self.results.load_from_xml(op.join(self.appdata, 'last_results.xml'), self._get_file, j)
|
|
||||||
|
|
||||||
def _get_file(self, str_path):
|
def _get_file(self, str_path):
|
||||||
p = Path(str_path)
|
p = Path(str_path)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
from _block import NoBlocksError, DifferentBlockCountError, avgdiff, getblocks2
|
from ._block import NoBlocksError, DifferentBlockCountError, avgdiff, getblocks2
|
||||||
|
|
||||||
# Converted to C
|
# Converted to C
|
||||||
# def getblock(image):
|
# def getblock(image):
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
import sqlite3 as sqlite
|
import sqlite3 as sqlite
|
||||||
|
|
||||||
from _cache import string_to_colors
|
from ._cache import string_to_colors
|
||||||
|
|
||||||
def colors_to_string(colors):
|
def colors_to_string(colors):
|
||||||
"""Transform the 3 sized tuples 'colors' into a hex string.
|
"""Transform the 3 sized tuples 'colors' into a hex string.
|
||||||
@@ -82,7 +82,7 @@ class Cache(object):
|
|||||||
self.con.execute(sql, [value, key])
|
self.con.execute(sql, [value, key])
|
||||||
except sqlite.OperationalError:
|
except sqlite.OperationalError:
|
||||||
logging.warning('Picture cache could not set %r for key %r', value, key)
|
logging.warning('Picture cache could not set %r for key %r', value, key)
|
||||||
except sqlite.DatabaseError, e:
|
except sqlite.DatabaseError as e:
|
||||||
logging.warning('DatabaseError while setting %r for key %r: %s', value, key, str(e))
|
logging.warning('DatabaseError while setting %r for key %r: %s', value, key, str(e))
|
||||||
|
|
||||||
def _create_con(self, second_try=False):
|
def _create_con(self, second_try=False):
|
||||||
@@ -97,7 +97,7 @@ class Cache(object):
|
|||||||
self.con.execute("select * from pictures where 1=2")
|
self.con.execute("select * from pictures where 1=2")
|
||||||
except sqlite.OperationalError: # new db
|
except sqlite.OperationalError: # new db
|
||||||
create_tables()
|
create_tables()
|
||||||
except sqlite.DatabaseError, e: # corrupted db
|
except sqlite.DatabaseError as e: # corrupted db
|
||||||
if second_try:
|
if second_try:
|
||||||
raise # Something really strange is happening
|
raise # Something really strange is happening
|
||||||
logging.warning('Could not create picture cache because of an error: %s', str(e))
|
logging.warning('Could not create picture cache because of an error: %s', str(e))
|
||||||
|
|||||||
@@ -18,19 +18,17 @@ COLUMNS = [
|
|||||||
{'attr':'size','display':'Size (KB)'},
|
{'attr':'size','display':'Size (KB)'},
|
||||||
{'attr':'extension','display':'Kind'},
|
{'attr':'extension','display':'Kind'},
|
||||||
{'attr':'dimensions','display':'Dimensions'},
|
{'attr':'dimensions','display':'Dimensions'},
|
||||||
{'attr':'ctime','display':'Creation'},
|
|
||||||
{'attr':'mtime','display':'Modification'},
|
{'attr':'mtime','display':'Modification'},
|
||||||
{'attr':'percentage','display':'Match %'},
|
{'attr':'percentage','display':'Match %'},
|
||||||
{'attr':'dupe_count','display':'Dupe Count'},
|
{'attr':'dupe_count','display':'Dupe Count'},
|
||||||
]
|
]
|
||||||
|
|
||||||
METADATA_TO_READ = ['size', 'ctime', 'mtime', 'dimensions']
|
METADATA_TO_READ = ['size', 'mtime', 'dimensions']
|
||||||
|
|
||||||
def GetDisplayInfo(dupe,group,delta=False):
|
def GetDisplayInfo(dupe,group,delta=False):
|
||||||
if (dupe is None) or (group is None):
|
if (dupe is None) or (group is None):
|
||||||
return ['---'] * len(COLUMNS)
|
return ['---'] * len(COLUMNS)
|
||||||
size = dupe.size
|
size = dupe.size
|
||||||
ctime = dupe.ctime
|
|
||||||
mtime = dupe.mtime
|
mtime = dupe.mtime
|
||||||
m = group.get_match_of(dupe)
|
m = group.get_match_of(dupe)
|
||||||
if m:
|
if m:
|
||||||
@@ -39,7 +37,6 @@ def GetDisplayInfo(dupe,group,delta=False):
|
|||||||
if delta:
|
if delta:
|
||||||
r = group.ref
|
r = group.ref
|
||||||
size -= r.size
|
size -= r.size
|
||||||
ctime -= r.ctime
|
|
||||||
mtime -= r.mtime
|
mtime -= r.mtime
|
||||||
else:
|
else:
|
||||||
percentage = group.percentage
|
percentage = group.percentage
|
||||||
@@ -51,7 +48,6 @@ def GetDisplayInfo(dupe,group,delta=False):
|
|||||||
format_size(size, 0, 1, False),
|
format_size(size, 0, 1, False),
|
||||||
dupe.extension,
|
dupe.extension,
|
||||||
format_dimensions(dupe.dimensions),
|
format_dimensions(dupe.dimensions),
|
||||||
format_timestamp(ctime, delta and m),
|
|
||||||
format_timestamp(mtime, delta and m),
|
format_timestamp(mtime, delta and m),
|
||||||
format_perc(percentage),
|
format_perc(percentage),
|
||||||
format_dupe_count(dupe_count)
|
format_dupe_count(dupe_count)
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# Created By: Virgil Dupras
|
|
||||||
# Created On: 2009-05-26
|
|
||||||
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
#
|
|
||||||
# This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
# which should be included with this package. The terms are also available at
|
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
|
||||||
|
|
||||||
import os
|
|
||||||
import os.path as op
|
|
||||||
|
|
||||||
def move(src, dst):
|
|
||||||
if not op.exists(src):
|
|
||||||
return
|
|
||||||
if op.exists(dst):
|
|
||||||
os.remove(dst)
|
|
||||||
print 'Moving %s --> %s' % (src, dst)
|
|
||||||
os.rename(src, dst)
|
|
||||||
|
|
||||||
os.chdir('modules')
|
|
||||||
os.system('python setup.py build_ext --inplace')
|
|
||||||
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')
|
|
||||||
@@ -34,16 +34,16 @@ def prepare_pictures(pictures, cache_path, j=job.nulljob):
|
|||||||
try:
|
try:
|
||||||
for picture in j.iter_with_progress(pictures, 'Analyzed %d/%d pictures'):
|
for picture in j.iter_with_progress(pictures, 'Analyzed %d/%d pictures'):
|
||||||
picture.dimensions
|
picture.dimensions
|
||||||
picture.unicode_path = unicode(picture.path)
|
picture.unicode_path = str(picture.path)
|
||||||
try:
|
try:
|
||||||
if picture.unicode_path not in cache:
|
if picture.unicode_path not in cache:
|
||||||
blocks = picture.get_blocks(BLOCK_COUNT_PER_SIDE)
|
blocks = picture.get_blocks(BLOCK_COUNT_PER_SIDE)
|
||||||
cache[picture.unicode_path] = blocks
|
cache[picture.unicode_path] = blocks
|
||||||
prepared.append(picture)
|
prepared.append(picture)
|
||||||
except (IOError, ValueError) as e:
|
except (IOError, ValueError) as e:
|
||||||
logging.warning(unicode(e))
|
logging.warning(str(e))
|
||||||
except MemoryError:
|
except MemoryError:
|
||||||
logging.warning(u'Ran out of memory while reading %s of size %d' % (picture.unicode_path, picture.size))
|
logging.warning('Ran out of memory while reading %s of size %d' % (picture.unicode_path, picture.size))
|
||||||
if picture.size < 10 * 1024 * 1024: # We're really running out of memory
|
if picture.size < 10 * 1024 * 1024: # We're really running out of memory
|
||||||
raise
|
raise
|
||||||
except MemoryError:
|
except MemoryError:
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ static PyObject* getblock(PyObject *image)
|
|||||||
pg = PySequence_ITEM(ppixel, 1);
|
pg = PySequence_ITEM(ppixel, 1);
|
||||||
pb = PySequence_ITEM(ppixel, 2);
|
pb = PySequence_ITEM(ppixel, 2);
|
||||||
Py_DECREF(ppixel);
|
Py_DECREF(ppixel);
|
||||||
r = PyInt_AsSsize_t(pr);
|
r = PyLong_AsLong(pr);
|
||||||
g = PyInt_AsSsize_t(pg);
|
g = PyLong_AsLong(pg);
|
||||||
b = PyInt_AsSsize_t(pb);
|
b = PyLong_AsLong(pb);
|
||||||
Py_DECREF(pr);
|
Py_DECREF(pr);
|
||||||
Py_DECREF(pg);
|
Py_DECREF(pg);
|
||||||
Py_DECREF(pb);
|
Py_DECREF(pb);
|
||||||
@@ -67,14 +67,14 @@ static PyObject* getblock(PyObject *image)
|
|||||||
*/
|
*/
|
||||||
static int diff(PyObject *first, PyObject *second)
|
static int diff(PyObject *first, PyObject *second)
|
||||||
{
|
{
|
||||||
Py_ssize_t r1, g1, b1, r2, b2, g2;
|
int r1, g1, b1, r2, b2, g2;
|
||||||
PyObject *pr, *pg, *pb;
|
PyObject *pr, *pg, *pb;
|
||||||
pr = PySequence_ITEM(first, 0);
|
pr = PySequence_ITEM(first, 0);
|
||||||
pg = PySequence_ITEM(first, 1);
|
pg = PySequence_ITEM(first, 1);
|
||||||
pb = PySequence_ITEM(first, 2);
|
pb = PySequence_ITEM(first, 2);
|
||||||
r1 = PyInt_AsSsize_t(pr);
|
r1 = PyLong_AsLong(pr);
|
||||||
g1 = PyInt_AsSsize_t(pg);
|
g1 = PyLong_AsLong(pg);
|
||||||
b1 = PyInt_AsSsize_t(pb);
|
b1 = PyLong_AsLong(pb);
|
||||||
Py_DECREF(pr);
|
Py_DECREF(pr);
|
||||||
Py_DECREF(pg);
|
Py_DECREF(pg);
|
||||||
Py_DECREF(pb);
|
Py_DECREF(pb);
|
||||||
@@ -82,9 +82,9 @@ static int diff(PyObject *first, PyObject *second)
|
|||||||
pr = PySequence_ITEM(second, 0);
|
pr = PySequence_ITEM(second, 0);
|
||||||
pg = PySequence_ITEM(second, 1);
|
pg = PySequence_ITEM(second, 1);
|
||||||
pb = PySequence_ITEM(second, 2);
|
pb = PySequence_ITEM(second, 2);
|
||||||
r2 = PyInt_AsSsize_t(pr);
|
r2 = PyLong_AsLong(pr);
|
||||||
g2 = PyInt_AsSsize_t(pg);
|
g2 = PyLong_AsLong(pg);
|
||||||
b2 = PyInt_AsSsize_t(pb);
|
b2 = PyLong_AsLong(pb);
|
||||||
Py_DECREF(pr);
|
Py_DECREF(pr);
|
||||||
Py_DECREF(pg);
|
Py_DECREF(pg);
|
||||||
Py_DECREF(pb);
|
Py_DECREF(pb);
|
||||||
@@ -115,8 +115,8 @@ static PyObject* block_getblocks2(PyObject *self, PyObject *args)
|
|||||||
pimage_size = PyObject_GetAttrString(image, "size");
|
pimage_size = PyObject_GetAttrString(image, "size");
|
||||||
pwidth = PySequence_ITEM(pimage_size, 0);
|
pwidth = PySequence_ITEM(pimage_size, 0);
|
||||||
pheight = PySequence_ITEM(pimage_size, 1);
|
pheight = PySequence_ITEM(pimage_size, 1);
|
||||||
width = PyInt_AsSsize_t(pwidth);
|
width = PyLong_AsLong(pwidth);
|
||||||
height = PyInt_AsSsize_t(pheight);
|
height = PyLong_AsLong(pheight);
|
||||||
Py_DECREF(pimage_size);
|
Py_DECREF(pimage_size);
|
||||||
Py_DECREF(pwidth);
|
Py_DECREF(pwidth);
|
||||||
Py_DECREF(pheight);
|
Py_DECREF(pheight);
|
||||||
@@ -147,8 +147,8 @@ static PyObject* block_getblocks2(PyObject *self, PyObject *args)
|
|||||||
left = min(iw*block_width, width-block_width);
|
left = min(iw*block_width, width-block_width);
|
||||||
right = left + block_width;
|
right = left + block_width;
|
||||||
pbox = inttuple(4, left, top, right, bottom);
|
pbox = inttuple(4, left, top, right, bottom);
|
||||||
pmethodname = PyString_FromString("crop");
|
pmethodname = PyUnicode_FromString("crop");
|
||||||
pcrop = PyObject_CallMethodObjArgs(image, pmethodname, pbox);
|
pcrop = PyObject_CallMethodObjArgs(image, pmethodname, pbox, NULL);
|
||||||
Py_DECREF(pmethodname);
|
Py_DECREF(pmethodname);
|
||||||
Py_DECREF(pbox);
|
Py_DECREF(pbox);
|
||||||
if (pcrop == NULL) {
|
if (pcrop == NULL) {
|
||||||
@@ -207,7 +207,7 @@ static PyObject* block_avgdiff(PyObject *self, PyObject *args)
|
|||||||
Py_DECREF(item1);
|
Py_DECREF(item1);
|
||||||
Py_DECREF(item2);
|
Py_DECREF(item2);
|
||||||
if ((sum > limit*iteration_count) && (iteration_count >= min_iterations)) {
|
if ((sum > limit*iteration_count) && (iteration_count >= min_iterations)) {
|
||||||
return PyInt_FromSsize_t(limit + 1);
|
return PyLong_FromLong(limit + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ static PyObject* block_avgdiff(PyObject *self, PyObject *args)
|
|||||||
if (!result && sum) {
|
if (!result && sum) {
|
||||||
result = 1;
|
result = 1;
|
||||||
}
|
}
|
||||||
return PyInt_FromSsize_t(result);
|
return PyLong_FromLong(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyMethodDef BlockMethods[] = {
|
static PyMethodDef BlockMethods[] = {
|
||||||
@@ -224,16 +224,30 @@ static PyMethodDef BlockMethods[] = {
|
|||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
PyMODINIT_FUNC
|
static struct PyModuleDef BlockDef = {
|
||||||
init_block(void)
|
PyModuleDef_HEAD_INIT,
|
||||||
|
"_block",
|
||||||
|
NULL,
|
||||||
|
-1,
|
||||||
|
BlockMethods,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyInit__block(void)
|
||||||
{
|
{
|
||||||
PyObject *m = Py_InitModule("_block", BlockMethods);
|
PyObject *m = PyModule_Create(&BlockDef);
|
||||||
if (m == NULL) {
|
if (m == NULL) {
|
||||||
return;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
NoBlocksError = PyErr_NewException("_block.NoBlocksError", NULL, NULL);
|
NoBlocksError = PyErr_NewException("_block.NoBlocksError", NULL, NULL);
|
||||||
PyModule_AddObject(m, "NoBlocksError", NoBlocksError);
|
PyModule_AddObject(m, "NoBlocksError", NoBlocksError);
|
||||||
DifferentBlockCountError = PyErr_NewException("_block.DifferentBlockCountError", NULL, NULL);
|
DifferentBlockCountError = PyErr_NewException("_block.DifferentBlockCountError", NULL, NULL);
|
||||||
PyModule_AddObject(m, "DifferentBlockCountError", DifferentBlockCountError);
|
PyModule_AddObject(m, "DifferentBlockCountError", DifferentBlockCountError);
|
||||||
|
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
@@ -29,8 +29,8 @@ pystring2cfstring(PyObject *pystring)
|
|||||||
Py_INCREF(encoded);
|
Py_INCREF(encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
s = (UInt8*)PyString_AS_STRING(encoded);
|
s = (UInt8*)PyBytes_AS_STRING(encoded);
|
||||||
size = PyString_GET_SIZE(encoded);
|
size = PyUnicode_GET_SIZE(encoded);
|
||||||
result = CFStringCreateWithBytes(NULL, s, size, kCFStringEncodingUTF8, FALSE);
|
result = CFStringCreateWithBytes(NULL, s, size, kCFStringEncodingUTF8, FALSE);
|
||||||
Py_DECREF(encoded);
|
Py_DECREF(encoded);
|
||||||
return result;
|
return result;
|
||||||
@@ -43,7 +43,7 @@ static PyObject* block_osx_get_image_size(PyObject *self, PyObject *args)
|
|||||||
CFURLRef image_url;
|
CFURLRef image_url;
|
||||||
CGImageSourceRef source;
|
CGImageSourceRef source;
|
||||||
CGImageRef image;
|
CGImageRef image;
|
||||||
size_t width, height;
|
long width, height;
|
||||||
PyObject *pwidth, *pheight;
|
PyObject *pwidth, *pheight;
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
|
|
||||||
@@ -72,11 +72,11 @@ static PyObject* block_osx_get_image_size(PyObject *self, PyObject *args)
|
|||||||
CFRelease(source);
|
CFRelease(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
pwidth = PyInt_FromSsize_t(width);
|
pwidth = PyLong_FromLong(width);
|
||||||
if (pwidth == NULL) {
|
if (pwidth == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
pheight = PyInt_FromSsize_t(height);
|
pheight = PyLong_FromLong(height);
|
||||||
if (pheight == NULL) {
|
if (pheight == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -228,8 +228,24 @@ static PyMethodDef BlockOsxMethods[] = {
|
|||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
PyMODINIT_FUNC
|
static struct PyModuleDef BlockOsxDef = {
|
||||||
init_block_osx(void)
|
PyModuleDef_HEAD_INIT,
|
||||||
|
"_block_osx",
|
||||||
|
NULL,
|
||||||
|
-1,
|
||||||
|
BlockOsxMethods,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyInit__block_osx(void)
|
||||||
{
|
{
|
||||||
Py_InitModule("_block_osx", BlockOsxMethods);
|
PyObject *m = PyModule_Create(&BlockOsxDef);
|
||||||
|
if (m == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
@@ -72,8 +72,24 @@ static PyMethodDef CacheMethods[] = {
|
|||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
PyMODINIT_FUNC
|
static struct PyModuleDef CacheDef = {
|
||||||
init_cache(void)
|
PyModuleDef_HEAD_INIT,
|
||||||
|
"_cache",
|
||||||
|
NULL,
|
||||||
|
-1,
|
||||||
|
CacheMethods,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyInit__cache(void)
|
||||||
{
|
{
|
||||||
(void)Py_InitModule("_cache", CacheMethods);
|
PyObject *m = PyModule_Create(&CacheDef);
|
||||||
|
if (m == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ PyObject* inttuple(int n, ...)
|
|||||||
result = PyTuple_New(n);
|
result = PyTuple_New(n);
|
||||||
|
|
||||||
for (i=0; i<n; i++) {
|
for (i=0; i<n; i++) {
|
||||||
pnumber = PyInt_FromLong(va_arg(numbers, int));
|
pnumber = PyLong_FromLong(va_arg(numbers, long));
|
||||||
if (pnumber == NULL) {
|
if (pnumber == NULL) {
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -1,30 +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
|
|
||||||
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
@@ -258,8 +258,8 @@ class TCavgdiff(unittest.TestCase):
|
|||||||
def test_return_at_least_1_at_the_slightest_difference(self):
|
def test_return_at_least_1_at_the_slightest_difference(self):
|
||||||
ref = (0,0,0)
|
ref = (0,0,0)
|
||||||
b1 = (1,0,0)
|
b1 = (1,0,0)
|
||||||
blocks1 = [ref for i in xrange(250)]
|
blocks1 = [ref for i in range(250)]
|
||||||
blocks2 = [ref for i in xrange(250)]
|
blocks2 = [ref for i in range(250)]
|
||||||
blocks2[0] = b1
|
blocks2[0] = b1
|
||||||
self.assertEqual(1,my_avgdiff(blocks1,blocks2))
|
self.assertEqual(1,my_avgdiff(blocks1,blocks2))
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,8 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
from StringIO import StringIO
|
|
||||||
import os.path as op
|
import os.path as op
|
||||||
import os
|
import os
|
||||||
import threading
|
|
||||||
|
|
||||||
from hsutil.testcase import TestCase
|
from hsutil.testcase import TestCase
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -24,17 +24,17 @@ def is_bundle(str_path):
|
|||||||
sw = NSWorkspace.sharedWorkspace()
|
sw = NSWorkspace.sharedWorkspace()
|
||||||
uti, error = sw.typeOfFile_error_(str_path, None)
|
uti, error = sw.typeOfFile_error_(str_path, None)
|
||||||
if error is not None:
|
if error is not None:
|
||||||
logging.warning(u'There was an error trying to detect the UTI of %s', str_path)
|
logging.warning('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')
|
return sw.type_conformsToType_(uti, 'com.apple.bundle') or sw.type_conformsToType_(uti, 'com.apple.package')
|
||||||
|
|
||||||
class Bundle(BundleBase):
|
class Bundle(BundleBase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_handle(cls, path):
|
def can_handle(cls, path):
|
||||||
return not io.islink(path) and io.isdir(path) and is_bundle(unicode(path))
|
return not io.islink(path) and io.isdir(path) and is_bundle(str(path))
|
||||||
|
|
||||||
|
|
||||||
class Directories(DirectoriesBase):
|
class Directories(DirectoriesBase):
|
||||||
ROOT_PATH_TO_EXCLUDE = map(Path, ['/Library', '/Volumes', '/System', '/bin', '/sbin', '/opt', '/private', '/dev'])
|
ROOT_PATH_TO_EXCLUDE = list(map(Path, ['/Library', '/Volumes', '/System', '/bin', '/sbin', '/opt', '/private', '/dev']))
|
||||||
HOME_PATH_TO_EXCLUDE = [Path('Library')]
|
HOME_PATH_TO_EXCLUDE = [Path('Library')]
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
DirectoriesBase.__init__(self, fileclasses=[Bundle, fs.File])
|
DirectoriesBase.__init__(self, fileclasses=[Bundle, fs.File])
|
||||||
|
|||||||
@@ -15,18 +15,16 @@ COLUMNS = [
|
|||||||
{'attr':'path','display':'Directory'},
|
{'attr':'path','display':'Directory'},
|
||||||
{'attr':'size','display':'Size (KB)'},
|
{'attr':'size','display':'Size (KB)'},
|
||||||
{'attr':'extension','display':'Kind'},
|
{'attr':'extension','display':'Kind'},
|
||||||
{'attr':'ctime','display':'Creation'},
|
|
||||||
{'attr':'mtime','display':'Modification'},
|
{'attr':'mtime','display':'Modification'},
|
||||||
{'attr':'percentage','display':'Match %'},
|
{'attr':'percentage','display':'Match %'},
|
||||||
{'attr':'words','display':'Words Used'},
|
{'attr':'words','display':'Words Used'},
|
||||||
{'attr':'dupe_count','display':'Dupe Count'},
|
{'attr':'dupe_count','display':'Dupe Count'},
|
||||||
]
|
]
|
||||||
|
|
||||||
METADATA_TO_READ = ['size', 'ctime', 'mtime']
|
METADATA_TO_READ = ['size', 'mtime']
|
||||||
|
|
||||||
def GetDisplayInfo(dupe, group, delta):
|
def GetDisplayInfo(dupe, group, delta):
|
||||||
size = dupe.size
|
size = dupe.size
|
||||||
ctime = dupe.ctime
|
|
||||||
mtime = dupe.mtime
|
mtime = dupe.mtime
|
||||||
m = group.get_match_of(dupe)
|
m = group.get_match_of(dupe)
|
||||||
if m:
|
if m:
|
||||||
@@ -35,7 +33,6 @@ def GetDisplayInfo(dupe, group, delta):
|
|||||||
if delta:
|
if delta:
|
||||||
r = group.ref
|
r = group.ref
|
||||||
size -= r.size
|
size -= r.size
|
||||||
ctime -= r.ctime
|
|
||||||
mtime -= r.mtime
|
mtime -= r.mtime
|
||||||
else:
|
else:
|
||||||
percentage = group.percentage
|
percentage = group.percentage
|
||||||
@@ -45,7 +42,6 @@ def GetDisplayInfo(dupe, group, delta):
|
|||||||
format_path(dupe.path),
|
format_path(dupe.path),
|
||||||
format_size(size, 0, 1, False),
|
format_size(size, 0, 1, False),
|
||||||
dupe.extension,
|
dupe.extension,
|
||||||
format_timestamp(ctime, delta and m),
|
|
||||||
format_timestamp(mtime, delta and m),
|
format_timestamp(mtime, delta and m),
|
||||||
format_perc(percentage),
|
format_perc(percentage),
|
||||||
format_words(dupe.words) if hasattr(dupe, 'words') else '',
|
format_words(dupe.words) if hasattr(dupe, 'words') else '',
|
||||||
|
|||||||
@@ -20,12 +20,11 @@ class Bundle(fs.File):
|
|||||||
to see them as files.
|
to see them as files.
|
||||||
"""
|
"""
|
||||||
def _read_info(self, field):
|
def _read_info(self, field):
|
||||||
if field in ('size', 'ctime', 'mtime'):
|
if field in ('size', 'mtime'):
|
||||||
files = fs.get_all_files(self.path)
|
files = fs.get_all_files(self.path)
|
||||||
size = sum((file.size for file in files), 0)
|
size = sum((file.size for file in files), 0)
|
||||||
self.size = size
|
self.size = size
|
||||||
stats = io.stat(self.path)
|
stats = io.stat(self.path)
|
||||||
self.ctime = nonone(stats.st_ctime, 0)
|
|
||||||
self.mtime = nonone(stats.st_mtime, 0)
|
self.mtime = nonone(stats.st_mtime, 0)
|
||||||
elif field in ('md5', 'md5partial'):
|
elif field in ('md5', 'md5partial'):
|
||||||
# What's sensitive here is that we must make sure that subfiles'
|
# What's sensitive here is that we must make sure that subfiles'
|
||||||
@@ -35,7 +34,7 @@ class Bundle(fs.File):
|
|||||||
files = fs.get_all_files(self.path)
|
files = fs.get_all_files(self.path)
|
||||||
files.sort(key=lambda f:f.path)
|
files.sort(key=lambda f:f.path)
|
||||||
md5s = [getattr(f, field) for f in files]
|
md5s = [getattr(f, field) for f in files]
|
||||||
return ''.join(md5s)
|
return b''.join(md5s)
|
||||||
|
|
||||||
md5 = hashlib.md5(get_dir_md5_concat())
|
md5 = hashlib.md5(get_dir_md5_concat())
|
||||||
digest = md5.digest()
|
digest = md5.digest()
|
||||||
|
|||||||
@@ -38,9 +38,8 @@ class TCBundle(TestCase):
|
|||||||
eq_(b.md5, md5.digest())
|
eq_(b.md5, md5.digest())
|
||||||
|
|
||||||
def test_has_file_attrs(self):
|
def test_has_file_attrs(self):
|
||||||
#a Bundle must behave like a file, so it must have ctime and mtime attributes
|
#a Bundle must behave like a file, so it must have mtime attributes
|
||||||
b = fs.Bundle(self.tmppath())
|
b = fs.Bundle(self.tmppath())
|
||||||
assert b.mtime > 0
|
assert b.mtime > 0
|
||||||
assert b.ctime > 0
|
|
||||||
eq_(b.extension, '')
|
eq_(b.extension, '')
|
||||||
|
|
||||||
1
debian_me/compat
Normal file
1
debian_me/compat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
7
|
||||||
@@ -8,5 +8,5 @@ Homepage: http://www.hardcoded.net
|
|||||||
|
|
||||||
Package: dupeguru-me
|
Package: dupeguru-me
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Depends: python (>= 2.6), python-qt4 (>= 4.6), python-lxml (>= 2.1)
|
Depends: python3 (>= 3.1)
|
||||||
Description: dupeGuru Music Edition
|
Description: dupeGuru Music Edition
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ install: build
|
|||||||
dh_installdirs
|
dh_installdirs
|
||||||
|
|
||||||
chmod +x src/start.py
|
chmod +x src/start.py
|
||||||
cp -R src/ $(CURDIR)/debian/tmp/usr/local/share/dupeguru_me
|
cp -R src/ $(CURDIR)/debian/dupeguru-me/usr/local/share/dupeguru_me
|
||||||
cp $(CURDIR)/debian/dupeguru_me.desktop $(CURDIR)/debian/tmp/usr/share/applications
|
cp $(CURDIR)/debian/dupeguru_me.desktop $(CURDIR)/debian/dupeguru-me/usr/share/applications
|
||||||
ln -s /usr/local/share/dupeguru_me/start.py $(CURDIR)/debian/tmp/usr/local/bin/dupeguru_me
|
ln -s /usr/local/share/dupeguru_me/start.py $(CURDIR)/debian/dupeguru-me/usr/local/bin/dupeguru_me
|
||||||
|
|
||||||
|
|
||||||
# Build architecture-independent files here.
|
# Build architecture-independent files here.
|
||||||
|
|||||||
1
debian_pe/compat
Normal file
1
debian_pe/compat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
7
|
||||||
@@ -8,5 +8,5 @@ Homepage: http://www.hardcoded.net
|
|||||||
|
|
||||||
Package: dupeguru-pe
|
Package: dupeguru-pe
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Depends: python (>= 2.6), python-qt4 (>= 4.6), python-lxml (>= 2.1), python-imaging (>= 1.1.6)
|
Depends: python3 (>= 3.1)
|
||||||
Description: dupeGuru Picture Edition
|
Description: dupeGuru Picture Edition
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ install: build
|
|||||||
dh_installdirs
|
dh_installdirs
|
||||||
|
|
||||||
chmod +x src/start.py
|
chmod +x src/start.py
|
||||||
cp -R src/ $(CURDIR)/debian/tmp/usr/local/share/dupeguru_pe
|
cp -R src/ $(CURDIR)/debian/dupeguru-pe/usr/local/share/dupeguru_pe
|
||||||
cp $(CURDIR)/debian/dupeguru_pe.desktop $(CURDIR)/debian/tmp/usr/share/applications
|
cp $(CURDIR)/debian/dupeguru_pe.desktop $(CURDIR)/debian/dupeguru-pe/usr/share/applications
|
||||||
ln -s /usr/local/share/dupeguru_pe/start.py $(CURDIR)/debian/tmp/usr/local/bin/dupeguru_pe
|
ln -s /usr/local/share/dupeguru_pe/start.py $(CURDIR)/debian/dupeguru-pe/usr/local/bin/dupeguru_pe
|
||||||
|
|
||||||
|
|
||||||
# Build architecture-independent files here.
|
# Build architecture-independent files here.
|
||||||
|
|||||||
1
debian_se/compat
Normal file
1
debian_se/compat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
7
|
||||||
@@ -8,5 +8,5 @@ Homepage: http://www.hardcoded.net
|
|||||||
|
|
||||||
Package: dupeguru-se
|
Package: dupeguru-se
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Depends: python (>= 2.6), python-qt4 (>= 4.6), python-lxml (>= 2.1)
|
Depends: python3 (>= 3.1)
|
||||||
Description: dupeGuru
|
Description: dupeGuru
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ install: build
|
|||||||
dh_installdirs
|
dh_installdirs
|
||||||
|
|
||||||
chmod +x src/start.py
|
chmod +x src/start.py
|
||||||
cp -R src/ $(CURDIR)/debian/tmp/usr/local/share/dupeguru_se
|
cp -R src/ $(CURDIR)/debian/dupeguru-se/usr/local/share/dupeguru_se
|
||||||
cp $(CURDIR)/debian/dupeguru_se.desktop $(CURDIR)/debian/tmp/usr/share/applications
|
cp $(CURDIR)/debian/dupeguru_se.desktop $(CURDIR)/debian/dupeguru-se/usr/share/applications
|
||||||
ln -s /usr/local/share/dupeguru_se/start.py $(CURDIR)/debian/tmp/usr/local/bin/dupeguru_se
|
ln -s /usr/local/share/dupeguru_se/start.py $(CURDIR)/debian/dupeguru-se/usr/local/bin/dupeguru_se
|
||||||
|
|
||||||
|
|
||||||
# Build architecture-independent files here.
|
# Build architecture-independent files here.
|
||||||
|
|||||||
@@ -1,3 +1,26 @@
|
|||||||
|
- date: 2010-09-27
|
||||||
|
version: 5.10.0
|
||||||
|
description: |
|
||||||
|
* Improved UI with a little revamp.
|
||||||
|
* Added the possibility to place hardlinks to references after having deleted duplicates. [Mac OS X, Linux] (#91)
|
||||||
|
* Added an option to ignore duplicates hardlinking to the same file. [Mac OS X, Linux] (#92)
|
||||||
|
* Added multiple selection in the "Add Directory" dialog. [Mac OS X] (#105)
|
||||||
|
* Fixed a bug preventing drag & drop from working in the Directories panel. [Windows, Linux]
|
||||||
|
* Fixed a crash on some badly formed Id3v2 tags. (#107)
|
||||||
|
- date: 2010-08-24
|
||||||
|
version: 5.9.1
|
||||||
|
description: |
|
||||||
|
* Fixed HTML exporting which was broken in 5.9.0.
|
||||||
|
* Fixed Xing-encoded mpeg decoding which was broken in 5.9.0.
|
||||||
|
- date: 2010-08-20
|
||||||
|
version: 5.9.0
|
||||||
|
description: |
|
||||||
|
* Added the ability to save results (and reload them) at arbitrary locations.
|
||||||
|
* Improved the way reference files in dupe groups are chosen. (#15)
|
||||||
|
* Remember size/position of all windows between launches. (#102)
|
||||||
|
* Fixed a bug sometimes preventing dupeGuru from reloading previous results.
|
||||||
|
* Fixed a bug sometimes causing the progress dialog to be stuck there. [Mac OS X] (#103)
|
||||||
|
* Removed the Creation Date column, which wasn't displaying the correct value anyway. (#101)
|
||||||
- date: 2010-07-16
|
- date: 2010-07-16
|
||||||
version: 5.8.1
|
version: 5.8.1
|
||||||
description: |
|
description: |
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ base:
|
|||||||
skeleton: skeleton
|
skeleton: skeleton
|
||||||
changelog: changelog.yaml
|
changelog: changelog.yaml
|
||||||
tixurl: "https://hardcoded.lighthouseapp.com/projects/31699-dupeguru/tickets/{0}"
|
tixurl: "https://hardcoded.lighthouseapp.com/projects/31699-dupeguru/tickets/{0}"
|
||||||
firstpage_meta: "<meta name=\"AppleTitle\" content=\"dupeGuru Help\"></meta>"
|
firstpage_meta: "<meta name=\"AppleTitle\" content=\"dupeGuru ME Help\"></meta>"
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
**Can mix file kind:** If you check this box, duplicate groups are allowed to have files with different extensions. If you don't check it, well, they aren't!
|
**Can mix file kind:** If you check this box, duplicate groups are allowed to have files with different extensions. If you don't check it, well, they aren't!
|
||||||
|
|
||||||
|
**Ignore duplicates hardlinking to the same file:** If this option is enabled, dupeGuru will verify duplicates to see if they refer to the same [inode](http://en.wikipedia.org/wiki/Inode). If they do, they will not be considered duplicates. (Only for OS X and Linux)
|
||||||
|
|
||||||
**Use regular expressions when filtering:** If you check this box, the filtering feature will treat your filter query as a **regular expression**. Explaining them is beyond the scope of this document. A good place to start learning it is <http://www.regular-expressions.info>.
|
**Use regular expressions when filtering:** If you check this box, the filtering feature will treat your filter query as a **regular expression**. Explaining them is beyond the scope of this document. A good place to start learning it is <http://www.regular-expressions.info>.
|
||||||
|
|
||||||
**Remove empty folders after delete or move:** When this option is enabled, folders are deleted after a file is deleted or moved and the folder is empty.
|
**Remove empty folders after delete or move:** When this option is enabled, folders are deleted after a file is deleted or moved and the folder is empty.
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ Action Menu
|
|||||||
* **Clear Ignore List:** Remove all ignored matches you added. You have to start a new scan for the newly cleared ignore list to be effective.
|
* **Clear Ignore List:** Remove all ignored matches you added. You have to start a new scan for the newly cleared ignore list to be effective.
|
||||||
* **Export Results to XHTML:** Take the current results, and create an XHTML file out of it. The columns that are visible when you click on this button will be the columns present in the XHTML file. The file will automatically be opened in your default browser.
|
* **Export Results to XHTML:** Take the current results, and create an XHTML file out of it. The columns that are visible when you click on this button will be the columns present in the XHTML file. The file will automatically be opened in your default browser.
|
||||||
* **Send Marked to Trash:** Send all marked duplicates to trash, obviously.
|
* **Send Marked to Trash:** Send all marked duplicates to trash, obviously.
|
||||||
|
* **Delete Marked and Replace with Hardlinks:** Sends all marked duplicates to trash, but after having done that, the deleted files are replaced by a [hard link](http://en.wikipedia.org/wiki/Hard_link) to the reference file. (Only for OS X and Linux)
|
||||||
* **Move Marked to...:** Prompt you for a destination, and then move all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
* **Move Marked to...:** Prompt you for a destination, and then move all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
||||||
* **Copy Marked to...:** Prompt you for a destination, and then copy all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
* **Copy Marked to...:** Prompt you for a destination, and then copy all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
||||||
* **Remove Marked from Results:** Remove all marked duplicates from results. The actual files will not be touched and will stay where they are.
|
* **Remove Marked from Results:** Remove all marked duplicates from results. The actual files will not be touched and will stay where they are.
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
- date: 2010-09-27
|
||||||
|
version: 1.11.0
|
||||||
|
description: |
|
||||||
|
* Improved UI with a little revamp.
|
||||||
|
* Added the possibility to place hardlinks to references after having deleted duplicates. [Mac OS X, Linux] (#91)
|
||||||
|
* Added an option to ignore duplicates hardlinking to the same file. [Mac OS X, Linux] (#92)
|
||||||
|
* Added multiple selection in the "Add Directory" dialog. [Mac OS X] (#105)
|
||||||
|
* Fixed a bug preventing drag & drop from working in the Directories panel. [Windows, Linux]
|
||||||
|
- date: 2010-08-21
|
||||||
|
version: 1.10.0
|
||||||
|
description: |
|
||||||
|
* Added the ability to save results (and reload them) at arbitrary locations.
|
||||||
|
* Improved the way reference files in dupe groups are chosen. (#15)
|
||||||
|
* Remember size/position of all windows between launches. (#102)
|
||||||
|
* Fixed a bug sometimes preventing dupeGuru from reloading previous results.
|
||||||
|
* Fixed a bug sometimes causing the progress dialog to be stuck there. [Mac OS X] (#103)
|
||||||
|
* Removed the Creation Date column, which wasn't displaying the correct value anyway. (#101)
|
||||||
- date: 2010-07-17
|
- date: 2010-07-17
|
||||||
version: 1.9.1
|
version: 1.9.1
|
||||||
description: |
|
description: |
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
**Can mix file kind:** If you check this box, duplicate groups are allowed to have files with different extensions. If you don't check it, well, they aren't!
|
**Can mix file kind:** If you check this box, duplicate groups are allowed to have files with different extensions. If you don't check it, well, they aren't!
|
||||||
|
|
||||||
|
**Ignore duplicates hardlinking to the same file:** If this option is enabled, dupeGuru will verify duplicates to see if they refer to the same [inode](http://en.wikipedia.org/wiki/Inode). If they do, they will not be considered duplicates. (Only for OS X and Linux)
|
||||||
|
|
||||||
**Use regular expressions when filtering:** If you check this box, the filtering feature will treat your filter query as a **regular expression**. Explaining them is beyond the scope of this document. A good place to start learning it is <http://www.regular-expressions.info>.
|
**Use regular expressions when filtering:** If you check this box, the filtering feature will treat your filter query as a **regular expression**. Explaining them is beyond the scope of this document. A good place to start learning it is <http://www.regular-expressions.info>.
|
||||||
|
|
||||||
**Remove empty folders after delete or move:** When this option is enabled, folders are deleted after a file is deleted or moved and the folder is empty.
|
**Remove empty folders after delete or move:** When this option is enabled, folders are deleted after a file is deleted or moved and the folder is empty.
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ Action Menu
|
|||||||
* **Clear Ignore List:** Remove all ignored matches you added. You have to start a new scan for the newly cleared ignore list to be effective.
|
* **Clear Ignore List:** Remove all ignored matches you added. You have to start a new scan for the newly cleared ignore list to be effective.
|
||||||
* **Export Results to XHTML:** Take the current results, and create an XHTML file out of it. The columns that are visible when you click on this button will be the columns present in the XHTML file. The file will automatically be opened in your default browser.
|
* **Export Results to XHTML:** Take the current results, and create an XHTML file out of it. The columns that are visible when you click on this button will be the columns present in the XHTML file. The file will automatically be opened in your default browser.
|
||||||
* **Send Marked to Trash:** Send all marked duplicates to trash, obviously.
|
* **Send Marked to Trash:** Send all marked duplicates to trash, obviously.
|
||||||
|
* **Delete Marked and Replace with Hardlinks:** Sends all marked duplicates to trash, but after having done that, the deleted files are replaced by a [hard link](http://en.wikipedia.org/wiki/Hard_link) to the reference file. (Only for OS X and Linux)
|
||||||
* **Move Marked to...:** Prompt you for a destination, and then move all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
* **Move Marked to...:** Prompt you for a destination, and then move all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
||||||
* **Copy Marked to...:** Prompt you for a destination, and then copy all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
* **Copy Marked to...:** Prompt you for a destination, and then copy all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
||||||
* **Remove Marked from Results:** Remove all marked duplicates from results. The actual files will not be touched and will stay where they are.
|
* **Remove Marked from Results:** Remove all marked duplicates from results. The actual files will not be touched and will stay where they are.
|
||||||
|
|||||||
@@ -1,3 +1,24 @@
|
|||||||
|
- date: 2010-09-26
|
||||||
|
version: 2.12.0
|
||||||
|
description: |
|
||||||
|
* Improved UI with a little revamp.
|
||||||
|
* Added the possibility to place hardlinks to references after having deleted duplicates. [Mac OS X, Linux] (#91)
|
||||||
|
* Added an option to ignore duplicates hardlinking to the same file. [Mac OS X, Linux] (#92)
|
||||||
|
* Added multiple selection in the "Add Directory" dialog. [Mac OS X] (#105)
|
||||||
|
* Fixed a bug preventing drag & drop from working in the Directories panel. [Windows, Linux]
|
||||||
|
- date: 2010-08-26
|
||||||
|
version: 2.11.1
|
||||||
|
description: |
|
||||||
|
* Fixed HTML exporting which was broken in 2.11.0.
|
||||||
|
- date: 2010-08-18
|
||||||
|
version: 2.11.0
|
||||||
|
description: |
|
||||||
|
* Added the ability to save results (and reload them) at arbitrary locations.
|
||||||
|
* Improved the way reference files in dupe groups are chosen. (#15)
|
||||||
|
* Remember size/position of all windows between launches. (#102)
|
||||||
|
* Fixed a bug sometimes preventing dupeGuru from reloading previous results.
|
||||||
|
* Fixed a bug sometimes causing the progress dialog to be stuck there. [Mac OS X] (#103)
|
||||||
|
* Removed the Creation Date column, which wasn't displaying the correct value anyway. (#101)
|
||||||
- date: 2010-07-15
|
- date: 2010-07-15
|
||||||
version: 2.10.1
|
version: 2.10.1
|
||||||
description: |
|
description: |
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
**Can mix file kind:** If you check this box, duplicate groups are allowed to have files with different extensions. If you don't check it, well, they aren't!
|
**Can mix file kind:** If you check this box, duplicate groups are allowed to have files with different extensions. If you don't check it, well, they aren't!
|
||||||
|
|
||||||
|
**Ignore duplicates hardlinking to the same file:** If this option is enabled, dupeGuru will verify duplicates to see if they refer to the same [inode](http://en.wikipedia.org/wiki/Inode). If they do, they will not be considered duplicates. (Only for OS X and Linux)
|
||||||
|
|
||||||
**Use regular expressions when filtering:** If you check this box, the filtering feature will treat your filter query as a **regular expression**. Explaining them is beyond the scope of this document. A good place to start learning it is <http://www.regular-expressions.info>.
|
**Use regular expressions when filtering:** If you check this box, the filtering feature will treat your filter query as a **regular expression**. Explaining them is beyond the scope of this document. A good place to start learning it is <http://www.regular-expressions.info>.
|
||||||
|
|
||||||
**Remove empty folders after delete or move:** When this option is enabled, folders are deleted after a file is deleted or moved and the folder is empty.
|
**Remove empty folders after delete or move:** When this option is enabled, folders are deleted after a file is deleted or moved and the folder is empty.
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ Action Menu
|
|||||||
* **Clear Ignore List:** Remove all ignored matches you added. You have to start a new scan for the newly cleared ignore list to be effective.
|
* **Clear Ignore List:** Remove all ignored matches you added. You have to start a new scan for the newly cleared ignore list to be effective.
|
||||||
* **Export Results to XHTML:** Take the current results, and create an XHTML file out of it. The columns that are visible when you click on this button will be the columns present in the XHTML file. The file will automatically be opened in your default browser.
|
* **Export Results to XHTML:** Take the current results, and create an XHTML file out of it. The columns that are visible when you click on this button will be the columns present in the XHTML file. The file will automatically be opened in your default browser.
|
||||||
* **Send Marked to Trash:** Send all marked duplicates to trash, obviously.
|
* **Send Marked to Trash:** Send all marked duplicates to trash, obviously.
|
||||||
|
* **Delete Marked and Replace with Hardlinks:** Sends all marked duplicates to trash, but after having done that, the deleted files are replaced by a [hard link](http://en.wikipedia.org/wiki/Hard_link) to the reference file. (Only for OS X and Linux)
|
||||||
* **Move Marked to...:** Prompt you for a destination, and then move all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
* **Move Marked to...:** Prompt you for a destination, and then move all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
||||||
* **Copy Marked to...:** Prompt you for a destination, and then copy all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
* **Copy Marked to...:** Prompt you for a destination, and then copy all marked files to that destination. Source file's path might be re-created in destination, depending on the "Copy and Move" preference.
|
||||||
* **Remove Marked from Results:** Remove all marked duplicates from results. The actual files will not be touched and will stay where they are.
|
* **Remove Marked from Results:** Remove all marked duplicates from results. The actual files will not be touched and will stay where they are.
|
||||||
|
|||||||
17
package.py
17
package.py
@@ -30,7 +30,7 @@ def package_windows(edition, dev):
|
|||||||
# On Windows, PyInstaller is used to build an exe (py2exe creates a very bad looking icon)
|
# 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
|
# The release version is outdated. Use at least r672 on http://svn.pyinstaller.org/trunk
|
||||||
if sys.platform != "win32":
|
if sys.platform != "win32":
|
||||||
print "Qt packaging only works under Windows."
|
print("Qt packaging only works under Windows.")
|
||||||
return
|
return
|
||||||
add_to_pythonpath('.')
|
add_to_pythonpath('.')
|
||||||
add_to_pythonpath('qt')
|
add_to_pythonpath('qt')
|
||||||
@@ -60,7 +60,7 @@ def package_windows(edition, dev):
|
|||||||
help_basedir = '..\\..\\help_{0}'.format(edition)
|
help_basedir = '..\\..\\help_{0}'.format(edition)
|
||||||
help_dir = 'dupeguru_{0}_help'.format(edition) if edition != 'se' else 'dupeguru_help'
|
help_dir = 'dupeguru_{0}_help'.format(edition) if edition != 'se' else 'dupeguru_help'
|
||||||
help_path = op.join(help_basedir, help_dir)
|
help_path = op.join(help_basedir, help_dir)
|
||||||
print "Copying {0} to dist\\help".format(help_path)
|
print("Copying {0} to dist\\help".format(help_path))
|
||||||
shutil.copytree(help_path, 'dist\\help')
|
shutil.copytree(help_path, 'dist\\help')
|
||||||
|
|
||||||
# AdvancedInstaller.com has to be in your PATH
|
# AdvancedInstaller.com has to be in your PATH
|
||||||
@@ -88,6 +88,15 @@ def package_debian(edition):
|
|||||||
if edition == 'me':
|
if edition == 'me':
|
||||||
packages.append('hsaudiotag')
|
packages.append('hsaudiotag')
|
||||||
copy_packages(packages, srcpath)
|
copy_packages(packages, srcpath)
|
||||||
|
import sip, PyQt4
|
||||||
|
shutil.copy(sip.__file__, srcpath)
|
||||||
|
qtsrcpath = op.dirname(PyQt4.__file__)
|
||||||
|
qtdestpath = op.join(srcpath, 'PyQt4')
|
||||||
|
os.makedirs(qtdestpath)
|
||||||
|
shutil.copy(op.join(qtsrcpath, '__init__.py'), qtdestpath)
|
||||||
|
shutil.copy(op.join(qtsrcpath, 'Qt.so'), qtdestpath)
|
||||||
|
shutil.copy(op.join(qtsrcpath, 'QtCore.so'), qtdestpath)
|
||||||
|
shutil.copy(op.join(qtsrcpath, 'QtGui.so'), qtdestpath)
|
||||||
shutil.copytree(ed('debian_{0}'), op.join(destpath, 'debian'))
|
shutil.copytree(ed('debian_{0}'), op.join(destpath, 'debian'))
|
||||||
yaml_path = op.join(help_src, 'changelog.yaml')
|
yaml_path = op.join(help_src, 'changelog.yaml')
|
||||||
changelog_dest = op.join(destpath, 'debian', 'changelog')
|
changelog_dest = op.join(destpath, 'debian', 'changelog')
|
||||||
@@ -106,7 +115,7 @@ def main():
|
|||||||
edition = conf['edition']
|
edition = conf['edition']
|
||||||
ui = conf['ui']
|
ui = conf['ui']
|
||||||
dev = conf['dev']
|
dev = conf['dev']
|
||||||
print "Packaging dupeGuru {0} with UI {1}".format(edition.upper(), ui)
|
print("Packaging dupeGuru {0} with UI {1}".format(edition.upper(), ui))
|
||||||
if ui == 'cocoa':
|
if ui == 'cocoa':
|
||||||
package_cocoa(edition)
|
package_cocoa(edition)
|
||||||
elif ui == 'qt':
|
elif ui == 'qt':
|
||||||
@@ -115,7 +124,7 @@ def main():
|
|||||||
elif sys.platform == "linux2":
|
elif sys.platform == "linux2":
|
||||||
package_debian(edition)
|
package_debian(edition)
|
||||||
else:
|
else:
|
||||||
print "Qt packaging only works under Windows or Linux."
|
print("Qt packaging only works under Windows or Linux.")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import os.path as op
|
import os.path as op
|
||||||
|
|
||||||
from PyQt4.QtCore import QTimer, QObject, QCoreApplication, QUrl, SIGNAL
|
from PyQt4.QtCore import QTimer, QObject, QCoreApplication, QUrl, SIGNAL, pyqtSignal
|
||||||
from PyQt4.QtGui import QDesktopServices, QFileDialog, QDialog, QMessageBox
|
from PyQt4.QtGui import QDesktopServices, QFileDialog, QDialog, QMessageBox
|
||||||
|
|
||||||
from hscommon import job
|
from hscommon import job
|
||||||
@@ -54,7 +54,7 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
DELTA_COLUMNS = frozenset()
|
DELTA_COLUMNS = frozenset()
|
||||||
|
|
||||||
def __init__(self, data_module, appid):
|
def __init__(self, data_module, appid):
|
||||||
appdata = unicode(QDesktopServices.storageLocation(QDesktopServices.DataLocation))
|
appdata = str(QDesktopServices.storageLocation(QDesktopServices.DataLocation))
|
||||||
if not op.exists(appdata):
|
if not op.exists(appdata):
|
||||||
os.makedirs(appdata)
|
os.makedirs(appdata)
|
||||||
# For basicConfig() to work, we have to be sure that no logging has taken place before this call.
|
# For basicConfig() to work, we have to be sure that no logging has taken place before this call.
|
||||||
@@ -86,7 +86,10 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
self._nagTimer = QTimer()
|
self._nagTimer = QTimer()
|
||||||
self.connect(self._nagTimer, SIGNAL('timeout()'), self.mustShowNag)
|
self.connect(self._nagTimer, SIGNAL('timeout()'), self.mustShowNag)
|
||||||
self._nagTimer.start(0)
|
self._nagTimer.start(0)
|
||||||
self.main_window.show()
|
if self.prefs.mainWindowIsMaximized:
|
||||||
|
self.main_window.showMaximized()
|
||||||
|
else:
|
||||||
|
self.main_window.show()
|
||||||
self.load()
|
self.load()
|
||||||
|
|
||||||
self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate)
|
self.connect(QCoreApplication.instance(), SIGNAL('aboutToQuit()'), self.application_will_terminate)
|
||||||
@@ -103,6 +106,7 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
self.scanner.mix_file_kind = self.prefs.mix_file_kind
|
self.scanner.mix_file_kind = self.prefs.mix_file_kind
|
||||||
self.options['escape_filter_regexp'] = self.prefs.use_regexp
|
self.options['escape_filter_regexp'] = self.prefs.use_regexp
|
||||||
self.options['clean_empty_dirs'] = self.prefs.remove_empty_folders
|
self.options['clean_empty_dirs'] = self.prefs.remove_empty_folders
|
||||||
|
self.options['ignore_hardlink_matches'] = self.prefs.ignore_hardlink_matches
|
||||||
|
|
||||||
#--- Virtual
|
#--- Virtual
|
||||||
def _create_details_dialog(self, parent):
|
def _create_details_dialog(self, parent):
|
||||||
@@ -120,18 +124,19 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
#--- Override
|
#--- Override
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _open_path(path):
|
def _open_path(path):
|
||||||
url = QUrl.fromLocalFile(unicode(path))
|
url = QUrl.fromLocalFile(str(path))
|
||||||
QDesktopServices.openUrl(url)
|
QDesktopServices.openUrl(url)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _reveal_path(path):
|
def _reveal_path(path):
|
||||||
DupeGuru._open_path(path[:-1])
|
DupeGuru._open_path(path[:-1])
|
||||||
|
|
||||||
def _start_job(self, jobid, func):
|
def _start_job(self, jobid, func, *args):
|
||||||
title = JOBID2TITLE[jobid]
|
title = JOBID2TITLE[jobid]
|
||||||
try:
|
try:
|
||||||
j = self._progress.create_job()
|
j = self._progress.create_job()
|
||||||
self._progress.run(jobid, title, func, args=(j, ))
|
args = tuple([j] + list(args))
|
||||||
|
self._progress.run(jobid, title, func, args=args)
|
||||||
except job.JobInProgressError:
|
except job.JobInProgressError:
|
||||||
msg = "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again."
|
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)
|
QMessageBox.information(self.main_window, 'Action in progress', msg)
|
||||||
@@ -150,7 +155,7 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
opname = 'copy' if copy else 'move'
|
opname = 'copy' if copy else 'move'
|
||||||
title = "Select a directory to {0} marked files to".format(opname)
|
title = "Select a directory to {0} marked files to".format(opname)
|
||||||
flags = QFileDialog.ShowDirsOnly
|
flags = QFileDialog.ShowDirsOnly
|
||||||
destination = unicode(QFileDialog.getExistingDirectory(self.main_window, title, '', flags))
|
destination = str(QFileDialog.getExistingDirectory(self.main_window, title, '', flags))
|
||||||
if not destination:
|
if not destination:
|
||||||
return
|
return
|
||||||
recreate_path = self.prefs.destination_type
|
recreate_path = self.prefs.destination_type
|
||||||
@@ -205,8 +210,13 @@ class DupeGuru(DupeGuruBase, QObject):
|
|||||||
self.prefs.save()
|
self.prefs.save()
|
||||||
self._update_options()
|
self._update_options()
|
||||||
|
|
||||||
|
#--- Signals
|
||||||
|
willSavePrefs = pyqtSignal()
|
||||||
|
|
||||||
#--- Events
|
#--- Events
|
||||||
def application_will_terminate(self):
|
def application_will_terminate(self):
|
||||||
|
self.willSavePrefs.emit()
|
||||||
|
self.prefs.save()
|
||||||
self.save()
|
self.save()
|
||||||
self.save_ignore_list()
|
self.save_ignore_list()
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# cxfreeze has some problems detecting all dependencies.
|
# cxfreeze has some problems detecting all dependencies.
|
||||||
# This modules explicitly import those problematic modules.
|
# This modules explicitly import those problematic modules.
|
||||||
|
|
||||||
import lxml._elementpath
|
import xml.etree.ElementPath
|
||||||
import gzip
|
import gzip
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -20,15 +20,23 @@ class DetailsDialog(QDialog):
|
|||||||
self.app = app
|
self.app = app
|
||||||
self.model = DetailsPanel(self, app)
|
self.model = DetailsPanel(self, app)
|
||||||
self._setupUi()
|
self._setupUi()
|
||||||
|
if self.app.prefs.detailsWindowRect is not None:
|
||||||
|
self.setGeometry(self.app.prefs.detailsWindowRect)
|
||||||
self.tableModel = DetailsModel(self.model)
|
self.tableModel = DetailsModel(self.model)
|
||||||
# tableView is defined in subclasses
|
# tableView is defined in subclasses
|
||||||
self.tableView.setModel(self.tableModel)
|
self.tableView.setModel(self.tableModel)
|
||||||
self.model.connect()
|
self.model.connect()
|
||||||
|
|
||||||
|
self.app.willSavePrefs.connect(self.appWillSavePrefs)
|
||||||
|
|
||||||
def _setupUi(self): # Virtual
|
def _setupUi(self): # Virtual
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# model --> view
|
#--- Events
|
||||||
|
def appWillSavePrefs(self):
|
||||||
|
self.app.prefs.detailsWindowRect = self.geometry()
|
||||||
|
|
||||||
|
#--- model --> view
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.tableModel.reset()
|
self.tableModel.reset()
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class DirectoriesDialog(QDialog, Ui_DirectoriesDialog):
|
|||||||
self.connect(self.addButton, SIGNAL('clicked()'), self.addButtonClicked)
|
self.connect(self.addButton, SIGNAL('clicked()'), self.addButtonClicked)
|
||||||
self.connect(self.removeButton, SIGNAL('clicked()'), self.removeButtonClicked)
|
self.connect(self.removeButton, SIGNAL('clicked()'), self.removeButtonClicked)
|
||||||
self.connect(self.treeView.selectionModel(), SIGNAL('selectionChanged(QItemSelection,QItemSelection)'), self.selectionChanged)
|
self.connect(self.treeView.selectionModel(), SIGNAL('selectionChanged(QItemSelection,QItemSelection)'), self.selectionChanged)
|
||||||
|
self.app.willSavePrefs.connect(self.appWillSavePrefs)
|
||||||
|
|
||||||
def _setupUi(self):
|
def _setupUi(self):
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
@@ -41,6 +42,9 @@ class DirectoriesDialog(QDialog, Ui_DirectoriesDialog):
|
|||||||
header.setResizeMode(1, QHeaderView.Fixed)
|
header.setResizeMode(1, QHeaderView.Fixed)
|
||||||
header.resizeSection(1, 100)
|
header.resizeSection(1, 100)
|
||||||
|
|
||||||
|
if self.app.prefs.directoriesWindowRect is not None:
|
||||||
|
self.setGeometry(self.app.prefs.directoriesWindowRect)
|
||||||
|
|
||||||
def _updateRemoveButton(self):
|
def _updateRemoveButton(self):
|
||||||
indexes = self.treeView.selectedIndexes()
|
indexes = self.treeView.selectedIndexes()
|
||||||
if not indexes:
|
if not indexes:
|
||||||
@@ -51,15 +55,19 @@ class DirectoriesDialog(QDialog, Ui_DirectoriesDialog):
|
|||||||
node = index.internalPointer()
|
node = index.internalPointer()
|
||||||
# label = 'Remove' if node.parent is None else 'Exclude'
|
# label = 'Remove' if node.parent is None else 'Exclude'
|
||||||
|
|
||||||
|
#--- Events
|
||||||
def addButtonClicked(self):
|
def addButtonClicked(self):
|
||||||
title = u"Select a directory to add to the scanning list"
|
title = "Select a directory to add to the scanning list"
|
||||||
flags = QFileDialog.ShowDirsOnly
|
flags = QFileDialog.ShowDirsOnly
|
||||||
dirpath = unicode(QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags))
|
dirpath = str(QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags))
|
||||||
if not dirpath:
|
if not dirpath:
|
||||||
return
|
return
|
||||||
self.lastAddedFolder = dirpath
|
self.lastAddedFolder = dirpath
|
||||||
self.app.add_directory(dirpath)
|
self.app.add_directory(dirpath)
|
||||||
|
|
||||||
|
def appWillSavePrefs(self):
|
||||||
|
self.app.prefs.directoriesWindowRect = self.geometry()
|
||||||
|
|
||||||
def doneButtonClicked(self):
|
def doneButtonClicked(self):
|
||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/hs_license
|
||||||
|
|
||||||
import urllib
|
import urllib.parse
|
||||||
|
|
||||||
from PyQt4.QtCore import QModelIndex, Qt, QRect, QEvent, QPoint, QUrl
|
from PyQt4.QtCore import QModelIndex, Qt, QRect, QEvent, QPoint, QUrl
|
||||||
from PyQt4.QtGui import (QComboBox, QStyledItemDelegate, QMouseEvent, QApplication, QBrush, QStyle,
|
from PyQt4.QtGui import (QComboBox, QStyledItemDelegate, QMouseEvent, QApplication, QBrush, QStyle,
|
||||||
@@ -96,14 +96,14 @@ class DirectoriesModel(TreeModel):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def dropMimeData(self, mimeData, action, row, column, parentIndex):
|
def dropMimeData(self, mimeData, action, row, column, parentIndex):
|
||||||
# the data in mimeData is urlencoded **in utf-8**!!! which means that urllib.unquote has
|
# the data in mimeData is urlencoded **in utf-8**!!! What we do is to decode, the mime data
|
||||||
# to be called on the utf-8 encoded string, and *only then*, decoded to unicode.
|
# with 'ascii', which works since it's urlencoded. Then, we pass that to urllib.
|
||||||
if not mimeData.hasFormat('text/uri-list'):
|
if not mimeData.hasFormat('text/uri-list'):
|
||||||
return False
|
return False
|
||||||
data = str(mimeData.data('text/uri-list'))
|
data = bytes(mimeData.data('text/uri-list')).decode('ascii')
|
||||||
unquoted = urllib.unquote(data)
|
unquoted = urllib.parse.unquote(data)
|
||||||
urls = unicode(unquoted, 'utf-8').split('\r\n')
|
urls = unquoted.split('\r\n')
|
||||||
paths = [unicode(QUrl(url).toLocalFile()) for url in urls if url]
|
paths = [str(QUrl(url).toLocalFile()) for url in urls if url]
|
||||||
for path in paths:
|
for path in paths:
|
||||||
self.model.add_directory(path)
|
self.model.add_directory(path)
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user