mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-25 08:01:39 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
526bcf2566 | ||
|
|
56207f4dbb | ||
|
|
cd9fd3a10b | ||
|
|
4399fe9d17 | ||
|
|
2a6f524a5b | ||
|
|
caee5e37f0 | ||
|
|
bbd9d68dfd | ||
|
|
16b1b00906 | ||
|
|
7183408535 | ||
|
|
591b4c7c6a | ||
|
|
8b1170a82b | ||
|
|
1f26fbeacc | ||
|
|
cc7ccff48e | ||
|
|
a0809333c1 | ||
|
|
8975f78a5f | ||
|
|
56bc1c1373 | ||
|
|
417233a47f | ||
|
|
59eaf5305a | ||
|
|
275c6be108 | ||
|
|
a0e2b11663 | ||
|
|
de23ce90d8 | ||
|
|
9d5f3029d0 |
2
.hgtags
2
.hgtags
@@ -47,3 +47,5 @@ f1d40b556c01f32c58f9ef9f9acac5b78e01ba7a pe2.0.0
|
||||
ff43c6d9feb388f103b7857eaa6f7809185f78ec before-pluginbuilder
|
||||
d274bcb98f2d02b86470a04cd62e718eff33b74f pe2.1.0
|
||||
77e169f757195c11e9c1c5febeb2db8eb3589510 se3.0.2
|
||||
97893f37d7d0767b5aedf1b4b40de57ee36d426b se3.1.0
|
||||
e44d5127ed605daa7a17a01eee65d0a157de20c0 pe2.2.0
|
||||
|
||||
41
README
41
README
@@ -29,7 +29,7 @@ General dependencies
|
||||
- Python 3.1 (3.2 on Mac OS X) (http://www.python.org)
|
||||
- Send2Trash3k (http://hg.hardcoded.net/send2trash)
|
||||
- hsaudiotag3k 1.1.0 (for ME) (http://hg.hardcoded.net/hsaudiotag)
|
||||
- jobprogress (http://hg.hardcoded.net/jobprogress)
|
||||
- jobprogress 1.0.1 (http://hg.hardcoded.net/jobprogress)
|
||||
- Sphinx 1.0.6 (http://sphinx.pocoo.org/)
|
||||
- pytest 2.0.0, to run unit tests. (http://pytest.org/)
|
||||
|
||||
@@ -54,6 +54,45 @@ Windows prerequisites
|
||||
- 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/)
|
||||
|
||||
Linux prerequisites
|
||||
-------------------
|
||||
|
||||
- PyQt 4.7.5 (http://www.riverbankcomputing.co.uk/news)
|
||||
|
||||
Prerequisite gotchas
|
||||
--------------------
|
||||
|
||||
Correctly installing the prerequisites is tricky. Make sure you have at least the version number
|
||||
required for each prerequisite.
|
||||
|
||||
If you didn't use mercurial to download this source, you probably have an incomplete source folder!
|
||||
External projects (hscommon, qtlib, cocoalib) need to be at the root of the dupeGuru project folder.
|
||||
You'll have to download those separately. Or use mercurial, it's much easier.
|
||||
|
||||
As far as I can tell, you don't *have* to compile/install everything manually and you can normally
|
||||
use `easy_install` to install python dependencies. However, be aware that compiling/installing
|
||||
manually from the repositories of each project is what I personally do, so if you hit a snag
|
||||
somewhere, you might want to try the manual way.
|
||||
|
||||
PyObjC's website is badly outdated. Also, as far as I can tell, the package installable with
|
||||
`easy_install` has good chances of not working. Your best bet is to download the latest tagged
|
||||
version from the repository and compile it from source.
|
||||
|
||||
Also, on OS X, don't try to use the built-in python 2.x to install Sphinx on (the only pre-requisite
|
||||
that doesn't run on python 3 yet). There's some weird error popping up when dupeGuru tries to build
|
||||
its help file. Install your own framework version of python 2.7, and then install Sphinx on that.
|
||||
When Sphinx supports Python 3, things will be easier because you'll be able to install sphinx on the
|
||||
same Python version you build dupeGuru with.
|
||||
|
||||
Another one on OS X: I wouldn't use macports/fink/whatever. Whenever I tried using those, I always
|
||||
ended up with problems.
|
||||
|
||||
Also, I don't know yet if it's possible to compile dupeGuru with XCode 4, I haven't tried it yet.
|
||||
It's safer to use XCode 3.x. However, I don't see why it wouldn't work, so it very well might work.
|
||||
|
||||
Whenever you have a problem, always double-check that you're running the correct python version.
|
||||
You'll probably have to tweak your $PATH.
|
||||
|
||||
Building dupeGuru
|
||||
=================
|
||||
|
||||
|
||||
27
build.py
27
build.py
@@ -24,17 +24,15 @@ def parse_args():
|
||||
parser = OptionParser(usage=usage)
|
||||
parser.add_option('--clean', action='store_true', dest='clean',
|
||||
help="Clean build folder before building")
|
||||
parser.add_option('--only-help', action='store_true', dest='only_help',
|
||||
help="Build only help file")
|
||||
parser.add_option('--doc', action='store_true', dest='doc',
|
||||
help="Build only the help file")
|
||||
parser.add_option('--loc', action='store_true', dest='loc',
|
||||
help="Build only localization")
|
||||
(options, args) = parser.parse_args()
|
||||
return options
|
||||
|
||||
def build_cocoa(edition, dev):
|
||||
from pluginbuilder import build_plugin
|
||||
build_all_cocoa_locs('cocoalib')
|
||||
build_all_cocoa_locs(op.join('cocoa', 'base'))
|
||||
build_all_cocoa_locs(op.join('cocoa', edition))
|
||||
|
||||
print("Building dg_cocoa.plugin")
|
||||
if not dev:
|
||||
specific_packages = {
|
||||
@@ -83,8 +81,6 @@ def build_cocoa(edition, dev):
|
||||
open('run.py', 'wt').write(run_contents)
|
||||
|
||||
def build_qt(edition, dev):
|
||||
print("Building .ts files")
|
||||
build_all_qt_locs(op.join('qt', 'lang'), extradirs=[op.join('qtlib', 'lang')])
|
||||
print("Building Qt stuff")
|
||||
print_and_do("pyrcc4 -py3 {0} > {1}".format(op.join('qt', 'base', 'dg.qrc'), op.join('qt', 'base', 'dg_rc.py')))
|
||||
print("Creating the run.py file")
|
||||
@@ -104,6 +100,16 @@ def build_help(edition):
|
||||
confrepl = {'edition': edition, 'appname': appname, 'homepage': homepage}
|
||||
sphinxgen.gen(help_basepath, help_destpath, changelog_path, tixurl, confrepl)
|
||||
|
||||
def build_localizations(ui, edition):
|
||||
print("Building localizations")
|
||||
if ui == 'cocoa':
|
||||
build_all_cocoa_locs('cocoalib')
|
||||
build_all_cocoa_locs(op.join('cocoa', 'base'))
|
||||
build_all_cocoa_locs(op.join('cocoa', edition))
|
||||
elif ui == 'qt':
|
||||
print("Building .ts files")
|
||||
build_all_qt_locs(op.join('qt', 'lang'), extradirs=[op.join('qtlib', 'lang')])
|
||||
|
||||
def build_pe_modules(ui):
|
||||
def move(src, dst):
|
||||
if not op.exists(src):
|
||||
@@ -144,6 +150,7 @@ def build_normal(edition, ui, dev):
|
||||
print("Building dupeGuru {0} with UI {1}".format(edition.upper(), ui))
|
||||
add_to_pythonpath('.')
|
||||
build_help(edition)
|
||||
build_localizations(ui, edition)
|
||||
print("Building dupeGuru")
|
||||
if edition == 'pe':
|
||||
build_pe_modules(ui)
|
||||
@@ -165,8 +172,10 @@ def main():
|
||||
shutil.rmtree('build')
|
||||
if not op.exists('build'):
|
||||
os.mkdir('build')
|
||||
if options.only_help:
|
||||
if options.doc:
|
||||
build_help(edition)
|
||||
elif options.loc:
|
||||
build_localizations(ui, edition)
|
||||
else:
|
||||
build_normal(edition, ui, dev)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
[self window];
|
||||
_app = aParentApp;
|
||||
_py = [_app py];
|
||||
[[self window] setTitle:[_py appName]];
|
||||
_alwaysShowPopUp = NO;
|
||||
[self fillPopUpMenu];
|
||||
_recentDirectories = [[HSRecentFiles alloc] initWithName:@"recentDirectories" menu:[addButtonPopUp menu]];
|
||||
|
||||
@@ -48,6 +48,7 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
- (NSArray *)deltaColumns;
|
||||
|
||||
//Scanning options
|
||||
- (void)setScanType:(NSNumber *)scan_type;
|
||||
- (void)setMinMatchPercentage:(NSNumber *)percentage;
|
||||
- (void)setMixFileKind:(BOOL)mix_file_kind;
|
||||
- (void)setEscapeFilterRegexp:(BOOL)escape_filter_regexp;
|
||||
|
||||
@@ -117,7 +117,8 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
}
|
||||
|
||||
- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)column row:(NSInteger)row
|
||||
{
|
||||
{
|
||||
BOOL isSelected = [tableView isRowSelected:row];
|
||||
BOOL isMarkable = n2b([[self py] valueForColumn:@"markable" row:row]);
|
||||
if ([[column identifier] isEqual:@"marked"]) {
|
||||
[cell setEnabled:isMarkable];
|
||||
@@ -126,20 +127,22 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
[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]];
|
||||
NSColor *color = [NSColor textColor];
|
||||
if (isSelected) {
|
||||
color = [NSColor selectedTextColor];
|
||||
}
|
||||
else if (isMarkable) {
|
||||
if ([self deltaValuesMode]) {
|
||||
NSInteger i = [[column identifier] integerValue];
|
||||
if ([_deltaColumns containsIndex:i]) {
|
||||
[textCell setTextColor:[NSColor orangeColor]];
|
||||
color = [NSColor orangeColor];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
[textCell setTextColor:[NSColor blueColor]];
|
||||
color = [NSColor blueColor];
|
||||
}
|
||||
[(NSTextFieldCell *)cell setTextColor:color];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
self = [super initWithWindowNibName:@"ResultWindow"];
|
||||
app = aApp;
|
||||
py = [app py];
|
||||
[[self window] setTitle:fmt(@"%@ Results", [py appName])];
|
||||
columnsMenu = [app columnsMenu];
|
||||
/* Put a cute iTunes-like bottom bar */
|
||||
[[self window] setContentBorderThickness:28 forEdge:NSMinYEdge];
|
||||
|
||||
@@ -13,7 +13,6 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
- (id)initWithParentApp:(id)aParentApp
|
||||
{
|
||||
self = [super initWithParentApp:aParentApp];
|
||||
[[self window] setTitle:@"dupeGuru Music Edition"];
|
||||
_alwaysShowPopUp = YES;
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
@interface PyDupeGuru : PyDupeGuruBase
|
||||
//Scanning options
|
||||
- (void)setScanType:(NSNumber *)scan_type;
|
||||
- (void)setMinWordCount:(NSNumber *)word_count;
|
||||
- (void)setMinWordLength:(NSNumber *)word_length;
|
||||
- (void)setWordWeighting:(NSNumber *)words_are_weighted;
|
||||
|
||||
@@ -20,14 +20,15 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
{
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithCapacity:10];
|
||||
[d setObject:[NSNumber numberWithInt:95] forKey:@"minMatchPercentage"];
|
||||
[d setObject:[NSNumber numberWithInt:1] forKey:@"recreatePathType"];
|
||||
[d setObject:[NSNumber numberWithBool:NO] forKey:@"matchScaled"];
|
||||
[d setObject:[NSNumber numberWithBool:YES] forKey:@"mixFileKind"];
|
||||
[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:@"debug"];
|
||||
[d setObject:i2n(0) forKey:@"scanType"];
|
||||
[d setObject:i2n(95) forKey:@"minMatchPercentage"];
|
||||
[d setObject:i2n(1) forKey:@"recreatePathType"];
|
||||
[d setObject:b2n(NO) forKey:@"matchScaled"];
|
||||
[d setObject:b2n(YES) forKey:@"mixFileKind"];
|
||||
[d setObject:b2n(NO) forKey:@"useRegexpFilter"];
|
||||
[d setObject:b2n(NO) forKey:@"ignoreHardlinkMatches"];
|
||||
[d setObject:b2n(NO) forKey:@"removeEmptyFolders"];
|
||||
[d setObject:b2n(NO) forKey:@"debug"];
|
||||
[d setObject:[NSArray array] forKey:@"recentDirectories"];
|
||||
[d setObject:[NSArray array] forKey:@"columnsOrder"];
|
||||
[d setObject:[NSDictionary dictionary] forKey:@"columnsWidth"];
|
||||
@@ -35,6 +36,15 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
[ud registerDefaults:d];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
NSMutableIndexSet *i = [NSMutableIndexSet indexSetWithIndex:0];
|
||||
VTIsIntIn *vtScanTypeIsFuzzy = [[[VTIsIntIn alloc] initWithValues:i reverse:NO] autorelease];
|
||||
[NSValueTransformer setValueTransformer:vtScanTypeIsFuzzy forName:@"vtScanTypeIsFuzzy"];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)homepageURL
|
||||
{
|
||||
return @"http://www.hardcoded.net/dupeguru_pe/";
|
||||
|
||||
@@ -13,7 +13,6 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
- (id)initWithParentApp:(id)aParentApp
|
||||
{
|
||||
self = [super initWithParentApp:aParentApp];
|
||||
[[self window] setTitle:@"dupeGuru Picture Edition"];
|
||||
_alwaysShowPopUp = YES;
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
{
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
PyDupeGuru *_py = (PyDupeGuru *)py;
|
||||
[_py setScanType:[ud objectForKey:@"scanType"]];
|
||||
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
|
||||
[_py setMixFileKind:n2b([ud objectForKey:@"mixFileKind"])];
|
||||
[_py setIgnoreHardlinkMatches:n2b([ud objectForKey:@"ignoreHardlinkMatches"])];
|
||||
|
||||
@@ -9,6 +9,7 @@ install_cocoa_trans()
|
||||
|
||||
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
|
||||
from core_pe import app_cocoa as app_pe_cocoa, __appname__
|
||||
from core.scanner import ScanType
|
||||
|
||||
class PyDupeGuru(PyDupeGuruBase):
|
||||
def init(self):
|
||||
@@ -27,6 +28,15 @@ class PyDupeGuru(PyDupeGuruBase):
|
||||
return str(self.py.selected_dupe_ref_path())
|
||||
|
||||
#---Properties
|
||||
def setScanType_(self, scan_type):
|
||||
try:
|
||||
self.py.scanner.scan_type = [
|
||||
ScanType.FuzzyBlock,
|
||||
ScanType.ExifTimestamp,
|
||||
][scan_type]
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
def setMatchScaled_(self,match_scaled):
|
||||
self.py.scanner.match_scaled = match_scaled
|
||||
|
||||
|
||||
Binary file not shown.
@@ -2,17 +2,17 @@
|
||||
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
||||
<data>
|
||||
<int key="IBDocument.SystemTarget">1050</int>
|
||||
<string key="IBDocument.SystemVersion">10J567</string>
|
||||
<string key="IBDocument.InterfaceBuilderVersion">823</string>
|
||||
<string key="IBDocument.SystemVersion">10J4138</string>
|
||||
<string key="IBDocument.InterfaceBuilderVersion">851</string>
|
||||
<string key="IBDocument.AppKitVersion">1038.35</string>
|
||||
<string key="IBDocument.HIToolboxVersion">462.00</string>
|
||||
<string key="IBDocument.HIToolboxVersion">461.00</string>
|
||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="NS.object.0">823</string>
|
||||
<string key="NS.object.0">851</string>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<integer value="3"/>
|
||||
<integer value="63"/>
|
||||
</object>
|
||||
<object class="NSArray" key="IBDocument.PluginDependencies">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
@@ -34,10 +34,6 @@
|
||||
<string key="NSClassName">NSApplication</string>
|
||||
</object>
|
||||
<object class="NSUserDefaultsController" id="455472712">
|
||||
<object class="NSMutableArray" key="NSDeclaredKeys">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<string>DebugMode</string>
|
||||
</object>
|
||||
<bool key="NSSharedInstance">YES</bool>
|
||||
</object>
|
||||
<object class="NSWindowTemplate" id="809668081">
|
||||
@@ -103,7 +99,7 @@
|
||||
<object class="NSSlider" id="266372855">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">292</int>
|
||||
<string key="NSFrame">{{117, 140}, {181, 21}}</string>
|
||||
<string key="NSFrame">{{117, 107}, {181, 21}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSSliderCell" key="NSCell" id="453640282">
|
||||
@@ -131,7 +127,7 @@
|
||||
<object class="NSTextField" id="869007847">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">292</int>
|
||||
<string key="NSFrame">{{119, 123}, {80, 13}}</string>
|
||||
<string key="NSFrame">{{119, 90}, {80, 13}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSTextFieldCell" key="NSCell" id="106025161">
|
||||
@@ -167,7 +163,7 @@
|
||||
<object class="NSTextField" id="171701149">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">289</int>
|
||||
<string key="NSFrame">{{216, 123}, {80, 13}}</string>
|
||||
<string key="NSFrame">{{216, 90}, {80, 13}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSTextFieldCell" key="NSCell" id="397705219">
|
||||
@@ -183,7 +179,7 @@
|
||||
<object class="NSTextField" id="638371207">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">292</int>
|
||||
<string key="NSFrame">{{14, 145}, {100, 14}}</string>
|
||||
<string key="NSFrame">{{14, 112}, {100, 14}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSTextFieldCell" key="NSCell" id="812365472">
|
||||
@@ -203,7 +199,7 @@
|
||||
<object class="NSButton" id="488256664">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{15, 79}, {316, 18}}</string>
|
||||
<string key="NSFrame">{{15, 46}, {316, 18}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSButtonCell" key="NSCell" id="401283671">
|
||||
@@ -226,13 +222,13 @@
|
||||
<object class="NSButton" id="722670516">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{15, 99}, {316, 18}}</string>
|
||||
<string key="NSFrame">{{15, 66}, {316, 18}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSButtonCell" key="NSCell" id="911281323">
|
||||
<int key="NSCellFlags">67239424</int>
|
||||
<int key="NSCellFlags2">0</int>
|
||||
<string key="NSContents">Match scaled pictures together</string>
|
||||
<string key="NSContents">Match pictures of different dimensions</string>
|
||||
<reference key="NSSupport" ref="26"/>
|
||||
<reference key="NSControlView" ref="722670516"/>
|
||||
<int key="NSButtonFlags">1211912703</int>
|
||||
@@ -247,7 +243,7 @@
|
||||
<object class="NSButton" id="472028782">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{15, 39}, {316, 18}}</string>
|
||||
<string key="NSFrame">{{15, 6}, {316, 18}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSButtonCell" key="NSCell" id="2297113">
|
||||
@@ -268,7 +264,7 @@
|
||||
<object class="NSButton" id="279087998">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{15, 59}, {316, 18}}</string>
|
||||
<string key="NSFrame">{{15, 26}, {316, 18}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSButtonCell" key="NSCell" id="287383961">
|
||||
@@ -289,7 +285,7 @@
|
||||
<object class="NSTextField" id="403531548">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{301, 145}, {31, 14}}</string>
|
||||
<string key="NSFrame">{{301, 112}, {31, 14}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSTextFieldCell" key="NSCell" id="983190380">
|
||||
@@ -367,6 +363,87 @@
|
||||
<reference key="NSTextColor" ref="538152464"/>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSTextField" id="536472926">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">292</int>
|
||||
<string key="NSFrame">{{14, 145}, {85, 13}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSTextFieldCell" key="NSCell" id="359086043">
|
||||
<int key="NSCellFlags">67239424</int>
|
||||
<int key="NSCellFlags2">272629760</int>
|
||||
<string key="NSContents">Scan type:</string>
|
||||
<reference key="NSSupport" ref="649492068"/>
|
||||
<reference key="NSControlView" ref="536472926"/>
|
||||
<reference key="NSBackgroundColor" ref="71910056"/>
|
||||
<reference key="NSTextColor" ref="538152464"/>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSPopUpButton" id="337614813">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">292</int>
|
||||
<string key="NSFrame">{{113, 135}, {219, 26}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSPopUpButtonCell" key="NSCell" id="697629846">
|
||||
<int key="NSCellFlags">-2076049856</int>
|
||||
<int key="NSCellFlags2">2048</int>
|
||||
<reference key="NSSupport" ref="882799568"/>
|
||||
<reference key="NSControlView" ref="337614813"/>
|
||||
<int key="NSButtonFlags">109199615</int>
|
||||
<int key="NSButtonFlags2">1</int>
|
||||
<reference key="NSAlternateImage" ref="882799568"/>
|
||||
<string key="NSAlternateContents"/>
|
||||
<object class="NSMutableString" key="NSKeyEquivalent">
|
||||
<characters key="NS.bytes"/>
|
||||
</object>
|
||||
<int key="NSPeriodicDelay">400</int>
|
||||
<int key="NSPeriodicInterval">75</int>
|
||||
<object class="NSMenuItem" key="NSMenuItem" id="1038855957">
|
||||
<reference key="NSMenu" ref="958971008"/>
|
||||
<string key="NSTitle">Contents</string>
|
||||
<string key="NSKeyEquiv"/>
|
||||
<int key="NSKeyEquivModMask">1048576</int>
|
||||
<int key="NSMnemonicLoc">2147483647</int>
|
||||
<int key="NSState">1</int>
|
||||
<object class="NSCustomResource" key="NSOnImage" id="875822430">
|
||||
<string key="NSClassName">NSImage</string>
|
||||
<string key="NSResourceName">NSMenuCheckmark</string>
|
||||
</object>
|
||||
<object class="NSCustomResource" key="NSMixedImage" id="731403416">
|
||||
<string key="NSClassName">NSImage</string>
|
||||
<string key="NSResourceName">NSMenuMixedState</string>
|
||||
</object>
|
||||
<string key="NSAction">_popUpItemAction:</string>
|
||||
<reference key="NSTarget" ref="697629846"/>
|
||||
</object>
|
||||
<bool key="NSMenuItemRespectAlignment">YES</bool>
|
||||
<object class="NSMenu" key="NSMenu" id="958971008">
|
||||
<object class="NSMutableString" key="NSTitle">
|
||||
<characters key="NS.bytes">OtherViews</characters>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="NSMenuItems">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="1038855957"/>
|
||||
<object class="NSMenuItem" id="820923003">
|
||||
<reference key="NSMenu" ref="958971008"/>
|
||||
<string key="NSTitle">EXIF Timestamp</string>
|
||||
<string key="NSKeyEquiv"/>
|
||||
<int key="NSKeyEquivModMask">1048576</int>
|
||||
<int key="NSMnemonicLoc">2147483647</int>
|
||||
<reference key="NSOnImage" ref="875822430"/>
|
||||
<reference key="NSMixedImage" ref="731403416"/>
|
||||
<string key="NSAction">_popUpItemAction:</string>
|
||||
<reference key="NSTarget" ref="697629846"/>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<int key="NSPreferredEdge">3</int>
|
||||
<bool key="NSUsesItemFromMenu">YES</bool>
|
||||
<bool key="NSAltersState">YES</bool>
|
||||
<int key="NSArrowPosition">1</int>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<string key="NSFrame">{{10, 33}, {346, 162}}</string>
|
||||
<reference key="NSSuperview" ref="211771207"/>
|
||||
@@ -504,14 +581,8 @@
|
||||
<int key="NSKeyEquivModMask">1048576</int>
|
||||
<int key="NSMnemonicLoc">2147483647</int>
|
||||
<int key="NSState">1</int>
|
||||
<object class="NSCustomResource" key="NSOnImage" id="867788054">
|
||||
<string key="NSClassName">NSImage</string>
|
||||
<string key="NSResourceName">NSMenuCheckmark</string>
|
||||
</object>
|
||||
<object class="NSCustomResource" key="NSMixedImage" id="554538570">
|
||||
<string key="NSClassName">NSImage</string>
|
||||
<string key="NSResourceName">NSMenuMixedState</string>
|
||||
</object>
|
||||
<reference key="NSOnImage" ref="875822430"/>
|
||||
<reference key="NSMixedImage" ref="731403416"/>
|
||||
<string key="NSAction">_popUpItemAction:</string>
|
||||
<reference key="NSTarget" ref="601288025"/>
|
||||
</object>
|
||||
@@ -529,8 +600,8 @@
|
||||
<string key="NSKeyEquiv"/>
|
||||
<int key="NSKeyEquivModMask">1048576</int>
|
||||
<int key="NSMnemonicLoc">2147483647</int>
|
||||
<reference key="NSOnImage" ref="867788054"/>
|
||||
<reference key="NSMixedImage" ref="554538570"/>
|
||||
<reference key="NSOnImage" ref="875822430"/>
|
||||
<reference key="NSMixedImage" ref="731403416"/>
|
||||
<string key="NSAction">_popUpItemAction:</string>
|
||||
<reference key="NSTarget" ref="601288025"/>
|
||||
</object>
|
||||
@@ -540,8 +611,8 @@
|
||||
<string key="NSKeyEquiv"/>
|
||||
<int key="NSKeyEquivModMask">1048576</int>
|
||||
<int key="NSMnemonicLoc">2147483647</int>
|
||||
<reference key="NSOnImage" ref="867788054"/>
|
||||
<reference key="NSMixedImage" ref="554538570"/>
|
||||
<reference key="NSOnImage" ref="875822430"/>
|
||||
<reference key="NSMixedImage" ref="731403416"/>
|
||||
<string key="NSAction">_popUpItemAction:</string>
|
||||
<reference key="NSTarget" ref="601288025"/>
|
||||
</object>
|
||||
@@ -877,6 +948,42 @@
|
||||
</object>
|
||||
<int key="connectionID">78</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBBindingConnection" key="connection">
|
||||
<string key="label">selectedIndex: values.scanType</string>
|
||||
<reference key="source" ref="337614813"/>
|
||||
<reference key="destination" ref="455472712"/>
|
||||
<object class="NSNibBindingConnector" key="connector">
|
||||
<reference key="NSSource" ref="337614813"/>
|
||||
<reference key="NSDestination" ref="455472712"/>
|
||||
<string key="NSLabel">selectedIndex: values.scanType</string>
|
||||
<string key="NSBinding">selectedIndex</string>
|
||||
<string key="NSKeyPath">values.scanType</string>
|
||||
<int key="NSNibBindingConnectorVersion">2</int>
|
||||
</object>
|
||||
</object>
|
||||
<int key="connectionID">96</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBBindingConnection" key="connection">
|
||||
<string key="label">enabled: values.scanType</string>
|
||||
<reference key="source" ref="266372855"/>
|
||||
<reference key="destination" ref="455472712"/>
|
||||
<object class="NSNibBindingConnector" key="connector">
|
||||
<reference key="NSSource" ref="266372855"/>
|
||||
<reference key="NSDestination" ref="455472712"/>
|
||||
<string key="NSLabel">enabled: values.scanType</string>
|
||||
<string key="NSBinding">enabled</string>
|
||||
<string key="NSKeyPath">values.scanType</string>
|
||||
<object class="NSDictionary" key="NSOptions">
|
||||
<string key="NS.key.0">NSValueTransformerName</string>
|
||||
<string key="NS.object.0">vtScanTypeIsFuzzy</string>
|
||||
</object>
|
||||
<int key="NSNibBindingConnectorVersion">2</int>
|
||||
</object>
|
||||
</object>
|
||||
<int key="connectionID">98</int>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||
<object class="NSArray" key="orderedObjects">
|
||||
@@ -993,15 +1100,17 @@
|
||||
<reference key="object" ref="1073354031"/>
|
||||
<object class="NSMutableArray" key="children">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="722670516"/>
|
||||
<reference ref="488256664"/>
|
||||
<reference ref="638371207"/>
|
||||
<reference ref="171701149"/>
|
||||
<reference ref="869007847"/>
|
||||
<reference ref="266372855"/>
|
||||
<reference ref="869007847"/>
|
||||
<reference ref="171701149"/>
|
||||
<reference ref="638371207"/>
|
||||
<reference ref="488256664"/>
|
||||
<reference ref="722670516"/>
|
||||
<reference ref="279087998"/>
|
||||
<reference ref="403531548"/>
|
||||
<reference ref="472028782"/>
|
||||
<reference ref="337614813"/>
|
||||
<reference ref="536472926"/>
|
||||
<reference ref="403531548"/>
|
||||
</object>
|
||||
<reference key="parent" ref="700068878"/>
|
||||
</object>
|
||||
@@ -1268,6 +1377,58 @@
|
||||
<reference key="object" ref="100803310"/>
|
||||
<reference key="parent" ref="606836304"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">87</int>
|
||||
<reference key="object" ref="536472926"/>
|
||||
<object class="NSMutableArray" key="children">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="359086043"/>
|
||||
</object>
|
||||
<reference key="parent" ref="1073354031"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">88</int>
|
||||
<reference key="object" ref="337614813"/>
|
||||
<object class="NSMutableArray" key="children">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="697629846"/>
|
||||
</object>
|
||||
<reference key="parent" ref="1073354031"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">89</int>
|
||||
<reference key="object" ref="697629846"/>
|
||||
<object class="NSMutableArray" key="children">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="958971008"/>
|
||||
</object>
|
||||
<reference key="parent" ref="337614813"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">90</int>
|
||||
<reference key="object" ref="958971008"/>
|
||||
<object class="NSMutableArray" key="children">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="820923003"/>
|
||||
<reference ref="1038855957"/>
|
||||
</object>
|
||||
<reference key="parent" ref="697629846"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">92</int>
|
||||
<reference key="object" ref="820923003"/>
|
||||
<reference key="parent" ref="958971008"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">93</int>
|
||||
<reference key="object" ref="1038855957"/>
|
||||
<reference key="parent" ref="958971008"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">94</int>
|
||||
<reference key="object" ref="359086043"/>
|
||||
<reference key="parent" ref="536472926"/>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSMutableDictionary" key="flattenedProperties">
|
||||
@@ -1281,16 +1442,22 @@
|
||||
<string>10.IBViewBoundsToFrameTransform</string>
|
||||
<string>10.ImportedFromIB2</string>
|
||||
<string>11.IBPluginDependency</string>
|
||||
<string>11.IBViewBoundsToFrameTransform</string>
|
||||
<string>11.ImportedFromIB2</string>
|
||||
<string>12.IBPluginDependency</string>
|
||||
<string>12.IBViewBoundsToFrameTransform</string>
|
||||
<string>12.ImportedFromIB2</string>
|
||||
<string>13.IBPluginDependency</string>
|
||||
<string>13.IBViewBoundsToFrameTransform</string>
|
||||
<string>13.ImportedFromIB2</string>
|
||||
<string>14.IBPluginDependency</string>
|
||||
<string>14.IBViewBoundsToFrameTransform</string>
|
||||
<string>14.ImportedFromIB2</string>
|
||||
<string>15.IBPluginDependency</string>
|
||||
<string>15.IBViewBoundsToFrameTransform</string>
|
||||
<string>15.ImportedFromIB2</string>
|
||||
<string>16.IBPluginDependency</string>
|
||||
<string>16.IBViewBoundsToFrameTransform</string>
|
||||
<string>16.ImportedFromIB2</string>
|
||||
<string>17.IBPluginDependency</string>
|
||||
<string>18.IBPluginDependency</string>
|
||||
@@ -1326,6 +1493,7 @@
|
||||
<string>4.IBPluginDependency</string>
|
||||
<string>4.ImportedFromIB2</string>
|
||||
<string>5.IBPluginDependency</string>
|
||||
<string>5.IBViewBoundsToFrameTransform</string>
|
||||
<string>5.ImportedFromIB2</string>
|
||||
<string>59.IBPluginDependency</string>
|
||||
<string>6.IBPluginDependency</string>
|
||||
@@ -1346,6 +1514,7 @@
|
||||
<string>69.IBViewBoundsToFrameTransform</string>
|
||||
<string>69.ImportedFromIB2</string>
|
||||
<string>7.IBPluginDependency</string>
|
||||
<string>7.IBViewBoundsToFrameTransform</string>
|
||||
<string>7.ImportedFromIB2</string>
|
||||
<string>70.IBPluginDependency</string>
|
||||
<string>74.IBPluginDependency</string>
|
||||
@@ -1355,9 +1524,24 @@
|
||||
<string>8.IBPluginDependency</string>
|
||||
<string>8.IBViewBoundsToFrameTransform</string>
|
||||
<string>8.ImportedFromIB2</string>
|
||||
<string>87.IBPluginDependency</string>
|
||||
<string>87.IBViewBoundsToFrameTransform</string>
|
||||
<string>87.ImportedFromIB2</string>
|
||||
<string>88.IBPluginDependency</string>
|
||||
<string>88.IBViewBoundsToFrameTransform</string>
|
||||
<string>88.ImportedFromIB2</string>
|
||||
<string>89.IBPluginDependency</string>
|
||||
<string>9.IBPluginDependency</string>
|
||||
<string>9.IBViewBoundsToFrameTransform</string>
|
||||
<string>9.ImportedFromIB2</string>
|
||||
<string>90.IBEditorWindowLastContentRect</string>
|
||||
<string>90.IBPluginDependency</string>
|
||||
<string>90.ImportedFromIB2</string>
|
||||
<string>92.IBPluginDependency</string>
|
||||
<string>92.ImportedFromIB2</string>
|
||||
<string>93.IBPluginDependency</string>
|
||||
<string>93.ImportedFromIB2</string>
|
||||
<string>94.IBPluginDependency</string>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="dict.values">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
@@ -1370,23 +1554,41 @@
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABDloAAwxAAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBcAAAwr4AAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBYAAAwx0AAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABDWAAAwwYAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABC7gAAwwYAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABC6gAAwx8AAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>{{88, 591}, {392, 254}}</string>
|
||||
<string>{{413, 591}, {392, 254}}</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>{{88, 591}, {392, 254}}</string>
|
||||
<string>{{413, 591}, {392, 254}}</string>
|
||||
<boolean value="YES"/>
|
||||
<boolean value="YES"/>
|
||||
<string>{213, 107}</string>
|
||||
@@ -1415,11 +1617,14 @@
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBcAAAwpYAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBcAAAwfAAAA</bytes>
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBcAAAwlwAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
@@ -1443,6 +1648,9 @@
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBcAAAwuYAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
@@ -1457,10 +1665,29 @@
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBYAAAwx0AAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABCbAAAwx8AAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBYAAAwjwAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>{{213, 762}, {216, 43}}</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSMutableDictionary" key="unlocalizedProperties">
|
||||
@@ -1479,7 +1706,7 @@
|
||||
</object>
|
||||
</object>
|
||||
<nil key="sourceID"/>
|
||||
<int key="maxID">78</int>
|
||||
<int key="maxID">100</int>
|
||||
</object>
|
||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
||||
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
/* Class = "NSMenuItem"; title = "Right in destination"; ObjectID = "30"; */
|
||||
"30.title" = "Directement à la destination";
|
||||
|
||||
/* Class = "NSButtonCell"; title = "Match scaled pictures together"; ObjectID = "31"; */
|
||||
/* Class = "NSButtonCell"; title = "Match pictures of different dimensions"; ObjectID = "31"; */
|
||||
"31.title" = "Comparer les images de tailles différentes";
|
||||
|
||||
/* Class = "NSButtonCell"; title = "Automatically check for updates"; ObjectID = "32"; */
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
|
||||
<data>
|
||||
<int key="IBDocument.SystemTarget">1050</int>
|
||||
<string key="IBDocument.SystemVersion">10J567</string>
|
||||
<string key="IBDocument.InterfaceBuilderVersion">823</string>
|
||||
<string key="IBDocument.SystemVersion">10J4138</string>
|
||||
<string key="IBDocument.InterfaceBuilderVersion">851</string>
|
||||
<string key="IBDocument.AppKitVersion">1038.35</string>
|
||||
<string key="IBDocument.HIToolboxVersion">462.00</string>
|
||||
<string key="IBDocument.HIToolboxVersion">461.00</string>
|
||||
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
|
||||
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string key="NS.object.0">823</string>
|
||||
<string key="NS.object.0">851</string>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
@@ -98,7 +98,7 @@
|
||||
<object class="NSSlider" id="266372855">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">292</int>
|
||||
<string key="NSFrame">{{117, 140}, {181, 21}}</string>
|
||||
<string key="NSFrame">{{117, 107}, {181, 21}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSSliderCell" key="NSCell" id="453640282">
|
||||
@@ -126,7 +126,7 @@
|
||||
<object class="NSTextField" id="869007847">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">292</int>
|
||||
<string key="NSFrame">{{119, 123}, {80, 13}}</string>
|
||||
<string key="NSFrame">{{119, 90}, {80, 13}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSTextFieldCell" key="NSCell" id="106025161">
|
||||
@@ -162,7 +162,7 @@
|
||||
<object class="NSTextField" id="171701149">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">289</int>
|
||||
<string key="NSFrame">{{216, 123}, {80, 13}}</string>
|
||||
<string key="NSFrame">{{216, 90}, {80, 13}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSTextFieldCell" key="NSCell" id="397705219">
|
||||
@@ -178,7 +178,7 @@
|
||||
<object class="NSTextField" id="638371207">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">292</int>
|
||||
<string key="NSFrame">{{14, 145}, {100, 14}}</string>
|
||||
<string key="NSFrame">{{14, 112}, {100, 14}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSTextFieldCell" key="NSCell" id="812365472">
|
||||
@@ -198,7 +198,7 @@
|
||||
<object class="NSButton" id="488256664">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{15, 79}, {316, 18}}</string>
|
||||
<string key="NSFrame">{{15, 46}, {316, 18}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSButtonCell" key="NSCell" id="401283671">
|
||||
@@ -209,7 +209,7 @@
|
||||
<reference key="NSControlView" ref="488256664"/>
|
||||
<int key="NSButtonFlags">1211912703</int>
|
||||
<int key="NSButtonFlags2">2</int>
|
||||
<object class="NSCustomResource" key="NSNormalImage" id="283442644">
|
||||
<object class="NSCustomResource" key="NSNormalImage" id="949163782">
|
||||
<string key="NSClassName">NSImage</string>
|
||||
<string key="NSResourceName">NSSwitch</string>
|
||||
</object>
|
||||
@@ -225,7 +225,7 @@
|
||||
<object class="NSButton" id="722670516">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{15, 99}, {316, 18}}</string>
|
||||
<string key="NSFrame">{{15, 66}, {316, 18}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSButtonCell" key="NSCell" id="911281323">
|
||||
@@ -236,7 +236,7 @@
|
||||
<reference key="NSControlView" ref="722670516"/>
|
||||
<int key="NSButtonFlags">1211912703</int>
|
||||
<int key="NSButtonFlags2">2</int>
|
||||
<reference key="NSNormalImage" ref="283442644"/>
|
||||
<reference key="NSNormalImage" ref="949163782"/>
|
||||
<reference key="NSAlternateImage" ref="990345653"/>
|
||||
<string key="NSAlternateContents"/>
|
||||
<string key="NSKeyEquivalent"/>
|
||||
@@ -247,7 +247,7 @@
|
||||
<object class="NSButton" id="472028782">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{15, 39}, {316, 18}}</string>
|
||||
<string key="NSFrame">{{15, 6}, {316, 18}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSButtonCell" key="NSCell" id="2297113">
|
||||
@@ -258,7 +258,7 @@
|
||||
<reference key="NSControlView" ref="472028782"/>
|
||||
<int key="NSButtonFlags">1211912703</int>
|
||||
<int key="NSButtonFlags2">2</int>
|
||||
<reference key="NSNormalImage" ref="283442644"/>
|
||||
<reference key="NSNormalImage" ref="949163782"/>
|
||||
<reference key="NSAlternateImage" ref="990345653"/>
|
||||
<string key="NSAlternateContents"/>
|
||||
<string key="NSKeyEquivalent"/>
|
||||
@@ -269,7 +269,7 @@
|
||||
<object class="NSButton" id="279087998">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{15, 59}, {316, 18}}</string>
|
||||
<string key="NSFrame">{{15, 26}, {316, 18}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSButtonCell" key="NSCell" id="287383961">
|
||||
@@ -280,7 +280,7 @@
|
||||
<reference key="NSControlView" ref="279087998"/>
|
||||
<int key="NSButtonFlags">1211912703</int>
|
||||
<int key="NSButtonFlags2">2</int>
|
||||
<reference key="NSNormalImage" ref="283442644"/>
|
||||
<reference key="NSNormalImage" ref="949163782"/>
|
||||
<reference key="NSAlternateImage" ref="990345653"/>
|
||||
<string key="NSAlternateContents"/>
|
||||
<string key="NSKeyEquivalent"/>
|
||||
@@ -291,7 +291,7 @@
|
||||
<object class="NSTextField" id="403531548">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">256</int>
|
||||
<string key="NSFrame">{{301, 145}, {31, 14}}</string>
|
||||
<string key="NSFrame">{{301, 112}, {31, 14}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSTextFieldCell" key="NSCell" id="983190380">
|
||||
@@ -369,6 +369,87 @@
|
||||
<reference key="NSTextColor" ref="538152464"/>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSTextField" id="536472926">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">292</int>
|
||||
<string key="NSFrame">{{14, 145}, {85, 13}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSTextFieldCell" key="NSCell" id="359086043">
|
||||
<int key="NSCellFlags">67239424</int>
|
||||
<int key="NSCellFlags2">272629760</int>
|
||||
<string key="NSContents">Scan type:</string>
|
||||
<reference key="NSSupport" ref="649492068"/>
|
||||
<reference key="NSControlView" ref="536472926"/>
|
||||
<reference key="NSBackgroundColor" ref="71910056"/>
|
||||
<reference key="NSTextColor" ref="538152464"/>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSPopUpButton" id="337614813">
|
||||
<reference key="NSNextResponder" ref="1073354031"/>
|
||||
<int key="NSvFlags">292</int>
|
||||
<string key="NSFrame">{{113, 135}, {219, 26}}</string>
|
||||
<reference key="NSSuperview" ref="1073354031"/>
|
||||
<bool key="NSEnabled">YES</bool>
|
||||
<object class="NSPopUpButtonCell" key="NSCell" id="697629846">
|
||||
<int key="NSCellFlags">-2076049856</int>
|
||||
<int key="NSCellFlags2">2048</int>
|
||||
<reference key="NSSupport" ref="882799568"/>
|
||||
<reference key="NSControlView" ref="337614813"/>
|
||||
<int key="NSButtonFlags">109199615</int>
|
||||
<int key="NSButtonFlags2">1</int>
|
||||
<reference key="NSAlternateImage" ref="882799568"/>
|
||||
<string key="NSAlternateContents"/>
|
||||
<object class="NSMutableString" key="NSKeyEquivalent">
|
||||
<characters key="NS.bytes"/>
|
||||
</object>
|
||||
<int key="NSPeriodicDelay">400</int>
|
||||
<int key="NSPeriodicInterval">75</int>
|
||||
<object class="NSMenuItem" key="NSMenuItem" id="1038855957">
|
||||
<reference key="NSMenu" ref="958971008"/>
|
||||
<string key="NSTitle">Contents</string>
|
||||
<string key="NSKeyEquiv"/>
|
||||
<int key="NSKeyEquivModMask">1048576</int>
|
||||
<int key="NSMnemonicLoc">2147483647</int>
|
||||
<int key="NSState">1</int>
|
||||
<object class="NSCustomResource" key="NSOnImage" id="875822430">
|
||||
<string key="NSClassName">NSImage</string>
|
||||
<string key="NSResourceName">NSMenuCheckmark</string>
|
||||
</object>
|
||||
<object class="NSCustomResource" key="NSMixedImage" id="731403416">
|
||||
<string key="NSClassName">NSImage</string>
|
||||
<string key="NSResourceName">NSMenuMixedState</string>
|
||||
</object>
|
||||
<string key="NSAction">_popUpItemAction:</string>
|
||||
<reference key="NSTarget" ref="697629846"/>
|
||||
</object>
|
||||
<bool key="NSMenuItemRespectAlignment">YES</bool>
|
||||
<object class="NSMenu" key="NSMenu" id="958971008">
|
||||
<object class="NSMutableString" key="NSTitle">
|
||||
<characters key="NS.bytes">OtherViews</characters>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="NSMenuItems">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="1038855957"/>
|
||||
<object class="NSMenuItem" id="820923003">
|
||||
<reference key="NSMenu" ref="958971008"/>
|
||||
<string key="NSTitle">EXIF Timestamp</string>
|
||||
<string key="NSKeyEquiv"/>
|
||||
<int key="NSKeyEquivModMask">1048576</int>
|
||||
<int key="NSMnemonicLoc">2147483647</int>
|
||||
<reference key="NSOnImage" ref="875822430"/>
|
||||
<reference key="NSMixedImage" ref="731403416"/>
|
||||
<string key="NSAction">_popUpItemAction:</string>
|
||||
<reference key="NSTarget" ref="697629846"/>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<int key="NSPreferredEdge">3</int>
|
||||
<bool key="NSUsesItemFromMenu">YES</bool>
|
||||
<bool key="NSAltersState">YES</bool>
|
||||
<int key="NSArrowPosition">1</int>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<string key="NSFrame">{{10, 33}, {346, 162}}</string>
|
||||
<reference key="NSSuperview" ref="211771207"/>
|
||||
@@ -398,7 +479,7 @@
|
||||
<reference key="NSControlView" ref="1018598123"/>
|
||||
<int key="NSButtonFlags">1211912703</int>
|
||||
<int key="NSButtonFlags2">2</int>
|
||||
<reference key="NSNormalImage" ref="283442644"/>
|
||||
<reference key="NSNormalImage" ref="949163782"/>
|
||||
<reference key="NSAlternateImage" ref="990345653"/>
|
||||
<string key="NSAlternateContents"/>
|
||||
<string key="NSKeyEquivalent"/>
|
||||
@@ -420,7 +501,7 @@
|
||||
<reference key="NSControlView" ref="519470955"/>
|
||||
<int key="NSButtonFlags">1211912703</int>
|
||||
<int key="NSButtonFlags2">2</int>
|
||||
<reference key="NSNormalImage" ref="283442644"/>
|
||||
<reference key="NSNormalImage" ref="949163782"/>
|
||||
<reference key="NSAlternateImage" ref="990345653"/>
|
||||
<string key="NSAlternateContents"/>
|
||||
<string key="NSKeyEquivalent"/>
|
||||
@@ -442,7 +523,7 @@
|
||||
<reference key="NSControlView" ref="606836304"/>
|
||||
<int key="NSButtonFlags">1211912703</int>
|
||||
<int key="NSButtonFlags2">2</int>
|
||||
<reference key="NSNormalImage" ref="283442644"/>
|
||||
<reference key="NSNormalImage" ref="949163782"/>
|
||||
<reference key="NSAlternateImage" ref="990345653"/>
|
||||
<string key="NSAlternateContents"/>
|
||||
<string key="NSKeyEquivalent"/>
|
||||
@@ -509,14 +590,8 @@
|
||||
<int key="NSKeyEquivModMask">1048576</int>
|
||||
<int key="NSMnemonicLoc">2147483647</int>
|
||||
<int key="NSState">1</int>
|
||||
<object class="NSCustomResource" key="NSOnImage" id="867788054">
|
||||
<string key="NSClassName">NSImage</string>
|
||||
<string key="NSResourceName">NSMenuCheckmark</string>
|
||||
</object>
|
||||
<object class="NSCustomResource" key="NSMixedImage" id="554538570">
|
||||
<string key="NSClassName">NSImage</string>
|
||||
<string key="NSResourceName">NSMenuMixedState</string>
|
||||
</object>
|
||||
<reference key="NSOnImage" ref="875822430"/>
|
||||
<reference key="NSMixedImage" ref="731403416"/>
|
||||
<string key="NSAction">_popUpItemAction:</string>
|
||||
<reference key="NSTarget" ref="601288025"/>
|
||||
</object>
|
||||
@@ -534,8 +609,8 @@
|
||||
<string key="NSKeyEquiv"/>
|
||||
<int key="NSKeyEquivModMask">1048576</int>
|
||||
<int key="NSMnemonicLoc">2147483647</int>
|
||||
<reference key="NSOnImage" ref="867788054"/>
|
||||
<reference key="NSMixedImage" ref="554538570"/>
|
||||
<reference key="NSOnImage" ref="875822430"/>
|
||||
<reference key="NSMixedImage" ref="731403416"/>
|
||||
<string key="NSAction">_popUpItemAction:</string>
|
||||
<reference key="NSTarget" ref="601288025"/>
|
||||
</object>
|
||||
@@ -545,8 +620,8 @@
|
||||
<string key="NSKeyEquiv"/>
|
||||
<int key="NSKeyEquivModMask">1048576</int>
|
||||
<int key="NSMnemonicLoc">2147483647</int>
|
||||
<reference key="NSOnImage" ref="867788054"/>
|
||||
<reference key="NSMixedImage" ref="554538570"/>
|
||||
<reference key="NSOnImage" ref="875822430"/>
|
||||
<reference key="NSMixedImage" ref="731403416"/>
|
||||
<string key="NSAction">_popUpItemAction:</string>
|
||||
<reference key="NSTarget" ref="601288025"/>
|
||||
</object>
|
||||
@@ -881,6 +956,42 @@
|
||||
</object>
|
||||
<int key="connectionID">78</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBBindingConnection" key="connection">
|
||||
<string key="label">selectedIndex: values.scanType</string>
|
||||
<reference key="source" ref="337614813"/>
|
||||
<reference key="destination" ref="455472712"/>
|
||||
<object class="NSNibBindingConnector" key="connector">
|
||||
<reference key="NSSource" ref="337614813"/>
|
||||
<reference key="NSDestination" ref="455472712"/>
|
||||
<string key="NSLabel">selectedIndex: values.scanType</string>
|
||||
<string key="NSBinding">selectedIndex</string>
|
||||
<string key="NSKeyPath">values.scanType</string>
|
||||
<int key="NSNibBindingConnectorVersion">2</int>
|
||||
</object>
|
||||
</object>
|
||||
<int key="connectionID">96</int>
|
||||
</object>
|
||||
<object class="IBConnectionRecord">
|
||||
<object class="IBBindingConnection" key="connection">
|
||||
<string key="label">enabled: values.scanType</string>
|
||||
<reference key="source" ref="266372855"/>
|
||||
<reference key="destination" ref="455472712"/>
|
||||
<object class="NSNibBindingConnector" key="connector">
|
||||
<reference key="NSSource" ref="266372855"/>
|
||||
<reference key="NSDestination" ref="455472712"/>
|
||||
<string key="NSLabel">enabled: values.scanType</string>
|
||||
<string key="NSBinding">enabled</string>
|
||||
<string key="NSKeyPath">values.scanType</string>
|
||||
<object class="NSDictionary" key="NSOptions">
|
||||
<string key="NS.key.0">NSValueTransformerName</string>
|
||||
<string key="NS.object.0">vtScanTypeIsFuzzy</string>
|
||||
</object>
|
||||
<int key="NSNibBindingConnectorVersion">2</int>
|
||||
</object>
|
||||
</object>
|
||||
<int key="connectionID">98</int>
|
||||
</object>
|
||||
</object>
|
||||
<object class="IBMutableOrderedSet" key="objectRecords">
|
||||
<object class="NSArray" key="orderedObjects">
|
||||
@@ -997,15 +1108,17 @@
|
||||
<reference key="object" ref="1073354031"/>
|
||||
<object class="NSMutableArray" key="children">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="722670516"/>
|
||||
<reference ref="488256664"/>
|
||||
<reference ref="638371207"/>
|
||||
<reference ref="171701149"/>
|
||||
<reference ref="869007847"/>
|
||||
<reference ref="266372855"/>
|
||||
<reference ref="869007847"/>
|
||||
<reference ref="171701149"/>
|
||||
<reference ref="638371207"/>
|
||||
<reference ref="488256664"/>
|
||||
<reference ref="722670516"/>
|
||||
<reference ref="279087998"/>
|
||||
<reference ref="403531548"/>
|
||||
<reference ref="472028782"/>
|
||||
<reference ref="337614813"/>
|
||||
<reference ref="536472926"/>
|
||||
<reference ref="403531548"/>
|
||||
</object>
|
||||
<reference key="parent" ref="700068878"/>
|
||||
</object>
|
||||
@@ -1272,6 +1385,58 @@
|
||||
<reference key="object" ref="100803310"/>
|
||||
<reference key="parent" ref="606836304"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">87</int>
|
||||
<reference key="object" ref="536472926"/>
|
||||
<object class="NSMutableArray" key="children">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="359086043"/>
|
||||
</object>
|
||||
<reference key="parent" ref="1073354031"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">88</int>
|
||||
<reference key="object" ref="337614813"/>
|
||||
<object class="NSMutableArray" key="children">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="697629846"/>
|
||||
</object>
|
||||
<reference key="parent" ref="1073354031"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">89</int>
|
||||
<reference key="object" ref="697629846"/>
|
||||
<object class="NSMutableArray" key="children">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="958971008"/>
|
||||
</object>
|
||||
<reference key="parent" ref="337614813"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">90</int>
|
||||
<reference key="object" ref="958971008"/>
|
||||
<object class="NSMutableArray" key="children">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
<reference ref="820923003"/>
|
||||
<reference ref="1038855957"/>
|
||||
</object>
|
||||
<reference key="parent" ref="697629846"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">92</int>
|
||||
<reference key="object" ref="820923003"/>
|
||||
<reference key="parent" ref="958971008"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">93</int>
|
||||
<reference key="object" ref="1038855957"/>
|
||||
<reference key="parent" ref="958971008"/>
|
||||
</object>
|
||||
<object class="IBObjectRecord">
|
||||
<int key="objectID">94</int>
|
||||
<reference key="object" ref="359086043"/>
|
||||
<reference key="parent" ref="536472926"/>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSMutableDictionary" key="flattenedProperties">
|
||||
@@ -1285,16 +1450,22 @@
|
||||
<string>10.IBViewBoundsToFrameTransform</string>
|
||||
<string>10.ImportedFromIB2</string>
|
||||
<string>11.IBPluginDependency</string>
|
||||
<string>11.IBViewBoundsToFrameTransform</string>
|
||||
<string>11.ImportedFromIB2</string>
|
||||
<string>12.IBPluginDependency</string>
|
||||
<string>12.IBViewBoundsToFrameTransform</string>
|
||||
<string>12.ImportedFromIB2</string>
|
||||
<string>13.IBPluginDependency</string>
|
||||
<string>13.IBViewBoundsToFrameTransform</string>
|
||||
<string>13.ImportedFromIB2</string>
|
||||
<string>14.IBPluginDependency</string>
|
||||
<string>14.IBViewBoundsToFrameTransform</string>
|
||||
<string>14.ImportedFromIB2</string>
|
||||
<string>15.IBPluginDependency</string>
|
||||
<string>15.IBViewBoundsToFrameTransform</string>
|
||||
<string>15.ImportedFromIB2</string>
|
||||
<string>16.IBPluginDependency</string>
|
||||
<string>16.IBViewBoundsToFrameTransform</string>
|
||||
<string>16.ImportedFromIB2</string>
|
||||
<string>17.IBPluginDependency</string>
|
||||
<string>18.IBPluginDependency</string>
|
||||
@@ -1331,6 +1502,7 @@
|
||||
<string>4.IBPluginDependency</string>
|
||||
<string>4.ImportedFromIB2</string>
|
||||
<string>5.IBPluginDependency</string>
|
||||
<string>5.IBViewBoundsToFrameTransform</string>
|
||||
<string>5.ImportedFromIB2</string>
|
||||
<string>59.IBPluginDependency</string>
|
||||
<string>6.IBPluginDependency</string>
|
||||
@@ -1351,6 +1523,7 @@
|
||||
<string>69.IBViewBoundsToFrameTransform</string>
|
||||
<string>69.ImportedFromIB2</string>
|
||||
<string>7.IBPluginDependency</string>
|
||||
<string>7.IBViewBoundsToFrameTransform</string>
|
||||
<string>7.ImportedFromIB2</string>
|
||||
<string>70.IBPluginDependency</string>
|
||||
<string>74.IBPluginDependency</string>
|
||||
@@ -1360,9 +1533,24 @@
|
||||
<string>8.IBPluginDependency</string>
|
||||
<string>8.IBViewBoundsToFrameTransform</string>
|
||||
<string>8.ImportedFromIB2</string>
|
||||
<string>87.IBPluginDependency</string>
|
||||
<string>87.IBViewBoundsToFrameTransform</string>
|
||||
<string>87.ImportedFromIB2</string>
|
||||
<string>88.IBPluginDependency</string>
|
||||
<string>88.IBViewBoundsToFrameTransform</string>
|
||||
<string>88.ImportedFromIB2</string>
|
||||
<string>89.IBPluginDependency</string>
|
||||
<string>9.IBPluginDependency</string>
|
||||
<string>9.IBViewBoundsToFrameTransform</string>
|
||||
<string>9.ImportedFromIB2</string>
|
||||
<string>90.IBEditorWindowLastContentRect</string>
|
||||
<string>90.IBPluginDependency</string>
|
||||
<string>90.ImportedFromIB2</string>
|
||||
<string>92.IBPluginDependency</string>
|
||||
<string>92.ImportedFromIB2</string>
|
||||
<string>93.IBPluginDependency</string>
|
||||
<string>93.ImportedFromIB2</string>
|
||||
<string>94.IBPluginDependency</string>
|
||||
</object>
|
||||
<object class="NSMutableArray" key="dict.values">
|
||||
<bool key="EncodedWithXMLCoder">YES</bool>
|
||||
@@ -1375,23 +1563,41 @@
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABDloAAwxAAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBcAAAwr4AAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBYAAAwx0AAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABDWAAAwwYAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABC7gAAwwYAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABC6gAAwx8AAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>{{88, 591}, {392, 254}}</string>
|
||||
<string>{{413, 591}, {392, 254}}</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>{{88, 591}, {392, 254}}</string>
|
||||
<string>{{413, 591}, {392, 254}}</string>
|
||||
<boolean value="YES"/>
|
||||
<boolean value="YES"/>
|
||||
<string>{1.79769e+308, 1.79769e+308}</string>
|
||||
@@ -1421,11 +1627,14 @@
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBcAAAwpYAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBcAAAwfAAAA</bytes>
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBcAAAwlwAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
@@ -1449,6 +1658,9 @@
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBcAAAwuYAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
@@ -1463,10 +1675,29 @@
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBYAAAwx0AAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABCbAAAwx8AAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<object class="NSAffineTransform">
|
||||
<bytes key="NSTransformStruct">P4AAAL+AAABBYAAAwjwAAA</bytes>
|
||||
</object>
|
||||
<boolean value="YES"/>
|
||||
<string>{{213, 762}, {216, 43}}</string>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
<boolean value="YES"/>
|
||||
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
|
||||
</object>
|
||||
</object>
|
||||
<object class="NSMutableDictionary" key="unlocalizedProperties">
|
||||
@@ -1485,7 +1716,7 @@
|
||||
</object>
|
||||
</object>
|
||||
<nil key="sourceID"/>
|
||||
<int key="maxID">78</int>
|
||||
<int key="maxID">100</int>
|
||||
</object>
|
||||
<object class="IBClassDescriber" key="IBDocument.Classes">
|
||||
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
|
||||
|
||||
@@ -11,7 +11,6 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
@interface PyDupeGuru : PyDupeGuruBase
|
||||
//Scanning options
|
||||
- (void)setScanType:(NSNumber *)scan_type;
|
||||
- (void)setWordWeighting:(NSNumber *)words_are_weighted;
|
||||
- (void)setMatchSimilarWords:(NSNumber *)match_similar_words;
|
||||
@end
|
||||
|
||||
@@ -92,10 +92,13 @@ class DupeGuru(RegistrableApplication, Broadcaster):
|
||||
logging.warning("Exception on GetDisplayInfo for %s: %s", str(dupe.path), str(e))
|
||||
return ['---'] * len(self.data.COLUMNS)
|
||||
|
||||
def _create_file(self, path):
|
||||
# We add fs.Folder to fileclasses in case the file we're loading contains folder paths.
|
||||
return fs.get_file(path, self.directories.fileclasses + [fs.Folder])
|
||||
|
||||
def _get_file(self, str_path):
|
||||
path = Path(str_path)
|
||||
# We add fs.Folder to fileclasses in case the file we're loading contains folder paths.
|
||||
f = fs.get_file(path, self.directories.fileclasses + [fs.Folder])
|
||||
f = self._create_file(path)
|
||||
if f is None:
|
||||
return None
|
||||
try:
|
||||
|
||||
@@ -14,9 +14,6 @@ import time
|
||||
|
||||
Column = namedtuple('Column', 'attr display')
|
||||
|
||||
def format_path(p):
|
||||
return str(p[:-1])
|
||||
|
||||
def format_timestamp(t, delta):
|
||||
if delta:
|
||||
return format_time_decimal(t)
|
||||
|
||||
@@ -149,6 +149,10 @@ class File:
|
||||
def name(self):
|
||||
return self.path[-1]
|
||||
|
||||
@property
|
||||
def folder_path(self):
|
||||
return self.path[:-1]
|
||||
|
||||
|
||||
class Folder(File):
|
||||
"""A wrapper around a folder path.
|
||||
|
||||
@@ -328,15 +328,13 @@ class Results(Markable):
|
||||
def sort_dupes(self, key, asc=True, delta=False):
|
||||
if not self.__dupes:
|
||||
self.__get_dupe_list()
|
||||
self.__dupes.sort(key=lambda d: self.data.GetDupeSortKey(d, lambda: self.get_group_of_duplicate(d), key, delta))
|
||||
if not asc:
|
||||
self.__dupes.reverse()
|
||||
keyfunc = lambda d: self.data.GetDupeSortKey(d, lambda: self.get_group_of_duplicate(d), key, delta)
|
||||
self.__dupes.sort(key=keyfunc, reverse=not asc)
|
||||
self.__dupes_sort_descriptor = (key,asc,delta)
|
||||
|
||||
def sort_groups(self,key,asc=True):
|
||||
self.groups.sort(key=lambda g: self.data.GetGroupSortKey(g, key))
|
||||
if not asc:
|
||||
self.groups.reverse()
|
||||
keyfunc = lambda g: self.data.GetGroupSortKey(g, key)
|
||||
self.groups.sort(key=keyfunc, reverse=not asc)
|
||||
self.__groups_sort_descriptor = (key,asc)
|
||||
|
||||
#---Properties
|
||||
|
||||
@@ -17,6 +17,10 @@ from hscommon.trans import tr
|
||||
from . import engine
|
||||
from .ignore import IgnoreList
|
||||
|
||||
# It's quite ugly to have scan types from all editions all put in the same class, but because there's
|
||||
# there will be some nasty bugs popping up (ScanType is used in core when in should exclusively be
|
||||
# used in core_*). One day I'll clean this up.
|
||||
|
||||
class ScanType:
|
||||
Filename = 0
|
||||
Fields = 1
|
||||
@@ -25,6 +29,10 @@ class ScanType:
|
||||
Folders = 4
|
||||
Contents = 5
|
||||
ContentsAudio = 6
|
||||
|
||||
#PE
|
||||
FuzzyBlock = 10
|
||||
ExifTimestamp = 11
|
||||
|
||||
SCANNABLE_TAGS = ['track', 'artist', 'album', 'title', 'genre', 'year']
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
# data module for tests
|
||||
|
||||
from hscommon.util import format_size
|
||||
from ..data import format_path, cmp_value, Column
|
||||
from ..data import cmp_value, Column
|
||||
|
||||
COLUMNS = [
|
||||
Column('name', 'Filename'),
|
||||
Column('path', 'Directory'),
|
||||
Column('folder_path', 'Directory'),
|
||||
Column('size', 'Size (KB)'),
|
||||
Column('extension', 'Kind'),
|
||||
]
|
||||
@@ -29,7 +29,7 @@ def GetDisplayInfo(dupe, group, delta):
|
||||
size -= r.size
|
||||
return [
|
||||
dupe.name,
|
||||
format_path(dupe.path),
|
||||
str(dupe.folder_path),
|
||||
format_size(size, 0, 1, False),
|
||||
dupe.extension if hasattr(dupe, 'extension') else '---',
|
||||
]
|
||||
|
||||
@@ -26,6 +26,11 @@ class NamedObject(engine_test.NamedObject):
|
||||
|
||||
def __bool__(self):
|
||||
return False #Make sure that operations are made correctly when the bool value of files is false.
|
||||
|
||||
@property
|
||||
def folder_path(self):
|
||||
return self.path[:-1]
|
||||
|
||||
|
||||
# Returns a group set that looks like that:
|
||||
# "foo bar" (1)
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
from hscommon.util import format_time, format_size
|
||||
from hscommon.trans import tr as trbase
|
||||
from core.data import (format_path, format_timestamp, format_words, format_perc,
|
||||
format_dupe_count, cmp_value, Column)
|
||||
from core.data import (format_timestamp, format_words, format_perc, format_dupe_count, cmp_value,
|
||||
Column)
|
||||
|
||||
tr = lambda s: trbase(s, 'columns')
|
||||
|
||||
COLUMNS = [
|
||||
Column('name', tr("Filename")),
|
||||
Column('path', tr("Folder")),
|
||||
Column('folder_path', tr("Folder")),
|
||||
Column('size', tr("Size (MB)")),
|
||||
Column('duration', tr("Time")),
|
||||
Column('bitrate', tr("Bitrate")),
|
||||
@@ -63,7 +63,7 @@ def GetDisplayInfo(dupe, group, delta):
|
||||
dupe_count = len(group.dupes)
|
||||
return [
|
||||
dupe.name,
|
||||
format_path(dupe.path),
|
||||
str(dupe.folder_path),
|
||||
format_size(size, 2, 2, False),
|
||||
format_time(duration, with_hours=False),
|
||||
str(bitrate),
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
__version__ = '2.1.0'
|
||||
__version__ = '2.2.1'
|
||||
__appname__ = 'dupeGuru Picture Edition'
|
||||
@@ -14,37 +14,28 @@ import re
|
||||
from appscript import app, its, CommandError, ApplicationNotFoundError
|
||||
|
||||
from hscommon import io
|
||||
from hscommon.util import get_file_ext, remove_invalid_xml
|
||||
from hscommon.util import remove_invalid_xml
|
||||
from hscommon.path import Path
|
||||
from hscommon.cocoa.objcmin import NSUserDefaults, NSURL
|
||||
from hscommon.trans import tr
|
||||
|
||||
from core import fs
|
||||
from core import app_cocoa, directories
|
||||
from . import data, _block_osx
|
||||
from .photo import Photo as PhotoBase
|
||||
from .scanner import ScannerPE
|
||||
|
||||
IPHOTO_PATH = Path('iPhoto Library')
|
||||
|
||||
class Photo(fs.File):
|
||||
INITIAL_INFO = fs.File.INITIAL_INFO.copy()
|
||||
INITIAL_INFO.update({
|
||||
'dimensions': (0,0),
|
||||
})
|
||||
HANDLED_EXTS = {'png', 'jpg', 'jpeg', 'gif', 'psd', 'bmp', 'tiff', 'tif', 'nef', 'cr2'}
|
||||
class Photo(PhotoBase):
|
||||
HANDLED_EXTS = PhotoBase.HANDLED_EXTS.copy()
|
||||
HANDLED_EXTS.update({'psd', 'nef', 'cr2', 'orf'})
|
||||
|
||||
@classmethod
|
||||
def can_handle(cls, path):
|
||||
return fs.File.can_handle(path) and get_file_ext(path[-1]) in cls.HANDLED_EXTS
|
||||
def _plat_get_dimensions(self):
|
||||
return _block_osx.get_image_size(str(self.path))
|
||||
|
||||
def _read_info(self, field):
|
||||
fs.File._read_info(self, field)
|
||||
if field == 'dimensions':
|
||||
self.dimensions = _block_osx.get_image_size(str(self.path))
|
||||
|
||||
def get_blocks(self, block_count_per_side):
|
||||
def _plat_get_blocks(self, block_count_per_side, orientation):
|
||||
try:
|
||||
blocks = _block_osx.getblocks(str(self.path), block_count_per_side)
|
||||
blocks = _block_osx.getblocks(str(self.path), block_count_per_side, orientation)
|
||||
except Exception as e:
|
||||
raise IOError('The reading of "%s" failed with "%s"' % (str(self.path), str(e)))
|
||||
if not blocks:
|
||||
@@ -54,8 +45,8 @@ class Photo(fs.File):
|
||||
|
||||
class IPhoto(Photo):
|
||||
@property
|
||||
def display_path(self):
|
||||
return Path(('iPhoto Library', self.name))
|
||||
def display_folder_path(self):
|
||||
return IPHOTO_PATH
|
||||
|
||||
def get_iphoto_database_path():
|
||||
ud = NSUserDefaults.standardUserDefaults()
|
||||
@@ -175,11 +166,10 @@ class DupeGuruPE(app_cocoa.DupeGuru):
|
||||
else:
|
||||
app_cocoa.DupeGuru._do_delete_dupe(self, dupe, replace_with_hardlinks)
|
||||
|
||||
def _get_file(self, str_path):
|
||||
p = Path(str_path)
|
||||
if (self.directories.iphoto_libpath is not None) and (p in self.directories.iphoto_libpath[:-1]):
|
||||
return IPhoto(p)
|
||||
return app_cocoa.DupeGuru._get_file(self, str_path)
|
||||
def _create_file(self, path):
|
||||
if (self.directories.iphoto_libpath is not None) and (path in self.directories.iphoto_libpath[:-1]):
|
||||
return IPhoto(path)
|
||||
return app_cocoa.DupeGuru._create_file(self, path)
|
||||
|
||||
def copy_or_move(self, dupe, copy, destination, dest_type):
|
||||
if isinstance(dupe, IPhoto):
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
from hscommon.util import format_size
|
||||
from hscommon.trans import tr as trbase
|
||||
from core.data import format_path, format_timestamp, format_perc, format_dupe_count, cmp_value, Column
|
||||
from core.data import format_timestamp, format_perc, format_dupe_count, cmp_value, Column
|
||||
|
||||
tr = lambda s: trbase(s, 'columns')
|
||||
|
||||
@@ -17,7 +17,7 @@ def format_dimensions(dimensions):
|
||||
|
||||
COLUMNS = [
|
||||
Column('name', tr("Filename")),
|
||||
Column('path', tr("Folder")),
|
||||
Column('folder_path', tr("Folder")),
|
||||
Column('size', tr("Size (KB)")),
|
||||
Column('extension', tr("Kind")),
|
||||
Column('dimensions', tr("Dimensions")),
|
||||
@@ -26,6 +26,7 @@ COLUMNS = [
|
||||
Column('dupe_count', tr("Dupe Count")),
|
||||
]
|
||||
|
||||
FOLDER_COL = 1
|
||||
MATCHPERC_COL = 6
|
||||
DUPECOUNT_COL = 7
|
||||
DELTA_COLUMNS = {2, 4, 5}
|
||||
@@ -53,10 +54,10 @@ def GetDisplayInfo(dupe,group,delta=False):
|
||||
else:
|
||||
percentage = group.percentage
|
||||
dupe_count = len(group.dupes)
|
||||
dupe_path = getattr(dupe, 'display_path', dupe.path)
|
||||
dupe_folder_path = getattr(dupe, 'display_folder_path', dupe.folder_path)
|
||||
return [
|
||||
dupe.name,
|
||||
format_path(dupe_path),
|
||||
str(dupe_folder_path),
|
||||
format_size(size, 0, 1, False),
|
||||
dupe.extension,
|
||||
format_dimensions(dimensions),
|
||||
@@ -71,6 +72,9 @@ def GetDupeSortKey(dupe, get_group, key, delta):
|
||||
return m.percentage
|
||||
if key == DUPECOUNT_COL:
|
||||
return 0
|
||||
if key == FOLDER_COL:
|
||||
dupe_folder_path = getattr(dupe, 'display_folder_path', dupe.folder_path)
|
||||
return cmp_value(str(dupe_folder_path))
|
||||
r = cmp_value(getattr(dupe, COLUMNS[key].attr, ''))
|
||||
if delta and (key in DELTA_COLUMNS):
|
||||
ref_value = cmp_value(getattr(get_group().ref, COLUMNS[key].attr, ''))
|
||||
@@ -85,5 +89,8 @@ def GetGroupSortKey(group, key):
|
||||
return group.percentage
|
||||
if key == DUPECOUNT_COL:
|
||||
return len(group)
|
||||
if key == FOLDER_COL:
|
||||
dupe_folder_path = getattr(group.ref, 'display_folder_path', group.ref.folder_path)
|
||||
return cmp_value(str(dupe_folder_path))
|
||||
return cmp_value(getattr(group.ref, COLUMNS[key].attr, ''))
|
||||
|
||||
|
||||
335
core_pe/exif.py
Normal file
335
core_pe/exif.py
Normal file
@@ -0,0 +1,335 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2011-04-20
|
||||
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "BSD" 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/bsd_license
|
||||
|
||||
# Heavily based on http://topo.math.u-psud.fr/~bousch/exifdump.py by Thierry Bousch (Public Domain)
|
||||
|
||||
import logging
|
||||
|
||||
EXIF_TAGS = {
|
||||
0x0100: "ImageWidth",
|
||||
0x0101: "ImageLength",
|
||||
0x0102: "BitsPerSample",
|
||||
0x0103: "Compression",
|
||||
0x0106: "PhotometricInterpretation",
|
||||
0x010A: "FillOrder",
|
||||
0x010D: "DocumentName",
|
||||
0x010E: "ImageDescription",
|
||||
0x010F: "Make",
|
||||
0x0110: "Model",
|
||||
0x0111: "StripOffsets",
|
||||
0x0112: "Orientation",
|
||||
0x0115: "SamplesPerPixel",
|
||||
0x0116: "RowsPerStrip",
|
||||
0x0117: "StripByteCounts",
|
||||
0x011A: "XResolution",
|
||||
0x011B: "YResolution",
|
||||
0x011C: "PlanarConfiguration",
|
||||
0x0128: "ResolutionUnit",
|
||||
0x012D: "TransferFunction",
|
||||
0x0131: "Software",
|
||||
0x0132: "DateTime",
|
||||
0x013B: "Artist",
|
||||
0x013E: "WhitePoint",
|
||||
0x013F: "PrimaryChromaticities",
|
||||
0x0156: "TransferRange",
|
||||
0x0200: "JPEGProc",
|
||||
0x0201: "JPEGInterchangeFormat",
|
||||
0x0202: "JPEGInterchangeFormatLength",
|
||||
0x0211: "YCbCrCoefficients",
|
||||
0x0212: "YCbCrSubSampling",
|
||||
0x0213: "YCbCrPositioning",
|
||||
0x0214: "ReferenceBlackWhite",
|
||||
0x828F: "BatteryLevel",
|
||||
0x8298: "Copyright",
|
||||
0x829A: "ExposureTime",
|
||||
0x829D: "FNumber",
|
||||
0x83BB: "IPTC/NAA",
|
||||
0x8769: "ExifIFDPointer",
|
||||
0x8773: "InterColorProfile",
|
||||
0x8822: "ExposureProgram",
|
||||
0x8824: "SpectralSensitivity",
|
||||
0x8825: "GPSInfoIFDPointer",
|
||||
0x8827: "ISOSpeedRatings",
|
||||
0x8828: "OECF",
|
||||
0x9000: "ExifVersion",
|
||||
0x9003: "DateTimeOriginal",
|
||||
0x9004: "DateTimeDigitized",
|
||||
0x9101: "ComponentsConfiguration",
|
||||
0x9102: "CompressedBitsPerPixel",
|
||||
0x9201: "ShutterSpeedValue",
|
||||
0x9202: "ApertureValue",
|
||||
0x9203: "BrightnessValue",
|
||||
0x9204: "ExposureBiasValue",
|
||||
0x9205: "MaxApertureValue",
|
||||
0x9206: "SubjectDistance",
|
||||
0x9207: "MeteringMode",
|
||||
0x9208: "LightSource",
|
||||
0x9209: "Flash",
|
||||
0x920A: "FocalLength",
|
||||
0x9214: "SubjectArea",
|
||||
0x927C: "MakerNote",
|
||||
0x9286: "UserComment",
|
||||
0x9290: "SubSecTime",
|
||||
0x9291: "SubSecTimeOriginal",
|
||||
0x9292: "SubSecTimeDigitized",
|
||||
0xA000: "FlashPixVersion",
|
||||
0xA001: "ColorSpace",
|
||||
0xA002: "PixelXDimension",
|
||||
0xA003: "PixelYDimension",
|
||||
0xA004: "RelatedSoundFile",
|
||||
0xA005: "InteroperabilityIFDPointer",
|
||||
0xA20B: "FlashEnergy", # 0x920B in TIFF/EP
|
||||
0xA20C: "SpatialFrequencyResponse", # 0x920C - -
|
||||
0xA20E: "FocalPlaneXResolution", # 0x920E - -
|
||||
0xA20F: "FocalPlaneYResolution", # 0x920F - -
|
||||
0xA210: "FocalPlaneResolutionUnit", # 0x9210 - -
|
||||
0xA214: "SubjectLocation", # 0x9214 - -
|
||||
0xA215: "ExposureIndex", # 0x9215 - -
|
||||
0xA217: "SensingMethod", # 0x9217 - -
|
||||
0xA300: "FileSource",
|
||||
0xA301: "SceneType",
|
||||
0xA302: "CFAPattern", # 0x828E in TIFF/EP
|
||||
0xA401: "CustomRendered",
|
||||
0xA402: "ExposureMode",
|
||||
0xA403: "WhiteBalance",
|
||||
0xA404: "DigitalZoomRatio",
|
||||
0xA405: "FocalLengthIn35mmFilm",
|
||||
0xA406: "SceneCaptureType",
|
||||
0xA407: "GainControl",
|
||||
0xA408: "Contrast",
|
||||
0xA409: "Saturation",
|
||||
0xA40A: "Sharpness",
|
||||
0xA40B: "DeviceSettingDescription",
|
||||
0xA40C: "SubjectDistanceRange",
|
||||
0xA420: "ImageUniqueID",
|
||||
}
|
||||
|
||||
INTR_TAGS = {
|
||||
0x0001: "InteroperabilityIndex",
|
||||
0x0002: "InteroperabilityVersion",
|
||||
0x1000: "RelatedImageFileFormat",
|
||||
0x1001: "RelatedImageWidth",
|
||||
0x1002: "RelatedImageLength",
|
||||
}
|
||||
|
||||
GPS_TA0GS = {
|
||||
0x00: "GPSVersionID",
|
||||
0x01: "GPSLatitudeRef",
|
||||
0x02: "GPSLatitude",
|
||||
0x03: "GPSLongitudeRef",
|
||||
0x04: "GPSLongitude",
|
||||
0x05: "GPSAltitudeRef",
|
||||
0x06: "GPSAltitude",
|
||||
0x07: "GPSTimeStamp",
|
||||
0x08: "GPSSatellites",
|
||||
0x09: "GPSStatus",
|
||||
0x0A: "GPSMeasureMode",
|
||||
0x0B: "GPSDOP",
|
||||
0x0C: "GPSSpeedRef",
|
||||
0x0D: "GPSSpeed",
|
||||
0x0E: "GPSTrackRef",
|
||||
0x0F: "GPSTrack",
|
||||
0x10: "GPSImgDirectionRef",
|
||||
0x11: "GPSImgDirection",
|
||||
0x12: "GPSMapDatum",
|
||||
0x13: "GPSDestLatitudeRef",
|
||||
0x14: "GPSDestLatitude",
|
||||
0x15: "GPSDestLongitudeRef",
|
||||
0x16: "GPSDestLongitude",
|
||||
0x17: "GPSDestBearingRef",
|
||||
0x18: "GPSDestBearing",
|
||||
0x19: "GPSDestDistanceRef",
|
||||
0x1A: "GPSDestDistance",
|
||||
0x1B: "GPSProcessingMethod",
|
||||
0x1C: "GPSAreaInformation",
|
||||
0x1D: "GPSDateStamp",
|
||||
0x1E: "GPSDifferential"
|
||||
}
|
||||
|
||||
INTEL_ENDIAN = ord('I')
|
||||
MOTOROLA_ENDIAN = ord('M')
|
||||
|
||||
# About MAX_COUNT: It's possible to have corrupted exif tags where the entry count is way too high
|
||||
# and thus makes us loop, not endlessly, but for heck of a long time for nothing. Therefore, we put
|
||||
# an arbitrary limit on the entry count we'll allow ourselves to read and any IFD reporting more
|
||||
# entries than that will be considered corrupt.
|
||||
MAX_COUNT = 0xffff
|
||||
|
||||
def s2n_motorola(bytes):
|
||||
x = 0
|
||||
for c in bytes:
|
||||
x = (x << 8) | c
|
||||
return x
|
||||
|
||||
def s2n_intel(bytes):
|
||||
x = 0
|
||||
y = 0
|
||||
for c in bytes:
|
||||
x = x | (c << y)
|
||||
y = y + 8
|
||||
return x
|
||||
|
||||
class Fraction:
|
||||
def __init__(self, num, den):
|
||||
self.num = num
|
||||
self.den = den
|
||||
|
||||
def __repr__(self):
|
||||
return '%d/%d' % (self.num, self.den)
|
||||
|
||||
|
||||
class TIFF_file:
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.endian = data[0]
|
||||
self.s2nfunc = s2n_intel if self.endian == INTEL_ENDIAN else s2n_motorola
|
||||
|
||||
def s2n(self, offset, length, signed=0, debug=False):
|
||||
slice = self.data[offset:offset+length]
|
||||
val = self.s2nfunc(slice)
|
||||
# Sign extension ?
|
||||
if signed:
|
||||
msb = 1 << (8*length - 1)
|
||||
if val & msb:
|
||||
val = val - (msb << 1)
|
||||
if debug:
|
||||
logging.debug(self.endian)
|
||||
logging.debug("Slice for offset %d length %d: %r and value: %d", offset, length, slice, val)
|
||||
return val
|
||||
|
||||
def first_IFD(self):
|
||||
return self.s2n(4, 4)
|
||||
|
||||
def next_IFD(self, ifd):
|
||||
entries = self.s2n(ifd, 2)
|
||||
return self.s2n(ifd + 2 + 12 * entries, 4)
|
||||
|
||||
def list_IFDs(self):
|
||||
i = self.first_IFD()
|
||||
a = []
|
||||
while i:
|
||||
a.append(i)
|
||||
i = self.next_IFD(i)
|
||||
return a
|
||||
|
||||
def dump_IFD(self, ifd):
|
||||
entries = self.s2n(ifd, 2)
|
||||
logging.debug("Entries for IFD %d: %d", ifd, entries)
|
||||
if entries > MAX_COUNT:
|
||||
logging.debug("Probably corrupt. Aborting.")
|
||||
return []
|
||||
a = []
|
||||
for i in range(entries):
|
||||
entry = ifd + 2 + 12*i
|
||||
tag = self.s2n(entry, 2)
|
||||
type = self.s2n(entry+2, 2)
|
||||
if not 1 <= type <= 10:
|
||||
continue # not handled
|
||||
typelen = [ 1, 1, 2, 4, 8, 1, 1, 2, 4, 8 ] [type-1]
|
||||
count = self.s2n(entry+4, 4)
|
||||
if count > MAX_COUNT:
|
||||
logging.debug("Probably corrupt. Aborting.")
|
||||
return []
|
||||
offset = entry+8
|
||||
if count*typelen > 4:
|
||||
offset = self.s2n(offset, 4)
|
||||
if type == 2:
|
||||
# Special case: nul-terminated ASCII string
|
||||
values = str(self.data[offset:offset+count-1], encoding='latin-1')
|
||||
else:
|
||||
values = []
|
||||
signed = (type == 6 or type >= 8)
|
||||
for j in range(count):
|
||||
if type in {5, 10}:
|
||||
# The type is either 5 or 10
|
||||
value_j = Fraction(self.s2n(offset, 4, signed),
|
||||
self.s2n(offset+4, 4, signed))
|
||||
else:
|
||||
# Not a fraction
|
||||
value_j = self.s2n(offset, typelen, signed)
|
||||
values.append(value_j)
|
||||
offset = offset + typelen
|
||||
# Now "values" is either a string or an array
|
||||
a.append((tag,type,values))
|
||||
return a
|
||||
|
||||
def read_exif_header(fp):
|
||||
# If `fp`'s first bytes are not exif, it tries to find it in the next 4kb
|
||||
def isexif(data):
|
||||
return data[0:4] == b'\377\330\377\341' and data[6:10] == b'Exif'
|
||||
data = fp.read(12)
|
||||
if isexif(data):
|
||||
return data
|
||||
# ok, not exif, try to find it
|
||||
large_data = fp.read(4096)
|
||||
try:
|
||||
index = large_data.index(b'Exif')
|
||||
data = large_data[index-6:index+6]
|
||||
# large_data omits the first 12 bytes, and the index is at the middle of the header, so we
|
||||
# must seek index + 18
|
||||
fp.seek(index+18)
|
||||
return data
|
||||
except ValueError:
|
||||
raise ValueError("Not an Exif file")
|
||||
|
||||
def get_fields(fp):
|
||||
data = read_exif_header(fp)
|
||||
length = data[4] * 256 + data[5]
|
||||
logging.debug("Exif header length: %d bytes", length)
|
||||
data = fp.read(length-8)
|
||||
data_format = data[0]
|
||||
logging.debug("%s format", {INTEL_ENDIAN:'Intel', MOTOROLA_ENDIAN:'Motorola'}[data_format])
|
||||
T = TIFF_file(data)
|
||||
# There may be more than one IFD per file, but we only read the first one because others are
|
||||
# most likely thumbnails.
|
||||
main_IFD_offset = T.first_IFD()
|
||||
result = {}
|
||||
|
||||
def add_tag_to_result(tag, values):
|
||||
try:
|
||||
stag = EXIF_TAGS[tag]
|
||||
except KeyError:
|
||||
stag = '0x%04X' % tag
|
||||
if stag in result:
|
||||
return # don't overwrite data
|
||||
result[stag] = values
|
||||
|
||||
logging.debug("IFD at offset %d", main_IFD_offset)
|
||||
IFD = T.dump_IFD(main_IFD_offset)
|
||||
exif_off = gps_off = 0
|
||||
for tag, type, values in IFD:
|
||||
if tag == 0x8769:
|
||||
exif_off = values[0]
|
||||
continue
|
||||
if tag == 0x8825:
|
||||
gps_off = values[0]
|
||||
continue
|
||||
add_tag_to_result(tag, values)
|
||||
if exif_off:
|
||||
logging.debug("Exif SubIFD at offset %d:", exif_off)
|
||||
IFD = T.dump_IFD(exif_off)
|
||||
# Recent digital cameras have a little subdirectory
|
||||
# here, pointed to by tag 0xA005. Apparently, it's the
|
||||
# "Interoperability IFD", defined in Exif 2.1 and DCF.
|
||||
intr_off = 0
|
||||
for tag, type, values in IFD:
|
||||
if tag == 0xA005:
|
||||
intr_off = values[0]
|
||||
continue
|
||||
add_tag_to_result(tag, values)
|
||||
if intr_off:
|
||||
logging.debug("Exif Interoperability SubSubIFD at offset %d:", intr_off)
|
||||
IFD = T.dump_IFD(intr_off)
|
||||
for tag, type, values in IFD:
|
||||
add_tag_to_result(tag, values)
|
||||
if gps_off:
|
||||
logging.debug("GPS SubIFD at offset %d:", gps_off)
|
||||
IFD = T.dump_IFD(gps_off)
|
||||
for tag, type, values in IFD:
|
||||
add_tag_to_result(tag, values)
|
||||
return result
|
||||
37
core_pe/matchexif.py
Normal file
37
core_pe/matchexif.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2011-04-20
|
||||
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "BSD" 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/bsd_license
|
||||
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from itertools import combinations
|
||||
|
||||
from hscommon import io
|
||||
from hscommon.trans import tr
|
||||
|
||||
from core.engine import Match
|
||||
from . import exif
|
||||
|
||||
def getmatches(files, match_scaled, j):
|
||||
timestamp2pic = defaultdict(set)
|
||||
for picture in j.iter_with_progress(files, tr("Read EXIF of %d/%d pictures")):
|
||||
try:
|
||||
with io.open(picture.path, 'rb') as fp:
|
||||
exifdata = exif.get_fields(fp)
|
||||
timestamp = exifdata['DateTimeOriginal']
|
||||
timestamp2pic[timestamp].add(picture)
|
||||
except Exception:
|
||||
logging.info("Couldn't read EXIF of picture: %s", picture.path)
|
||||
if '0000:00:00 00:00:00' in timestamp2pic: # very likely false matches
|
||||
del timestamp2pic['0000:00:00 00:00:00']
|
||||
matches = []
|
||||
for pictures in timestamp2pic.values():
|
||||
for p1, p2 in combinations(pictures, 2):
|
||||
if (not match_scaled) and (p1.dimensions != p2.dimensions):
|
||||
continue
|
||||
matches.append(Match(p1, p2, 100))
|
||||
return matches
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define RADIANS( degrees ) ( degrees * M_PI / 180 )
|
||||
|
||||
static CFStringRef
|
||||
pystring2cfstring(PyObject *pystring)
|
||||
{
|
||||
@@ -149,12 +151,10 @@ static PyObject* block_osx_getblocks(PyObject *self, PyObject *args)
|
||||
CFURLRef image_url;
|
||||
CGImageSourceRef source;
|
||||
CGImageRef image;
|
||||
size_t width, height;
|
||||
int block_count, block_width, block_height, i;
|
||||
size_t width, height, image_width, image_height;
|
||||
int block_count, block_width, block_height, orientation, i;
|
||||
|
||||
width = 0;
|
||||
height = 0;
|
||||
if (!PyArg_ParseTuple(args, "Oi", &path, &block_count)) {
|
||||
if (!PyArg_ParseTuple(args, "Oii", &path, &block_count, &orientation)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -163,6 +163,10 @@ static PyObject* block_osx_getblocks(PyObject *self, PyObject *args)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((orientation > 8) || (orientation < 0)) {
|
||||
orientation = 0; // simplifies checks later since we can only have values in 0-8
|
||||
}
|
||||
|
||||
image_path = pystring2cfstring(path);
|
||||
if (image_path == NULL) {
|
||||
return PyErr_NoMemory();
|
||||
@@ -182,13 +186,61 @@ static PyObject* block_osx_getblocks(PyObject *self, PyObject *args)
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
|
||||
width = CGImageGetWidth(image);
|
||||
height = CGImageGetHeight(image);
|
||||
CGContextRef myContext = MyCreateBitmapContext(width, height);
|
||||
CGRect myBoundingBox = CGRectMake(0, 0, width, height);
|
||||
CGContextDrawImage(myContext, myBoundingBox, image);
|
||||
unsigned char *bitmapData = CGBitmapContextGetData(myContext);
|
||||
CGContextRelease(myContext);
|
||||
|
||||
width = image_width = CGImageGetWidth(image);
|
||||
height = image_height = CGImageGetHeight(image);
|
||||
if (orientation >= 5) {
|
||||
// orientations 5-8 rotate the photo sideways, so we have to swap width and height
|
||||
width = image_height;
|
||||
height = image_width;
|
||||
}
|
||||
|
||||
CGContextRef context = MyCreateBitmapContext(width, height);
|
||||
|
||||
if (orientation == 2) {
|
||||
// Flip X
|
||||
CGContextTranslateCTM(context, width, 0);
|
||||
CGContextScaleCTM(context, -1, 1);
|
||||
}
|
||||
else if (orientation == 3) {
|
||||
// Rot 180
|
||||
CGContextTranslateCTM(context, width, height);
|
||||
CGContextRotateCTM(context, RADIANS(180));
|
||||
}
|
||||
else if (orientation == 4) {
|
||||
// Flip Y
|
||||
CGContextTranslateCTM(context, 0, height);
|
||||
CGContextScaleCTM(context, 1, -1);
|
||||
}
|
||||
else if (orientation == 5) {
|
||||
// Flip X + Rot CW 90
|
||||
CGContextTranslateCTM(context, width, 0);
|
||||
CGContextScaleCTM(context, -1, 1);
|
||||
CGContextTranslateCTM(context, 0, height);
|
||||
CGContextRotateCTM(context, RADIANS(-90));
|
||||
}
|
||||
else if (orientation == 6) {
|
||||
// Rot CW 90
|
||||
CGContextTranslateCTM(context, 0, height);
|
||||
CGContextRotateCTM(context, RADIANS(-90));
|
||||
}
|
||||
else if (orientation == 7) {
|
||||
// Rot CCW 90 + Flip X
|
||||
CGContextTranslateCTM(context, width, 0);
|
||||
CGContextScaleCTM(context, -1, 1);
|
||||
CGContextTranslateCTM(context, width, 0);
|
||||
CGContextRotateCTM(context, RADIANS(90));
|
||||
}
|
||||
else if (orientation == 8) {
|
||||
// Rot CCW 90
|
||||
CGContextTranslateCTM(context, width, 0);
|
||||
CGContextRotateCTM(context, RADIANS(90));
|
||||
}
|
||||
CGRect myBoundingBox = CGRectMake(0, 0, image_width, image_height);
|
||||
CGContextDrawImage(context, myBoundingBox, image);
|
||||
unsigned char *bitmapData = CGBitmapContextGetData(context);
|
||||
CGContextRelease(context);
|
||||
|
||||
CGImageRelease(image);
|
||||
CFRelease(source);
|
||||
if (bitmapData == NULL) {
|
||||
|
||||
53
core_pe/photo.py
Normal file
53
core_pe/photo.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2011-05-29
|
||||
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
|
||||
#
|
||||
# This software is licensed under the "BSD" 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/bsd_license
|
||||
|
||||
from hscommon import io
|
||||
from hscommon.util import get_file_ext
|
||||
from core import fs
|
||||
from . import exif
|
||||
|
||||
class Photo(fs.File):
|
||||
INITIAL_INFO = fs.File.INITIAL_INFO.copy()
|
||||
INITIAL_INFO.update({
|
||||
'dimensions': (0,0),
|
||||
})
|
||||
# These extensions are supported on all platforms
|
||||
HANDLED_EXTS = {'png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff', 'tif'}
|
||||
|
||||
def _plat_get_dimensions(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _plat_get_blocks(self, block_count_per_side, orientation):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_orientation(self):
|
||||
if not hasattr(self, '_cached_orientation'):
|
||||
try:
|
||||
with io.open(self.path, 'rb') as fp:
|
||||
exifdata = exif.get_fields(fp)
|
||||
# the value is a list (probably one-sized) of ints
|
||||
orientations = exifdata['Orientation']
|
||||
self._cached_orientation = orientations[0]
|
||||
except Exception: # Couldn't read EXIF data, no transforms
|
||||
self._cached_orientation = 0
|
||||
return self._cached_orientation
|
||||
|
||||
@classmethod
|
||||
def can_handle(cls, path):
|
||||
return fs.File.can_handle(path) and get_file_ext(path[-1]) in cls.HANDLED_EXTS
|
||||
|
||||
def _read_info(self, field):
|
||||
fs.File._read_info(self, field)
|
||||
if field == 'dimensions':
|
||||
self.dimensions = self._plat_get_dimensions()
|
||||
if self._get_orientation() in {5, 6, 7, 8}:
|
||||
self.dimensions = (self.dimensions[1], self.dimensions[0])
|
||||
|
||||
def get_blocks(self, block_count_per_side):
|
||||
return self._plat_get_blocks(block_count_per_side, self._get_orientation())
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2009-10-18
|
||||
# Copyright 2011 Hardcoded Software (http://www.hardcoded.net)
|
||||
@@ -7,9 +6,9 @@
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
from core.scanner import Scanner
|
||||
from core.scanner import Scanner, ScanType
|
||||
|
||||
from . import matchbase
|
||||
from . import matchblock, matchexif
|
||||
from .cache import Cache
|
||||
|
||||
class ScannerPE(Scanner):
|
||||
@@ -18,7 +17,12 @@ class ScannerPE(Scanner):
|
||||
threshold = 75
|
||||
|
||||
def _getmatches(self, files, j):
|
||||
return matchbase.getmatches(files, self.cache_path, self.threshold, self.match_scaled, j)
|
||||
if self.scan_type == ScanType.FuzzyBlock:
|
||||
return matchblock.getmatches(files, self.cache_path, self.threshold, self.match_scaled, j)
|
||||
elif self.scan_type == ScanType.ExifTimestamp:
|
||||
return matchexif.getmatches(files, self.match_scaled, j)
|
||||
else:
|
||||
raise Exception("Invalid scan type")
|
||||
|
||||
def clear_picture_cache(self):
|
||||
cache = Cache(self.cache_path)
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
|
||||
from hscommon.util import format_size
|
||||
from hscommon.trans import tr as trbase
|
||||
from core.data import (format_path, format_timestamp, format_words, format_perc,
|
||||
format_dupe_count, cmp_value, Column)
|
||||
from core.data import (format_timestamp, format_words, format_perc, format_dupe_count, cmp_value,
|
||||
Column)
|
||||
|
||||
tr = lambda s: trbase(s, 'columns')
|
||||
|
||||
COLUMNS = [
|
||||
Column('name', tr("Filename")),
|
||||
Column('path', tr("Folder")),
|
||||
Column('folder_path', tr("Folder")),
|
||||
Column('size', tr("Size (KB)")),
|
||||
Column('extension', tr("Kind")),
|
||||
Column('mtime', tr("Modification")),
|
||||
@@ -46,7 +46,7 @@ def GetDisplayInfo(dupe, group, delta):
|
||||
dupe_count = len(group.dupes)
|
||||
return [
|
||||
dupe.name,
|
||||
format_path(dupe.path),
|
||||
str(dupe.folder_path),
|
||||
format_size(size, 0, 1, False),
|
||||
dupe.extension,
|
||||
format_timestamp(mtime, delta and m),
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
=== 2.2.1 (2011-06-15)
|
||||
|
||||
* Brought back the filter hardness label which disappeared in 2.2.0. [Mac OS X] (#164)
|
||||
* Added support for Olympus RAW files (.orf). [Mac OS X] (#159)
|
||||
* Fixed a bug causing the picture analysis to stall on some corrupt EXIF data.
|
||||
* Make the "Match scaled pictures" option apply to the new EXIF timestamp scan. (#162)
|
||||
* Fixed glitch in the sorting feature of the Folder column. (#161)
|
||||
|
||||
=== 2.2.0 (2011-06-02)
|
||||
|
||||
* Added the EXIF Timestamp scan type.
|
||||
* Correctly rotate pictures before analysis when EXIF orientation indicates it. (#154)
|
||||
* Make sure that saved results have the ".dupeguru" extension. [Linux] (#157)
|
||||
* Fixed a text coloring glitch in the results. (#156)
|
||||
* Fixed a refresh bug in directory panel. (#153)
|
||||
* Fixed a crash on saving results. (#149)
|
||||
* Improved reliability of the "Send to Trash" operation. [Linux]
|
||||
|
||||
=== 2.1.0 (2011-03-07)
|
||||
|
||||
* Improved scanning speed, especially for big scans with a lot of CPU cores.
|
||||
|
||||
@@ -32,9 +32,11 @@ Preferences
|
||||
|
||||
.. only:: edition_pe
|
||||
|
||||
**Filter Hardness:** The higher is this setting, the "harder" is the filter (In other words, the less results you get). Most pictures of the same quality match at 100% even if the format is different (PNG and JPG for example.). However, if you want to make a PNG match with a lower quality JPG, you will have to set the filer hardness to lower than 100. The default, 95, is a sweet spot.
|
||||
**Scan Type:** This option determines the type of scan that will be made on your pictures. The **Contents** scan type compares the actual contents of the pictures in a fuzzy way (making it possible to find not only exact duplicates, but also similar ones). The **EXIF Timestamp** scan type looks at the EXIF metadata of the picture (if it exists) and matches pictures that have the same one. It's much faster than the Contents scan. **Warning:** Modified pictures often keep the same EXIF timestamp, so watch out for false positives when you use that scan type.
|
||||
|
||||
**Filter Hardness:** *Contents scan type only.* The higher is this setting, the "harder" is the filter (In other words, the less results you get). Most pictures of the same quality match at 100% even if the format is different (PNG and JPG for example.). However, if you want to make a PNG match with a lower quality JPG, you will have to set the filer hardness to lower than 100. The default, 95, is a sweet spot.
|
||||
|
||||
**Match scaled pictures together:** If you check this box, pictures of different dimensions will be allowed in the same duplicate group.
|
||||
**Match pictures of different dimensions:** If you check this box, pictures of different dimensions will be allowed in the same duplicate group.
|
||||
|
||||
**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!
|
||||
|
||||
|
||||
@@ -32,7 +32,9 @@ Préférences
|
||||
|
||||
.. only:: edition_pe
|
||||
|
||||
**Seuil du filtre:** Plus il est élevé, plus les images doivent être similaires pour être considérées comme des doublons. Le défaut de 95% permet quelques petites différence, comme par exemple une différence de qualité ou bien une légère modification des couleurs.
|
||||
**Type de scan:** Détermine le type de scan qui sera fait sur vos images. Le type **Contenu** compare le contenu des images de façon "fuzzy", rendant possible de trouver non seulement les doublons exactes, mais aussi les similaires. Le type **EXIF Timestamp** compare les métadonnées EXIF des images (si existantes) et détermine si le "timestamp" (moment de prise de la photo) est pareille. C'est beaucoup plus rapide que le scan par Contenu. **Attention:** Les photos modifiées gardent souvent le même timestamp, donc faites attention aux faux doublons si vous utilisez cette méthode.
|
||||
|
||||
**Seuil du filtre:** *Scan par Contenu seulement.* Plus il est élevé, plus les images doivent être similaires pour être considérées comme des doublons. Le défaut de 95% permet quelques petites différence, comme par exemple une différence de qualité ou bien une légère modification des couleurs.
|
||||
|
||||
**Comparer les images de tailles différentes:** Le nom dit tout. Sans cette option, les images de tailles différentes ne sont pas comparées.
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ class DirectoriesDialog(QMainWindow):
|
||||
|
||||
def loadResultsTriggered(self):
|
||||
title = trmsg("SelectResultToLoadMsg")
|
||||
files = tr("dupeGuru Results (*.dupeguru)")
|
||||
files = ';;'.join([tr("dupeGuru Results (*.dupeguru)"), tr("All Files (*.*)")])
|
||||
destination = QFileDialog.getOpenFileName(self, title, '', files)
|
||||
if destination:
|
||||
self.app.load_from(destination)
|
||||
|
||||
@@ -25,6 +25,19 @@ class PreferencesDialogBase(QDialog):
|
||||
self.buttonBox.accepted.connect(self.accept)
|
||||
self.buttonBox.rejected.connect(self.reject)
|
||||
|
||||
def _setupScanTypeBox(self, labels):
|
||||
self.scanTypeHLayout = QHBoxLayout()
|
||||
self.scanTypeLabel = QLabel(self)
|
||||
self.scanTypeLabel.setText(tr("Scan Type:"))
|
||||
self.scanTypeLabel.setMinimumSize(QSize(100, 0))
|
||||
self.scanTypeLabel.setMaximumSize(QSize(100, 16777215))
|
||||
self.scanTypeHLayout.addWidget(self.scanTypeLabel)
|
||||
self.scanTypeComboBox = QComboBox(self)
|
||||
for label in labels:
|
||||
self.scanTypeComboBox.addItem(label)
|
||||
self.scanTypeHLayout.addWidget(self.scanTypeComboBox)
|
||||
self.widgetsVLayout.addLayout(self.scanTypeHLayout)
|
||||
|
||||
def _setupFilterHardnessBox(self):
|
||||
self.filterHardnessHLayout = QHBoxLayout()
|
||||
self.filterHardnessLabel = QLabel(self)
|
||||
|
||||
@@ -165,7 +165,7 @@ class ResultWindow(QMainWindow):
|
||||
self.actionActions.setMenu(actionMenu)
|
||||
|
||||
def _setupUi(self):
|
||||
self.setWindowTitle(tr("dupeGuru Results"))
|
||||
self.setWindowTitle(tr("{} Results").format(self.app.NAME))
|
||||
self.resize(630, 514)
|
||||
self.centralwidget = QWidget(self)
|
||||
self.verticalLayout_2 = QVBoxLayout(self.centralwidget)
|
||||
@@ -335,6 +335,8 @@ class ResultWindow(QMainWindow):
|
||||
files = tr("dupeGuru Results (*.dupeguru)")
|
||||
destination = QFileDialog.getSaveFileName(self, title, '', files)
|
||||
if destination:
|
||||
if not destination.endswith('.dupeguru'):
|
||||
destination = '{}.dupeguru'.format(destination)
|
||||
self.app.save_as(destination)
|
||||
self.app.recentResults.insertItem(destination)
|
||||
|
||||
|
||||
@@ -235,6 +235,10 @@
|
||||
<source>dupeGuru Results (*.dupeguru)</source>
|
||||
<translation>Résultats dupeGuru (*.dupeguru)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>All Files (*.*)</source>
|
||||
<translation>Tout les fichiers (*.*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Start a new scan</source>
|
||||
<translation>Commencer un nouveau scan</translation>
|
||||
@@ -388,8 +392,8 @@
|
||||
<translation>Réinitialiser</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>dupeGuru Results</source>
|
||||
<translation>dupeGuru (Résultats)</translation>
|
||||
<source>{} Results</source>
|
||||
<translation>{} (Résultats)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete duplicates</source>
|
||||
@@ -526,7 +530,7 @@
|
||||
<translation>Année</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Match scaled pictures together</source>
|
||||
<source>Match pictures of different dimensions</source>
|
||||
<translation>Comparer les images de tailles différentes</translation>
|
||||
</message>
|
||||
<message>
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
# http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
import sys
|
||||
from PyQt4.QtCore import SIGNAL, QSize
|
||||
from PyQt4.QtGui import (QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QSizePolicy, QSpacerItem,
|
||||
QWidget, QApplication)
|
||||
from PyQt4.QtCore import QSize
|
||||
from PyQt4.QtGui import (QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QSpacerItem, QWidget,
|
||||
QApplication)
|
||||
|
||||
from hscommon.trans import tr
|
||||
from core.scanner import ScanType
|
||||
@@ -30,24 +30,12 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
def __init__(self, parent, app):
|
||||
PreferencesDialogBase.__init__(self, parent, app)
|
||||
|
||||
self.connect(self.scanTypeComboBox, SIGNAL('currentIndexChanged(int)'), self.scanTypeChanged)
|
||||
self.scanTypeComboBox.currentIndexChanged[int].connect(self.scanTypeChanged)
|
||||
|
||||
def _setupPreferenceWidgets(self):
|
||||
self.horizontalLayout = QHBoxLayout()
|
||||
self.label_2 = QLabel(self)
|
||||
self.label_2.setText(tr("Scan Type:"))
|
||||
self.label_2.setMinimumSize(QSize(100, 0))
|
||||
self.label_2.setMaximumSize(QSize(100, 16777215))
|
||||
self.horizontalLayout.addWidget(self.label_2)
|
||||
self.scanTypeComboBox = QComboBox(self)
|
||||
self.scanTypeComboBox.addItem(tr("Filename"))
|
||||
self.scanTypeComboBox.addItem(tr("Filename - Fields"))
|
||||
self.scanTypeComboBox.addItem(tr("Filename - Fields (No Order)"))
|
||||
self.scanTypeComboBox.addItem(tr("Tags"))
|
||||
self.scanTypeComboBox.addItem(tr("Contents"))
|
||||
self.scanTypeComboBox.addItem(tr("Audio Contents"))
|
||||
self.horizontalLayout.addWidget(self.scanTypeComboBox)
|
||||
self.widgetsVLayout.addLayout(self.horizontalLayout)
|
||||
scanTypeLabels = [tr(s) for s in ["Filename", "Filename - Fields",
|
||||
"Filename - Fields (No Order)", "Tags", "Contents", "Audio Contents"]]
|
||||
self._setupScanTypeBox(scanTypeLabels)
|
||||
self._setupFilterHardnessBox()
|
||||
self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
|
||||
self.widget = QWidget(self)
|
||||
|
||||
69
qt/pe/app.py
69
qt/pe/app.py
@@ -9,12 +9,10 @@
|
||||
import os.path as op
|
||||
import logging
|
||||
|
||||
from PyQt4.QtGui import QImage, QImageReader
|
||||
from PyQt4.QtGui import QImage, QImageReader, QTransform
|
||||
|
||||
from hscommon.util import get_file_ext
|
||||
|
||||
from core import fs
|
||||
from core_pe import data as data_pe, __appname__
|
||||
from core_pe.photo import Photo as PhotoBase
|
||||
from core_pe.scanner import ScannerPE
|
||||
|
||||
from ..base.app import DupeGuru as DupeGuruBase
|
||||
@@ -24,34 +22,46 @@ from .result_window import ResultWindow
|
||||
from .preferences import Preferences
|
||||
from .preferences_dialog import PreferencesDialog
|
||||
|
||||
class File(fs.File):
|
||||
INITIAL_INFO = fs.File.INITIAL_INFO.copy()
|
||||
INITIAL_INFO.update({
|
||||
'dimensions': (0,0),
|
||||
})
|
||||
HANDLED_EXTS = set(['png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff', 'tif'])
|
||||
class File(PhotoBase):
|
||||
def _plat_get_dimensions(self):
|
||||
try:
|
||||
ir = QImageReader(str(self.path))
|
||||
size = ir.size()
|
||||
if size.isValid():
|
||||
return (size.width(), size.height())
|
||||
else:
|
||||
return (0, 0)
|
||||
except EnvironmentError:
|
||||
logging.warning("Could not read image '%s'", str(self.path))
|
||||
return (0, 0)
|
||||
|
||||
@classmethod
|
||||
def can_handle(cls, path):
|
||||
return fs.File.can_handle(path) and get_file_ext(path[-1]) in cls.HANDLED_EXTS
|
||||
|
||||
def _read_info(self, field):
|
||||
fs.File._read_info(self, field)
|
||||
if field == 'dimensions':
|
||||
try:
|
||||
ir = QImageReader(str(self.path))
|
||||
size = ir.size()
|
||||
if size.isValid():
|
||||
self.dimensions = (size.width(), size.height())
|
||||
else:
|
||||
self.dimensions = (0, 0)
|
||||
except EnvironmentError:
|
||||
self.dimensions = (0, 0)
|
||||
logging.warning("Could not read image '%s'", str(self.path))
|
||||
|
||||
def get_blocks(self, block_count_per_side):
|
||||
def _plat_get_blocks(self, block_count_per_side, orientation):
|
||||
image = QImage(str(self.path))
|
||||
image = image.convertToFormat(QImage.Format_RGB888)
|
||||
# MYSTERY TO SOLVE: For reasons I cannot explain, orientations 5 and 7 don't work for
|
||||
# duplicate scanning. The transforms seems to work fine (if I try to save the image after
|
||||
# the transform, we see that the image has been correctly flipped and rotated), but the
|
||||
# analysis part yields wrong blocks. I spent enought time with this feature, so I'll leave
|
||||
# like that for now. (by the way, orientations 5 and 7 work fine under Cocoa)
|
||||
if 2 <= orientation <= 8:
|
||||
t = QTransform()
|
||||
if orientation == 2:
|
||||
t.scale(-1, 1)
|
||||
elif orientation == 3:
|
||||
t.rotate(180)
|
||||
elif orientation == 4:
|
||||
t.scale(1, -1)
|
||||
elif orientation == 5:
|
||||
t.scale(-1, 1)
|
||||
t.rotate(90)
|
||||
elif orientation == 6:
|
||||
t.rotate(90)
|
||||
elif orientation == 7:
|
||||
t.scale(-1, 1)
|
||||
t.rotate(270)
|
||||
elif orientation == 8:
|
||||
t.rotate(270)
|
||||
image = image.transformed(t)
|
||||
return getblocks(image, block_count_per_side)
|
||||
|
||||
|
||||
@@ -71,6 +81,7 @@ class DupeGuru(DupeGuruBase):
|
||||
|
||||
def _update_options(self):
|
||||
DupeGuruBase._update_options(self)
|
||||
self.scanner.scan_type = self.prefs.scan_type
|
||||
self.scanner.match_scaled = self.prefs.match_scaled
|
||||
self.scanner.threshold = self.prefs.filter_hardness
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
from PyQt4.QtCore import QSettings, QVariant
|
||||
from core.scanner import ScanType
|
||||
|
||||
from ..base.preferences import Preferences as PreferencesBase
|
||||
|
||||
@@ -24,12 +24,17 @@ class Preferences(PreferencesBase):
|
||||
]
|
||||
|
||||
def _load_specific(self, settings):
|
||||
self.match_scaled = self.get_value('MatchScaled', self.match_scaled)
|
||||
get = self.get_value
|
||||
self.scan_type = get('ScanType', self.scan_type)
|
||||
self.match_scaled = get('MatchScaled', self.match_scaled)
|
||||
|
||||
def _reset_specific(self):
|
||||
self.scan_type = ScanType.FuzzyBlock
|
||||
self.filter_hardness = 95
|
||||
self.match_scaled = False
|
||||
|
||||
def _save_specific(self, settings):
|
||||
self.set_value('MatchScaled', self.match_scaled)
|
||||
set_ = self.set_value
|
||||
set_('ScanType', self.scan_type)
|
||||
set_('MatchScaled', self.match_scaled)
|
||||
|
||||
|
||||
@@ -10,15 +10,29 @@ import sys
|
||||
from PyQt4.QtGui import QLabel, QApplication
|
||||
|
||||
from hscommon.trans import tr
|
||||
from core.scanner import ScanType
|
||||
|
||||
from ..base.preferences_dialog import PreferencesDialogBase
|
||||
from . import preferences
|
||||
|
||||
|
||||
SCAN_TYPE_ORDER = [
|
||||
ScanType.FuzzyBlock,
|
||||
ScanType.ExifTimestamp,
|
||||
]
|
||||
|
||||
class PreferencesDialog(PreferencesDialogBase):
|
||||
def __init__(self, parent, app):
|
||||
PreferencesDialogBase.__init__(self, parent, app)
|
||||
|
||||
self.scanTypeComboBox.currentIndexChanged[int].connect(self.scanTypeChanged)
|
||||
|
||||
def _setupPreferenceWidgets(self):
|
||||
scanTypeLabels = [tr(s) for s in ["Contents", "EXIF Timestamp"]]
|
||||
self._setupScanTypeBox(scanTypeLabels)
|
||||
self._setupFilterHardnessBox()
|
||||
self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
|
||||
self._setupAddCheckbox('matchScaledBox', tr("Match scaled pictures together"))
|
||||
self._setupAddCheckbox('matchScaledBox', tr("Match pictures of different dimensions"))
|
||||
self.widgetsVLayout.addWidget(self.matchScaledBox)
|
||||
self._setupAddCheckbox('mixFileKindBox', tr("Can mix file kind"))
|
||||
self.widgetsVLayout.addWidget(self.mixFileKindBox)
|
||||
@@ -33,14 +47,23 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
self._setupBottomPart()
|
||||
|
||||
def _load(self, prefs, setchecked):
|
||||
scan_type_index = SCAN_TYPE_ORDER.index(prefs.scan_type)
|
||||
self.scanTypeComboBox.setCurrentIndex(scan_type_index)
|
||||
setchecked(self.matchScaledBox, prefs.match_scaled)
|
||||
|
||||
def _save(self, prefs, ischecked):
|
||||
prefs.scan_type = SCAN_TYPE_ORDER[self.scanTypeComboBox.currentIndex()]
|
||||
prefs.match_scaled = ischecked(self.matchScaledBox)
|
||||
|
||||
def resetToDefaults(self):
|
||||
self.load(preferences.Preferences())
|
||||
|
||||
#--- Events
|
||||
def scanTypeChanged(self, index):
|
||||
scan_type = SCAN_TYPE_ORDER[self.scanTypeComboBox.currentIndex()]
|
||||
fuzzy_scan = scan_type == ScanType.FuzzyBlock
|
||||
self.filterHardnessSlider.setEnabled(fuzzy_scan)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from ..testapp import TestApp
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
import sys
|
||||
from PyQt4.QtCore import QSize
|
||||
from PyQt4.QtGui import (QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QSizePolicy, QSpacerItem,
|
||||
QWidget, QLineEdit, QApplication)
|
||||
from PyQt4.QtGui import (QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QSpacerItem, QWidget,
|
||||
QLineEdit, QApplication)
|
||||
|
||||
from hscommon.trans import tr
|
||||
from hscommon.util import tryint
|
||||
@@ -32,17 +32,8 @@ class PreferencesDialog(PreferencesDialogBase):
|
||||
self.scanTypeComboBox.currentIndexChanged[int].connect(self.scanTypeChanged)
|
||||
|
||||
def _setupPreferenceWidgets(self):
|
||||
self.horizontalLayout = QHBoxLayout()
|
||||
self.label_2 = QLabel(self)
|
||||
self.label_2.setText(tr("Scan Type:"))
|
||||
self.label_2.setMinimumSize(QSize(100, 0))
|
||||
self.label_2.setMaximumSize(QSize(100, 16777215))
|
||||
self.horizontalLayout.addWidget(self.label_2)
|
||||
self.scanTypeComboBox = QComboBox(self)
|
||||
for label in [tr("Filename"), tr("Contents"), tr("Folders")]:
|
||||
self.scanTypeComboBox.addItem(label)
|
||||
self.horizontalLayout.addWidget(self.scanTypeComboBox)
|
||||
self.widgetsVLayout.addLayout(self.horizontalLayout)
|
||||
scanTypeLabels = [tr(s) for s in ["Filename", "Contents", "Folders"]]
|
||||
self._setupScanTypeBox(scanTypeLabels)
|
||||
self._setupFilterHardnessBox()
|
||||
self.widgetsVLayout.addLayout(self.filterHardnessHLayout)
|
||||
self.widget = QWidget(self)
|
||||
|
||||
Reference in New Issue
Block a user