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

Compare commits

..

112 Commits

Author SHA1 Message Date
Virgil Dupras
7b0d3ea8ac me v5.10.4 2010-12-30 14:55:13 +01:00
Virgil Dupras
1c88b6bb26 Tweaked the wording of the fairware pop up. 2010-12-30 13:07:04 +01:00
Virgil Dupras
e5e8e5d908 Replaced the about box with one that supports fairware registering. 2010-12-30 13:00:44 +01:00
Virgil Dupras
92fadd26b7 [#120 state:fixed] Fixed dangling bogus results after cancelled scan. 2010-12-30 10:24:37 +01:00
Virgil Dupras
45d783ac43 Removed CallLogger-related code in app_test. This code was duplicating the code that was recently added to hscommon.testutil. 2010-12-30 10:00:29 +01:00
Virgil Dupras
ea9e76e7ae Removed conftest.py modules in tests, which aren't required anymore with pytest v2.0 2010-12-30 09:47:22 +01:00
Virgil Dupras
28426c0e91 [#121 state:fixed] Catch HTTPException in Fairware fetching. 2010-12-30 09:35:45 +01:00
Virgil Dupras
3a9f51b600 [#122 state:fixed] Fixed crash on scanning when file is being deleted during the scan. 2010-12-29 15:41:12 +01:00
Virgil Dupras
f1b4db368e [#123 state:fixed] Updated codebase to use hsaudiotag v1.1.0 (which fixed the AIFF bug) and made it use the new auto.File wrapper. 2010-12-29 13:17:30 +01:00
Virgil Dupras
95efac187b Updated hscommon and adapted to changes in hscommon.gui.table.Table.refresh(). 2010-11-24 16:12:10 +01:00
Virgil Dupras
6770d22438 Updated hscommon subrepo. 2010-11-22 10:06:42 +01:00
Virgil Dupras
4ce97613c4 Added tag me5.10.3 for changeset ca93352ce351 2010-11-21 17:56:37 +01:00
Virgil Dupras
030eb8eb6e Fixed debian packaging 2010-11-21 07:49:38 -08:00
Virgil Dupras
c9da8e26e6 Fixed crash caused by outdated hsgui. Also, fixed app_test, which was also outdated. 2010-11-21 16:45:02 +01:00
Virgil Dupras
7ddf9772df v5.10.3 2010-11-21 16:25:16 +01:00
Virgil Dupras
0382ad1534 Adapted to the job-related code moving to the 'jobprogress' package. 2010-11-20 12:42:15 +01:00
Virgil Dupras
1b6e1369a0 Tranformed PyQt's license warning into a licensing note
--HG--
rename : qt/WARNING => qt/ABOUT_LICENSE
2010-11-13 14:37:20 +01:00
Virgil Dupras
835050c337 Added tag pe1.11.2 for changeset 154c8cb6f018 2010-10-07 12:44:04 +02:00
Virgil Dupras
ca6a42e6eb pe 1.11.2 2010-10-07 11:34:29 +02:00
Virgil Dupras
a2e4d893ac Added tag me5.10.2 for changeset f9cae82a0752 2010-10-06 12:44:43 +02:00
Virgil Dupras
657520b0b3 me5.10.2 2010-10-06 11:43:37 +02:00
Virgil Dupras
ea4b87895c Fixed typo in fairware reminder. 2010-10-06 11:37:26 +02:00
Virgil Dupras
19db500a19 Updated cocoalib subrepo. 2010-10-06 10:47:05 +02:00
Virgil Dupras
1366cfd478 Added a "Don't contribute" feedback dialog to fairware reminder. 2010-10-06 10:45:27 +02:00
Virgil Dupras
56a6df1f68 Added tag se2.12.2 for changeset 22239f94589b 2010-10-05 15:56:21 +02:00
Virgil Dupras
a1b35a8abf Fixed reg demo dialog for windows. 2010-10-05 13:55:26 +01:00
Virgil Dupras
8a8a181186 se 2.12.2 2010-10-05 03:50:28 -07:00
Virgil Dupras
463a551f7d Fixed debian packaging. 2010-10-05 02:02:02 -07:00
Virgil Dupras
fc613fb325 Fixed Qt packaging under Windows. 2010-10-05 09:48:07 +01:00
Virgil Dupras
4517bea664 Moved the start.py file directly in qt run template instead of using this subprocess thingy. Much easier for packaging. 2010-10-05 10:22:02 +02:00
Virgil Dupras
81dcfbe6ae Use sys.platform instead of platform.system(). The latter somes crashes with "Interrupted system call". 2010-10-05 10:03:56 +02:00
Virgil Dupras
fa8e64d04a Fixed delta values color which were mixed up. 2010-10-05 09:54:52 +02:00
Virgil Dupras
562123b219 Fixed qt run template so that the current environment is sent to the new python process. 2010-10-05 00:36:20 -07:00
Virgil Dupras
b217309618 Replaced the use of runpy for running Qt by a simple subprocess call. runpy would cause weird QTimer warnings. 2010-10-05 09:27:32 +02:00
Virgil Dupras
357a02c74b Use QTimer.singleShot() for nag window showing instead of a complicated scheme like the old one. 2010-10-05 08:44:32 +02:00
Virgil Dupras
508eeffa6e Fixed register button that linked to the wrong method. 2010-10-05 08:20:53 +02:00
Virgil Dupras
31555aa473 Rather than having a run.py file that checks build config at runtime, this file is generated at build time, making it easier to package it. 2010-10-04 15:42:38 +02:00
Virgil Dupras
d2f968def7 Removed .ui files and made the UI setup "by hand". ui files cause more problems than they solve (UI designer is limited in what it can do). 2010-10-04 15:29:00 +02:00
Virgil Dupras
d574bc611b [#108 state:fixed] Fixed column mess after the earlier removal of the ctime col. 2010-10-04 10:01:52 +02:00
Virgil Dupras
a50a3b0123 [#106 state:fixed] Wrapped getJobDesc in a try except in case it isn't defined yet. 2010-10-04 09:40:46 +02:00
Virgil Dupras
5b6891dd45 se v2.12.1 me v5.10.1 pe v1.11.1 2010-09-30 12:35:40 +02:00
Virgil Dupras
4886982d43 Re-licensed to BSD 2010-09-30 12:17:41 +02:00
Virgil Dupras
7360f57beb Converted registration system to Fairware. 2010-09-29 16:49:50 +02:00
Virgil Dupras
491279b7a8 Added tag before-fairware for changeset 96b6aee66839 2010-09-29 16:11:52 +02:00
Virgil Dupras
05b79f81af Added tag pe1.11.0 for changeset b07ac1398703 2010-09-27 15:13:42 +02:00
Virgil Dupras
96ef2f2dd3 Added tag me5.10.0 for changeset d3fe0d0dcda1 2010-09-27 15:13:36 +02:00
Virgil Dupras
2542af17b6 Adjusted default column widths so it fits better with the revamped UI. 2010-09-27 12:25:31 +02:00
Virgil Dupras
c86bc649ff pe 1.11.0 and me 5.10.0. 2010-09-27 11:56:02 +02:00
Virgil Dupras
4b8e48ed88 Added tag se2.12.0 for changeset dbfee3ee2fa5 2010-09-26 14:26:06 +02:00
Virgil Dupras
a1addfd416 Fixed typo in changelog. 2010-09-26 14:25:53 +02:00
Virgil Dupras
a1a57d8933 Adjusted default column widths to fit better with UI revamp. 2010-09-26 12:50:45 +02:00
Virgil Dupras
864970b860 se2.12.0 2010-09-26 12:33:39 +02:00
Virgil Dupras
a056be0842 Fixed UI glitch introduced by the move from outline to table for results (the selected row would not be kept visible after refreshes). 2010-09-26 12:09:50 +02:00
Virgil Dupras
c672e75739 Updated subrepo. 2010-09-26 02:20:18 -07:00
Virgil Dupras
7b5dd3f964 Adjusted the height of the pref pane in SE under Linux. 2010-09-26 02:17:29 -07:00
Virgil Dupras
a6072f608b [#105 state:fixed] Allow multiple selection in Add Directory. 2010-09-25 16:12:20 +02:00
Virgil Dupras
06462c65a5 Updated help files. 2010-09-25 15:49:19 +02:00
Virgil Dupras
359f9c0680 [#92 state:fixed] Added an action to delete duplicates and then create hardlinks to group ref. 2010-09-25 15:37:18 +02:00
Virgil Dupras
01db7c4948 Fixed a py3k-induced bug when drag & dropping directories in the directories panel. 2010-09-25 15:34:42 +02:00
Virgil Dupras
f67f14a78d Fixed compilation warning under cocoa. 2010-09-25 12:35:51 +02:00
Virgil Dupras
0a64d653e1 [#92 state:fixed] Added an option to ignore duplicates hardlinking to the same file. 2010-09-25 12:28:34 +02:00
Virgil Dupras
456a835285 Made the main window under cocoa a little cuter. 2010-09-24 16:01:38 +02:00
Virgil Dupras
0d8ed92a68 Converted the result tree into a result table.
--HG--
rename : cocoa/base/PyResultTree.h => cocoa/base/PyResultTable.h
rename : cocoa/base/ResultOutline.h => cocoa/base/ResultTable.h
rename : cocoa/base/ResultOutline.m => cocoa/base/ResultTable.m
rename : core/gui/result_tree.py => core/gui/result_table.py
2010-09-24 15:48:59 +02:00
Virgil Dupras
9bd093a03c [#106 state:fixed] I couldn't find the root cause of the problem, but I wrapped it anyway... 2010-09-24 09:56:08 +02:00
Virgil Dupras
361d4698a9 Added tag se2.11.1 for changeset 9735a5218d2b 2010-08-26 13:39:42 +02:00
Virgil Dupras
b342b15011 se v2.11.1 2010-08-26 13:03:14 +02:00
Virgil Dupras
95638a3a80 Added tag me5.9.1 for changeset 95b3a4b564c6 2010-08-26 12:58:43 +02:00
Virgil Dupras
2204fe3355 Fixed help file under Cocoa, which strangely stopped working. 2010-08-24 02:34:27 -07:00
Virgil Dupras
abcd774c9d me5.9.1 2010-08-24 10:46:47 +02:00
Virgil Dupras
ee209f8f88 Added tag pe1.10.0 for changeset 618a7365457d 2010-08-21 18:27:27 +02:00
Virgil Dupras
b1f2e1c191 Fixed debian packaging for PE. 2010-08-21 08:26:56 -07:00
Virgil Dupras
33f372f6c6 Fixed the building process of the block module for Qt. 2010-08-21 16:04:23 +01:00
Virgil Dupras
8e5c2a8875 pe v1.10.0 2010-08-21 16:44:50 +02:00
Virgil Dupras
36f3638ae4 [#104 state:fixed] Fixed str/bytes mixup in HTML export. 2010-08-21 16:34:35 +02:00
Virgil Dupras
d10210011f Added tag me5.9.0 for changeset b56fe4dd8c95 2010-08-20 12:19:06 +02:00
Virgil Dupras
e867840d81 Fixed debian packaging for ME. 2010-08-20 02:29:51 -07:00
Virgil Dupras
fb7e3189a8 me v5.9.0 2010-08-20 09:51:30 +02:00
Virgil Dupras
5733c0143b With PyQt 4.7.5's new from_imports option, sys.path hackage is not required anymore. 2010-08-20 09:48:16 +02:00
Virgil Dupras
ac4881f231 Updated py2app workarounds for ME again. 2010-08-18 12:01:10 +02:00
Virgil Dupras
939efd7dab Updated py2app workarounds for ME. 2010-08-18 11:37:18 +02:00
Virgil Dupras
a93d96d742 Added tag se2.11.0 for changeset 2b67955db2b0 2010-08-18 10:07:06 +02:00
Virgil Dupras
f21804c769 Fixed typo in changelog. 2010-08-18 08:56:22 +02:00
Virgil Dupras
4bc05a8d46 dgse v2.11.0 was delayed. 2010-08-18 07:59:26 +02:00
Virgil Dupras
eebe2b0e80 Fixed debian packaging some more. 2010-08-18 07:55:01 +02:00
Virgil Dupras
250a496a78 Fixed debian packaging for SE under Python 3. 2010-08-17 07:26:46 -07:00
Virgil Dupras
29163ed053 se v2.11.0 2010-08-17 11:32:20 +02:00
Virgil Dupras
cc05661f9e Qt: Fixed packaging which didn't work under py3k. 2010-08-17 09:27:38 +01:00
Virgil Dupras
89409c22d1 Removed dependencies on PIL. Man, I wish I had known about QImageReader sooner... That was a little stupid on my part not to look further than QImage. 2010-08-17 09:38:58 +02:00
Virgil Dupras
e2f240ebc9 Prettified the build system by getting rid of those "gen.py" files and hardcoded "python3" calls. Also, ported Qt's block.c to Python3, which hadn't been done yet. 2010-08-17 09:30:25 +02:00
Virgil Dupras
8d56f4c33b Fixed broken test. 2010-08-15 15:09:40 +02:00
Virgil Dupras
36eccb7122 Removed the "all files are refs" error message and made the "no files, can't scan" message quicker. That's because when scanning iPhoto libraries with big libraries, the GUI would hang because these checks would involve loading the whole library. 2010-08-15 15:07:44 +02:00
Virgil Dupras
c8827769b4 Removed dependency on lxml (it made the final package much bigger, and building it on windows is not fun). 2010-08-15 14:42:55 +02:00
Virgil Dupras
12e6c400b9 Fixes here and there to make dupeGuru PE run. 2010-08-15 14:23:16 +02:00
Virgil Dupras
4c273a7910 [#102 state:fixed] Remember the size/position of all window between launches. 2010-08-15 12:27:15 +02:00
Virgil Dupras
58da335b17 Enum-ified Scan Type constants, looks nicer. 2010-08-14 19:52:23 +02:00
Virgil Dupras
5b2d506462 [#15 state:fixed] Improved tie breaker in cases where filenames end with digits inside brackets. 2010-08-14 19:32:09 +02:00
Virgil Dupras
531430d44a Updated dependencies in the README file. 2010-08-14 13:20:57 +02:00
Virgil Dupras
7450eec7eb Added Load/Save Results menu items, allowing to save results at arbitrary places. 2010-08-13 13:06:18 +02:00
Virgil Dupras
3a5802435f Only save results on quit if the results are actually modified. 2010-08-13 11:48:05 +02:00
Virgil Dupras
1b6b058097 Added a is_modified flag to Results. 2010-08-13 11:37:45 +02:00
Virgil Dupras
a5797a2350 Semi-pytest-ified results_test. 2010-08-13 09:48:37 +02:00
Virgil Dupras
e81a5147c5 Adjusted details panel's eight in SE. 2010-08-13 09:32:05 +02:00
Virgil Dupras
565c990687 [#101 state:fixed] Remove the Creation Time column. 2010-08-13 09:26:38 +02:00
Virgil Dupras
0ccdfe0e26 Adjusted xcode project to registration.xib move due to localization. 2010-08-12 17:29:22 +02:00
Virgil Dupras
f8a558e3a7 Updated cocoalib subrepo. 2010-08-12 17:24:54 +02:00
Virgil Dupras
c5fa195cc6 [#103 state:fixed] Correctly hide progress dialog when a job was completed while dupeGuru was inactive. 2010-08-12 17:21:01 +02:00
Virgil Dupras
3a821edd45 Results loading now takes place in one shot (file locate and metadata read). It makes weeding out the bad files more convenient and fixes the Cancel loading glitch where we end up with "ghost" results. 2010-08-12 15:57:47 +02:00
Virgil Dupras
854d194f88 Converted to py3k. There's probably some bugs still. So far, I managed to run dupeGuru SE under pyobjc and qt. 2010-08-11 16:39:06 +02:00
Virgil Dupras
fb79daad6a Added tag before-py3k for changeset 634b66415c65 2010-08-11 16:15:32 +02:00
Virgil Dupras
b2ae0e8759 Added tag pe1.9.1 for changeset 724ff565dd78 2010-07-17 10:33:19 +02:00
Virgil Dupras
09f73988b3 pe v1.9.1 2010-07-17 07:14:39 +02:00
Virgil Dupras
9e6f289319 Added tag me5.8.1 for changeset 3742e83edd9e 2010-07-16 10:04:28 +02:00
202 changed files with 4416 additions and 5399 deletions

View File

@@ -1,6 +1,7 @@
syntax: glob
.DS_Store
run.py
*.pyc
*.so
*.mode1v3
@@ -9,17 +10,13 @@ syntax: glob
*.pyd
conf.yaml
build
dist
install
installer_tmp-cache
cocoa/*/Info.plist
cocoa/*/build
cocoa/*/dg_cocoa.plugin
qt/base/*_rc.py
qt/base/*_ui.py
qt/*/*_ui.py
qt/*/build
qt/*/dist
qt/*/install
qt/*/logdict*.log
qt/*/warn*.txt
help_se/dupeguru_help
help_me/dupeguru_me_help
help_pe/dupeguru_pe_help

16
.hgtags
View File

@@ -21,3 +21,19 @@ cbcf9c80fee4c908ef2efbf1c143c9e47676c9b2 pe1.8.0
cb0a860430bacd712820bce426bcf47a4135efe1 se2.10.1
cb0a860430bacd712820bce426bcf47a4135efe1 se2.10.1
f71d405e62badcfdc1b037facaac043cece40ee5 se2.10.1
3742e83edd9eadf44e1a501859f5e2462b1ef6fd me5.8.1
724ff565dd785fb739774588c6ee652cfc0612d5 pe1.9.1
634b66415c6529f46ae4f837318027cc9d70c3b5 before-py3k
2b67955db2b0580a8b0854dc918b6ab0d1fa3b88 se2.11.0
b56fe4dd8c95bca270b078a09e86848df77e2b2d me5.9.0
618a7365457d56fdc6920c70843a244762e2ea00 pe1.10.0
95b3a4b564c6222b414f2b40182dde2bd6d0e8a4 me5.9.1
9735a5218d2b5b3b1e1dfe17f2f874177cf8f61c se2.11.1
dbfee3ee2fa5cbb9e7ab36570659c17cd5b8561f se2.12.0
d3fe0d0dcda1e0bf1100d02f117503d3bf6baacf me5.10.0
b07ac1398703dd358912c1f3d20bd995633db9fe pe1.11.0
96b6aee668398d663b04eafc8d5dae05e18500ee before-fairware
22239f94589baf2a9fad2123045b8a718dbd68f5 se2.12.2
f9cae82a0752191276b24ffb2cc4e4a8afb5d754 me5.10.2
154c8cb6f018d446d88fa099490c900906e86386 pe1.11.2
ca93352ce35184853ad9fcb881935a43a8b1e249 me5.10.3

View File

@@ -6,6 +6,5 @@ Redistribution and use in source and binary forms, with or without modification,
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Hardcoded Software Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
* If the source code has been published less than two years ago, any redistribution, in whole or in part, must retain full licensing functionality, without any attempt to change, obscure or in other ways circumvent its intent.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

27
README
View File

@@ -13,7 +13,6 @@ There are also other sub-folder that comes from external repositories (automatic
with svn:externals):
- hscommon: A collection of helpers used across HS applications.
- hsdocgen: An ad-hoc document generation used across HS project (used for help files)
- cocoalib: A collection of helpers used across Cocoa UI codebases of HS applications.
- qtlib: A collection of helpers used across Qt UI codebases of HS applications.
@@ -25,29 +24,28 @@ Before being able to build dupeGuru, a few dependencies have to be installed:
General dependencies
-----
- Python 2.6 (http://www.python.org)
- Send2Trash (http://hg.hardcoded.net/send2trash)
- hsutil (http://hg.hardcoded.net/hsutil)
- hsaudiotag (for ME) (http://hg.hardcoded.net/hsaudiotag)
- lxml, to read and write XML files. (http://codespeak.net/lxml/)
- Mako, to generate help files. (http://www.makotemplates.org/)
- Python 3.1 (http://www.python.org)
- Send2Trash3k (http://hg.hardcoded.net/send2trash)
- hsutil3k (http://hg.hardcoded.net/hsutil)
- hsaudiotag3k 1.1.0 (for ME) (http://hg.hardcoded.net/hsaudiotag)
- jobprogress (http://hg.hardcoded.net/jobprogress)
- Markdown, to generate help files. (http://pypi.python.org/pypi/Markdown)
- PyYaml, for help files and the build system. (http://pyyaml.org/)
- py.test, to run unit tests. (http://codespeak.net/py/dist/test/)
- pytest 2.0.0, to run unit tests. (http://pytest.org/)
OS X prerequisites
-----
- XCode 3.1 (http://developer.apple.com/TOOLS/xcode/)
- Sparkle (http://sparkle.andymatuschak.org/)
- PyObjC 2.2. (http://pyobjc.sourceforge.net/)
- py2app (http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html)
- PyObjC 2.3. (http://pyobjc.sourceforge.net/)
- py2app 0.5.4 (http://bitbucket.org/ronaldoussoren/py2app)
Windows prerequisites
---
- Visual Studio 2008 (Express is enough) is needed to build C extensions. (http://www.microsoft.com/Express/)
- PyQt 4.6 (http://www.riverbankcomputing.co.uk/news)
- Python Imaging Library for dupeGuru PE. (http://www.pythonware.com/products/pil/)
- PyQt 4.7.5 (http://www.riverbankcomputing.co.uk/news)
- 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/)
@@ -68,8 +66,3 @@ Then, just build the thing and then run it with:
If you want to create ready-to-upload package, run:
python package.py
64-bit on OS X
---
The "release" configuration of dupeGuru's XCode project build with archs "i386 x86_64 ppc". However there are currently problems with py2app and 64 bit. If you want to correctly build 64-bit apps, refer to http://www.hardcoded.net/articles/building-64-bit-pyobjc-applications-with-py2app.htm .

View File

@@ -3,9 +3,9 @@
# Created On: 2009-12-30
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import sys
import os
@@ -13,6 +13,7 @@ import os.path as op
import shutil
from setuptools import setup
from distutils.extension import Extension
import yaml
from hscommon import helpgen
@@ -20,10 +21,10 @@ from hscommon.build import add_to_pythonpath, print_and_do, build_all_qt_ui, cop
def build_cocoa(edition, dev, help_destpath):
if not dev:
print "Building help index"
print("Building help index")
os.system('open -a /Developer/Applications/Utilities/Help\\ Indexer.app {0}'.format(help_destpath))
print "Building dg_cocoa.plugin"
print("Building dg_cocoa.plugin")
if op.exists('build'):
shutil.rmtree('build')
os.mkdir('build')
@@ -54,7 +55,7 @@ def build_cocoa(edition, dev, help_destpath):
pthpath = op.join(pluginpath, 'Contents/Resources/dev.pth')
open(pthpath, 'w').write(op.abspath('.'))
os.chdir(cocoa_project_path)
print "Building the XCode project"
print("Building the XCode project")
args = []
if dev:
args.append('-configuration dev')
@@ -62,28 +63,72 @@ def build_cocoa(edition, dev, help_destpath):
args.append('-configuration release')
args = ' '.join(args)
os.system('xcodebuild {0}'.format(args))
os.chdir('..')
os.chdir('../..')
print("Creating the run.py file")
subfolder = 'dev' if dev else 'release'
app_path = {
'se': 'cocoa/se/build/{0}/dupeGuru.app',
'me': 'cocoa/me/build/{0}/dupeGuru\\ ME.app',
'pe': 'cocoa/pe/build/{0}/dupeGuru\\ PE.app',
}[edition].format(subfolder)
tmpl = open('run_template_cocoa.py', 'rt').read()
run_contents = tmpl.replace('{{app_path}}', app_path)
open('run.py', 'wt').write(run_contents)
def build_qt(edition, dev):
build_all_qt_ui(op.join('qtlib', 'ui'))
build_all_qt_ui(op.join('qt', 'base'))
build_all_qt_ui(op.join('qt', edition))
print_and_do("pyrcc4 {0} > {1}".format(op.join('qt', 'base', 'dg.qrc'), op.join('qt', 'base', 'dg_rc.py')))
if edition == 'pe':
os.chdir(op.join('qt', edition))
os.system('python gen.py')
os.chdir(op.join('..', '..'))
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")
tmpl = open('run_template_qt.py', 'rt').read()
run_contents = tmpl.replace('{{edition}}', edition)
open('run.py', 'wt').write(run_contents)
def build_pe_modules(ui):
def move(src, dst):
if not op.exists(src):
return
if op.exists(dst):
os.remove(dst)
print('Moving %s --> %s' % (src, dst))
os.rename(src, dst)
print("Building PE Modules")
exts = [
Extension("_block", [op.join('core_pe', 'modules', 'block.c'), op.join('core_pe', 'modules', 'common.c')]),
Extension("_cache", [op.join('core_pe', 'modules', 'cache.c'), op.join('core_pe', 'modules', 'common.c')]),
]
if ui == 'qt':
exts.append(Extension("_block_qt", [op.join('qt', 'pe', 'modules', 'block.c')]))
elif ui == 'cocoa':
exts.append(Extension(
"_block_osx", [op.join('core_pe', 'modules', 'block_osx.m'), op.join('core_pe', 'modules', 'common.c')],
extra_link_args=[
"-framework", "CoreFoundation",
"-framework", "Foundation",
"-framework", "ApplicationServices",]
))
setup(
script_args = ['build_ext', '--inplace'],
ext_modules = exts,
)
move('_block.so', op.join('core_pe', '_block.so'))
move('_block.pyd', op.join('core_pe', '_block.pyd'))
move('_block_osx.so', op.join('core_pe', '_block_osx.so'))
move('_cache.so', op.join('core_pe', '_cache.so'))
move('_cache.pyd', op.join('core_pe', '_cache.pyd'))
move('_block_qt.so', op.join('qt', 'pe', '_block_qt.so'))
move('_block_qt.pyd', op.join('qt', 'pe', '_block_qt.pyd'))
def main():
conf = yaml.load(open('conf.yaml'))
edition = conf['edition']
ui = conf['ui']
dev = conf['dev']
print "Building dupeGuru {0} with UI {1}".format(edition.upper(), ui)
print("Building dupeGuru {0} with UI {1}".format(edition.upper(), ui))
if dev:
print "Building in Dev mode"
print("Building in Dev mode")
add_to_pythonpath('.')
print "Generating Help"
print("Generating Help")
windows = sys.platform == 'win32'
profile = 'win_en' if windows else 'osx_en'
help_dir = 'help_{0}'.format(edition)
@@ -91,11 +136,9 @@ def main():
help_basepath = op.abspath(help_dir)
help_destpath = op.abspath(op.join(help_dir, dest_dir))
helpgen.gen(help_basepath, help_destpath, profile=profile)
print "Building dupeGuru"
print("Building dupeGuru")
if edition == 'pe':
os.chdir('core_pe')
os.system('python gen.py')
os.chdir('..')
build_pe_modules(ui)
if ui == 'cocoa':
build_cocoa(edition, dev, help_destpath)
elif ui == 'qt':

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>
@@ -12,23 +12,24 @@ http://www.hardcoded.net/licenses/hs_license
#import "ResultWindow.h"
#import "DetailsPanel.h"
#import "DirectoryPanel.h"
#import "HSAboutBox.h"
@interface AppDelegateBase : NSObject
{
IBOutlet PyDupeGuruBase *py;
IBOutlet RecentDirectories *recentDirectories;
IBOutlet NSMenuItem *unlockMenuItem;
IBOutlet ResultWindowBase *result;
DirectoryPanel *_directoryPanel;
DetailsPanel *_detailsPanel;
HSAboutBox *_aboutBox;
BOOL _savedResults;
}
- (IBAction)unlockApp:(id)sender;
- (PyDupeGuruBase *)py;
- (RecentDirectories *)recentDirectories;
- (DirectoryPanel *)directoryPanel;
- (DetailsPanel *)detailsPanel;
- (void)saveResults;
- (IBAction)showAboutBox:(id)sender;
@end

View File

@@ -1,32 +1,19 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "AppDelegate.h"
#import "ProgressController.h"
#import "RegistrationInterface.h"
#import "HSFairwareReminder.h"
#import "Utils.h"
#import "Consts.h"
#import <Sparkle/SUUpdater.h>
@implementation AppDelegateBase
- (IBAction)unlockApp:(id)sender
{
if ([[self py] isRegistered])
return;
RegistrationInterface *ri = [[RegistrationInterface alloc] initWithApp:[self py]];
if ([ri enterCode] == NSOKButton)
{
NSString *menuTitle = [NSString stringWithFormat:@"Thanks for buying %@!",[py appName]];
[unlockMenuItem setTitle:menuTitle];
}
[ri release];
}
- (PyDupeGuruBase *)py { return py; }
- (RecentDirectories *)recentDirectories { return recentDirectories; }
- (DirectoryPanel *)directoryPanel
@@ -53,6 +40,14 @@ http://www.hardcoded.net/licenses/hs_license
_savedResults = YES;
}
- (IBAction)showAboutBox:(id)sender
{
if (_aboutBox == nil) {
_aboutBox = [[HSAboutBox alloc] initWithApp:py];
}
[[_aboutBox window] makeKeyAndOrderFront:sender];
}
/* Delegate */
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
@@ -65,9 +60,7 @@ http://www.hardcoded.net/licenses/hs_license
[result restoreColumnsPosition:columnsOrder widths:columnsWidth];
else
[result resetColumnsToDefault:nil];
//Reg stuff
if ([RegistrationInterface showNagWithApp:[self py]])
[unlockMenuItem setTitle:[NSString stringWithFormat:@"Thanks for buying %@!",[py appName]]];
[HSFairwareReminder showNagWithApp:[self py]];
//Restore results
[py loadIgnoreList];
[py loadResults];

View File

@@ -1,14 +1,13 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>
#define RegistrationRequired @"RegistrationRequired"
#define JobStarted @"JobStarted"
#define JobInProgress @"JobInProgress"

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "DetailsPanel.h"

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "DirectoryOutline.h"

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "DirectoryPanel.h"
@@ -39,15 +39,15 @@ http://www.hardcoded.net/licenses/hs_license
NSOpenPanel *op = [NSOpenPanel openPanel];
[op setCanChooseFiles:YES];
[op setCanChooseDirectories:YES];
[op setAllowsMultipleSelection:NO];
[op setAllowsMultipleSelection:YES];
[op setTitle:@"Select a directory to add to the scanning list"];
[op setDelegate:self];
if ([op runModalForTypes:nil] == NSOKButton)
{
NSString *directory = [[op filenames] objectAtIndex:0];
if ([op runModal] == NSOKButton) {
for (NSString *directory in [op filenames]) {
[self addDirectory:directory];
}
}
}
- (IBAction)popupAddDirectoryMenu:(id)sender
{
@@ -95,18 +95,14 @@ http://www.hardcoded.net/licenses/hs_license
{
NSInteger r = [[_py addDirectory:directory] intValue];
if (r) {
NSString *m;
switch (r) {
case 1: {
m = @"This directory already is in the list.";
break;
NSString *m = @"";
if (r == 1) {
m = @"'%@' already is in the list.";
}
case 2: {
m = @"This directory does not exist.";
break;
else if (r == 2) {
m = @"'%@' does not exist.";
}
}
[Dialogs showMessage:m];
[Dialogs showMessage:[NSString stringWithFormat:m,directory]];
}
[_recentDirectories addDirectory:directory];
[[self window] makeKeyAndOrderFront:nil];

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "ProblemDialog.h"

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>
@@ -14,7 +14,9 @@ http://www.hardcoded.net/licenses/hs_license
- (NSNumber *)addDirectory:(NSString *)name;
- (void)removeDirectory:(NSNumber *)index;
- (void)loadResults;
- (void)loadResultsFrom:(NSString *)filename;
- (void)saveResults;
- (void)saveResultsAs:(NSString *)filename;
- (void)loadIgnoreList;
- (void)saveIgnoreList;
- (void)clearIgnoreList;
@@ -37,6 +39,7 @@ http://www.hardcoded.net/licenses/hs_license
- (void)copyOrMove:(NSNumber *)aCopy markedTo:(NSString *)destination recreatePath:(NSNumber *)aRecreateType;
- (void)deleteMarked;
- (void)hardlinkMarked;
- (void)removeMarked;
//Data
@@ -46,8 +49,9 @@ http://www.hardcoded.net/licenses/hs_license
//Scanning options
- (void)setMinMatchPercentage:(NSNumber *)percentage;
- (void)setMixFileKind:(NSNumber *)mix_file_kind;
- (void)setEscapeFilterRegexp:(NSNumber *)escape_filter_regexp;
- (void)setRemoveEmptyFolders:(NSNumber *)remove_empty_folders;
- (void)setMixFileKind:(BOOL)mix_file_kind;
- (void)setEscapeFilterRegexp:(BOOL)escape_filter_regexp;
- (void)setRemoveEmptyFolders:(BOOL)remove_empty_folders;
- (void)setIgnoreHardlinkMatches:(BOOL)ignore_hardlink_matches;
- (void)setSizeThreshold:(NSInteger)size_threshold;
@end

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,24 +1,24 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>
#import "PyOutline.h"
#import "PyTable.h"
@interface PyResultTree : PyOutline
@interface PyResultTable : PyTable
- (BOOL)powerMarkerMode;
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode;
- (BOOL)deltaValuesMode;
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode;
- (NSString *)valueForPath:(NSArray *)aPath column:(NSInteger)aColumn;
- (NSString *)valueForRow:(NSInteger)rowIndex column:(NSInteger)aColumn;
- (BOOL)renameSelected:(NSString *)aNewName;
- (void)sortBy:(NSInteger)aIdentifier ascending:(BOOL)aAscending;
- (void)markSelected;
- (void)removeSelected;
- (NSArray *)rootChildrenCounts;
- (NSInteger)selectedDupeCount;
@end

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,207 +0,0 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
which should be included with this package. The terms are also available at
http://www.hardcoded.net/licenses/hs_license
*/
#import "ResultOutline.h"
#import "Dialogs.h"
#import "Utils.h"
#import "Consts.h"
@implementation ResultOutline
- (id)initWithPyParent:(id)aPyParent view:(HSOutlineView *)aOutlineView
{
self = [super initWithPyClassName:@"PyResultOutline" pyParent:aPyParent view:aOutlineView];
_rootChildrenCounts = nil;
[self connect];
return self;
}
- (void)dealloc
{
[self disconnect];
[_deltaColumns release];
[super dealloc];
}
- (PyResultTree *)py
{
return (PyResultTree *)py;
}
/* Public */
- (BOOL)powerMarkerMode
{
return [[self py] powerMarkerMode];
}
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode
{
[[self py] setPowerMarkerMode:aPowerMarkerMode];
}
- (BOOL)deltaValuesMode
{
return [[self py] deltaValuesMode];
}
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode
{
[[self py] setDeltaValuesMode:aDeltaValuesMode];
}
- (void)setDeltaColumns:(NSIndexSet *)aDeltaColumns
{
[_deltaColumns release];
_deltaColumns = [aDeltaColumns retain];
}
- (NSInteger)selectedDupeCount
{
NSArray *selected = [self selectedIndexPaths];
if ([self powerMarkerMode]) {
return [selected count];
}
else {
NSInteger r = 0;
for (NSIndexPath *path in selected) {
if ([path length] == 2) {
r++;
}
}
return r;
}
}
- (void)removeSelected
{
NSInteger selectedDupeCount = [self selectedDupeCount];
if (!selectedDupeCount)
return;
NSString *msg = [NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",selectedDupeCount];
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) // NO
return;
[[self py] removeSelected];
}
/* Datasource */
- (NSInteger)outlineView:(NSOutlineView *)aOutlineView numberOfChildrenOfItem:(id)item
{
NSIndexPath *path = item;
if ((path != nil) && ([path length] == 1)) {
if (_rootChildrenCounts == nil) {
_rootChildrenCounts = [[[self py] rootChildrenCounts] retain];
}
NSInteger index = [path indexAtPosition:0];
return n2i([_rootChildrenCounts objectAtIndex:index]);
}
return [super outlineView:aOutlineView numberOfChildrenOfItem:item];
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)column byItem:(id)item
{
NSIndexPath *path = item;
NSString *identifier = [column identifier];
if ([identifier isEqual:@"marked"]) {
return b2n([self boolProperty:@"marked" valueAtPath:path]);
}
NSInteger columnId = [identifier integerValue];
return [[self py] valueForPath:p2a(path) column:columnId];
}
- (void)outlineView:(NSOutlineView *)aOutlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
if ([[tableColumn identifier] isEqual:@"0"]) {
NSIndexPath *path = item;
NSString *oldName = [[self py] valueForPath:p2a(path) column:0];
NSString *newName = object;
if (![newName isEqual:oldName]) {
BOOL renamed = [[self py] renameSelected:newName];
if (!renamed) {
[Dialogs showMessage:[NSString stringWithFormat:@"The name '%@' already exists.", newName]];
}
else {
[self refreshItemAtPath:path];
}
}
}
else {
[super outlineView:aOutlineView setObjectValue:object forTableColumn:tableColumn byItem:item];
}
}
/* Delegate */
- (void)outlineView:(NSOutlineView *)aOutlineView didClickTableColumn:(NSTableColumn *)tableColumn
{
if ([[outlineView sortDescriptors] count] < 1)
return;
NSSortDescriptor *sd = [[outlineView sortDescriptors] objectAtIndex:0];
[[self py] sortBy:[[sd key] integerValue] ascending:[sd ascending]];
}
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
NSIndexPath *path = item;
BOOL isMarkable = [self boolProperty:@"markable" valueAtPath:path];
if ([[tableColumn identifier] isEqual:@"marked"]) {
[cell setEnabled:isMarkable];
}
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
// Determine if the text color will be blue due to directory being reference.
NSTextFieldCell *textCell = cell;
if (isMarkable) {
[textCell setTextColor:[NSColor blackColor]];
}
else {
[textCell setTextColor:[NSColor blueColor]];
}
if (([self deltaValuesMode]) && ([self powerMarkerMode] || ([path length] > 1))) {
NSInteger i = [[tableColumn identifier] integerValue];
if ([_deltaColumns containsIndex:i]) {
[textCell setTextColor:[NSColor orangeColor]];
}
}
}
}
- (BOOL)tableViewHadDeletePressed:(NSTableView *)tableView
{
[self removeSelected];
return YES;
}
- (BOOL)tableViewHadSpacePressed:(NSTableView *)tableView
{
[[self py] markSelected];
return YES;
}
/* don't calls saveEdits and cancelEdits */
- (void)outlineViewDidEndEditing:(HSOutlineView *)outlineView
{
}
- (void)outlineViewCancelsEdition:(HSOutlineView *)outlineView
{
}
/* Python --> Cocoa */
- (void)refresh /* Override */
{
[_rootChildrenCounts release];
_rootChildrenCounts = nil;
[super refresh];
[outlineView expandItem:nil expandChildren:YES];
}
- (void)invalidateMarkings
{
for (NSMutableDictionary *props in [itemData objectEnumerator]) {
[props removeObjectForKey:@"marked"];
}
[outlineView setNeedsDisplay:YES];
}
@end

View File

@@ -1,21 +1,21 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>
#import "HSOutline.h"
#import "PyResultTree.h"
#import "HSTable.h"
#import "PyResultTable.h"
@interface ResultOutline : HSOutline
@interface ResultTable : HSTable
{
NSIndexSet *_deltaColumns;
NSArray *_rootChildrenCounts;
}
- (PyResultTree *)py;
- (id)initWithPyParent:(id)aPyParent view:(NSTableView *)aTableView;
- (PyResultTable *)py;
- (BOOL)powerMarkerMode;
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode;
- (BOOL)deltaValuesMode;

162
cocoa/base/ResultTable.m Normal file
View File

@@ -0,0 +1,162 @@
/*
Copyright 2010 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 "ResultTable.h"
#import "Dialogs.h"
#import "Utils.h"
#import "Consts.h"
@implementation ResultTable
- (id)initWithPyParent:(id)aPyParent view:(NSTableView *)aTableView
{
self = [super initWithPyClassName:@"PyResultTable" pyParent:aPyParent view:aTableView];
[self connect];
return self;
}
- (void)dealloc
{
[self disconnect];
[_deltaColumns release];
[super dealloc];
}
- (PyResultTable *)py
{
return (PyResultTable *)py;
}
/* Public */
- (BOOL)powerMarkerMode
{
return [[self py] powerMarkerMode];
}
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode
{
[[self py] setPowerMarkerMode:aPowerMarkerMode];
}
- (BOOL)deltaValuesMode
{
return [[self py] deltaValuesMode];
}
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode
{
[[self py] setDeltaValuesMode:aDeltaValuesMode];
}
- (void)setDeltaColumns:(NSIndexSet *)aDeltaColumns
{
[_deltaColumns release];
_deltaColumns = [aDeltaColumns retain];
}
- (NSInteger)selectedDupeCount
{
return [[self py] selectedDupeCount];
}
- (void)removeSelected
{
NSInteger selectedDupeCount = [self selectedDupeCount];
if (!selectedDupeCount)
return;
NSString *msg = [NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",selectedDupeCount];
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) // NO
return;
[[self py] removeSelected];
}
/* Datasource */
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)row
{
NSString *identifier = [column identifier];
if ([identifier isEqual:@"marked"]) {
return [[self py] valueForColumn:@"marked" row:row];
}
NSInteger columnId = [identifier integerValue];
return [[self py] valueForRow:row column:columnId];
}
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)column row:(NSInteger)row
{
NSString *identifier = [column identifier];
if ([identifier isEqual:@"marked"]) {
[[self py] setValue:object forColumn:identifier row:row];
}
else if ([identifier isEqual:@"0"]) {
NSString *oldName = [[self py] valueForRow:row column:0];
NSString *newName = object;
if (![newName isEqual:oldName]) {
BOOL renamed = [[self py] renameSelected:newName];
if (!renamed) {
[Dialogs showMessage:[NSString stringWithFormat:@"The name '%@' already exists.", newName]];
}
else {
[tableView setNeedsDisplay:YES];
}
}
}
}
/* Delegate */
- (void)tableView:(NSTableView *)aTableView didClickTableColumn:(NSTableColumn *)tableColumn
{
if ([[tableView sortDescriptors] count] < 1)
return;
NSSortDescriptor *sd = [[tableView sortDescriptors] objectAtIndex:0];
[[self py] sortBy:[[sd key] integerValue] ascending:[sd ascending]];
}
- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)column row:(NSInteger)row
{
BOOL isMarkable = n2b([[self py] valueForColumn:@"markable" row:row]);
if ([[column identifier] isEqual:@"marked"]) {
[cell setEnabled:isMarkable];
// Low-tech solution, for indentation, but it works...
NSCellImagePosition pos = isMarkable ? NSImageRight : NSImageLeft;
[cell setImagePosition:pos];
}
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
// Determine if the text color will be blue due to directory being reference.
NSTextFieldCell *textCell = cell;
if (isMarkable) {
[textCell setTextColor:[NSColor blackColor]];
if ([self deltaValuesMode]) {
NSInteger i = [[column identifier] integerValue];
if ([_deltaColumns containsIndex:i]) {
[textCell setTextColor:[NSColor orangeColor]];
}
}
}
else {
[textCell setTextColor:[NSColor blueColor]];
}
}
}
- (BOOL)tableViewHadDeletePressed:(NSTableView *)tableView
{
[self removeSelected];
return YES;
}
- (BOOL)tableViewHadSpacePressed:(NSTableView *)tableView
{
[[self py] markSelected];
return YES;
}
/* Python --> Cocoa */
- (void)invalidateMarkings
{
[tableView setNeedsDisplay:YES];
}
@end

View File

@@ -1,16 +1,16 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>
#import "HSOutlineView.h"
#import "StatsLabel.h"
#import "ResultOutline.h"
#import "ResultTable.h"
#import "ProblemDialog.h"
#import "HSTableView.h"
#import "PyDupeGuru.h"
@interface ResultWindowBase : NSWindowController
@@ -19,7 +19,7 @@ http://www.hardcoded.net/licenses/hs_license
IBOutlet PyDupeGuruBase *py;
IBOutlet id app;
IBOutlet NSSegmentedControl *deltaSwitch;
IBOutlet HSOutlineView *matches;
IBOutlet HSTableView *matches;
IBOutlet NSSegmentedControl *pmSwitch;
IBOutlet NSTextField *stats;
IBOutlet NSMenu *columnsMenu;
@@ -27,7 +27,7 @@ http://www.hardcoded.net/licenses/hs_license
NSMutableArray *_resultColumns;
NSWindowController *preferencesPanel;
ResultOutline *outline;
ResultTable *table;
StatsLabel *statsLabel;
ProblemDialog *problemDialog;
}
@@ -38,6 +38,7 @@ http://www.hardcoded.net/licenses/hs_license
- (NSDictionary *)getColumnsWidth;
- (void)initResultColumns;
- (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth;
- (void)sendMarkedToTrash:(BOOL)hardlinkDeleted;
/* Actions */
- (IBAction)clearIgnoreList:(id)sender;
@@ -45,10 +46,12 @@ http://www.hardcoded.net/licenses/hs_license
- (IBAction)changePowerMarker:(id)sender;
- (IBAction)copyMarked:(id)sender;
- (IBAction)deleteMarked:(id)sender;
- (IBAction)hardlinkMarked:(id)sender;
- (IBAction)exportToXHTML:(id)sender;
- (IBAction)filter:(id)sender;
- (IBAction)ignoreSelected:(id)sender;
- (IBAction)invokeCustomCommand:(id)sender;
- (IBAction)loadResults:(id)sender;
- (IBAction)markAll:(id)sender;
- (IBAction)markInvert:(id)sender;
- (IBAction)markNone:(id)sender;
@@ -61,6 +64,7 @@ http://www.hardcoded.net/licenses/hs_license
- (IBAction)renameSelected:(id)sender;
- (IBAction)resetColumnsToDefault:(id)sender;
- (IBAction)revealSelected:(id)sender;
- (IBAction)saveResults:(id)sender;
- (IBAction)showPreferencesPanel:(id)sender;
- (IBAction)startDuplicateScan:(id)sender;
- (IBAction)switchSelected:(id)sender;

View File

@@ -1,16 +1,15 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "ResultWindow.h"
#import "Dialogs.h"
#import "ProgressController.h"
#import "Utils.h"
#import "RegistrationInterface.h"
#import "AppDelegate.h"
#import "Consts.h"
@@ -18,8 +17,10 @@ http://www.hardcoded.net/licenses/hs_license
- (void)awakeFromNib
{
[self window];
/* Put a cute iTunes-like bottom bar */
[[self window] setContentBorderThickness:28 forEdge:NSMinYEdge];
preferencesPanel = [[NSWindowController alloc] initWithWindowNibName:@"Preferences"];
outline = [[ResultOutline alloc] initWithPyParent:py view:matches];
table = [[ResultTable alloc] initWithPyParent:py view:matches];
statsLabel = [[StatsLabel alloc] initWithPyParent:py labelView:stats];
problemDialog = [[ProblemDialog alloc] initWithPy:py];
[self initResultColumns];
@@ -29,7 +30,6 @@ http://www.hardcoded.net/licenses/hs_license
[matches setTarget:self];
[matches setDoubleAction:@selector(openClicked:)];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registrationRequired:) name:RegistrationRequired object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobCompleted:) name:JobCompletedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobStarted:) name:JobStarted object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobInProgress:) name:JobInProgress object:nil];
@@ -37,7 +37,7 @@ http://www.hardcoded.net/licenses/hs_license
- (void)dealloc
{
[outline release];
[table release];
[preferencesPanel release];
[statsLabel release];
[problemDialog release];
@@ -124,6 +124,29 @@ http://www.hardcoded.net/licenses/hs_license
}
}
- (void)sendMarkedToTrash:(BOOL)hardlinkDeleted
{
NSInteger mark_count = [[py getMarkCount] intValue];
if (!mark_count) {
return;
}
NSString *msg = @"You are about to send %d files to Trash. Continue?";
if (hardlinkDeleted) {
msg = @"You are about to send %d files to Trash (and hardlink them afterwards). Continue?";
}
if ([Dialogs askYesNo:[NSString stringWithFormat:msg,mark_count]] == NSAlertSecondButtonReturn) { // NO
return;
}
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[py setRemoveEmptyFolders:n2b([ud objectForKey:@"removeEmptyFolders"])];
if (hardlinkDeleted) {
[py hardlinkMarked];
}
else {
[py deleteMarked];
}
}
/* Actions */
- (IBAction)clearIgnoreList:(id)sender
{
@@ -137,12 +160,12 @@ http://www.hardcoded.net/licenses/hs_license
- (IBAction)changeDelta:(id)sender
{
[outline setDeltaValuesMode:[deltaSwitch selectedSegment] == 1];
[table setDeltaValuesMode:[deltaSwitch selectedSegment] == 1];
}
- (IBAction)changePowerMarker:(id)sender
{
[outline setPowerMarkerMode:[pmSwitch selectedSegment] == 1];
[table setPowerMarkerMode:[pmSwitch selectedSegment] == 1];
}
- (IBAction)copyMarked:(id)sender
@@ -166,14 +189,12 @@ http://www.hardcoded.net/licenses/hs_license
- (IBAction)deleteMarked:(id)sender
{
NSInteger mark_count = [[py getMarkCount] intValue];
if (!mark_count)
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to send %d files to Trash. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
return;
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[py setRemoveEmptyFolders:[ud objectForKey:@"removeEmptyFolders"]];
[py deleteMarked];
[self sendMarkedToTrash:NO];
}
- (IBAction)hardlinkMarked:(id)sender
{
[self sendMarkedToTrash:YES];
}
- (IBAction)exportToXHTML:(id)sender
@@ -185,13 +206,13 @@ http://www.hardcoded.net/licenses/hs_license
- (IBAction)filter:(id)sender
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[py setEscapeFilterRegexp:b2n(!n2b([ud objectForKey:@"useRegexpFilter"]))];
[py setEscapeFilterRegexp:!n2b([ud objectForKey:@"useRegexpFilter"])];
[py applyFilter:[filterField stringValue]];
}
- (IBAction)ignoreSelected:(id)sender
{
NSInteger selectedDupeCount = [outline selectedDupeCount];
NSInteger selectedDupeCount = [table selectedDupeCount];
if (!selectedDupeCount)
return;
NSString *msg = [NSString stringWithFormat:@"All selected %d matches are going to be ignored in all subsequent scans. Continue?",selectedDupeCount];
@@ -212,6 +233,21 @@ http://www.hardcoded.net/licenses/hs_license
}
}
- (IBAction)loadResults:(id)sender
{
NSOpenPanel *op = [NSOpenPanel openPanel];
[op setCanChooseFiles:YES];
[op setCanChooseDirectories:NO];
[op setCanCreateDirectories:NO];
[op setAllowsMultipleSelection:NO];
[op setAllowedFileTypes:[NSArray arrayWithObject:@"dupeguru"]];
[op setTitle:@"Select a results file to load"];
if ([op runModal] == NSOKButton) {
NSString *filename = [[op filenames] objectAtIndex:0];
[py loadResultsFrom:filename];
}
}
- (IBAction)markAll:(id)sender
{
[py markAll];
@@ -247,7 +283,7 @@ http://www.hardcoded.net/licenses/hs_license
{
NSString *directory = [[op filenames] objectAtIndex:0];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[py setRemoveEmptyFolders:[ud objectForKey:@"removeEmptyFolders"]];
[py setRemoveEmptyFolders:n2b([ud objectForKey:@"removeEmptyFolders"])];
[py copyOrMove:b2n(NO) markedTo:directory recreatePath:[ud objectForKey:@"recreatePathType"]];
}
}
@@ -278,7 +314,7 @@ http://www.hardcoded.net/licenses/hs_license
- (IBAction)removeSelected:(id)sender
{
[outline removeSelected];
[table removeSelected];
}
- (IBAction)renameSelected:(id)sender
@@ -303,6 +339,17 @@ http://www.hardcoded.net/licenses/hs_license
[preferencesPanel showWindow:sender];
}
- (IBAction)saveResults:(id)sender
{
NSSavePanel *sp = [NSSavePanel savePanel];
[sp setCanCreateDirectories:YES];
[sp setAllowedFileTypes:[NSArray arrayWithObject:@"dupeguru"]];
[sp setTitle:@"Select a file to save your results to"];
if ([sp runModal] == NSOKButton) {
[py saveResultsAs:[sp filename]];
}
}
- (IBAction)startDuplicateScan:(id)sender
{
// Virtual
@@ -390,8 +437,8 @@ http://www.hardcoded.net/licenses/hs_license
}
}
else if ([lastAction isEqualTo:jobScan]) {
NSInteger groupCount = [outline intProperty:@"children_count" valueAtPath:nil];
if (groupCount == 0)
NSInteger rowCount = [[table py] numberOfRows];
if (rowCount == 0)
[Dialogs showMessage:@"No duplicates found."];
}
@@ -415,12 +462,6 @@ http://www.hardcoded.net/licenses/hs_license
[[ProgressController mainProgressController] showSheetForParent:[self window]];
}
- (void)registrationRequired:(NSNotification *)aNotification
{
NSString *msg = @"This is a demo version, which only allows you 10 delete/copy/move actions per session. You cannot continue.";
[Dialogs showMessage:msg];
}
- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
{
return ![[ProgressController mainProgressController] isShown];

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "StatsLabel.h"

View File

@@ -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">10C540</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.AppKitVersion">1038.25</string>
<string key="IBDocument.HIToolboxVersion">458.00</string>
<string key="IBDocument.SystemVersion">10F569</string>
<string key="IBDocument.InterfaceBuilderVersion">788</string>
<string key="IBDocument.AppKitVersion">1038.29</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">740</string>
<string key="NS.object.0">788</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -41,7 +41,7 @@
<object class="NSWindowTemplate" id="476890502">
<int key="NSWindowStyleMask">155</int>
<int key="NSWindowBacking">2</int>
<string key="NSWindowRect">{{33, 261}, {451, 161}}</string>
<string key="NSWindowRect">{{33, 261}, {451, 146}}</string>
<int key="NSWTFlags">-260571136</int>
<string key="NSWindowTitle">Details of Selected File</string>
<object class="NSMutableString" key="NSWindowClass">
@@ -51,7 +51,7 @@
<characters key="NS.bytes">View</characters>
</object>
<string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
<string key="NSWindowContentMinSize">{451, 161}</string>
<string key="NSWindowContentMinSize">{451, 146}</string>
<object class="NSView" key="NSWindowView" id="1027711962">
<reference key="NSNextResponder"/>
<int key="NSvFlags">256</int>
@@ -70,7 +70,7 @@
<object class="NSTableView" id="251969872">
<reference key="NSNextResponder" ref="488480549"/>
<int key="NSvFlags">256</int>
<string key="NSFrameSize">{449, 143}</string>
<string key="NSFrameSize">{449, 128}</string>
<reference key="NSSuperview" ref="488480549"/>
<int key="NSTag">2</int>
<bool key="NSEnabled">YES</bool>
@@ -224,7 +224,7 @@
<int key="NSTableViewDraggingDestinationStyle">0</int>
</object>
</object>
<string key="NSFrame">{{1, 17}, {449, 143}}</string>
<string key="NSFrame">{{1, 17}, {449, 128}}</string>
<reference key="NSSuperview" ref="362108788"/>
<reference key="NSNextKeyView" ref="251969872"/>
<reference key="NSDocView" ref="251969872"/>
@@ -266,7 +266,7 @@
</object>
<reference ref="717380566"/>
</object>
<string key="NSFrameSize">{451, 161}</string>
<string key="NSFrameSize">{451, 146}</string>
<reference key="NSSuperview" ref="1027711962"/>
<reference key="NSNextKeyView" ref="488480549"/>
<int key="NSsFlags">530</int>
@@ -278,12 +278,13 @@
<bytes key="NSScrollAmts">QSAAAEEgAABBgAAAQYAAAA</bytes>
</object>
</object>
<string key="NSFrameSize">{451, 161}</string>
<string key="NSFrameSize">{451, 146}</string>
<reference key="NSSuperview"/>
</object>
<string key="NSScreenRect">{{0, 0}, {1024, 746}}</string>
<string key="NSMinSize">{451, 177}</string>
<string key="NSMinSize">{451, 162}</string>
<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
<string key="NSFrameAutosaveName">DetailsPanel</string>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
@@ -497,12 +498,12 @@
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>{{109, 656}, {451, 161}}</string>
<string>{{109, 671}, {451, 146}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{109, 656}, {451, 161}}</string>
<string>{{109, 671}, {451, 146}}</string>
<boolean value="YES"/>
<boolean value="YES"/>
<string>{451, 161}</string>
<string>{451, 146}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -536,11 +537,18 @@
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">DetailsPanel</string>
<string key="superclassName">NSWindowController</string>
<string key="superclassName">HSWindowController</string>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">detailsTable</string>
<string key="NS.object.0">NSTableView</string>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<string key="NS.key.0">detailsTable</string>
<object class="IBToOneOutletInfo" key="NS.object.0">
<string key="name">detailsTable</string>
<string key="candidateClassName">NSTableView</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../base/DetailsPanel.h</string>
@@ -548,7 +556,7 @@
</object>
<object class="IBPartialClassDescription">
<string key="className">DetailsPanel</string>
<string key="superclassName">NSWindowController</string>
<string key="superclassName">HSWindowController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string>
<string key="minorKey"/>
@@ -562,6 +570,32 @@
<string key="minorKey"/>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">HSWindowController</string>
<string key="superclassName">NSWindowController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../controllers/HSWindowController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../views/HSOutlineView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="111130406">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../views/NSTableViewAdditions.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSTableView</string>
<reference key="sourceIdentifier" ref="111130406"/>
</object>
</object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -1029,6 +1063,13 @@
<string key="NS.key.0">showWindow:</string>
<string key="NS.object.0">id</string>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<string key="NS.key.0">showWindow:</string>
<object class="IBActionInfo" key="NS.object.0">
<string key="name">showWindow:</string>
<string key="candidateClassName">id</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
@@ -1037,6 +1078,7 @@
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
<integer value="1050" key="NS.object.0"/>

View File

@@ -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">10C540</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.AppKitVersion">1038.25</string>
<string key="IBDocument.HIToolboxVersion">458.00</string>
<string key="IBDocument.SystemVersion">10F569</string>
<string key="IBDocument.InterfaceBuilderVersion">788</string>
<string key="IBDocument.AppKitVersion">1038.29</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">740</string>
<string key="NS.object.0">788</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -428,6 +428,7 @@
<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
<string key="NSMinSize">{369, 291}</string>
<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
<string key="NSFrameAutosaveName">DirectoryPanel</string>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
@@ -869,6 +870,35 @@
<string>id</string>
</object>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>askForDirectory:</string>
<string>popupAddDirectoryMenu:</string>
<string>removeSelectedDirectory:</string>
<string>toggleVisible:</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBActionInfo">
<string key="name">askForDirectory:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">popupAddDirectoryMenu:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">removeSelectedDirectory:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">toggleVisible:</string>
<string key="candidateClassName">id</string>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
@@ -884,6 +914,30 @@
<string>NSButton</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>addButtonPopUp</string>
<string>outlineView</string>
<string>removeButton</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
<string key="name">addButtonPopUp</string>
<string key="candidateClassName">NSPopUpButton</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">outlineView</string>
<string key="candidateClassName">HSOutlineView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">removeButton</string>
<string key="candidateClassName">NSButton</string>
</object>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../base/DirectoryPanel.h</string>
@@ -1437,6 +1491,13 @@
<string key="NS.key.0">showWindow:</string>
<string key="NS.object.0">id</string>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<string key="NS.key.0">showWindow:</string>
<object class="IBActionInfo" key="NS.object.0">
<string key="name">showWindow:</string>
<string key="candidateClassName">id</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
@@ -1445,6 +1506,7 @@
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
<integer value="1050" key="NS.object.0"/>
@@ -1460,5 +1522,18 @@
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">../../se/dupeguru.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSMenuCheckmark</string>
<string>NSMenuMixedState</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>{9, 8}</string>
<string>{7, 2}</string>
</object>
</object>
</data>
</archive>

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,14 +1,13 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "AppDelegate.h"
#import "../../cocoalib/ProgressController.h"
#import "../../cocoalib/RegistrationInterface.h"
#import "../../cocoalib/Utils.h"
#import "../../cocoalib/ValueTransformers.h"
#import "../../cocoalib/Dialogs.h"
@@ -28,6 +27,7 @@ http://www.hardcoded.net/licenses/hs_license
[d setObject:b2n(NO) forKey:@"matchSimilarWords"];
[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:b2n(NO) forKey:@"scanTagTrack"];

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "../base/Consts.h"

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "DirectoryPanel.h"

View File

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

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,15 +1,14 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "ResultWindow.h"
#import "../../cocoalib/Dialogs.h"
#import "../../cocoalib/ProgressController.h"
#import "../../cocoalib/RegistrationInterface.h"
#import "../../cocoalib/Utils.h"
#import "AppDelegate.h"
#import "Consts.h"
@@ -20,9 +19,9 @@ http://www.hardcoded.net/licenses/hs_license
{
[super awakeFromNib];
[[self window] setTitle:@"dupeGuru Music Edition"];
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,7)];
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,6)];
[deltaColumns removeIndex:6];
[outline setDeltaColumns:deltaColumns];
[table setDeltaColumns:deltaColumns];
}
/* Actions */
@@ -38,13 +37,15 @@ http://www.hardcoded.net/licenses/hs_license
[columnsOrder addObject:@"2"];
[columnsOrder addObject:@"3"];
[columnsOrder addObject:@"4"];
[columnsOrder addObject:@"16"];
[columnsOrder addObject:@"6"];
[columnsOrder addObject:@"15"];
NSMutableDictionary *columnsWidth = [NSMutableDictionary dictionary];
[columnsWidth setObject:i2n(214) forKey:@"0"];
[columnsWidth setObject:i2n(235) forKey:@"0"];
[columnsWidth setObject:i2n(63) forKey:@"2"];
[columnsWidth setObject:i2n(50) forKey:@"3"];
[columnsWidth setObject:i2n(50) forKey:@"4"];
[columnsWidth setObject:i2n(57) forKey:@"16"];
[columnsWidth setObject:i2n(40) forKey:@"6"];
[columnsWidth setObject:i2n(57) forKey:@"15"];
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
}
@@ -66,11 +67,10 @@ http://www.hardcoded.net/licenses/hs_license
[_py enable:[ud objectForKey:@"scanTagYear"] scanForTag:@"year"];
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
[_py setWordWeighting:[ud objectForKey:@"wordWeighting"]];
[_py setMixFileKind:[ud objectForKey:@"mixFileKind"]];
[_py setMixFileKind:n2b([ud objectForKey:@"mixFileKind"])];
[_py setIgnoreHardlinkMatches:n2b([ud objectForKey:@"ignoreHardlinkMatches"])];
[_py setMatchSimilarWords:[ud objectForKey:@"matchSimilarWords"]];
NSInteger r = n2i([py doScan]);
if (r == 1)
[Dialogs showMessage:@"You cannot make a duplicate scan with only reference directories."];
if (r == 3)
{
[Dialogs showMessage:@"The selected directories contain no scannable file."];
@@ -96,18 +96,17 @@ http://www.hardcoded.net/licenses/hs_license
[_resultColumns addObject:brCol];
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Sample Rate" width:60 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Kind" width:40 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Creation" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Modification" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:9 title:@"Title" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:10 title:@"Artist" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:11 title:@"Album" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:12 title:@"Genre" width:80 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:13 title:@"Year" width:40 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:14 title:@"Track Number" width:40 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:15 title:@"Comment" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:16 title:@"Match %" width:57 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:17 title:@"Words Used" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:18 title:@"Dupe Count" width:80 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Modification" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Title" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:9 title:@"Artist" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:10 title:@"Album" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:11 title:@"Genre" width:80 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:12 title:@"Year" width:40 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:13 title:@"Track Number" width:40 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:14 title:@"Comment" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:15 title:@"Match %" width:57 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:16 title:@"Words Used" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:17 title:@"Dupe Count" width:80 refCol:refCol]];
}
/* Notifications */

View File

@@ -1,21 +1,26 @@
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from hscommon.cocoa import signature
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
from core_me.app_cocoa import DupeGuruME
from core.scanner import (SCAN_TYPE_FILENAME, SCAN_TYPE_FIELDS, SCAN_TYPE_FIELDS_NO_ORDER,
SCAN_TYPE_TAG, SCAN_TYPE_CONTENT, SCAN_TYPE_CONTENT_AUDIO)
from core.scanner import ScanType
# Fix py2app imports which chokes on relative imports and other stuff
from core_me import app_cocoa, data, fs, scanner
import core_me.app_cocoa, core_me.data, core_me.fs, core_me.scanner
import hsaudiotag.aiff, hsaudiotag.flac, hsaudiotag.genres, hsaudiotag.id3v1,\
hsaudiotag.id3v2, hsaudiotag.mp4, hsaudiotag.mpeg, hsaudiotag.ogg, hsaudiotag.wma
from hsaudiotag import aiff, flac, genres, id3v1, id3v2, mp4, mpeg, ogg, wma
from lxml import etree, _elementpath
import hsutil.conflict
import core.engine, core.fs, core.app
import xml.etree.ElementPath
import gzip
import aem.kae
import appscript.defaultterminology
class PyDupeGuru(PyDupeGuruBase):
def init(self):
@@ -41,12 +46,12 @@ class PyDupeGuru(PyDupeGuruBase):
def setScanType_(self, scan_type):
try:
self.py.scanner.scan_type = [
SCAN_TYPE_FILENAME,
SCAN_TYPE_FIELDS,
SCAN_TYPE_FIELDS_NO_ORDER,
SCAN_TYPE_TAG,
SCAN_TYPE_CONTENT,
SCAN_TYPE_CONTENT_AUDIO
ScanType.Filename,
ScanType.Fields,
ScanType.FieldsNoOrder,
ScanType.Tag,
ScanType.Contents,
ScanType.ContentsAudio,
][scan_type]
except IndexError:
pass

View File

@@ -33,7 +33,6 @@
CE0A0C001175A1C000DCA3C6 /* HSTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0A0BFF1175A1C000DCA3C6 /* HSTable.m */; };
CE0A0C041175A1DE00DCA3C6 /* ProblemDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0A0C021175A1DE00DCA3C6 /* ProblemDialog.m */; };
CE0A0C061175A24800DCA3C6 /* ProblemDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE0A0C051175A24800DCA3C6 /* ProblemDialog.xib */; };
CE0B3D6711243F83009A7A30 /* ResultOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0B3D6611243F83009A7A30 /* ResultOutline.m */; };
CE1425890AFB718500BD5167 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
@@ -44,23 +43,27 @@
CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */; };
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */; };
CE4B59C91119919700C06C9E /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C61119919700C06C9E /* progress.xib */; };
CE4B59CA1119919700C06C9E /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C71119919700C06C9E /* registration.xib */; };
CE4F934612CCA9470067A3AE /* about.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4F934512CCA9470067A3AE /* about.xib */; };
CE4F934912CCA96C0067A3AE /* HSAboutBox.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4F934812CCA96C0067A3AE /* HSAboutBox.m */; };
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE10FC6C12E00EC695D /* Dialogs.m */; };
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */; };
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE70FC6C12E00EC695D /* ProgressController.m */; };
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */; };
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */; };
CE515DFA0FC6C12E00EC695D /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DF00FC6C12E00EC695D /* Utils.m */; };
CE515DFB0FC6C12E00EC695D /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DF20FC6C12E00EC695D /* ValueTransformers.m */; };
CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E160FC6C19300EC695D /* AppDelegate.m */; };
CE515E1E0FC6C19300EC695D /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E190FC6C19300EC695D /* DirectoryPanel.m */; };
CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E1C0FC6C19300EC695D /* ResultWindow.m */; };
CE578303124DFC660004769C /* HSTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE578302124DFC660004769C /* HSTableView.m */; };
CE6032C00FE6784C007E33FF /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE6032BF0FE6784C007E33FF /* DetailsPanel.m */; };
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE68EE6609ABC48000971085 /* DirectoryPanel.m */; };
CE6E0E9F1054EB97008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0E9E1054EB97008D9390 /* dsa_pub.pem */; };
CE74A12412537F06008A8DF0 /* HSFairwareReminder.m in Sources */ = {isa = PBXBuildFile; fileRef = CE74A12212537F06008A8DF0 /* HSFairwareReminder.m */; };
CE74A12712537F2E008A8DF0 /* FairwareReminder.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE74A12512537F2E008A8DF0 /* FairwareReminder.xib */; };
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
CE900AD2109B238600754048 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD1109B238600754048 /* Preferences.xib */; };
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD6109B2A9B00754048 /* MainMenu.xib */; };
CEB14D29124DFC2800FA7481 /* ResultTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CEB14D28124DFC2800FA7481 /* ResultTable.m */; };
CEDF07A3112493B200EE5BC0 /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDF07A2112493B200EE5BC0 /* StatsLabel.m */; };
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; };
@@ -100,7 +103,6 @@
CE003CBA11242D00004B0AA7 /* NSEventAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSEventAdditions.m; path = ../../cocoalib/NSEventAdditions.m; sourceTree = SOURCE_ROOT; };
CE003CBC11242D00004B0AA7 /* PyGUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyGUI.h; sourceTree = "<group>"; };
CE003CBD11242D00004B0AA7 /* PyOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyOutline.h; sourceTree = "<group>"; };
CE003CBE11242D00004B0AA7 /* PyRegistrable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyRegistrable.h; path = ../../cocoalib/PyRegistrable.h; sourceTree = SOURCE_ROOT; };
CE003CC011242D00004B0AA7 /* HSOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutlineView.h; sourceTree = "<group>"; };
CE003CC111242D00004B0AA7 /* HSOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutlineView.m; sourceTree = "<group>"; };
CE003CC211242D00004B0AA7 /* NSIndexPathAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSIndexPathAdditions.h; sourceTree = "<group>"; };
@@ -118,9 +120,6 @@
CE0A0C031175A1DE00DCA3C6 /* PyProblemDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyProblemDialog.h; path = ../base/PyProblemDialog.h; sourceTree = SOURCE_ROOT; };
CE0A0C051175A24800DCA3C6 /* ProblemDialog.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ProblemDialog.xib; path = ../base/xib/ProblemDialog.xib; sourceTree = SOURCE_ROOT; };
CE0A0C131175A28100DCA3C6 /* PyTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyTable.h; sourceTree = "<group>"; };
CE0B3D6411243F83009A7A30 /* PyResultTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTree.h; path = ../base/PyResultTree.h; sourceTree = SOURCE_ROOT; };
CE0B3D6511243F83009A7A30 /* ResultOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultOutline.h; path = ../base/ResultOutline.h; sourceTree = SOURCE_ROOT; };
CE0B3D6611243F83009A7A30 /* ResultOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultOutline.m; path = ../base/ResultOutline.m; sourceTree = SOURCE_ROOT; };
CE1425880AFB718500BD5167 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
@@ -133,7 +132,9 @@
CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; };
CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ErrorReportWindow.xib; sourceTree = "<group>"; };
CE4B59C61119919700C06C9E /* progress.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = progress.xib; sourceTree = "<group>"; };
CE4B59C71119919700C06C9E /* registration.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = registration.xib; sourceTree = "<group>"; };
CE4F934512CCA9470067A3AE /* about.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = about.xib; path = ../../cocoalib/xib/about.xib; sourceTree = SOURCE_ROOT; };
CE4F934712CCA96C0067A3AE /* HSAboutBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSAboutBox.h; path = ../../cocoalib/HSAboutBox.h; sourceTree = SOURCE_ROOT; };
CE4F934812CCA96C0067A3AE /* HSAboutBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSAboutBox.m; path = ../../cocoalib/HSAboutBox.m; sourceTree = SOURCE_ROOT; };
CE515DE00FC6C12E00EC695D /* Dialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Dialogs.h; path = ../../cocoalib/Dialogs.h; sourceTree = SOURCE_ROOT; };
CE515DE10FC6C12E00EC695D /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; };
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
@@ -143,8 +144,6 @@
CE515DE80FC6C12E00EC695D /* PyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyApp.h; path = ../../cocoalib/PyApp.h; sourceTree = SOURCE_ROOT; };
CE515DE90FC6C12E00EC695D /* RecentDirectories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RecentDirectories.h; path = ../../cocoalib/RecentDirectories.h; sourceTree = SOURCE_ROOT; };
CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RecentDirectories.m; path = ../../cocoalib/RecentDirectories.m; sourceTree = SOURCE_ROOT; };
CE515DEB0FC6C12E00EC695D /* RegistrationInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegistrationInterface.h; path = ../../cocoalib/RegistrationInterface.h; sourceTree = SOURCE_ROOT; };
CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegistrationInterface.m; path = ../../cocoalib/RegistrationInterface.m; sourceTree = SOURCE_ROOT; };
CE515DEF0FC6C12E00EC695D /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = ../../cocoalib/Utils.h; sourceTree = SOURCE_ROOT; };
CE515DF00FC6C12E00EC695D /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Utils.m; path = ../../cocoalib/Utils.m; sourceTree = SOURCE_ROOT; };
CE515DF10FC6C12E00EC695D /* ValueTransformers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueTransformers.h; path = ../../cocoalib/ValueTransformers.h; sourceTree = SOURCE_ROOT; };
@@ -157,14 +156,23 @@
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDupeGuru.h; path = ../base/PyDupeGuru.h; sourceTree = SOURCE_ROOT; };
CE515E1B0FC6C19300EC695D /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultWindow.h; path = ../base/ResultWindow.h; sourceTree = SOURCE_ROOT; };
CE515E1C0FC6C19300EC695D /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultWindow.m; path = ../base/ResultWindow.m; sourceTree = SOURCE_ROOT; };
CE578301124DFC660004769C /* HSTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSTableView.h; path = ../../cocoalib/views/HSTableView.h; sourceTree = SOURCE_ROOT; };
CE578302124DFC660004769C /* HSTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSTableView.m; path = ../../cocoalib/views/HSTableView.m; sourceTree = SOURCE_ROOT; };
CE6032BE0FE6784C007E33FF /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DetailsPanel.h; path = ../base/DetailsPanel.h; sourceTree = SOURCE_ROOT; };
CE6032BF0FE6784C007E33FF /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DetailsPanel.m; path = ../base/DetailsPanel.m; sourceTree = SOURCE_ROOT; };
CE68EE6509ABC48000971085 /* DirectoryPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DirectoryPanel.h; sourceTree = SOURCE_ROOT; };
CE68EE6609ABC48000971085 /* DirectoryPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DirectoryPanel.m; sourceTree = SOURCE_ROOT; };
CE6E0E9E1054EB97008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = "<group>"; };
CE74A12112537F06008A8DF0 /* HSFairwareReminder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSFairwareReminder.h; path = ../../cocoalib/HSFairwareReminder.h; sourceTree = SOURCE_ROOT; };
CE74A12212537F06008A8DF0 /* HSFairwareReminder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSFairwareReminder.m; path = ../../cocoalib/HSFairwareReminder.m; sourceTree = SOURCE_ROOT; };
CE74A12312537F06008A8DF0 /* PyFairware.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyFairware.h; path = ../../cocoalib/PyFairware.h; sourceTree = SOURCE_ROOT; };
CE74A12612537F2E008A8DF0 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = ../../cocoalib/en.lproj/FairwareReminder.xib; sourceTree = SOURCE_ROOT; };
CE848A1809DD85810004CB44 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
CE900AD1109B238600754048 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; };
CE900AD6109B2A9B00754048 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; };
CEB14D26124DFC2800FA7481 /* PyResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTable.h; path = ../base/PyResultTable.h; sourceTree = SOURCE_ROOT; };
CEB14D27124DFC2800FA7481 /* ResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultTable.h; path = ../base/ResultTable.h; sourceTree = SOURCE_ROOT; };
CEB14D28124DFC2800FA7481 /* ResultTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultTable.m; path = ../base/ResultTable.m; sourceTree = SOURCE_ROOT; };
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
CEDF07A0112493B200EE5BC0 /* PyStatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyStatsLabel.h; path = ../base/PyStatsLabel.h; sourceTree = SOURCE_ROOT; };
CEDF07A1112493B200EE5BC0 /* StatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StatsLabel.h; path = ../base/StatsLabel.h; sourceTree = SOURCE_ROOT; };
@@ -306,6 +314,8 @@
CE003CBF11242D00004B0AA7 /* views */ = {
isa = PBXGroup;
children = (
CE578301124DFC660004769C /* HSTableView.h */,
CE578302124DFC660004769C /* HSTableView.m */,
CE003CC011242D00004B0AA7 /* HSOutlineView.h */,
CE003CC111242D00004B0AA7 /* HSOutlineView.m */,
CE003CC211242D00004B0AA7 /* NSIndexPathAdditions.h */,
@@ -342,9 +352,10 @@
CE4B59C41119919700C06C9E /* xib */ = {
isa = PBXGroup;
children = (
CE4F934512CCA9470067A3AE /* about.xib */,
CE74A12512537F2E008A8DF0 /* FairwareReminder.xib */,
CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */,
CE4B59C61119919700C06C9E /* progress.xib */,
CE4B59C71119919700C06C9E /* registration.xib */,
);
name = xib;
path = ../../cocoalib/xib;
@@ -362,16 +373,18 @@
CE515DE10FC6C12E00EC695D /* Dialogs.m */,
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */,
CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */,
CE74A12112537F06008A8DF0 /* HSFairwareReminder.h */,
CE74A12212537F06008A8DF0 /* HSFairwareReminder.m */,
CE74A12312537F06008A8DF0 /* PyFairware.h */,
CE4F934712CCA96C0067A3AE /* HSAboutBox.h */,
CE4F934812CCA96C0067A3AE /* HSAboutBox.m */,
CE003CB911242D00004B0AA7 /* NSEventAdditions.h */,
CE003CBA11242D00004B0AA7 /* NSEventAdditions.m */,
CE515DE60FC6C12E00EC695D /* ProgressController.h */,
CE515DE70FC6C12E00EC695D /* ProgressController.m */,
CE515DE80FC6C12E00EC695D /* PyApp.h */,
CE003CBE11242D00004B0AA7 /* PyRegistrable.h */,
CE515DE90FC6C12E00EC695D /* RecentDirectories.h */,
CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */,
CE515DEB0FC6C12E00EC695D /* RegistrationInterface.h */,
CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */,
CE515DEF0FC6C12E00EC695D /* Utils.h */,
CE515DF00FC6C12E00EC695D /* Utils.m */,
CE515DF10FC6C12E00EC695D /* ValueTransformers.h */,
@@ -383,6 +396,9 @@
CE515E140FC6C17900EC695D /* dgbase */ = {
isa = PBXGroup;
children = (
CEB14D26124DFC2800FA7481 /* PyResultTable.h */,
CEB14D27124DFC2800FA7481 /* ResultTable.h */,
CEB14D28124DFC2800FA7481 /* ResultTable.m */,
CE003CCD11242D2C004B0AA7 /* DirectoryOutline.h */,
CE003CCE11242D2C004B0AA7 /* DirectoryOutline.m */,
CE003CCF11242D2C004B0AA7 /* PyDirectoryOutline.h */,
@@ -397,14 +413,11 @@
CE0A0C021175A1DE00DCA3C6 /* ProblemDialog.m */,
CE515E1B0FC6C19300EC695D /* ResultWindow.h */,
CE515E1C0FC6C19300EC695D /* ResultWindow.m */,
CE0B3D6511243F83009A7A30 /* ResultOutline.h */,
CE0B3D6611243F83009A7A30 /* ResultOutline.m */,
CEDF07A1112493B200EE5BC0 /* StatsLabel.h */,
CEDF07A2112493B200EE5BC0 /* StatsLabel.m */,
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */,
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */,
CE0A0C031175A1DE00DCA3C6 /* PyProblemDialog.h */,
CE0B3D6411243F83009A7A30 /* PyResultTree.h */,
CEDF07A0112493B200EE5BC0 /* PyStatsLabel.h */,
);
name = dgbase;
@@ -450,7 +463,15 @@
isa = PBXProject;
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */;
compatibilityVersion = "Xcode 3.0";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
en,
);
mainGroup = 29B97314FDCFA39411CA2CEA /* dupeguru */;
projectDirPath = "";
projectRoot = "";
@@ -478,8 +499,9 @@
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */,
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */,
CE4B59C91119919700C06C9E /* progress.xib in Resources */,
CE4B59CA1119919700C06C9E /* registration.xib in Resources */,
CE0A0C061175A24800DCA3C6 /* ProblemDialog.xib in Resources */,
CE74A12712537F2E008A8DF0 /* FairwareReminder.xib in Resources */,
CE4F934612CCA9470067A3AE /* about.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -498,7 +520,6 @@
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */,
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */,
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */,
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */,
CE515DFA0FC6C12E00EC695D /* Utils.m in Sources */,
CE515DFB0FC6C12E00EC695D /* ValueTransformers.m in Sources */,
CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */,
@@ -514,15 +535,30 @@
CE003CCB11242D00004B0AA7 /* NSIndexPathAdditions.m in Sources */,
CE003CCC11242D00004B0AA7 /* NSTableViewAdditions.m in Sources */,
CE003CD011242D2C004B0AA7 /* DirectoryOutline.m in Sources */,
CE0B3D6711243F83009A7A30 /* ResultOutline.m in Sources */,
CEDF07A3112493B200EE5BC0 /* StatsLabel.m in Sources */,
CE0A0C001175A1C000DCA3C6 /* HSTable.m in Sources */,
CE0A0C041175A1DE00DCA3C6 /* ProblemDialog.m in Sources */,
CEB14D29124DFC2800FA7481 /* ResultTable.m in Sources */,
CE578303124DFC660004769C /* HSTableView.m in Sources */,
CE74A12412537F06008A8DF0 /* HSFairwareReminder.m in Sources */,
CE4F934912CCA96C0067A3AE /* HSAboutBox.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
CE74A12512537F2E008A8DF0 /* FairwareReminder.xib */ = {
isa = PBXVariantGroup;
children = (
CE74A12612537F2E008A8DF0 /* en */,
);
name = FairwareReminder.xib;
path = ../../cocoalib/xib;
sourceTree = SOURCE_ROOT;
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
C01FCF4C08A954540054247B /* release */ = {
isa = XCBuildConfiguration;

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -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">10D573</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.SystemVersion">10F569</string>
<string key="IBDocument.InterfaceBuilderVersion">788</string>
<string key="IBDocument.AppKitVersion">1038.29</string>
<string key="IBDocument.HIToolboxVersion">460.00</string>
<string key="IBDocument.HIToolboxVersion">461.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">740</string>
<string key="NS.object.0">788</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -41,7 +41,7 @@
<object class="NSUserDefaultsController" id="579641073">
<object class="NSMutableArray" key="NSDeclaredKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>CustomCommand</string>
<string>ignoreHardlinkMatches</string>
</object>
<bool key="NSSharedInstance">YES</bool>
</object>
@@ -698,7 +698,7 @@
<object class="NSTextField" id="519483808">
<reference key="NSNextResponder" ref="76055040"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{14, 192}, {85, 13}}</string>
<string key="NSFrame">{{14, 172}, {85, 13}}</string>
<reference key="NSSuperview" ref="76055040"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="142188233">
@@ -714,7 +714,7 @@
<object class="NSTextField" id="839713145">
<reference key="NSNextResponder" ref="76055040"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{14, 164}, {324, 17}}</string>
<string key="NSFrame">{{14, 144}, {324, 17}}</string>
<reference key="NSSuperview" ref="76055040"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="380716547">
@@ -730,7 +730,7 @@
<object class="NSPopUpButton" id="1046542754">
<reference key="NSNextResponder" ref="76055040"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{104, 185}, {234, 26}}</string>
<string key="NSFrame">{{104, 165}, {234, 26}}</string>
<reference key="NSSuperview" ref="76055040"/>
<bool key="NSEnabled">YES</bool>
<object class="NSPopUpButtonCell" key="NSCell" id="923770094">
@@ -800,7 +800,7 @@
<object class="NSTextField" id="330569030">
<reference key="NSNextResponder" ref="76055040"/>
<int key="NSvFlags">266</int>
<string key="NSFrame">{{17, 142}, {326, 22}}</string>
<string key="NSFrame">{{17, 122}, {326, 22}}</string>
<reference key="NSSuperview" ref="76055040"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="573680954">
@@ -827,6 +827,27 @@
</object>
</object>
</object>
<object class="NSButton" id="1065764374">
<reference key="NSNextResponder" ref="76055040"/>
<int key="NSvFlags">256</int>
<string key="NSFrame">{{15, 195}, {265, 18}}</string>
<reference key="NSSuperview" ref="76055040"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="621426332">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">0</int>
<string key="NSContents">Ignore duplicates hardlinking to the same file</string>
<reference key="NSSupport" ref="26"/>
<reference key="NSControlView" ref="1065764374"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<reference key="NSAlternateImage" ref="150447483"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
</object>
</object>
<string key="NSFrame">{{10, 33}, {360, 234}}</string>
</object>
@@ -1461,6 +1482,22 @@
</object>
<int key="connectionID">124</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: values.ignoreHardlinkMatches</string>
<reference key="source" ref="1065764374"/>
<reference key="destination" ref="579641073"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="1065764374"/>
<reference key="NSDestination" ref="579641073"/>
<string key="NSLabel">value: values.ignoreHardlinkMatches</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">values.ignoreHardlinkMatches</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">128</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -1563,9 +1600,10 @@
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="836095588"/>
<reference ref="1046542754"/>
<reference ref="1065764374"/>
<reference ref="519483808"/>
<reference ref="839713145"/>
<reference ref="1046542754"/>
<reference ref="330569030"/>
</object>
<reference key="parent" ref="963172262"/>
@@ -2020,6 +2058,20 @@
<reference key="object" ref="573680954"/>
<reference key="parent" ref="330569030"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">125</int>
<reference key="object" ref="1065764374"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="621426332"/>
</object>
<reference key="parent" ref="76055040"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">126</int>
<reference key="object" ref="621426332"/>
<reference key="parent" ref="1065764374"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
@@ -2045,6 +2097,9 @@
<string>121.IBPluginDependency</string>
<string>122.IBPluginDependency</string>
<string>123.IBPluginDependency</string>
<string>125.IBPluginDependency</string>
<string>125.ImportedFromIB2</string>
<string>126.IBPluginDependency</string>
<string>13.IBPluginDependency</string>
<string>13.ImportedFromIB2</string>
<string>14.IBPluginDependency</string>
@@ -2165,6 +2220,9 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
@@ -2174,9 +2232,9 @@
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>{{318, 425}, {406, 326}}</string>
<string>{{555, 271}, {406, 326}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{318, 425}, {406, 326}}</string>
<string>{{555, 271}, {406, 326}}</string>
<boolean value="YES"/>
<boolean value="YES"/>
<string>{213, 107}</string>
@@ -2275,7 +2333,7 @@
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">124</int>
<int key="maxID">128</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -2825,6 +2883,13 @@
<string key="NS.key.0">showWindow:</string>
<string key="NS.object.0">id</string>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<string key="NS.key.0">showWindow:</string>
<object class="IBActionInfo" key="NS.object.0">
<string key="name">showWindow:</string>
<string key="candidateClassName">id</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
@@ -2833,6 +2898,7 @@
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
<integer value="1050" key="NS.object.0"/>
@@ -2844,5 +2910,18 @@
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">../dupeguru.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSMenuCheckmark</string>
<string>NSMenuMixedState</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>{9, 8}</string>
<string>{7, 2}</string>
</object>
</object>
</data>
</archive>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,14 +1,13 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "AppDelegate.h"
#import "ProgressController.h"
#import "RegistrationInterface.h"
#import "Utils.h"
#import "ValueTransformers.h"
#import "Consts.h"
@@ -25,6 +24,7 @@ http://www.hardcoded.net/licenses/hs_license
[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:[NSArray array] forKey:@"recentDirectories"];

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "../base/Consts.h"

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "Utils.h"

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "DirectoryPanel.h"

View File

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

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,15 +1,14 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "ResultWindow.h"
#import "Dialogs.h"
#import "ProgressController.h"
#import "RegistrationInterface.h"
#import "Utils.h"
#import "AppDelegate.h"
#import "Consts.h"
@@ -20,10 +19,9 @@ http://www.hardcoded.net/licenses/hs_license
{
[super awakeFromNib];
[[self window] setTitle:@"dupeGuru Picture Edition"];
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,5)];
[deltaColumns removeIndex:3];
[deltaColumns removeIndex:4];
[outline setDeltaColumns:deltaColumns];
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndex:2];
[deltaColumns addIndex:5];
[table setDeltaColumns:deltaColumns];
}
/* Actions */
@@ -41,13 +39,13 @@ http://www.hardcoded.net/licenses/hs_license
[columnsOrder addObject:@"1"];
[columnsOrder addObject:@"2"];
[columnsOrder addObject:@"4"];
[columnsOrder addObject:@"7"];
[columnsOrder addObject:@"6"];
NSMutableDictionary *columnsWidth = [NSMutableDictionary dictionary];
[columnsWidth setObject:i2n(121) forKey:@"0"];
[columnsWidth setObject:i2n(120) forKey:@"1"];
[columnsWidth setObject:i2n(162) forKey:@"0"];
[columnsWidth setObject:i2n(142) forKey:@"1"];
[columnsWidth setObject:i2n(63) forKey:@"2"];
[columnsWidth setObject:i2n(73) forKey:@"4"];
[columnsWidth setObject:i2n(58) forKey:@"7"];
[columnsWidth setObject:i2n(58) forKey:@"6"];
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
}
@@ -61,13 +59,12 @@ http://www.hardcoded.net/licenses/hs_license
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
PyDupeGuru *_py = (PyDupeGuru *)py;
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
[_py setMixFileKind:[ud objectForKey:@"mixFileKind"]];
[_py setMixFileKind:n2b([ud objectForKey:@"mixFileKind"])];
[_py setIgnoreHardlinkMatches:n2b([ud objectForKey:@"ignoreHardlinkMatches"])];
[_py setMatchScaled:[ud objectForKey:@"matchScaled"]];
int r = n2i([py doScan]);
if (r != 0)
[[ProgressController mainProgressController] hide];
if (r == 1)
[Dialogs showMessage:@"You cannot make a duplicate scan with only reference directories."];
if (r == 3)
{
[Dialogs showMessage:@"The selected directories contain no scannable file."];
@@ -92,9 +89,8 @@ http://www.hardcoded.net/licenses/hs_license
[_resultColumns addObject:sizeCol];
[_resultColumns addObject:[self getColumnForIdentifier:3 title:@"Kind" width:40 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:4 title:@"Dimensions" width:80 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Creation" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Modification" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Match %" width:58 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Dupe Count" width:80 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Modification" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Match %" width:58 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Dupe Count" width:80 refCol:refCol]];
}
@end

View File

@@ -1,16 +1,20 @@
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
from core_pe import app_cocoa as app_pe_cocoa
# Fix py2app imports which chokes on relative imports and other stuff
from core_pe import block, cache, matchbase, data, _block_osx
from lxml import etree, _elementpath
import hsutil.conflict
import core.engine, core.fs, core.app
import core_pe.block, core_pe.cache, core_pe.matchbase, core_pe.data, core_pe._block_osx
import xml.etree.ElementPath
import gzip
import aem.kae
import appscript.defaultterminology
class PyDupeGuru(PyDupeGuruBase):
def init(self):
@@ -23,10 +27,10 @@ class PyDupeGuru(PyDupeGuruBase):
#---Information
def getSelectedDupePath(self):
return unicode(self.py.selected_dupe_path())
return str(self.py.selected_dupe_path())
def getSelectedDupeRefPath(self):
return unicode(self.py.selected_dupe_ref_path())
return str(self.py.selected_dupe_ref_path())
#---Properties
def setMatchScaled_(self,match_scaled):

View File

@@ -17,6 +17,8 @@
CE0C2AC81177021600BC749F /* ProblemDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE0C2AC71177021600BC749F /* ProblemDialog.xib */; };
CE15C8A80ADEB8B50061D4A5 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; };
CE15C8C00ADEB8D40061D4A5 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; };
CE1EB5FE12537F9D0034AABB /* HSFairwareReminder.m in Sources */ = {isa = PBXBuildFile; fileRef = CE1EB5FC12537F9D0034AABB /* HSFairwareReminder.m */; };
CE1EB60112537FB90034AABB /* FairwareReminder.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE1EB5FF12537FB90034AABB /* FairwareReminder.xib */; };
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9A09914ADF003581CE /* ResultWindow.m */; };
CE381D0509915304003581CE /* dg_cocoa.plugin in Resources */ = {isa = PBXBuildFile; fileRef = CE381CF509915304003581CE /* dg_cocoa.plugin */; };
@@ -27,12 +29,10 @@
CE77C8A810946CE20078B0DB /* DetailsPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE77C8A710946CE20078B0DB /* DetailsPanel.xib */; };
CE7AC9181119911200D02F6C /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */; };
CE7AC9191119911200D02F6C /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9161119911200D02F6C /* progress.xib */; };
CE7AC91A1119911200D02F6C /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9171119911200D02F6C /* registration.xib */; };
CE80DB2E0FC192D60086DCA6 /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB1C0FC192D60086DCA6 /* Dialogs.m */; };
CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB1E0FC192D60086DCA6 /* HSErrorReportWindow.m */; };
CE80DB310FC192D60086DCA6 /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB220FC192D60086DCA6 /* ProgressController.m */; };
CE80DB320FC192D60086DCA6 /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB250FC192D60086DCA6 /* RecentDirectories.m */; };
CE80DB330FC192D60086DCA6 /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB270FC192D60086DCA6 /* RegistrationInterface.m */; };
CE80DB350FC192D60086DCA6 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB2B0FC192D60086DCA6 /* Utils.m */; };
CE80DB360FC192D60086DCA6 /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB2D0FC192D60086DCA6 /* ValueTransformers.m */; };
CE80DB470FC193650086DCA6 /* NSNotificationAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB460FC193650086DCA6 /* NSNotificationAdditions.m */; };
@@ -41,7 +41,6 @@
CE80DB8B0FC1951C0086DCA6 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */; };
CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB890FC1951C0086DCA6 /* ResultWindow.m */; };
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
CE95865E112C516400F95FD2 /* ResultOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE95865B112C516400F95FD2 /* ResultOutline.m */; };
CE95865F112C516400F95FD2 /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE95865D112C516400F95FD2 /* StatsLabel.m */; };
CE9EA7561122C96C008CD2BC /* HSGUIController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7441122C96C008CD2BC /* HSGUIController.m */; };
CE9EA7571122C96C008CD2BC /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7461122C96C008CD2BC /* HSOutline.m */; };
@@ -52,9 +51,13 @@
CE9EA75C1122C96C008CD2BC /* NSTableViewAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7551122C96C008CD2BC /* NSTableViewAdditions.m */; };
CE9EA7721122CA0B008CD2BC /* DirectoryOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7701122CA0B008CD2BC /* DirectoryOutline.m */; };
CEBAE4270FDA97E000B7887D /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CEBAE4240FDA97E000B7887D /* BRSingleLineFormatter.m */; };
CEC9DB4712CCAA6B003102F0 /* about.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEC9DB4612CCAA6B003102F0 /* about.xib */; };
CEC9DB4C12CCAA7D003102F0 /* HSAboutBox.m in Sources */ = {isa = PBXBuildFile; fileRef = CEC9DB4B12CCAA7D003102F0 /* HSAboutBox.m */; };
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECA899A09DB132E00A3D774 /* DetailsPanel.h */; };
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CECA899B09DB132E00A3D774 /* DetailsPanel.m */; };
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
CEF12A7E124DFD400087B51D /* HSTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = CEF12A7D124DFD400087B51D /* HSTableView.m */; };
CEF12A84124DFD620087B51D /* ResultTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CEF12A83124DFD620087B51D /* ResultTable.m */; };
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; };
CEFC295509C89FF200D9F998 /* details32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295309C89FF200D9F998 /* details32.png */; };
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295409C89FF200D9F998 /* preferences32.png */; };
@@ -96,6 +99,10 @@
CE0C2AC71177021600BC749F /* ProblemDialog.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ProblemDialog.xib; path = ../base/xib/ProblemDialog.xib; sourceTree = SOURCE_ROOT; };
CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
CE18126F111C9D5100E49FCE /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
CE1EB5FB12537F9D0034AABB /* HSFairwareReminder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSFairwareReminder.h; path = ../../cocoalib/HSFairwareReminder.h; sourceTree = SOURCE_ROOT; };
CE1EB5FC12537F9D0034AABB /* HSFairwareReminder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSFairwareReminder.m; path = ../../cocoalib/HSFairwareReminder.m; sourceTree = SOURCE_ROOT; };
CE1EB5FD12537F9D0034AABB /* PyFairware.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyFairware.h; path = ../../cocoalib/PyFairware.h; sourceTree = SOURCE_ROOT; };
CE1EB60012537FB90034AABB /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = ../../cocoalib/en.lproj/FairwareReminder.xib; sourceTree = SOURCE_ROOT; };
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; };
@@ -110,7 +117,6 @@
CE77C8A710946CE20078B0DB /* DetailsPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DetailsPanel.xib; sourceTree = "<group>"; };
CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ErrorReportWindow.xib; sourceTree = "<group>"; };
CE7AC9161119911200D02F6C /* progress.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = progress.xib; sourceTree = "<group>"; };
CE7AC9171119911200D02F6C /* registration.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = registration.xib; sourceTree = "<group>"; };
CE80DB1B0FC192D60086DCA6 /* Dialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Dialogs.h; path = ../../cocoalib/Dialogs.h; sourceTree = SOURCE_ROOT; };
CE80DB1C0FC192D60086DCA6 /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; };
CE80DB1D0FC192D60086DCA6 /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
@@ -120,8 +126,6 @@
CE80DB230FC192D60086DCA6 /* PyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyApp.h; path = ../../cocoalib/PyApp.h; sourceTree = SOURCE_ROOT; };
CE80DB240FC192D60086DCA6 /* RecentDirectories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RecentDirectories.h; path = ../../cocoalib/RecentDirectories.h; sourceTree = SOURCE_ROOT; };
CE80DB250FC192D60086DCA6 /* RecentDirectories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RecentDirectories.m; path = ../../cocoalib/RecentDirectories.m; sourceTree = SOURCE_ROOT; };
CE80DB260FC192D60086DCA6 /* RegistrationInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegistrationInterface.h; path = ../../cocoalib/RegistrationInterface.h; sourceTree = SOURCE_ROOT; };
CE80DB270FC192D60086DCA6 /* RegistrationInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegistrationInterface.m; path = ../../cocoalib/RegistrationInterface.m; sourceTree = SOURCE_ROOT; };
CE80DB2A0FC192D60086DCA6 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = ../../cocoalib/Utils.h; sourceTree = SOURCE_ROOT; };
CE80DB2B0FC192D60086DCA6 /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Utils.m; path = ../../cocoalib/Utils.m; sourceTree = SOURCE_ROOT; };
CE80DB2C0FC192D60086DCA6 /* ValueTransformers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueTransformers.h; path = ../../cocoalib/ValueTransformers.h; sourceTree = SOURCE_ROOT; };
@@ -139,10 +143,7 @@
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultWindow.h; path = ../base/ResultWindow.h; sourceTree = SOURCE_ROOT; };
CE80DB890FC1951C0086DCA6 /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultWindow.m; path = ../base/ResultWindow.m; sourceTree = SOURCE_ROOT; };
CE848A1809DD85810004CB44 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
CE958658112C516400F95FD2 /* PyResultTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTree.h; path = ../base/PyResultTree.h; sourceTree = SOURCE_ROOT; };
CE958659112C516400F95FD2 /* PyStatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyStatsLabel.h; path = ../base/PyStatsLabel.h; sourceTree = SOURCE_ROOT; };
CE95865A112C516400F95FD2 /* ResultOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultOutline.h; path = ../base/ResultOutline.h; sourceTree = SOURCE_ROOT; };
CE95865B112C516400F95FD2 /* ResultOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultOutline.m; path = ../base/ResultOutline.m; sourceTree = SOURCE_ROOT; };
CE95865C112C516400F95FD2 /* StatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StatsLabel.h; path = ../base/StatsLabel.h; sourceTree = SOURCE_ROOT; };
CE95865D112C516400F95FD2 /* StatsLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StatsLabel.m; path = ../base/StatsLabel.m; sourceTree = SOURCE_ROOT; };
CE9EA7431122C96C008CD2BC /* HSGUIController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSGUIController.h; sourceTree = "<group>"; };
@@ -155,7 +156,6 @@
CE9EA74A1122C96C008CD2BC /* NSEventAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSEventAdditions.m; path = ../../cocoalib/NSEventAdditions.m; sourceTree = SOURCE_ROOT; };
CE9EA74C1122C96C008CD2BC /* PyGUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyGUI.h; sourceTree = "<group>"; };
CE9EA74D1122C96C008CD2BC /* PyOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyOutline.h; sourceTree = "<group>"; };
CE9EA74E1122C96C008CD2BC /* PyRegistrable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyRegistrable.h; path = ../../cocoalib/PyRegistrable.h; sourceTree = SOURCE_ROOT; };
CE9EA7501122C96C008CD2BC /* HSOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutlineView.h; sourceTree = "<group>"; };
CE9EA7511122C96C008CD2BC /* HSOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutlineView.m; sourceTree = "<group>"; };
CE9EA7521122C96C008CD2BC /* NSIndexPathAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSIndexPathAdditions.h; sourceTree = "<group>"; };
@@ -167,9 +167,17 @@
CE9EA7711122CA0B008CD2BC /* PyDirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDirectoryOutline.h; path = ../base/PyDirectoryOutline.h; sourceTree = SOURCE_ROOT; };
CEBAE4230FDA97E000B7887D /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; };
CEBAE4240FDA97E000B7887D /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; };
CEC9DB4612CCAA6B003102F0 /* about.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = about.xib; path = ../../cocoalib/xib/about.xib; sourceTree = SOURCE_ROOT; };
CEC9DB4A12CCAA7D003102F0 /* HSAboutBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSAboutBox.h; path = ../../cocoalib/HSAboutBox.h; sourceTree = SOURCE_ROOT; };
CEC9DB4B12CCAA7D003102F0 /* HSAboutBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSAboutBox.m; path = ../../cocoalib/HSAboutBox.m; sourceTree = SOURCE_ROOT; };
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; };
CECA899B09DB132E00A3D774 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DetailsPanel.m; sourceTree = "<group>"; };
CEEB135109C837A2004D2330 /* dupeguru.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = dupeguru.icns; sourceTree = "<group>"; };
CEF12A7C124DFD400087B51D /* HSTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSTableView.h; path = ../../cocoalib/views/HSTableView.h; sourceTree = SOURCE_ROOT; };
CEF12A7D124DFD400087B51D /* HSTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSTableView.m; path = ../../cocoalib/views/HSTableView.m; sourceTree = SOURCE_ROOT; };
CEF12A81124DFD620087B51D /* PyResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTable.h; path = ../base/PyResultTable.h; sourceTree = SOURCE_ROOT; };
CEF12A82124DFD620087B51D /* ResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultTable.h; path = ../base/ResultTable.h; sourceTree = SOURCE_ROOT; };
CEF12A83124DFD620087B51D /* ResultTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultTable.m; path = ../base/ResultTable.m; sourceTree = SOURCE_ROOT; };
CEFC294509C89E3D00D9F998 /* folder32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = folder32.png; path = ../../images/folder32.png; sourceTree = SOURCE_ROOT; };
CEFC295309C89FF200D9F998 /* details32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = details32.png; path = ../../images/details32.png; sourceTree = SOURCE_ROOT; };
CEFC295409C89FF200D9F998 /* preferences32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = preferences32.png; path = ../../images/preferences32.png; sourceTree = SOURCE_ROOT; };
@@ -294,9 +302,10 @@
CE7AC9141119911200D02F6C /* xib */ = {
isa = PBXGroup;
children = (
CEC9DB4612CCAA6B003102F0 /* about.xib */,
CE1EB5FF12537FB90034AABB /* FairwareReminder.xib */,
CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */,
CE7AC9161119911200D02F6C /* progress.xib */,
CE7AC9171119911200D02F6C /* registration.xib */,
);
name = xib;
path = ../../cocoalib/xib;
@@ -320,14 +329,16 @@
CE80DB1C0FC192D60086DCA6 /* Dialogs.m */,
CE80DB1D0FC192D60086DCA6 /* HSErrorReportWindow.h */,
CE80DB1E0FC192D60086DCA6 /* HSErrorReportWindow.m */,
CE1EB5FB12537F9D0034AABB /* HSFairwareReminder.h */,
CE1EB5FC12537F9D0034AABB /* HSFairwareReminder.m */,
CE1EB5FD12537F9D0034AABB /* PyFairware.h */,
CEC9DB4A12CCAA7D003102F0 /* HSAboutBox.h */,
CEC9DB4B12CCAA7D003102F0 /* HSAboutBox.m */,
CE80DB210FC192D60086DCA6 /* ProgressController.h */,
CE80DB220FC192D60086DCA6 /* ProgressController.m */,
CE80DB230FC192D60086DCA6 /* PyApp.h */,
CE9EA74E1122C96C008CD2BC /* PyRegistrable.h */,
CE80DB240FC192D60086DCA6 /* RecentDirectories.h */,
CE80DB250FC192D60086DCA6 /* RecentDirectories.m */,
CE80DB260FC192D60086DCA6 /* RegistrationInterface.h */,
CE80DB270FC192D60086DCA6 /* RegistrationInterface.m */,
CE80DB2A0FC192D60086DCA6 /* Utils.h */,
CE80DB2B0FC192D60086DCA6 /* Utils.m */,
CE80DB2C0FC192D60086DCA6 /* ValueTransformers.h */,
@@ -339,6 +350,9 @@
CE80DB810FC194BD0086DCA6 /* dgbase */ = {
isa = PBXGroup;
children = (
CEF12A81124DFD620087B51D /* PyResultTable.h */,
CEF12A82124DFD620087B51D /* ResultTable.h */,
CEF12A83124DFD620087B51D /* ResultTable.m */,
CE80DB820FC1951C0086DCA6 /* AppDelegate.h */,
CE80DB830FC1951C0086DCA6 /* AppDelegate.m */,
CE80DB840FC1951C0086DCA6 /* Consts.h */,
@@ -352,9 +366,6 @@
CE0C2ABB1177014200BC749F /* ProblemDialog.m */,
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */,
CE80DB890FC1951C0086DCA6 /* ResultWindow.m */,
CE958658112C516400F95FD2 /* PyResultTree.h */,
CE95865A112C516400F95FD2 /* ResultOutline.h */,
CE95865B112C516400F95FD2 /* ResultOutline.m */,
CE95865C112C516400F95FD2 /* StatsLabel.h */,
CE95865D112C516400F95FD2 /* StatsLabel.m */,
CE80DB870FC1951C0086DCA6 /* PyDupeGuru.h */,
@@ -396,6 +407,8 @@
CE9EA74F1122C96C008CD2BC /* views */ = {
isa = PBXGroup;
children = (
CEF12A7C124DFD400087B51D /* HSTableView.h */,
CEF12A7D124DFD400087B51D /* HSTableView.m */,
CE9EA7501122C96C008CD2BC /* HSOutlineView.h */,
CE9EA7511122C96C008CD2BC /* HSOutlineView.m */,
CE9EA7521122C96C008CD2BC /* NSIndexPathAdditions.h */,
@@ -457,7 +470,15 @@
isa = PBXProject;
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */;
compatibilityVersion = "Xcode 3.0";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
en,
);
mainGroup = 29B97314FDCFA39411CA2CEA /* dupeguru */;
projectDirPath = "";
projectRoot = "";
@@ -486,8 +507,9 @@
CE031754109B345200517EE6 /* MainMenu.xib in Resources */,
CE7AC9181119911200D02F6C /* ErrorReportWindow.xib in Resources */,
CE7AC9191119911200D02F6C /* progress.xib in Resources */,
CE7AC91A1119911200D02F6C /* registration.xib in Resources */,
CE0C2AC81177021600BC749F /* ProblemDialog.xib in Resources */,
CE1EB60112537FB90034AABB /* FairwareReminder.xib in Resources */,
CEC9DB4712CCAA6B003102F0 /* about.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -507,7 +529,6 @@
CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */,
CE80DB310FC192D60086DCA6 /* ProgressController.m in Sources */,
CE80DB320FC192D60086DCA6 /* RecentDirectories.m in Sources */,
CE80DB330FC192D60086DCA6 /* RegistrationInterface.m in Sources */,
CE80DB350FC192D60086DCA6 /* Utils.m in Sources */,
CE80DB360FC192D60086DCA6 /* ValueTransformers.m in Sources */,
CE80DB470FC193650086DCA6 /* NSNotificationAdditions.m in Sources */,
@@ -525,15 +546,30 @@
CE9EA75B1122C96C008CD2BC /* NSIndexPathAdditions.m in Sources */,
CE9EA75C1122C96C008CD2BC /* NSTableViewAdditions.m in Sources */,
CE9EA7721122CA0B008CD2BC /* DirectoryOutline.m in Sources */,
CE95865E112C516400F95FD2 /* ResultOutline.m in Sources */,
CE95865F112C516400F95FD2 /* StatsLabel.m in Sources */,
CE0C2AB61177011000BC749F /* HSTable.m in Sources */,
CE0C2ABD1177014200BC749F /* ProblemDialog.m in Sources */,
CEF12A7E124DFD400087B51D /* HSTableView.m in Sources */,
CEF12A84124DFD620087B51D /* ResultTable.m in Sources */,
CE1EB5FE12537F9D0034AABB /* HSFairwareReminder.m in Sources */,
CEC9DB4C12CCAA7D003102F0 /* HSAboutBox.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
CE1EB5FF12537FB90034AABB /* FairwareReminder.xib */ = {
isa = PBXVariantGroup;
children = (
CE1EB60012537FB90034AABB /* en */,
);
name = FairwareReminder.xib;
path = ../../cocoalib/xib;
sourceTree = SOURCE_ROOT;
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
C01FCF4C08A954540054247B /* release */ = {
isa = XCBuildConfiguration;

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -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">10C540</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.AppKitVersion">1038.25</string>
<string key="IBDocument.HIToolboxVersion">458.00</string>
<string key="IBDocument.SystemVersion">10F569</string>
<string key="IBDocument.InterfaceBuilderVersion">788</string>
<string key="IBDocument.AppKitVersion">1038.29</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">740</string>
<string key="NS.object.0">788</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -434,6 +434,7 @@
<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
<string key="NSMinSize">{451, 177}</string>
<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
<string key="NSFrameAutosaveName">DetailsPanel</string>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
@@ -866,6 +867,13 @@
<string key="NS.key.0">detailsTable</string>
<string key="NS.object.0">NSTableView</string>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<string key="NS.key.0">detailsTable</string>
<object class="IBToOneOutletInfo" key="NS.object.0">
<string key="name">detailsTable</string>
<string key="candidateClassName">NSTableView</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../base/DetailsPanel.h</string>
@@ -891,6 +899,35 @@
<string>NSProgressIndicator</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>dupeImage</string>
<string>dupeProgressIndicator</string>
<string>refImage</string>
<string>refProgressIndicator</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
<string key="name">dupeImage</string>
<string key="candidateClassName">NSImageView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">dupeProgressIndicator</string>
<string key="candidateClassName">NSProgressIndicator</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">refImage</string>
<string key="candidateClassName">NSImageView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">refProgressIndicator</string>
<string key="candidateClassName">NSProgressIndicator</string>
</object>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">DetailsPanel.h</string>
@@ -903,6 +940,13 @@
<string key="NS.key.0">detailsTable</string>
<string key="NS.object.0">NSTableView</string>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<string key="NS.key.0">detailsTable</string>
<object class="IBToOneOutletInfo" key="NS.object.0">
<string key="name">detailsTable</string>
<string key="candidateClassName">NSTableView</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string>
<string key="minorKey"/>
@@ -1423,6 +1467,13 @@
<string key="NS.key.0">showWindow:</string>
<string key="NS.object.0">id</string>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<string key="NS.key.0">showWindow:</string>
<object class="IBActionInfo" key="NS.object.0">
<string key="name">showWindow:</string>
<string key="candidateClassName">id</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
@@ -1431,6 +1482,7 @@
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
<integer value="1050" key="NS.object.0"/>
@@ -1446,5 +1498,9 @@
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<nil key="IBDocument.LastKnownRelativeProjectPath"/>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<string key="NS.key.0">NSApplicationIcon</string>
<string key="NS.object.0">{128, 128}</string>
</object>
</data>
</archive>

View File

@@ -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">10D573</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.SystemVersion">10F569</string>
<string key="IBDocument.InterfaceBuilderVersion">788</string>
<string key="IBDocument.AppKitVersion">1038.29</string>
<string key="IBDocument.HIToolboxVersion">460.00</string>
<string key="IBDocument.HIToolboxVersion">461.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">740</string>
<string key="NS.object.0">788</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -41,7 +41,7 @@
<object class="NSUserDefaultsController" id="455472712">
<object class="NSMutableArray" key="NSDeclaredKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>CustomCommand</string>
<string>ignoreHardlinkMatches</string>
</object>
<bool key="NSSharedInstance">YES</bool>
</object>
@@ -406,10 +406,31 @@
<int key="NSPeriodicInterval">25</int>
</object>
</object>
<object class="NSButton" id="519470955">
<reference key="NSNextResponder" ref="581039403"/>
<int key="NSvFlags">256</int>
<string key="NSFrame">{{15, 100}, {265, 18}}</string>
<reference key="NSSuperview" ref="581039403"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="595497720">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">0</int>
<string key="NSContents">Ignore duplicates hardlinking to the same file</string>
<reference key="NSSupport" ref="26"/>
<reference key="NSControlView" ref="519470955"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<reference key="NSAlternateImage" ref="990345653"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
</object>
<object class="NSTextField" id="748076392">
<reference key="NSNextResponder" ref="581039403"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{14, 97}, {85, 13}}</string>
<string key="NSFrame">{{14, 77}, {85, 13}}</string>
<reference key="NSSuperview" ref="581039403"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="936873031">
@@ -425,7 +446,7 @@
<object class="NSTextField" id="526155835">
<reference key="NSNextResponder" ref="581039403"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{14, 69}, {306, 17}}</string>
<string key="NSFrame">{{14, 49}, {306, 17}}</string>
<reference key="NSSuperview" ref="581039403"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="765798142">
@@ -441,7 +462,7 @@
<object class="NSPopUpButton" id="724953200">
<reference key="NSNextResponder" ref="581039403"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{104, 90}, {216, 26}}</string>
<string key="NSFrame">{{104, 70}, {216, 26}}</string>
<reference key="NSSuperview" ref="581039403"/>
<bool key="NSEnabled">YES</bool>
<object class="NSPopUpButtonCell" key="NSCell" id="601288025">
@@ -517,7 +538,7 @@
<object class="NSTextField" id="590530357">
<reference key="NSNextResponder" ref="581039403"/>
<int key="NSvFlags">266</int>
<string key="NSFrame">{{17, 47}, {312, 22}}</string>
<string key="NSFrame">{{17, 27}, {312, 22}}</string>
<reference key="NSSuperview" ref="581039403"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="922246764">
@@ -790,6 +811,30 @@
</object>
<int key="connectionID">68</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">nextKeyView</string>
<reference key="source" ref="519470955"/>
<reference key="destination" ref="279087998"/>
</object>
<int key="connectionID">72</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: values.ignoreHardlinkMatches</string>
<reference key="source" ref="519470955"/>
<reference key="destination" ref="455472712"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="519470955"/>
<reference key="NSDestination" ref="455472712"/>
<string key="NSLabel">value: values.ignoreHardlinkMatches</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">values.ignoreHardlinkMatches</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">73</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -892,9 +937,10 @@
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="1018598123"/>
<reference ref="724953200"/>
<reference ref="519470955"/>
<reference ref="748076392"/>
<reference ref="526155835"/>
<reference ref="724953200"/>
<reference ref="590530357"/>
</object>
<reference key="parent" ref="1045400351"/>
@@ -1151,6 +1197,20 @@
<reference key="object" ref="922246764"/>
<reference key="parent" ref="590530357"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">69</int>
<reference key="object" ref="519470955"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="595497720"/>
</object>
<reference key="parent" ref="581039403"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">70</int>
<reference key="object" ref="595497720"/>
<reference key="parent" ref="519470955"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
@@ -1221,8 +1281,11 @@
<string>65.IBPluginDependency</string>
<string>66.IBPluginDependency</string>
<string>67.IBPluginDependency</string>
<string>69.IBPluginDependency</string>
<string>69.ImportedFromIB2</string>
<string>7.IBPluginDependency</string>
<string>7.ImportedFromIB2</string>
<string>70.IBPluginDependency</string>
<string>8.IBPluginDependency</string>
<string>8.ImportedFromIB2</string>
<string>9.IBPluginDependency</string>
@@ -1299,6 +1362,9 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
</object>
</object>
@@ -1318,7 +1384,7 @@
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">68</int>
<int key="maxID">73</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -1330,6 +1396,13 @@
<string key="minorKey">../views/HSOutlineView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../views/HSTableView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
@@ -1868,6 +1941,13 @@
<string key="NS.key.0">showWindow:</string>
<string key="NS.object.0">id</string>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<string key="NS.key.0">showWindow:</string>
<object class="IBActionInfo" key="NS.object.0">
<string key="name">showWindow:</string>
<string key="candidateClassName">id</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
@@ -1876,6 +1956,7 @@
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
<integer value="1050" key="NS.object.0"/>
@@ -1887,5 +1968,18 @@
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">../dupeguru.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSMenuCheckmark</string>
<string>NSMenuMixedState</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>{9, 8}</string>
<string>{7, 2}</string>
</object>
</object>
</data>
</archive>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,14 +1,13 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "AppDelegate.h"
#import "../../cocoalib/ProgressController.h"
#import "../../cocoalib/RegistrationInterface.h"
#import "../../cocoalib/Utils.h"
#import "../../cocoalib/ValueTransformers.h"
#import "DetailsPanel.h"
@@ -28,6 +27,7 @@ http://www.hardcoded.net/licenses/hs_license
[d setObject:b2n(NO) forKey:@"matchSimilarWords"];
[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(YES) forKey:@"ignoreSmallFiles"];
[d setObject:b2n(NO) forKey:@"debug"];

View File

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

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import "ResultWindow.h"
@@ -18,9 +18,9 @@ http://www.hardcoded.net/licenses/hs_license
- (void)awakeFromNib
{
[super awakeFromNib];
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,4)];
[deltaColumns removeIndex:3];
[outline setDeltaColumns:deltaColumns];
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndex:2];
[deltaColumns addIndex:4];
[table setDeltaColumns:deltaColumns];
}
/* Actions */
@@ -30,12 +30,12 @@ http://www.hardcoded.net/licenses/hs_license
[columnsOrder addObject:@"0"];
[columnsOrder addObject:@"1"];
[columnsOrder addObject:@"2"];
[columnsOrder addObject:@"6"];
[columnsOrder addObject:@"5"];
NSMutableDictionary *columnsWidth = [NSMutableDictionary dictionary];
[columnsWidth setObject:i2n(195) forKey:@"0"];
[columnsWidth setObject:i2n(120) forKey:@"1"];
[columnsWidth setObject:i2n(183) forKey:@"1"];
[columnsWidth setObject:i2n(63) forKey:@"2"];
[columnsWidth setObject:i2n(60) forKey:@"6"];
[columnsWidth setObject:i2n(60) forKey:@"5"];
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
}
@@ -51,7 +51,8 @@ http://www.hardcoded.net/licenses/hs_license
[_py setScanType:[ud objectForKey:@"scanType"]];
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
[_py setWordWeighting:[ud objectForKey:@"wordWeighting"]];
[_py setMixFileKind:[ud objectForKey:@"mixFileKind"]];
[_py setMixFileKind:n2b([ud objectForKey:@"mixFileKind"])];
[_py setIgnoreHardlinkMatches:n2b([ud objectForKey:@"ignoreHardlinkMatches"])];
[_py setMatchSimilarWords:[ud objectForKey:@"matchSimilarWords"]];
int smallFileThreshold = [ud integerForKey:@"smallFileThreshold"]; // In KB
int sizeThreshold = [ud boolForKey:@"ignoreSmallFiles"] ? smallFileThreshold * 1024 : 0; // The py side wants bytes
@@ -59,8 +60,6 @@ http://www.hardcoded.net/licenses/hs_license
int r = n2i([py doScan]);
if (r != 0)
[[ProgressController mainProgressController] hide];
if (r == 1)
[Dialogs showMessage:@"You cannot make a duplicate scan with only reference directories."];
if (r == 3)
{
[Dialogs showMessage:@"The selected directories contain no scannable file."];
@@ -80,10 +79,9 @@ http://www.hardcoded.net/licenses/hs_license
[[sizeCol dataCell] setAlignment:NSRightTextAlignment];
[_resultColumns addObject:sizeCol];
[_resultColumns addObject:[self getColumnForIdentifier:3 title:@"Kind" width:40 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:4 title:@"Creation" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Modification" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Match %" width:60 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Words Used" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Dupe Count" width:80 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:4 title:@"Modification" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Match %" width:60 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Words Used" width:120 refCol:refCol]];
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Dupe Count" width:80 refCol:refCol]];
}
@end

View File

@@ -1,18 +1,20 @@
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from hscommon.cocoa import signature
from core import scanner
from core.scanner import ScanType
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
from core_se.app_cocoa import DupeGuru
# Fix py2app imports with chokes on relative imports and other stuff
from core_se import fs, data
from lxml import etree, _elementpath
import hsutil.conflict
import core.engine, core.fs, core.app
import core_se.fs, core_se.data
import xml.etree.ElementPath
import gzip
class PyDupeGuru(PyDupeGuruBase):
@@ -28,8 +30,8 @@ class PyDupeGuru(PyDupeGuruBase):
def setScanType_(self,scan_type):
try:
self.py.scanner.scan_type = [
scanner.SCAN_TYPE_FILENAME,
scanner.SCAN_TYPE_CONTENT
ScanType.Filename,
ScanType.Contents,
][scan_type]
except IndexError:
pass

View File

@@ -12,7 +12,8 @@
CE073F6309CAE1A3005C1D2F /* dupeguru_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_help */; };
CE19BC6311199231007CCEB0 /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */; };
CE19BC6411199231007CCEB0 /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6111199231007CCEB0 /* progress.xib */; };
CE19BC6511199231007CCEB0 /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6211199231007CCEB0 /* registration.xib */; };
CE27D3C112CCA42500859E67 /* about.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE27D3C012CCA42500859E67 /* about.xib */; };
CE27D3C412CCA43800859E67 /* HSAboutBox.m in Sources */ = {isa = PBXBuildFile; fileRef = CE27D3C312CCA43800859E67 /* HSAboutBox.m */; };
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9A09914ADF003581CE /* ResultWindow.m */; };
CE381D0509915304003581CE /* dg_cocoa.plugin in Resources */ = {isa = PBXBuildFile; fileRef = CE381CF509915304003581CE /* dg_cocoa.plugin */; };
@@ -21,6 +22,8 @@
CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; };
CE647E571173024A006D28BA /* ProblemDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = CE647E551173024A006D28BA /* ProblemDialog.m */; };
CE647E591173026F006D28BA /* ProblemDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE647E581173026F006D28BA /* ProblemDialog.xib */; };
CE6DD4E7124CA3070089A48D /* ResultTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CE6DD4E6124CA3070089A48D /* ResultTable.m */; };
CE6DD547124CAF1F0089A48D /* HSTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE6DD546124CAF1F0089A48D /* HSTableView.m */; };
CE6E0DFE1054E9EF008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */; };
CE76FDC4111EE37C006618EA /* HSOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDBF111EE37C006618EA /* HSOutlineView.m */; };
CE76FDC5111EE37C006618EA /* NSIndexPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */; };
@@ -29,8 +32,9 @@
CE76FDD4111EE3A7006618EA /* DirectoryOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDD2111EE3A7006618EA /* DirectoryOutline.m */; };
CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDDE111EE42F006618EA /* HSOutline.m */; };
CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDF6111EE561006618EA /* NSEventAdditions.m */; };
CE79638612536C94008D405B /* FairwareReminder.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE79638412536C94008D405B /* FairwareReminder.xib */; };
CE79638C12536F4E008D405B /* HSFairwareReminder.m in Sources */ = {isa = PBXBuildFile; fileRef = CE79638B12536F4E008D405B /* HSFairwareReminder.m */; };
CE8C53BC117324CE0011B41F /* HSTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CE8C53BB117324CE0011B41F /* HSTable.m */; };
CE91F215113BC22D0010360B /* ResultOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE91F212113BC22D0010360B /* ResultOutline.m */; };
CE91F216113BC22D0010360B /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE91F214113BC22D0010360B /* StatsLabel.m */; };
CEAC6811109B0B7E00B43C85 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEAC6810109B0B7E00B43C85 /* Preferences.xib */; };
CEBE4D74111F0EE1009AAC6D /* HSWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CEBE4D73111F0EE1009AAC6D /* HSWindowController.m */; };
@@ -46,7 +50,6 @@
CEFC7F9F0FC9517500CD5728 /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F8D0FC9517500CD5728 /* HSErrorReportWindow.m */; };
CEFC7FA10FC9517500CD5728 /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F910FC9517500CD5728 /* ProgressController.m */; };
CEFC7FA20FC9517500CD5728 /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F950FC9517500CD5728 /* RecentDirectories.m */; };
CEFC7FA30FC9517500CD5728 /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F970FC9517500CD5728 /* RegistrationInterface.m */; };
CEFC7FA50FC9517500CD5728 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F9B0FC9517500CD5728 /* Utils.m */; };
CEFC7FA60FC9517500CD5728 /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F9D0FC9517500CD5728 /* ValueTransformers.m */; };
CEFC7FB90FC951A700CD5728 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7FB20FC951A700CD5728 /* AppDelegate.m */; };
@@ -78,7 +81,9 @@
CE073F5409CAE1A3005C1D2F /* dupeguru_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_help; path = ../../help_se/dupeguru_help; sourceTree = "<group>"; };
CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ErrorReportWindow.xib; sourceTree = "<group>"; };
CE19BC6111199231007CCEB0 /* progress.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = progress.xib; sourceTree = "<group>"; };
CE19BC6211199231007CCEB0 /* registration.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = registration.xib; sourceTree = "<group>"; };
CE27D3C012CCA42500859E67 /* about.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = about.xib; path = ../../cocoalib/xib/about.xib; sourceTree = SOURCE_ROOT; };
CE27D3C212CCA43800859E67 /* HSAboutBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSAboutBox.h; path = ../../cocoalib/HSAboutBox.h; sourceTree = SOURCE_ROOT; };
CE27D3C312CCA43800859E67 /* HSAboutBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSAboutBox.m; path = ../../cocoalib/HSAboutBox.m; sourceTree = SOURCE_ROOT; };
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; };
@@ -90,6 +95,11 @@
CE647E551173024A006D28BA /* ProblemDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProblemDialog.m; path = ../base/ProblemDialog.m; sourceTree = SOURCE_ROOT; };
CE647E561173024A006D28BA /* PyProblemDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyProblemDialog.h; path = ../base/PyProblemDialog.h; sourceTree = SOURCE_ROOT; };
CE647E581173026F006D28BA /* ProblemDialog.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ProblemDialog.xib; path = ../base/xib/ProblemDialog.xib; sourceTree = SOURCE_ROOT; };
CE6DD4E4124CA3070089A48D /* PyResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTable.h; path = ../base/PyResultTable.h; sourceTree = SOURCE_ROOT; };
CE6DD4E5124CA3070089A48D /* ResultTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultTable.h; path = ../base/ResultTable.h; sourceTree = SOURCE_ROOT; };
CE6DD4E6124CA3070089A48D /* ResultTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultTable.m; path = ../base/ResultTable.m; sourceTree = SOURCE_ROOT; };
CE6DD545124CAF1F0089A48D /* HSTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSTableView.h; path = ../../cocoalib/views/HSTableView.h; sourceTree = SOURCE_ROOT; };
CE6DD546124CAF1F0089A48D /* HSTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSTableView.m; path = ../../cocoalib/views/HSTableView.m; sourceTree = SOURCE_ROOT; };
CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = "<group>"; };
CE6E7407111C997500C350E3 /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
CE76FDBE111EE37C006618EA /* HSOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutlineView.h; sourceTree = "<group>"; };
@@ -109,12 +119,13 @@
CE76FDDE111EE42F006618EA /* HSOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutline.m; sourceTree = "<group>"; };
CE76FDF5111EE561006618EA /* NSEventAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSEventAdditions.h; path = ../../cocoalib/NSEventAdditions.h; sourceTree = SOURCE_ROOT; };
CE76FDF6111EE561006618EA /* NSEventAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSEventAdditions.m; path = ../../cocoalib/NSEventAdditions.m; sourceTree = SOURCE_ROOT; };
CE79638212536C6E008D405B /* PyFairware.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyFairware.h; path = ../../cocoalib/PyFairware.h; sourceTree = SOURCE_ROOT; };
CE79638512536C94008D405B /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = ../../cocoalib/en.lproj/FairwareReminder.xib; sourceTree = SOURCE_ROOT; };
CE79638A12536F4E008D405B /* HSFairwareReminder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSFairwareReminder.h; path = ../../cocoalib/HSFairwareReminder.h; sourceTree = SOURCE_ROOT; };
CE79638B12536F4E008D405B /* HSFairwareReminder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSFairwareReminder.m; path = ../../cocoalib/HSFairwareReminder.m; sourceTree = SOURCE_ROOT; };
CE8C53B61173248F0011B41F /* PyTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyTable.h; sourceTree = "<group>"; };
CE8C53BB117324CE0011B41F /* HSTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSTable.m; sourceTree = "<group>"; };
CE91F20F113BC22D0010360B /* PyResultTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTree.h; path = ../base/PyResultTree.h; sourceTree = SOURCE_ROOT; };
CE91F210113BC22D0010360B /* PyStatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyStatsLabel.h; path = ../base/PyStatsLabel.h; sourceTree = SOURCE_ROOT; };
CE91F211113BC22D0010360B /* ResultOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultOutline.h; path = ../base/ResultOutline.h; sourceTree = SOURCE_ROOT; };
CE91F212113BC22D0010360B /* ResultOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultOutline.m; path = ../base/ResultOutline.m; sourceTree = SOURCE_ROOT; };
CE91F213113BC22D0010360B /* StatsLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StatsLabel.h; path = ../base/StatsLabel.h; sourceTree = SOURCE_ROOT; };
CE91F214113BC22D0010360B /* StatsLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StatsLabel.m; path = ../base/StatsLabel.m; sourceTree = SOURCE_ROOT; };
CEAC6810109B0B7E00B43C85 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Preferences.xib; path = xib/Preferences.xib; sourceTree = "<group>"; };
@@ -137,11 +148,8 @@
CEFC7F900FC9517500CD5728 /* ProgressController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressController.h; path = ../../cocoalib/ProgressController.h; sourceTree = SOURCE_ROOT; };
CEFC7F910FC9517500CD5728 /* ProgressController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProgressController.m; path = ../../cocoalib/ProgressController.m; sourceTree = SOURCE_ROOT; };
CEFC7F920FC9517500CD5728 /* PyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyApp.h; path = ../../cocoalib/PyApp.h; sourceTree = SOURCE_ROOT; };
CEFC7F930FC9517500CD5728 /* PyRegistrable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyRegistrable.h; path = ../../cocoalib/PyRegistrable.h; sourceTree = SOURCE_ROOT; };
CEFC7F940FC9517500CD5728 /* RecentDirectories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RecentDirectories.h; path = ../../cocoalib/RecentDirectories.h; sourceTree = SOURCE_ROOT; };
CEFC7F950FC9517500CD5728 /* RecentDirectories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RecentDirectories.m; path = ../../cocoalib/RecentDirectories.m; sourceTree = SOURCE_ROOT; };
CEFC7F960FC9517500CD5728 /* RegistrationInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegistrationInterface.h; path = ../../cocoalib/RegistrationInterface.h; sourceTree = SOURCE_ROOT; };
CEFC7F970FC9517500CD5728 /* RegistrationInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegistrationInterface.m; path = ../../cocoalib/RegistrationInterface.m; sourceTree = SOURCE_ROOT; };
CEFC7F9A0FC9517500CD5728 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = ../../cocoalib/Utils.h; sourceTree = SOURCE_ROOT; };
CEFC7F9B0FC9517500CD5728 /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Utils.m; path = ../../cocoalib/Utils.m; sourceTree = SOURCE_ROOT; };
CEFC7F9C0FC9517500CD5728 /* ValueTransformers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueTransformers.h; path = ../../cocoalib/ValueTransformers.h; sourceTree = SOURCE_ROOT; };
@@ -249,9 +257,10 @@
CE19BC5F11199231007CCEB0 /* xib */ = {
isa = PBXGroup;
children = (
CE27D3C012CCA42500859E67 /* about.xib */,
CE79638412536C94008D405B /* FairwareReminder.xib */,
CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */,
CE19BC6111199231007CCEB0 /* progress.xib */,
CE19BC6211199231007CCEB0 /* registration.xib */,
);
name = xib;
path = ../../cocoalib/xib;
@@ -260,6 +269,8 @@
CE76FDBD111EE37C006618EA /* views */ = {
isa = PBXGroup;
children = (
CE6DD545124CAF1F0089A48D /* HSTableView.h */,
CE6DD546124CAF1F0089A48D /* HSTableView.m */,
CE76FDBE111EE37C006618EA /* HSOutlineView.h */,
CE76FDBF111EE37C006618EA /* HSOutlineView.m */,
CE76FDC0111EE37C006618EA /* NSIndexPathAdditions.h */,
@@ -343,14 +354,16 @@
CEFC7F8B0FC9517500CD5728 /* Dialogs.m */,
CEFC7F8C0FC9517500CD5728 /* HSErrorReportWindow.h */,
CEFC7F8D0FC9517500CD5728 /* HSErrorReportWindow.m */,
CE79638A12536F4E008D405B /* HSFairwareReminder.h */,
CE79638B12536F4E008D405B /* HSFairwareReminder.m */,
CE79638212536C6E008D405B /* PyFairware.h */,
CE27D3C212CCA43800859E67 /* HSAboutBox.h */,
CE27D3C312CCA43800859E67 /* HSAboutBox.m */,
CEFC7F900FC9517500CD5728 /* ProgressController.h */,
CEFC7F910FC9517500CD5728 /* ProgressController.m */,
CEFC7F920FC9517500CD5728 /* PyApp.h */,
CEFC7F930FC9517500CD5728 /* PyRegistrable.h */,
CEFC7F940FC9517500CD5728 /* RecentDirectories.h */,
CEFC7F950FC9517500CD5728 /* RecentDirectories.m */,
CEFC7F960FC9517500CD5728 /* RegistrationInterface.h */,
CEFC7F970FC9517500CD5728 /* RegistrationInterface.m */,
CEFC7F9A0FC9517500CD5728 /* Utils.h */,
CEFC7F9B0FC9517500CD5728 /* Utils.m */,
CEFC7F9C0FC9517500CD5728 /* ValueTransformers.h */,
@@ -362,10 +375,10 @@
CEFC7FB00FC9518F00CD5728 /* dgbase */ = {
isa = PBXGroup;
children = (
CE91F20F113BC22D0010360B /* PyResultTree.h */,
CE6DD4E4124CA3070089A48D /* PyResultTable.h */,
CE6DD4E5124CA3070089A48D /* ResultTable.h */,
CE6DD4E6124CA3070089A48D /* ResultTable.m */,
CE91F210113BC22D0010360B /* PyStatsLabel.h */,
CE91F211113BC22D0010360B /* ResultOutline.h */,
CE91F212113BC22D0010360B /* ResultOutline.m */,
CE91F213113BC22D0010360B /* StatsLabel.h */,
CE91F214113BC22D0010360B /* StatsLabel.m */,
CE76FDD1111EE3A7006618EA /* DirectoryOutline.h */,
@@ -421,7 +434,15 @@
};
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */;
compatibilityVersion = "Xcode 3.0";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
en,
);
mainGroup = 29B97314FDCFA39411CA2CEA /* dupeguru */;
projectDirPath = "";
projectRoot = "";
@@ -449,8 +470,9 @@
CE3A46FA109B212E002ABFD5 /* MainMenu.xib in Resources */,
CE19BC6311199231007CCEB0 /* ErrorReportWindow.xib in Resources */,
CE19BC6411199231007CCEB0 /* progress.xib in Resources */,
CE19BC6511199231007CCEB0 /* registration.xib in Resources */,
CE647E591173026F006D28BA /* ProblemDialog.xib in Resources */,
CE79638612536C94008D405B /* FairwareReminder.xib in Resources */,
CE27D3C112CCA42500859E67 /* about.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -468,7 +490,6 @@
CEFC7F9F0FC9517500CD5728 /* HSErrorReportWindow.m in Sources */,
CEFC7FA10FC9517500CD5728 /* ProgressController.m in Sources */,
CEFC7FA20FC9517500CD5728 /* RecentDirectories.m in Sources */,
CEFC7FA30FC9517500CD5728 /* RegistrationInterface.m in Sources */,
CEFC7FA50FC9517500CD5728 /* Utils.m in Sources */,
CEFC7FA60FC9517500CD5728 /* ValueTransformers.m in Sources */,
CEFC7FB90FC951A700CD5728 /* AppDelegate.m in Sources */,
@@ -484,15 +505,30 @@
CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */,
CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */,
CEBE4D74111F0EE1009AAC6D /* HSWindowController.m in Sources */,
CE91F215113BC22D0010360B /* ResultOutline.m in Sources */,
CE91F216113BC22D0010360B /* StatsLabel.m in Sources */,
CE647E571173024A006D28BA /* ProblemDialog.m in Sources */,
CE8C53BC117324CE0011B41F /* HSTable.m in Sources */,
CE6DD4E7124CA3070089A48D /* ResultTable.m in Sources */,
CE6DD547124CAF1F0089A48D /* HSTableView.m in Sources */,
CE79638C12536F4E008D405B /* HSFairwareReminder.m in Sources */,
CE27D3C412CCA43800859E67 /* HSAboutBox.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
CE79638412536C94008D405B /* FairwareReminder.xib */ = {
isa = PBXVariantGroup;
children = (
CE79638512536C94008D405B /* en */,
);
name = FairwareReminder.xib;
path = ../../cocoalib/xib;
sourceTree = SOURCE_ROOT;
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
C01FCF4C08A954540054247B /* release */ = {
isa = XCBuildConfiguration;

View File

@@ -1,9 +1,9 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
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/hs_license
http://www.hardcoded.net/licenses/bsd_license
*/
#import <Cocoa/Cocoa.h>

View File

@@ -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">10D573</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.SystemVersion">10F569</string>
<string key="IBDocument.InterfaceBuilderVersion">788</string>
<string key="IBDocument.AppKitVersion">1038.29</string>
<string key="IBDocument.HIToolboxVersion">460.00</string>
<string key="IBDocument.HIToolboxVersion">461.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">740</string>
<string key="NS.object.0">788</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -623,10 +623,31 @@
<int key="NSPeriodicInterval">25</int>
</object>
</object>
<object class="NSButton" id="647216699">
<reference key="NSNextResponder" ref="448252432"/>
<int key="NSvFlags">256</int>
<string key="NSFrame">{{15, 171}, {265, 18}}</string>
<reference key="NSSuperview" ref="448252432"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="118836063">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">0</int>
<string key="NSContents">Ignore duplicates hardlinking to the same file</string>
<reference key="NSSupport" ref="26"/>
<reference key="NSControlView" ref="647216699"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<reference key="NSAlternateImage" ref="589920880"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
</object>
<object class="NSTextField" id="962804407">
<reference key="NSNextResponder" ref="448252432"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{14, 168}, {315, 17}}</string>
<string key="NSFrame">{{14, 148}, {315, 17}}</string>
<reference key="NSSuperview" ref="448252432"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="111092399">
@@ -642,7 +663,7 @@
<object class="NSTextField" id="873032174">
<reference key="NSNextResponder" ref="448252432"/>
<int key="NSvFlags">266</int>
<string key="NSFrame">{{17, 138}, {309, 22}}</string>
<string key="NSFrame">{{17, 118}, {309, 22}}</string>
<reference key="NSSuperview" ref="448252432"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="249250691">
@@ -659,7 +680,7 @@
<object class="NSTextField" id="511043844">
<reference key="NSNextResponder" ref="448252432"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{14, 117}, {85, 13}}</string>
<string key="NSFrame">{{14, 97}, {85, 13}}</string>
<reference key="NSSuperview" ref="448252432"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="669919489">
@@ -675,7 +696,7 @@
<object class="NSPopUpButton" id="857082145">
<reference key="NSNextResponder" ref="448252432"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{101, 106}, {216, 26}}</string>
<string key="NSFrame">{{101, 86}, {216, 26}}</string>
<reference key="NSSuperview" ref="448252432"/>
<bool key="NSEnabled">YES</bool>
<object class="NSPopUpButtonCell" key="NSCell" id="307074513">
@@ -1068,6 +1089,22 @@
</object>
<int key="connectionID">140</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: values.ignoreHardlinkMatches</string>
<reference key="source" ref="647216699"/>
<reference key="destination" ref="75941798"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="647216699"/>
<reference key="NSDestination" ref="75941798"/>
<string key="NSLabel">value: values.ignoreHardlinkMatches</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">values.ignoreHardlinkMatches</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">144</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -1170,6 +1207,7 @@
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="724127338"/>
<reference ref="647216699"/>
<reference ref="962804407"/>
<reference ref="873032174"/>
<reference ref="511043844"/>
@@ -1552,6 +1590,20 @@
<reference key="object" ref="97487610"/>
<reference key="parent" ref="593115874"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">141</int>
<reference key="object" ref="647216699"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="118836063"/>
</object>
<reference key="parent" ref="448252432"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">142</int>
<reference key="object" ref="118836063"/>
<reference key="parent" ref="647216699"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
@@ -1573,6 +1625,9 @@
<string>134.IBPluginDependency</string>
<string>138.IBPluginDependency</string>
<string>139.IBPluginDependency</string>
<string>141.IBPluginDependency</string>
<string>141.ImportedFromIB2</string>
<string>142.IBPluginDependency</string>
<string>51.IBPluginDependency</string>
<string>51.ImportedFromIB2</string>
<string>52.IBEditorWindowLastContentRect</string>
@@ -1675,6 +1730,9 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/>
<string>{{88, 543}, {389, 302}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{88, 543}, {389, 302}}</string>
@@ -1773,7 +1831,7 @@
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">140</int>
<int key="maxID">144</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -1785,6 +1843,13 @@
<string key="minorKey">../views/HSOutlineView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../views/HSTableView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
@@ -2323,6 +2388,13 @@
<string key="NS.key.0">showWindow:</string>
<string key="NS.object.0">id</string>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<string key="NS.key.0">showWindow:</string>
<object class="IBActionInfo" key="NS.object.0">
<string key="name">showWindow:</string>
<string key="candidateClassName">id</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">AppKit.framework/Headers/NSWindowController.h</string>
@@ -2331,6 +2403,7 @@
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
<integer value="1050" key="NS.object.0"/>
@@ -2342,5 +2415,18 @@
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">../dupeguru.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSMenuCheckmark</string>
<string>NSMenuMixedState</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>{9, 8}</string>
<string>{7, 2}</string>
</object>
</object>
</data>
</archive>

View File

@@ -3,9 +3,9 @@
# Created On: 2009-12-30
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import sys
from optparse import OptionParser
@@ -18,7 +18,7 @@ def main(edition, ui, dev):
if ui not in ('cocoa', 'qt'):
ui = 'cocoa' if sys.platform == 'darwin' else 'qt'
build_type = 'Dev' if dev else 'Release'
print "Configuring dupeGuru {0} for UI {1} ({2})".format(edition.upper(), ui, build_type)
print("Configuring dupeGuru {0} for UI {1} ({2})".format(edition.upper(), ui, build_type))
conf = {
'edition': edition,
'ui': ui,

View File

@@ -2,11 +2,9 @@
# Created On: 2006/11/11
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
from __future__ import unicode_literals
# http://www.hardcoded.net/licenses/bsd_license
import os
import os.path as op
@@ -15,7 +13,7 @@ import subprocess
import re
from send2trash import send2trash
from hscommon.reg import RegistrableApplication, RegistrationRequired
from hscommon.reg import RegistrableApplication
from hscommon.notify import Broadcaster
from hsutil import io, files
from hsutil.path import Path
@@ -33,14 +31,9 @@ JOB_DELETE = 'job_delete'
class NoScannableFileError(Exception):
pass
class AllFilesAreRefError(Exception):
pass
class DupeGuru(RegistrableApplication, Broadcaster):
DEMO_LIMIT_DESC = "In the demo version, only 10 duplicates per session can be sent to the recycle bin, moved or copied."
def __init__(self, data_module, appdata, appid):
RegistrableApplication.__init__(self, appid)
def __init__(self, data_module, appdata):
RegistrableApplication.__init__(self, appid=1)
Broadcaster.__init__(self)
self.appdata = appdata
if not op.exists(self.appdata):
@@ -49,50 +42,35 @@ class DupeGuru(RegistrableApplication, Broadcaster):
self.directories = directories.Directories()
self.results = results.Results(data_module)
self.scanner = scanner.Scanner()
self.action_count = 0
self.options = {
'escape_filter_regexp': True,
'clean_empty_dirs': False,
'ignore_hardlink_matches': False,
}
self.selected_dupes = []
def _demo_check(self):
if self.registered:
return
count = self.results.mark_count
if count + self.action_count > 10:
raise RegistrationRequired()
else:
self.action_count += count
def _do_delete(self, j):
def _do_delete(self, j, replace_with_hardlinks):
def op(dupe):
j.add_progress()
return self._do_delete_dupe(dupe)
return self._do_delete_dupe(dupe, replace_with_hardlinks)
j.start_job(self.results.mark_count)
self.results.perform_on_marked(op, True)
def _do_delete_dupe(self, dupe):
def _do_delete_dupe(self, dupe, replace_with_hardlinks):
if not io.exists(dupe.path):
return
send2trash(unicode(dupe.path)) # Raises OSError when there's a problem
send2trash(str(dupe.path)) # Raises OSError when there's a problem
if replace_with_hardlinks:
group = self.results.get_group_of_duplicate(dupe)
ref = group.ref
os.link(str(ref.path), str(dupe.path))
self.clean_empty_dirs(dupe.path[:-1])
def _do_load(self, j):
self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml'))
self.notify('directories_changed')
j = j.start_subjob([1, 9])
self.results.load_from_xml(op.join(self.appdata, 'last_results.xml'), self._get_file, j)
files = flatten(g[:] for g in self.results.groups)
try:
for file in j.iter_with_progress(files, 'Reading metadata %d/%d'):
file._read_all_info(attrnames=self.data.METADATA_TO_READ)
except (OSError, IOError):
# If this error is raised, it means that a file was deleted while we were reading
# metadata. Proper handling of this rare occurrence is complex because there's no easy
# way to remove an arbitrary file from the Results. Thus, we simply clear them.
self.results.groups = []
def _get_display_info(self, dupe, group, delta=False):
if (dupe is None) or (group is None):
@@ -100,12 +78,19 @@ class DupeGuru(RegistrableApplication, Broadcaster):
try:
return self.data.GetDisplayInfo(dupe, group, delta)
except Exception as e:
logging.warning("Exception on GetDisplayInfo for %s: %s", unicode(dupe.path), unicode(e))
logging.warning("Exception on GetDisplayInfo for %s: %s", str(dupe.path), str(e))
return ['---'] * len(self.data.COLUMNS)
def _get_file(self, str_path):
path = Path(str_path)
return fs.get_file(path, self.directories.fileclasses)
f = fs.get_file(path, self.directories.fileclasses)
if f is None:
return None
try:
f._read_all_info(attrnames=self.data.METADATA_TO_READ)
return f
except EnvironmentError:
return None
def _job_completed(self, jobid):
# Must be called by subclasses when they detect that an async job is completed.
@@ -123,14 +108,29 @@ class DupeGuru(RegistrableApplication, Broadcaster):
def _reveal_path(path):
raise NotImplementedError()
@staticmethod
def _remove_hardlink_dupes(files):
seen_inodes = set()
result = []
for file in files:
try:
inode = io.stat(file.path).st_ino
except OSError:
# The file was probably deleted or something
continue
if inode not in seen_inodes:
seen_inodes.add(inode)
result.append(file)
return result
def _select_dupes(self, dupes):
if dupes == self.selected_dupes:
return
self.selected_dupes = dupes
self.notify('dupes_selected')
def _start_job(self, jobid, func):
# func(j)
def _start_job(self, jobid, func, *args):
# func(j, *args)
raise NotImplementedError()
def add_directory(self, d):
@@ -149,7 +149,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
g = self.results.get_group_of_duplicate(dupe)
for other in g:
if other is not dupe:
self.scanner.ignore_list.Ignore(unicode(other.path), unicode(dupe.path))
self.scanner.ignore_list.Ignore(str(other.path), str(dupe.path))
self.remove_duplicates(dupes)
def apply_filter(self, filter):
@@ -198,17 +198,15 @@ class DupeGuru(RegistrableApplication, Broadcaster):
j.start_job(self.results.mark_count)
self.results.perform_on_marked(op, not copy)
self._demo_check()
jobid = JOB_COPY if copy else JOB_MOVE
self._start_job(jobid, do)
def delete_marked(self):
self._demo_check()
self._start_job(JOB_DELETE, self._do_delete)
def delete_marked(self, replace_with_hardlinks=False):
self._start_job(JOB_DELETE, self._do_delete, replace_with_hardlinks)
def export_to_xhtml(self, column_ids):
column_ids = [colid for colid in column_ids if colid.isdigit()]
column_ids = map(int, column_ids)
column_ids = list(map(int, column_ids))
column_ids.sort()
colnames = [col['display'] for i, col in enumerate(self.data.COLUMNS) if i in column_ids]
rows = []
@@ -232,8 +230,8 @@ class DupeGuru(RegistrableApplication, Broadcaster):
dupe = self.selected_dupes[0]
group = self.results.get_group_of_duplicate(dupe)
ref = group.ref
cmd = cmd.replace('%d', unicode(dupe.path))
cmd = cmd.replace('%r', unicode(ref.path))
cmd = cmd.replace('%d', str(dupe.path))
cmd = cmd.replace('%r', str(ref.path))
match = re.match(r'"([^"]+)"(.*)', cmd)
if match is not None:
# This code here is because subprocess. Popen doesn't seem to accept, under Windows,
@@ -249,6 +247,11 @@ class DupeGuru(RegistrableApplication, Broadcaster):
self._start_job(JOB_LOAD, self._do_load)
self.load_ignore_list()
def load_from(self, filename):
def do(j):
self.results.load_from_xml(filename, self._get_file, j)
self._start_job(JOB_LOAD, do)
def load_ignore_list(self):
p = op.join(self.appdata, 'ignore_list.xml')
self.scanner.ignore_list.load_from_xml(p)
@@ -313,7 +316,7 @@ class DupeGuru(RegistrableApplication, Broadcaster):
d.rename(newname)
return True
except (IndexError, fs.FSError) as e:
logging.warning("dupeGuru Warning: %s" % unicode(e))
logging.warning("dupeGuru Warning: %s" % str(e))
return False
def reveal_selected(self):
@@ -324,8 +327,14 @@ class DupeGuru(RegistrableApplication, Broadcaster):
if not op.exists(self.appdata):
os.makedirs(self.appdata)
self.directories.save_to_file(op.join(self.appdata, 'last_directories.xml'))
if self.results.is_modified:
self.results.save_to_xml(op.join(self.appdata, 'last_results.xml'))
def save_as(self, filename):
self.results.save_to_xml(filename)
# It's not because we saved it here that we don't want to save it in appdata when we quit
self.results.is_modified = True
def save_ignore_list(self):
if not op.exists(self.appdata):
os.makedirs(self.appdata)
@@ -336,16 +345,15 @@ class DupeGuru(RegistrableApplication, Broadcaster):
def do(j):
j.set_progress(0, 'Collecting files to scan')
files = list(self.directories.get_files())
if self.options['ignore_hardlink_matches']:
files = self._remove_hardlink_dupes(files)
logging.info('Scanning %d files' % len(files))
self.results.groups = self.scanner.GetDupeGroups(files, j)
files = self.directories.get_files()
first_file = first(files)
if first_file is None:
if not self.directories.has_any_file():
raise NoScannableFileError()
if first_file.is_ref and all(f.is_ref for f in files):
raise AllFilesAreRefError()
self.results.groups = []
self.notify('results_changed')
self._start_job(JOB_SCAN, do)
def toggle_selected_mark_state(self):

View File

@@ -2,19 +2,19 @@
# Created On: 2006/11/11
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import logging
import os.path as op
from hscommon import cocoa, job
from jobprogress import job
from hscommon import cocoa
from hscommon.cocoa import install_exception_hook
from hscommon.cocoa.objcmin import (NSNotificationCenter, NSUserDefaults,
NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSUserDomainMask,
NSWorkspace)
from hscommon.reg import RegistrationRequired
from . import app
@@ -26,39 +26,31 @@ JOBID2TITLE = {
app.JOB_DELETE: "Sending to Trash",
}
def demo_method(method):
def wrapper(self, *args, **kwargs):
try:
return method(self, *args, **kwargs)
except RegistrationRequired:
NSNotificationCenter.defaultCenter().postNotificationName_object_('RegistrationRequired', self)
return wrapper
class DupeGuru(app.DupeGuru):
def __init__(self, data_module, appdata_subdir, appid):
def __init__(self, data_module, appdata_subdir):
LOGGING_LEVEL = logging.DEBUG if NSUserDefaults.standardUserDefaults().boolForKey_('debug') else logging.WARNING
logging.basicConfig(level=LOGGING_LEVEL, format='%(levelname)s %(message)s')
logging.debug('started in debug mode')
install_exception_hook()
appsupport = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)[0]
appdata = op.join(appsupport, appdata_subdir)
app.DupeGuru.__init__(self, data_module, appdata, appid)
app.DupeGuru.__init__(self, data_module, appdata)
self.progress = cocoa.ThreadedJobPerformer()
#--- Override
@staticmethod
def _open_path(path):
NSWorkspace.sharedWorkspace().openFile_(unicode(path))
NSWorkspace.sharedWorkspace().openFile_(str(path))
@staticmethod
def _reveal_path(path):
NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(unicode(path), '')
NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(str(path), '')
def _start_job(self, jobid, func):
def _start_job(self, jobid, func, *args):
try:
j = self.progress.create_job()
self.progress.run_threaded(func, args=(j, ))
args = tuple([j] + list(args))
self.progress.run_threaded(func, args=args)
except job.JobInProgressError:
NSNotificationCenter.defaultCenter().postNotificationName_object_('JobInProgress', self)
else:
@@ -66,9 +58,6 @@ class DupeGuru(app.DupeGuru):
NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_('JobStarted', self, ud)
#---Public
copy_or_move_marked = demo_method(app.DupeGuru.copy_or_move_marked)
delete_marked = demo_method(app.DupeGuru.delete_marked)
def start_scanning(self):
self._select_dupes([])
try:
@@ -76,6 +65,4 @@ class DupeGuru(app.DupeGuru):
return 0
except app.NoScannableFileError:
return 3
except app.AllFilesAreRefError:
return 1

View File

@@ -3,26 +3,26 @@
# Created On: 2010-02-02
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
# Common interface for all editions' dg_cocoa unit.
from hscommon.cocoa.inter import signature, PyTable, PyOutline, PyGUIObject, PyRegistrable
from hscommon.cocoa.inter import signature, PyTable, PyOutline, PyGUIObject, PyFairware
from .gui.details_panel import DetailsPanel
from .gui.directory_tree import DirectoryTree
from .gui.problem_dialog import ProblemDialog
from .gui.problem_table import ProblemTable
from .gui.result_tree import ResultTree
from .gui.result_table import ResultTable
from .gui.stats_label import StatsLabel
# Fix py2app's problems on relative imports
from core import app, app_cocoa, data, directories, engine, export, ignore, results, fs, scanner
from hsutil import conflict
class PyDupeGuruBase(PyRegistrable):
class PyDupeGuruBase(PyFairware):
#---Directories
def addDirectory_(self, directory):
return self.py.add_directory(directory)
@@ -46,6 +46,9 @@ class PyDupeGuruBase(PyRegistrable):
def loadResults(self):
self.py.load()
def loadResultsFrom_(self, filename):
self.py.load_from(filename)
def markAll(self):
self.py.mark_all()
@@ -67,6 +70,9 @@ class PyDupeGuruBase(PyRegistrable):
def saveResults(self):
self.py.save()
def saveResultsAs_(self, filename):
self.py.save_as(filename)
#---Actions
def addSelectedToIgnoreList(self):
self.py.add_selected_to_ignore_list()
@@ -74,6 +80,9 @@ class PyDupeGuruBase(PyRegistrable):
def deleteMarked(self):
self.py.delete_marked()
def hardlinkMarked(self):
self.py.delete_marked(replace_with_hardlinks=True)
def applyFilter_(self, filter):
self.py.apply_filter(filter)
@@ -110,21 +119,38 @@ class PyDupeGuruBase(PyRegistrable):
return bool(self.py.results.problems)
#---Properties
@signature('v@:c')
def setMixFileKind_(self, mix_file_kind):
self.py.scanner.mix_file_kind = mix_file_kind
@signature('v@:c')
def setEscapeFilterRegexp_(self, escape_filter_regexp):
self.py.options['escape_filter_regexp'] = escape_filter_regexp
@signature('v@:c')
def setRemoveEmptyFolders_(self, remove_empty_folders):
self.py.options['clean_empty_dirs'] = remove_empty_folders
@signature('v@:c')
def setIgnoreHardlinkMatches_(self, ignore_hardlink_matches):
self.py.options['ignore_hardlink_matches'] = ignore_hardlink_matches
#---Worker
def getJobProgress(self):
try:
return self.py.progress.last_progress
except AttributeError:
# I have *no idea* why this can possible happen (last_progress is always set by
# create_job() *before* any threaded job notification, which shows the progress panel,
# is sent), but it happens anyway, so there we go. ref: #106
return -1
def getJobDesc(self):
try:
return self.py.progress.last_desc
except AttributeError:
# see getJobProgress
return ''
def cancelJob(self):
self.py.progress.job_cancelled = True
@@ -151,8 +177,8 @@ class PyDirectoryOutline(PyOutline):
self.py.add_directory(path)
class PyResultOutline(PyOutline):
py_class = ResultTree
class PyResultTable(PyTable):
py_class = ResultTable
@signature('c@:')
def powerMarkerMode(self):
@@ -170,9 +196,9 @@ class PyResultOutline(PyOutline):
def setDeltaValuesMode_(self, value):
self.py.delta_values = value
@signature('@@:@i')
def valueForPath_column_(self, path, column):
return self.py.get_node_value(path, column)
@signature('@@:ii')
def valueForRow_column_(self, row_index, column):
return self.py.get_row_value(row_index, column)
@signature('c@:@')
def renameSelected_(self, newname):
@@ -188,8 +214,9 @@ class PyResultOutline(PyOutline):
def removeSelected(self):
self.py.app.remove_selected()
def rootChildrenCounts(self):
return self.py.root_children_counts()
@signature('i@:')
def selectedDupeCount(self):
return self.py.selected_dupe_count
# python --> cocoa
def invalidate_markings(self):

View File

@@ -2,16 +2,16 @@
# Created On: 2006/03/15
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from hsutil.str import format_time, FT_DECIMAL, format_size
import time
def format_path(p):
return unicode(p[:-1])
return str(p[:-1])
def format_timestamp(t, delta):
if delta:
@@ -38,4 +38,4 @@ def format_dupe_count(c):
return str(c) if c else '---'
def cmp_value(value):
return value.lower() if isinstance(value, basestring) else value
return value.lower() if isinstance(value, str) else value

View File

@@ -2,11 +2,11 @@
# Created On: 2006/02/27
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from lxml import etree
from xml.etree import ElementTree as ET
from hsutil import io
from hsutil.files import FileOrPath
@@ -124,12 +124,19 @@ class Directories(object):
else:
return STATE_NORMAL
def has_any_file(self):
try:
next(self.get_files())
return True
except StopIteration:
return False
def load_from_file(self, infile):
try:
root = etree.parse(infile).getroot()
except:
root = ET.parse(infile).getroot()
except Exception:
return
for rdn in root.iterchildren('root_directory'):
for rdn in root.getiterator('root_directory'):
attrib = rdn.attrib
if 'path' not in attrib:
continue
@@ -138,7 +145,7 @@ class Directories(object):
self.add_path(Path(path))
except (AlreadyThereError, InvalidPathError):
pass
for sn in root.iterchildren('state'):
for sn in root.getiterator('state'):
attrib = sn.attrib
if not ('path' in attrib and 'value' in attrib):
continue
@@ -148,15 +155,15 @@ class Directories(object):
def save_to_file(self, outfile):
with FileOrPath(outfile, 'wb') as fp:
root = etree.Element('directories')
root = ET.Element('directories')
for root_path in self:
root_path_node = etree.SubElement(root, 'root_directory')
root_path_node.set('path', unicode(root_path))
for path, state in self.states.iteritems():
state_node = etree.SubElement(root, 'state')
state_node.set('path', unicode(path))
state_node.set('value', unicode(state))
tree = etree.ElementTree(root)
root_path_node = ET.SubElement(root, 'root_directory')
root_path_node.set('path', str(root_path))
for path, state in self.states.items():
state_node = ET.SubElement(root, 'state')
state_node.set('path', str(path))
state_node.set('value', str(state))
tree = ET.ElementTree(root)
tree.write(fp, encoding='utf-8')
def set_state(self, path, state):

View File

@@ -2,11 +2,11 @@
# Created On: 2006/01/29
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from __future__ import division
import difflib
import itertools
import logging
@@ -16,7 +16,7 @@ from unicodedata import normalize
from hsutil.misc import flatten
from hsutil.str import multi_replace
from hscommon import job
from jobprogress import job
(WEIGHT_WORDS,
MATCH_SIMILAR_WORDS,
@@ -25,15 +25,15 @@ NO_FIELD_ORDER) = range(3)
JOB_REFRESH_RATE = 100
def getwords(s):
if isinstance(s, unicode):
if isinstance(s, str):
s = normalize('NFD', s)
s = multi_replace(s, "-_&+():;\\[]{}.,<>/?~!@#$*", ' ').lower()
s = ''.join(c for c in s if c in string.ascii_letters + string.digits + string.whitespace)
return filter(None, s.split(' ')) # filter() is to remove empty elements
return [_f for _f in s.split(' ') if _f] # remove empty elements
def getfields(s):
fields = [getwords(field) for field in s.split(' - ')]
return filter(None, fields)
return [_f for _f in fields if _f]
def unpack_fields(fields):
result = []
@@ -118,7 +118,7 @@ def build_word_dict(objects, j=job.nulljob):
def merge_similar_words(word_dict):
"""Take all keys in word_dict that are similar, and merge them together.
"""
keys = word_dict.keys()
keys = list(word_dict.keys())
keys.sort(key=len)# we want the shortest word to stay
while keys:
key = keys.pop(0)
@@ -138,7 +138,7 @@ def reduce_common_words(word_dict, threshold):
Because if we remove them, we will miss some duplicates!
"""
uncommon_words = set(word for word, objects in word_dict.items() if len(objects) < threshold)
for word, objects in word_dict.items():
for word, objects in list(word_dict.items()):
if len(objects) < threshold:
continue
reduced = set()

View File

@@ -2,18 +2,17 @@
# Created On: 2006/09/16
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import tempfile
import os.path as op
from tempfile import mkdtemp
# Yes, this is a very low-tech solution, but at least it doesn't have all these annoying dependency
# and resource problems.
MAIN_TEMPLATE = u"""
MAIN_TEMPLATE = """
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>
<html xmlns="http://www.w3.org/1999/xhtml">
@@ -104,34 +103,34 @@ $rows
</html>
"""
COLHEADERS_TEMPLATE = u"<th>{name}</th>"
COLHEADERS_TEMPLATE = "<th>{name}</th>"
ROW_TEMPLATE = u"""
ROW_TEMPLATE = """
<tr>
<td class="{indented}">{filename}</td>{cells}
</tr>
"""
CELL_TEMPLATE = u"""<td>{value}</td>"""
CELL_TEMPLATE = """<td>{value}</td>"""
def export_to_xhtml(colnames, rows):
# a row is a list of values with the first value being a flag indicating if the row should be indented
if rows:
assert len(rows[0]) == len(colnames) + 1 # + 1 is for the "indented" flag
colheaders = u''.join(COLHEADERS_TEMPLATE.format(name=name) for name in colnames)
colheaders = ''.join(COLHEADERS_TEMPLATE.format(name=name) for name in colnames)
rendered_rows = []
for row in rows:
# [2:] is to remove the indented flag + filename
indented = u'indented' if row[0] else u''
indented = 'indented' if row[0] else ''
filename = row[1]
cells = u''.join(CELL_TEMPLATE.format(value=value) for value in row[2:])
cells = ''.join(CELL_TEMPLATE.format(value=value) for value in row[2:])
rendered_rows.append(ROW_TEMPLATE.format(indented=indented, filename=filename, cells=cells))
rendered_rows = u''.join(rendered_rows)
rendered_rows = ''.join(rendered_rows)
# The main template can't use format because the css code uses {}
content = MAIN_TEMPLATE.replace('$colheaders', colheaders).replace('$rows', rendered_rows)
folder = mkdtemp()
destpath = op.join(folder, u'export.htm')
fp = open(destpath, 'w')
fp.write(content.encode('utf-8'))
destpath = op.join(folder, 'export.htm')
fp = open(destpath, 'wt', encoding='utf-8')
fp.write(content)
fp.close()
return destpath

View File

@@ -3,16 +3,16 @@
# Created On: 2009-10-22
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
# This is a fork from hsfs. The reason for this fork is that hsfs has been designed for musicGuru
# and was re-used for dupeGuru. The problem is that hsfs is way over-engineered for dupeGuru,
# resulting needless complexity and memory usage. It's been a while since I wanted to do that fork,
# and I'm doing it now.
from __future__ import unicode_literals
import hashlib
import logging
@@ -25,13 +25,13 @@ class FSError(Exception):
cls_message = "An error has occured on '{name}' in '{parent}'"
def __init__(self, fsobject, parent=None):
message = self.cls_message
if isinstance(fsobject, basestring):
if isinstance(fsobject, str):
name = fsobject
elif isinstance(fsobject, File):
name = fsobject.name
else:
name = ''
parentname = unicode(parent) if parent is not None else ''
parentname = str(parent) if parent is not None else ''
Exception.__init__(self, message.format(name=name, parent=parentname))
@@ -55,7 +55,6 @@ class OperationError(FSError):
class File(object):
INITIAL_INFO = {
'size': 0,
'ctime': 0,
'mtime': 0,
'md5': '',
'md5partial': '',
@@ -82,10 +81,9 @@ class File(object):
raise AttributeError()
def _read_info(self, field):
if field in ('size', 'ctime', 'mtime'):
if field in ('size', 'mtime'):
stats = io.stat(self.path)
self.size = nonone(stats.st_size, 0)
self.ctime = nonone(stats.st_ctime, 0)
self.mtime = nonone(stats.st_mtime, 0)
elif field == 'md5partial':
try:
@@ -119,7 +117,7 @@ class File(object):
If `attrnames` is not None, caches only attrnames.
"""
if attrnames is None:
attrnames = self.INITIAL_INFO.keys()
attrnames = list(self.INITIAL_INFO.keys())
for attrname in attrnames:
if attrname not in self.__dict__:
self._read_info(attrname)

View File

@@ -3,9 +3,9 @@
# Created On: 2010-02-06
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from hscommon.notify import Listener

View File

@@ -3,9 +3,9 @@
# Created On: 2010-02-05
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from .base import GUIObject
@@ -32,7 +32,7 @@ class DetailsPanel(GUIObject):
ref = group.ref if group is not None and group.ref is not dupe else None
l2 = self.app._get_display_info(ref, group, False)
names = [c['display'] for c in self.app.data.COLUMNS]
self._table = zip(names, l1, l2)
self._table = list(zip(names, l1, l2))
#--- Public
def row_count(self):

View File

@@ -3,11 +3,11 @@
# Created On: 2010-02-06
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from hsgui.tree import Tree, Node
from hscommon.gui.tree import Tree, Node
from ..directories import STATE_NORMAL, STATE_REFERENCE, STATE_EXCLUDED
from .base import GUIObject
@@ -62,7 +62,7 @@ class DirectoryTree(GUIObject, Tree):
def _refresh(self):
self.clear()
for path in self.app.directories:
self.append(DirectoryNode(self.app, path, unicode(path)))
self.append(DirectoryNode(self.app, path, str(path)))
def add_directory(self, path):
self.app.add_directory(path)

View File

@@ -3,9 +3,9 @@
# Created On: 2010-04-12
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from hscommon.notify import Broadcaster

View File

@@ -3,12 +3,12 @@
# Created On: 2010-04-12
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from hscommon.notify import Listener
from hsgui.table import GUITable, Row
from hscommon.gui.table import GUITable, Row
class ProblemTable(GUITable, Listener):
def __init__(self, view, problem_dialog):
@@ -31,7 +31,6 @@ class ProblemTable(GUITable, Listener):
#--- Event handlers
def problems_changed(self):
self.refresh()
self.view.refresh()
class ProblemRow(Row):
@@ -39,5 +38,5 @@ class ProblemRow(Row):
Row.__init__(self, table)
self.dupe = dupe
self.msg = msg
self.path = unicode(dupe.path)
self.path = str(dupe.path)

View File

@@ -3,20 +3,20 @@
# Created On: 2010-02-11
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from operator import attrgetter
from hsgui.tree import Tree, Node
from hscommon.gui.table import GUITable, Row
from .base import GUIObject
class DupeNode(Node):
def __init__(self, app, group, dupe):
Node.__init__(self, '')
self._app = app
class DupeRow(Row):
def __init__(self, table, group, dupe):
Row.__init__(self, table)
self._app = table.app
self._group = group
self._dupe = dupe
self._data = None
@@ -34,6 +34,10 @@ class DupeNode(Node):
self._data_delta = self._app._get_display_info(self._dupe, self._group, True)
return self._data_delta
@property
def isref(self):
return self._dupe is self._group.ref
@property
def markable(self):
return self._app.results.is_markable(self._dupe)
@@ -47,10 +51,10 @@ class DupeNode(Node):
self._app.mark_dupe(self._dupe, value)
class ResultTree(GUIObject, Tree):
class ResultTable(GUIObject, GUITable):
def __init__(self, view, app):
GUIObject.__init__(self, view, app)
Tree.__init__(self)
GUITable.__init__(self)
self._power_marker = False
self._delta_values = False
self._sort_descriptors = (0, True)
@@ -58,61 +62,57 @@ class ResultTree(GUIObject, Tree):
#--- Override
def connect(self):
GUIObject.connect(self)
self._refresh()
self.view.refresh()
self._refresh_with_view()
def _select_nodes(self, nodes):
Tree._select_nodes(self, nodes)
self.app._select_dupes(map(attrgetter('_dupe'), nodes))
def _restore_selection(self, previous_selection):
if self.app.selected_dupes:
to_find = set(self.app.selected_dupes)
indexes = [i for i, r in enumerate(self) if r._dupe in to_find]
self.selected_indexes = indexes
#--- Private
def _refresh(self):
self.clear()
def _update_selection(self):
rows = self.selected_rows
self.app._select_dupes(list(map(attrgetter('_dupe'), rows)))
def _fill(self):
if not self.power_marker:
for group in self.app.results.groups:
group_node = DupeNode(self.app, group, group.ref)
self.append(group_node)
self.append(DupeRow(self, group, group.ref))
for dupe in group.dupes:
group_node.append(DupeNode(self.app, group, dupe))
self.append(DupeRow(self, group, dupe))
else:
for dupe in self.app.results.dupes:
group = self.app.results.get_group_of_duplicate(dupe)
self.append(DupeNode(self.app, group, dupe))
if self.app.selected_dupes:
to_find = set(self.app.selected_dupes)
nodes = list(self.findall(lambda n: n is not self and n._dupe in to_find))
self.selected_nodes = nodes
self.append(DupeRow(self, group, dupe))
def _refresh_with_view(self):
self.refresh()
self.view.show_selected_row()
#--- Public
def get_node_value(self, path, column):
def get_row_value(self, index, column):
try:
node = self.get_node(path)
row = self[index]
except IndexError:
return '---'
if self.delta_values:
return node.data_delta[column]
return row.data_delta[column]
else:
return node.data[column]
return row.data[column]
def rename_selected(self, newname):
node = self.selected_node
node._data = None
node._data_delta = None
row = self.selected_row
row._data = None
row._data_delta = None
return self.app.rename_selected(newname)
def root_children_counts(self):
# This is a speed optimization for cases where there's a lot of results so that there is
# not thousands of children_count queries when expandAll is called.
return [len(node) for node in self]
def sort(self, key, asc):
if self.power_marker:
self.app.results.sort_dupes(key, asc, self.delta_values)
else:
self.app.results.sort_groups(key, asc)
self._sort_descriptors = (key, asc)
self._refresh()
self.view.refresh()
self._refresh_with_view()
#--- Properties
@property
@@ -126,8 +126,7 @@ class ResultTree(GUIObject, Tree):
self._power_marker = value
key, asc = self._sort_descriptors
self.sort(key, asc)
self._refresh()
self.view.refresh()
# no need to refresh, it has happened in sort()
@property
def delta_values(self):
@@ -138,22 +137,24 @@ class ResultTree(GUIObject, Tree):
if value == self._delta_values:
return
self._delta_values = value
self._refresh()
self.view.refresh()
self.refresh()
@property
def selected_dupe_count(self):
return sum(1 for row in self.selected_rows if not row.isref)
#--- Event Handlers
def marking_changed(self):
self.view.invalidate_markings()
def results_changed(self):
self._refresh()
self.view.refresh()
self._refresh_with_view()
def results_changed_but_keep_selection(self):
# What we want to to here is that instead of restoring selected *dupes* after refresh, we
# restore selected *paths*.
paths = self.selected_paths
self._refresh()
self.selected_paths = paths
indexes = self.selected_indexes
self.refresh(refresh_view=False)
self.select(indexes)
self.view.refresh()

View File

@@ -3,9 +3,9 @@
# Created On: 2010-02-11
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from .base import GUIObject

View File

@@ -2,11 +2,11 @@
# Created On: 2006/05/02
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from lxml import etree
from xml.etree import ElementTree as ET
from hsutil.files import FileOrPath
@@ -22,7 +22,7 @@ class IgnoreList(object):
self._count = 0
def __iter__(self):
for first,seconds in self._ignored.iteritems():
for first,seconds in self._ignored.items():
for second in seconds:
yield (first,second)
@@ -77,14 +77,16 @@ class IgnoreList(object):
infile can be a file object or a filename.
"""
try:
root = etree.parse(infile).getroot()
root = ET.parse(infile).getroot()
except Exception:
return
for fn in root.iterchildren('file'):
file_elems = (e for e in root if e.tag == 'file')
for fn in file_elems:
file_path = fn.get('path')
if not file_path:
continue
for sfn in fn.iterchildren('file'):
subfile_elems = (e for e in fn if e.tag == 'file')
for sfn in subfile_elems:
subfile_path = sfn.get('path')
if subfile_path:
self.Ignore(file_path, subfile_path)
@@ -94,14 +96,14 @@ class IgnoreList(object):
outfile can be a file object or a filename.
"""
root = etree.Element('ignore_list')
root = ET.Element('ignore_list')
for filename, subfiles in self._ignored.items():
file_node = etree.SubElement(root, 'file')
file_node = ET.SubElement(root, 'file')
file_node.set('path', filename)
for subfilename in subfiles:
subfile_node = etree.SubElement(file_node, 'file')
subfile_node = ET.SubElement(file_node, 'file')
subfile_node.set('path', subfilename)
tree = etree.ElementTree(root)
tree = ET.ElementTree(root)
with FileOrPath(outfile, 'wb') as fp:
tree.write(fp, encoding='utf-8')

View File

@@ -2,16 +2,16 @@
# Created On: 2006/02/23
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import logging
import re
from lxml import etree
from xml.etree import ElementTree as ET
from . import engine
from hscommon.job import nulljob
from jobprogress.job import nulljob
from hscommon.markable import Markable
from hsutil.misc import flatten, nonone
from hsutil.str import format_size
@@ -33,6 +33,7 @@ class Results(Markable):
self.__marked_size = 0
self.data = data_module
self.problems = [] # (dupe, error_msg)
self.is_modified = False
def _did_mark(self, dupe):
self.__marked_size += dupe.size
@@ -115,6 +116,7 @@ class Results(Markable):
self.__group_of_duplicate[dupe] = g
if not hasattr(dupe, 'is_ref'):
dupe.is_ref = False
self.is_modified = True
old_filters = nonone(self.__filters, [])
self.apply_filter(None)
for filter_str in old_filters:
@@ -147,7 +149,7 @@ class Results(Markable):
self.__filters.append(filter_str)
if self.__filtered_dupes is None:
self.__filtered_dupes = flatten(g[:] for g in self.groups)
self.__filtered_dupes = set(dupe for dupe in self.__filtered_dupes if filter_re.search(unicode(dupe.path)))
self.__filtered_dupes = set(dupe for dupe in self.__filtered_dupes if filter_re.search(str(dupe.path)))
filtered_groups = set()
for dupe in self.__filtered_dupes:
filtered_groups.add(self.get_group_of_duplicate(dupe))
@@ -176,16 +178,16 @@ class Results(Markable):
self.apply_filter(None)
try:
root = etree.parse(infile).getroot()
root = ET.parse(infile).getroot()
except Exception:
return
group_elems = list(root.iterchildren('group'))
group_elems = list(root.getiterator('group'))
groups = []
marked = set()
for group_elem in j.iter_with_progress(group_elems, every=100):
group = engine.Group()
dupes = []
for file_elem in group_elem.iterchildren('file'):
for file_elem in group_elem.getiterator('file'):
path = file_elem.get('path')
words = file_elem.get('words', '')
if not path:
@@ -198,7 +200,7 @@ class Results(Markable):
dupes.append(file)
if file_elem.get('marked') == 'y':
marked.add(file)
for match_elem in group_elem.iterchildren('match'):
for match_elem in group_elem.getiterator('match'):
try:
attrs = match_elem.attrib
first_file = dupes[int(attrs['first'])]
@@ -216,6 +218,7 @@ class Results(Markable):
self.groups = groups
for dupe_file in marked:
self.mark(dupe_file)
self.is_modified = False
def make_ref(self, dupe):
g = self.get_group_of_duplicate(dupe)
@@ -229,6 +232,7 @@ class Results(Markable):
self.__total_count -= 1
self.__total_size -= dupe.size
self.__dupes = None
self.is_modified = True
def perform_on_marked(self, func, remove_from_results):
# Performs `func` on all marked dupes. If an EnvironmentError is raised during the call,
@@ -241,7 +245,7 @@ class Results(Markable):
func(dupe)
to_remove.append(dupe)
except EnvironmentError as e:
self.problems.append((dupe, unicode(e)))
self.problems.append((dupe, str(e)))
if remove_from_results:
self.remove_duplicates(to_remove)
self.mark_none()
@@ -269,13 +273,14 @@ class Results(Markable):
for group in affected_groups:
group.discard_matches()
self.__dupes = None
self.is_modified = True
def save_to_xml(self, outfile):
self.apply_filter(None)
root = etree.Element('results')
root = ET.Element('results')
# writer = XMLGenerator(outfile, 'utf-8')
for g in self.groups:
group_elem = etree.SubElement(root, 'group')
group_elem = ET.SubElement(root, 'group')
dupe2index = {}
for index, d in enumerate(g):
dupe2index[d] = index
@@ -283,22 +288,23 @@ class Results(Markable):
words = engine.unpack_fields(d.words)
except AttributeError:
words = ()
file_elem = etree.SubElement(group_elem, 'file')
file_elem = ET.SubElement(group_elem, 'file')
try:
file_elem.set('path', unicode(d.path))
file_elem.set('path', str(d.path))
file_elem.set('words', ','.join(words))
except ValueError: # If there's an invalid character, just skip the file
file_elem.set('path', '')
file_elem.set('is_ref', ('y' if d.is_ref else 'n'))
file_elem.set('marked', ('y' if self.is_marked(d) else 'n'))
for match in g.matches:
match_elem = etree.SubElement(group_elem, 'match')
match_elem.set('first', unicode(dupe2index[match.first]))
match_elem.set('second', unicode(dupe2index[match.second]))
match_elem.set('percentage', unicode(int(match.percentage)))
tree = etree.ElementTree(root)
match_elem = ET.SubElement(group_elem, 'match')
match_elem.set('first', str(dupe2index[match.first]))
match_elem.set('second', str(dupe2index[match.second]))
match_elem.set('percentage', str(int(match.percentage)))
tree = ET.ElementTree(root)
with FileOrPath(outfile, 'wb') as fp:
tree.write(fp, encoding='utf-8')
self.is_modified = False
def sort_dupes(self, key, asc=True, delta=False):
if not self.__dupes:

View File

@@ -2,14 +2,14 @@
# Created On: 2006/03/03
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import logging
import re
from hscommon import job
from jobprogress import job
from hsutil import io
from hsutil.misc import dedupe
from hsutil.str import get_file_ext, rem_file_ext
@@ -17,16 +17,26 @@ from hsutil.str import get_file_ext, rem_file_ext
from . import engine
from .ignore import IgnoreList
(SCAN_TYPE_FILENAME,
SCAN_TYPE_FIELDS,
SCAN_TYPE_FIELDS_NO_ORDER,
SCAN_TYPE_TAG,
UNUSED, # Must not be removed. Constants here are what scan_type in the prefs are.
SCAN_TYPE_CONTENT,
SCAN_TYPE_CONTENT_AUDIO) = range(7)
class ScanType:
Filename = 0
Fields = 1
FieldsNoOrder = 2
Tag = 3
# number 4 is obsolete
Contents = 5
ContentsAudio = 6
SCANNABLE_TAGS = ['track', 'artist', 'album', 'title', 'genre', 'year']
RE_DIGIT_ENDING = re.compile(r'\d+|\(\d+\)|\[\d+\]|{\d+}')
def is_same_with_digit(name, refname):
# Returns True if name is the same as refname, but with digits (with brackets or not) at the end
if not name.startswith(refname):
return False
end = name[len(refname):].strip()
return RE_DIGIT_ENDING.match(end) is not None
class Scanner(object):
def __init__(self):
self.ignore_list = IgnoreList()
@@ -38,22 +48,22 @@ class Scanner(object):
for f in j.iter_with_progress(files, 'Read size of %d/%d files'):
f.size # pre-read, makes a smoother progress if read here (especially for bundles)
files = [f for f in files if f.size >= self.size_threshold]
if self.scan_type in (SCAN_TYPE_CONTENT, SCAN_TYPE_CONTENT_AUDIO):
sizeattr = 'size' if self.scan_type == SCAN_TYPE_CONTENT else 'audiosize'
return engine.getmatches_by_contents(files, sizeattr, partial=self.scan_type==SCAN_TYPE_CONTENT_AUDIO, j=j)
if self.scan_type in (ScanType.Contents, ScanType.ContentsAudio):
sizeattr = 'size' if self.scan_type == ScanType.Contents else 'audiosize'
return engine.getmatches_by_contents(files, sizeattr, partial=self.scan_type==ScanType.ContentsAudio, j=j)
else:
j = j.start_subjob([2, 8])
kw = {}
kw['match_similar_words'] = self.match_similar_words
kw['weight_words'] = self.word_weighting
kw['min_match_percentage'] = self.min_match_percentage
if self.scan_type == SCAN_TYPE_FIELDS_NO_ORDER:
self.scan_type = SCAN_TYPE_FIELDS
if self.scan_type == ScanType.FieldsNoOrder:
self.scan_type = ScanType.Fields
kw['no_field_order'] = True
func = {
SCAN_TYPE_FILENAME: lambda f: engine.getwords(rem_file_ext(f.name)),
SCAN_TYPE_FIELDS: lambda f: engine.getfields(rem_file_ext(f.name)),
SCAN_TYPE_TAG: lambda f: [engine.getwords(unicode(getattr(f, attrname))) for attrname in SCANNABLE_TAGS if attrname in self.scanned_tags],
ScanType.Filename: lambda f: engine.getwords(rem_file_ext(f.name)),
ScanType.Fields: lambda f: engine.getfields(rem_file_ext(f.name)),
ScanType.Tag: lambda f: [engine.getwords(str(getattr(f, attrname))) for attrname in SCANNABLE_TAGS if attrname in self.scanned_tags],
}[self.scan_type]
for f in j.iter_with_progress(files, 'Read metadata of %d/%d files'):
f.words = func(f)
@@ -67,9 +77,13 @@ class Scanner(object):
def _tie_breaker(ref, dupe):
refname = rem_file_ext(ref.name).lower()
dupename = rem_file_ext(dupe.name).lower()
if 'copy' in refname and 'copy' not in dupename:
if 'copy' in dupename:
return False
if 'copy' in refname:
return True
if refname.startswith(dupename) and (refname[len(dupename):].strip().isdigit()):
if is_same_with_digit(dupename, refname):
return False
if is_same_with_digit(refname, dupename):
return True
return len(dupe.path) > len(ref.path)
@@ -88,7 +102,7 @@ class Scanner(object):
j = j.start_subjob(2)
iter_matches = j.iter_with_progress(matches, 'Processed %d/%d matches against the ignore list')
matches = [m for m in iter_matches
if not self.ignore_list.AreIgnored(unicode(m.first.path), unicode(m.second.path))]
if not self.ignore_list.AreIgnored(str(m.first.path), str(m.second.path))]
logging.info('Grouping matches')
groups = engine.get_groups(matches, j)
matched_files = dedupe([m.first for m in matches] + [m.second for m in matches])
@@ -103,7 +117,7 @@ class Scanner(object):
match_similar_words = False
min_match_percentage = 80
mix_file_kind = True
scan_type = SCAN_TYPE_FILENAME
scan_type = ScanType.Filename
scanned_tags = set(['artist', 'title'])
size_threshold = 0
word_weighting = False

View File

@@ -2,9 +2,9 @@
# Created On: 2007-06-23
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import os
import logging
@@ -15,7 +15,8 @@ from hsutil import io
from hsutil.path import Path
from hsutil.decorators import log_calls
import hsutil.files
from hscommon.job import nulljob
from hscommon.testutil import CallLogger
from jobprogress.job import nulljob, Job, JobCancelled
from . import data
from .results_test import GetTestGroups
@@ -23,32 +24,25 @@ from .. import app, fs, engine
from ..app import DupeGuru as DupeGuruBase
from ..gui.details_panel import DetailsPanel
from ..gui.directory_tree import DirectoryTree
from ..gui.result_tree import ResultTree
from ..gui.result_table import ResultTable
from ..scanner import ScanType
class DupeGuru(DupeGuruBase):
JOB = nulljob
def __init__(self):
DupeGuruBase.__init__(self, data, '/tmp', appid=4)
DupeGuruBase.__init__(self, data, '/tmp')
def _start_job(self, jobid, func):
func(nulljob)
def _start_job(self, jobid, func, *args):
try:
func(self.JOB, *args)
except JobCancelled:
return
class CallLogger(object):
"""This is a dummy object that logs all calls made to it.
It is used to simulate the GUI layer.
"""
def __init__(self):
self.calls = []
def __getattr__(self, func_name):
def func(*args, **kw):
self.calls.append(func_name)
return func
def clear_calls(self):
del self.calls[:]
def add_fake_files_to_directories(directories, files):
directories.get_files = lambda: iter(files)
directories._dirs.append('this is just so Scan() doesnt return 3')
class TCDupeGuru(TestCase):
cls_tested_module = app
@@ -109,7 +103,7 @@ class TCDupeGuru(TestCase):
def test_Scan_with_objects_evaluating_to_false(self):
class FakeFile(fs.File):
def __nonzero__(self):
def __bool__(self):
return False
@@ -118,10 +112,22 @@ class TCDupeGuru(TestCase):
f1, f2 = [FakeFile('foo') for i in range(2)]
f1.is_ref, f2.is_ref = (False, False)
assert not (bool(f1) and bool(f2))
app.directories.get_files = lambda: [f1, f2]
app.directories._dirs.append('this is just so Scan() doesnt return 3')
add_fake_files_to_directories(app.directories, [f1, f2])
app.start_scanning() # no exception
def test_ignore_hardlink_matches(self):
# If the ignore_hardlink_matches option is set, don't match files hardlinking to the same
# inode.
tmppath = Path(self.tmpdir())
io.open(tmppath + 'myfile', 'w').write('foo')
os.link(str(tmppath + 'myfile'), str(tmppath + 'hardlink'))
app = DupeGuru()
app.directories.add_path(tmppath)
app.scanner.scan_type = ScanType.Contents
app.options['ignore_hardlink_matches'] = True
app.start_scanning()
eq_(len(app.results.groups), 0)
class TCDupeGuru_clean_empty_dirs(TestCase):
cls_tested_module = app
@@ -166,93 +172,48 @@ class TCDupeGuruWithResults(TestCase):
self.dpanel = DetailsPanel(self.dpanel_gui, self.app)
self.dtree_gui = CallLogger()
self.dtree = DirectoryTree(self.dtree_gui, self.app)
self.rtree_gui = CallLogger()
self.rtree = ResultTree(self.rtree_gui, self.app)
self.rtable_gui = CallLogger()
self.rtable = ResultTable(self.rtable_gui, self.app)
self.dpanel.connect()
self.dtree.connect()
self.rtree.connect()
self.rtable.connect()
tmppath = self.tmppath()
io.mkdir(tmppath + 'foo')
io.mkdir(tmppath + 'bar')
self.app.directories.add_path(tmppath)
def check_gui_calls(self, gui, expected, verify_order=False):
"""Checks that the expected calls have been made to 'gui', then clears the log.
`expected` is an iterable of strings representing method names.
If `verify_order` is True, the order of the calls matters.
"""
if verify_order:
eq_(gui.calls, expected)
else:
eq_(set(gui.calls), set(expected))
gui.clear_calls()
def check_gui_calls_partial(self, gui, expected=None, not_expected=None):
"""Checks that the expected calls have been made to 'gui', then clears the log.
`expected` is an iterable of strings representing method names. Order doesn't matter.
Moreover, if calls have been made that are not in expected, no failure occur.
`not_expected` can be used for a more explicit check (rather than calling `check_gui_calls`
with an empty `expected`) to assert that calls have *not* been made.
"""
calls = set(gui.calls)
if expected is not None:
expected = set(expected)
not_called = expected - calls
assert not not_called, u"These calls haven't been made: {0}".format(not_called)
if not_expected is not None:
not_expected = set(not_expected)
called = not_expected & calls
assert not called, u"These calls shouldn't have been made: {0}".format(called)
gui.clear_calls()
def clear_gui_calls(self):
for attr in dir(self):
if attr.endswith('_gui'):
gui = getattr(self, attr)
if hasattr(gui, 'calls'): # We might have test methods ending with '_gui'
gui.clear_calls()
def test_GetObjects(self):
objects = self.objects
groups = self.groups
n = self.rtree.get_node([0])
assert n._group is groups[0]
assert n._dupe is objects[0]
n = self.rtree.get_node([0, 0])
assert n._group is groups[0]
assert n._dupe is objects[1]
n = self.rtree.get_node([1, 0])
assert n._group is groups[1]
assert n._dupe is objects[4]
r = self.rtable[0]
assert r._group is groups[0]
assert r._dupe is objects[0]
r = self.rtable[1]
assert r._group is groups[0]
assert r._dupe is objects[1]
r = self.rtable[4]
assert r._group is groups[1]
assert r._dupe is objects[4]
def test_GetObjects_after_sort(self):
objects = self.objects
groups = self.groups[:] # we need an un-sorted reference
self.rtree.sort(0, False) #0 = Filename
n = self.rtree.get_node([0, 0])
assert n._group is groups[1]
assert n._dupe is objects[4]
def test_selected_result_node_paths(self):
# app.selected_dupes is correctly converted into node paths
paths = [[0, 0], [0, 1], [1]]
self.rtree.selected_paths = paths
eq_(self.rtree.selected_paths, paths)
self.rtable.sort(0, False) #0 = Filename
r = self.rtable[1]
assert r._group is groups[1]
assert r._dupe is objects[4]
def test_selected_result_node_paths_after_deletion(self):
# cases where the selected dupes aren't there are correctly handled
paths = [[0, 0], [0, 1], [1]]
self.rtree.selected_paths = paths
self.rtable.select([1, 2, 3])
self.app.remove_selected()
# The first 2 dupes have been removed. The 3rd one is a ref. it stays there, in first pos.
eq_(self.rtree.selected_paths, [[0, 0]]) # no exception
eq_(self.rtable.selected_indexes, [1]) # no exception
def test_selectResultNodePaths(self):
app = self.app
objects = self.objects
self.rtree.selected_paths = [[0, 0], [0, 1]]
self.rtable.select([1, 2])
eq_(len(app.selected_dupes), 2)
assert app.selected_dupes[0] is objects[1]
assert app.selected_dupes[1] is objects[2]
@@ -260,7 +221,7 @@ class TCDupeGuruWithResults(TestCase):
def test_selectResultNodePaths_with_ref(self):
app = self.app
objects = self.objects
self.rtree.selected_paths = [[0, 0], [0, 1], [1]]
self.rtable.select([1, 2, 3])
eq_(len(app.selected_dupes), 3)
assert app.selected_dupes[0] is objects[1]
assert app.selected_dupes[1] is objects[2]
@@ -270,9 +231,9 @@ class TCDupeGuruWithResults(TestCase):
app = self.app
objects = self.objects
groups = self.groups[:] #To keep the old order in memory
self.rtree.sort(0, False) #0 = Filename
self.rtable.sort(0, False) #0 = Filename
#Now, the group order is supposed to be reversed
self.rtree.selected_paths = [[0, 0], [1], [1, 0]]
self.rtable.select([1, 2, 3])
eq_(len(app.selected_dupes), 3)
assert app.selected_dupes[0] is objects[4]
assert app.selected_dupes[1] is groups[0].ref
@@ -280,41 +241,25 @@ class TCDupeGuruWithResults(TestCase):
def test_selected_powermarker_node_paths(self):
# app.selected_dupes is correctly converted into paths
app = self.app
objects = self.objects
self.rtree.power_marker = True
self.rtree.selected_paths = [[0], [1], [2]]
self.rtree.power_marker = False
eq_(self.rtree.selected_paths, [[0, 0], [0, 1], [1, 0]])
self.rtable.power_marker = True
self.rtable.select([0, 1, 2])
self.rtable.power_marker = False
eq_(self.rtable.selected_indexes, [1, 2, 4])
def test_selected_powermarker_node_paths_after_deletion(self):
# cases where the selected dupes aren't there are correctly handled
app = self.app
objects = self.objects
self.rtree.power_marker = True
self.rtree.selected_paths = [[0], [1], [2]]
self.rtable.power_marker = True
self.rtable.select([0, 1, 2])
app.remove_selected()
eq_(self.rtree.selected_paths, []) # no exception
def test_selectPowerMarkerRows(self):
app = self.app
objects = self.objects
self.rtree.selected_paths = [[0, 0], [0, 1], [1, 0]]
eq_(len(app.selected_dupes), 3)
assert app.selected_dupes[0] is objects[1]
assert app.selected_dupes[1] is objects[2]
assert app.selected_dupes[2] is objects[4]
def test_selectPowerMarkerRows_empty(self):
self.rtree.selected_paths = []
eq_(len(self.app.selected_dupes), 0)
eq_(self.rtable.selected_indexes, []) # no exception
def test_selectPowerMarkerRows_after_sort(self):
app = self.app
objects = self.objects
self.rtree.power_marker = True
self.rtree.sort(0, False) #0 = Filename
self.rtree.selected_paths = [[0], [1], [2]]
self.rtable.power_marker = True
self.rtable.sort(0, False) #0 = Filename
self.rtable.select([0, 1, 2])
eq_(len(app.selected_dupes), 3)
assert app.selected_dupes[0] is objects[4]
assert app.selected_dupes[1] is objects[2]
@@ -325,7 +270,7 @@ class TCDupeGuruWithResults(TestCase):
objects = self.objects
app.toggle_selected_mark_state()
eq_(app.results.mark_count, 0)
self.rtree.selected_paths = [[0, 0], [1, 0]]
self.rtable.select([1, 4])
app.toggle_selected_mark_state()
eq_(app.results.mark_count, 2)
assert not app.results.is_marked(objects[0])
@@ -335,18 +280,18 @@ class TCDupeGuruWithResults(TestCase):
assert app.results.is_marked(objects[4])
def test_refreshDetailsWithSelected(self):
self.rtree.selected_paths = [[0, 0], [1, 0]]
self.rtable.select([1, 4])
eq_(self.dpanel.row(0), ('Filename', 'bar bleh', 'foo bar'))
self.check_gui_calls(self.dpanel_gui, ['refresh'])
self.rtree.selected_paths = []
self.dpanel_gui.check_gui_calls(['refresh'])
self.rtable.select([])
eq_(self.dpanel.row(0), ('Filename', '---', '---'))
self.check_gui_calls(self.dpanel_gui, ['refresh'])
self.dpanel_gui.check_gui_calls(['refresh'])
def test_makeSelectedReference(self):
app = self.app
objects = self.objects
groups = self.groups
self.rtree.selected_paths = [[0, 0], [1, 0]]
self.rtable.select([1, 4])
app.make_selected_reference()
assert groups[0].ref is objects[1]
assert groups[1].ref is objects[4]
@@ -355,7 +300,7 @@ class TCDupeGuruWithResults(TestCase):
app = self.app
objects = self.objects
groups = self.groups
self.rtree.selected_paths = [[0, 0], [0, 1], [1, 0]]
self.rtable.select([1, 2, 4])
#Only [0, 0] and [1, 0] must go ref, not [0, 1] because it is a part of the same group
app.make_selected_reference()
assert groups[0].ref is objects[1]
@@ -363,7 +308,7 @@ class TCDupeGuruWithResults(TestCase):
def test_removeSelected(self):
app = self.app
self.rtree.selected_paths = [[0, 0], [1, 0]]
self.rtable.select([1, 4])
app.remove_selected()
eq_(len(app.results.dupes), 1) # the first path is now selected
app.remove_selected()
@@ -386,10 +331,10 @@ class TCDupeGuruWithResults(TestCase):
def test_ignore(self):
app = self.app
self.rtree.selected_path = [1, 0] #The dupe of the second, 2 sized group
self.rtable.select([4]) #The dupe of the second, 2 sized group
app.add_selected_to_ignore_list()
eq_(len(app.scanner.ignore_list), 1)
self.rtree.selected_path = [0, 0] #first dupe of the 3 dupes group
self.rtable.select([1]) #first dupe of the 3 dupes group
app.add_selected_to_ignore_list()
#BOTH the ref and the other dupe should have been added
eq_(len(app.scanner.ignore_list), 3)
@@ -409,25 +354,33 @@ class TCDupeGuruWithResults(TestCase):
def test_only_unicode_is_added_to_ignore_list(self):
def FakeIgnore(first,second):
if not isinstance(first,unicode):
if not isinstance(first,str):
self.fail()
if not isinstance(second,unicode):
if not isinstance(second,str):
self.fail()
app = self.app
app.scanner.ignore_list.Ignore = FakeIgnore
self.rtree.selected_path = [1, 0]
self.rtable.select([4])
app.add_selected_to_ignore_list()
def test_cancel_scan_with_previous_results(self):
# When doing a scan with results being present prior to the scan, correctly invalidate the
# results table.
app = self.app
app.JOB = Job(1, lambda *args, **kw: False) # Cancels the task
add_fake_files_to_directories(app.directories, self.objects) # We want the scan to at least start
app.start_scanning() # will be cancelled immediately
eq_(len(self.rtable), 0)
class TCDupeGuru_renameSelected(TestCase):
def setUp(self):
p = self.tmppath()
fp = open(unicode(p + 'foo bar 1'),mode='w')
fp = open(str(p + 'foo bar 1'),mode='w')
fp.close()
fp = open(unicode(p + 'foo bar 2'),mode='w')
fp = open(str(p + 'foo bar 2'),mode='w')
fp.close()
fp = open(unicode(p + 'foo bar 3'),mode='w')
fp = open(str(p + 'foo bar 3'),mode='w')
fp.close()
files = fs.get_files(p)
matches = engine.getmatches(files)
@@ -440,14 +393,14 @@ class TCDupeGuru_renameSelected(TestCase):
self.groups = groups
self.p = p
self.files = files
self.rtree_gui = CallLogger()
self.rtree = ResultTree(self.rtree_gui, self.app)
self.rtree.connect()
self.rtable_gui = CallLogger()
self.rtable = ResultTable(self.rtable_gui, self.app)
self.rtable.connect()
def test_simple(self):
app = self.app
g = self.groups[0]
self.rtree.selected_path = [0, 0]
self.rtable.select([1])
assert app.rename_selected('renamed')
names = io.listdir(self.p)
assert 'renamed' in names
@@ -457,7 +410,7 @@ class TCDupeGuru_renameSelected(TestCase):
def test_none_selected(self):
app = self.app
g = self.groups[0]
self.rtree.selected_paths = []
self.rtable.select([])
self.mock(logging, 'warning', log_calls(lambda msg: None))
assert not app.rename_selected('renamed')
msg = logging.warning.calls[0]['msg']
@@ -470,7 +423,7 @@ class TCDupeGuru_renameSelected(TestCase):
def test_name_already_exists(self):
app = self.app
g = self.groups[0]
self.rtree.selected_path = [0, 0]
self.rtable.select([1])
self.mock(logging, 'warning', log_calls(lambda msg: None))
assert not app.rename_selected('foo bar 1')
msg = logging.warning.calls[0]['msg']

View File

@@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2010-07-11
# Copyright 2010 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
# This unit is required to make tests work with py.test. When running
import py
def get_testunit(item):
if hasattr(item, 'obj'):
testunit = py.builtin._getimself(item.obj)
if hasattr(testunit, 'global_setup'):
return testunit
def pytest_runtest_setup(item):
testunit = get_testunit(item)
if testunit is not None:
testunit.global_setup()
def pytest_runtest_teardown(item):
testunit = get_testunit(item)
if testunit is not None:
testunit.global_teardown()

View File

@@ -3,9 +3,9 @@
# Created On: 2009-10-23
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
# data module for tests

View File

@@ -2,9 +2,9 @@
# Created On: 2006/02/27
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import os.path as op
import os
@@ -82,8 +82,8 @@ class TCDirectories(TestCase):
def test_AddPath_non_latin(self):
p = Path(self.tmpdir())
to_add = p + u'unicode\u201a'
os.mkdir(unicode(to_add))
to_add = p + 'unicode\u201a'
os.mkdir(str(to_add))
d = Directories()
try:
d.add_path(to_add)
@@ -111,7 +111,7 @@ class TCDirectories(TestCase):
self.assertEqual(STATE_REFERENCE,d.get_state(p))
self.assertEqual(STATE_REFERENCE,d.get_state(p + 'dir1'))
self.assertEqual(1,len(d.states))
self.assertEqual(p,d.states.keys()[0])
self.assertEqual(p,list(d.states.keys())[0])
self.assertEqual(STATE_REFERENCE,d.states[p])
def test_get_state_with_path_not_there(self):
@@ -213,11 +213,11 @@ class TCDirectories(TestCase):
def test_unicode_save(self):
d = Directories()
p1 = self.tmppath() + u'hello\xe9'
p1 = self.tmppath() + 'hello\xe9'
io.mkdir(p1)
io.mkdir(p1 + u'foo\xe9')
io.mkdir(p1 + 'foo\xe9')
d.add_path(p1)
d.set_state(p1 + u'foo\xe9', STATE_EXCLUDED)
d.set_state(p1 + 'foo\xe9', STATE_EXCLUDED)
tmpxml = op.join(self.tmpdir(), 'directories_testunit.xml')
try:
d.save_to_file(tmpxml)

View File

@@ -2,13 +2,13 @@
# Created On: 2006/01/29
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import sys
from hscommon import job
from jobprogress import job
from hsutil.decorators import log_calls
from hsutil.misc import first
from hsutil.testutil import eq_
@@ -62,12 +62,12 @@ class TCgetwords(TestCase):
def test_splitter_chars(self):
self.assertEqual(
[chr(i) for i in xrange(ord('a'),ord('z')+1)],
[chr(i) for i in range(ord('a'),ord('z')+1)],
getwords("a-b_c&d+e(f)g;h\\i[j]k{l}m:n.o,p<q>r/s?t~u!v@w#x$y*z")
)
def test_joiner_chars(self):
self.assertEqual(["aec"], getwords(u"a'e\u0301c"))
self.assertEqual(["aec"], getwords("a'e\u0301c"))
def test_empty(self):
self.assertEqual([], getwords(''))
@@ -76,7 +76,7 @@ class TCgetwords(TestCase):
self.assertEqual(['foo', 'bar'], getwords('FOO BAR'))
def test_decompose_unicode(self):
self.assertEqual(getwords(u'foo\xe9bar'), ['fooebar'])
self.assertEqual(getwords('foo\xe9bar'), ['fooebar'])
class TCgetfields(TestCase):
@@ -768,7 +768,7 @@ class TCget_groups(TestCase):
self.assert_(o3 in g)
def test_four_sized_group(self):
l = [NamedObject("foobar") for i in xrange(4)]
l = [NamedObject("foobar") for i in range(4)]
m = getmatches(l)
r = get_groups(m)
self.assertEqual(1,len(r))

View File

@@ -2,12 +2,12 @@
# Created On: 2006/05/02
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import cStringIO
from lxml import etree
import io
from xml.etree import ElementTree as ET
from hsutil.testutil import eq_
@@ -59,10 +59,10 @@ def test_save_to_xml():
il.Ignore('foo','bar')
il.Ignore('foo','bleh')
il.Ignore('bleh','bar')
f = cStringIO.StringIO()
f = io.BytesIO()
il.save_to_xml(f)
f.seek(0)
doc = etree.parse(f)
doc = ET.parse(f)
root = doc.getroot()
eq_(root.tag, 'ignore_list')
eq_(len(root), 2)
@@ -76,19 +76,18 @@ def test_SaveThenLoad():
il.Ignore('foo', 'bar')
il.Ignore('foo', 'bleh')
il.Ignore('bleh', 'bar')
il.Ignore(u'\u00e9', 'bar')
f = cStringIO.StringIO()
il.Ignore('\u00e9', 'bar')
f = io.BytesIO()
il.save_to_xml(f)
f.seek(0)
f.seek(0)
il = IgnoreList()
il.load_from_xml(f)
eq_(4,len(il))
assert il.AreIgnored(u'\u00e9','bar')
assert il.AreIgnored('\u00e9','bar')
def test_LoadXML_with_empty_file_tags():
f = cStringIO.StringIO()
f.write('<?xml version="1.0" encoding="utf-8"?><ignore_list><file><file/></file></ignore_list>')
f = io.BytesIO()
f.write(b'<?xml version="1.0" encoding="utf-8"?><ignore_list><file><file/></file></ignore_list>')
f.seek(0)
il = IgnoreList()
il.load_from_xml(f)
@@ -130,12 +129,12 @@ def test_filter():
def test_save_with_non_ascii_items():
il = IgnoreList()
il.Ignore(u'\xac', u'\xbf')
f = cStringIO.StringIO()
il.Ignore('\xac', '\xbf')
f = io.BytesIO()
try:
il.save_to_xml(f)
except Exception as e:
raise AssertionError(unicode(e))
raise AssertionError(str(e))
def test_len():
il = IgnoreList()

View File

@@ -3,14 +3,14 @@
# $Id$
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import StringIO
import io
import os.path as op
from lxml import etree
from xml.etree import ElementTree as ET
from hsutil.path import Path
from hsutil.testutil import eq_
@@ -25,7 +25,7 @@ class NamedObject(engine_test.NamedObject):
path = property(lambda x:Path('basepath') + x.name)
is_ref = False
def __nonzero__(self):
def __bool__(self):
return False #Make sure that operations are made correctly when the bool value of files is false.
# Returns a group set that looks like that:
@@ -54,21 +54,24 @@ class TCResultsEmpty(TestCase):
self.test_stat_line() # make sure that the stats line isn't saying we applied a '[' filter
def test_stat_line(self):
self.assertEqual("0 / 0 (0.00 B / 0.00 B) duplicates marked.",self.results.stat_line)
eq_("0 / 0 (0.00 B / 0.00 B) duplicates marked.",self.results.stat_line)
def test_groups(self):
self.assertEqual(0,len(self.results.groups))
eq_(0,len(self.results.groups))
def test_get_group_of_duplicate(self):
self.assert_(self.results.get_group_of_duplicate('foo') is None)
assert self.results.get_group_of_duplicate('foo') is None
def test_save_to_xml(self):
f = StringIO.StringIO()
f = io.BytesIO()
self.results.save_to_xml(f)
f.seek(0)
doc = etree.parse(f)
doc = ET.parse(f)
root = doc.getroot()
self.assertEqual('results', root.tag)
eq_('results', root.tag)
def test_is_modified(self):
assert not self.results.is_modified
class TCResultsWithSomeGroups(TestCase):
@@ -78,57 +81,57 @@ class TCResultsWithSomeGroups(TestCase):
self.results.groups = self.groups
def test_stat_line(self):
self.assertEqual("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
eq_("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
def test_groups(self):
self.assertEqual(2,len(self.results.groups))
eq_(2,len(self.results.groups))
def test_get_group_of_duplicate(self):
for o in self.objects:
g = self.results.get_group_of_duplicate(o)
self.assert_(isinstance(g, engine.Group))
self.assert_(o in g)
self.assert_(self.results.get_group_of_duplicate(self.groups[0]) is None)
assert isinstance(g, engine.Group)
assert o in g
assert self.results.get_group_of_duplicate(self.groups[0]) is None
def test_remove_duplicates(self):
g1,g2 = self.results.groups
self.results.remove_duplicates([g1.dupes[0]])
self.assertEqual(2,len(g1))
self.assert_(g1 in self.results.groups)
eq_(2,len(g1))
assert g1 in self.results.groups
self.results.remove_duplicates([g1.ref])
self.assertEqual(2,len(g1))
self.assert_(g1 in self.results.groups)
eq_(2,len(g1))
assert g1 in self.results.groups
self.results.remove_duplicates([g1.dupes[0]])
self.assertEqual(0,len(g1))
self.assert_(g1 not in self.results.groups)
eq_(0,len(g1))
assert g1 not in self.results.groups
self.results.remove_duplicates([g2.dupes[0]])
self.assertEqual(0,len(g2))
self.assert_(g2 not in self.results.groups)
self.assertEqual(0,len(self.results.groups))
eq_(0,len(g2))
assert g2 not in self.results.groups
eq_(0,len(self.results.groups))
def test_remove_duplicates_with_ref_files(self):
g1,g2 = self.results.groups
self.objects[0].is_ref = True
self.objects[1].is_ref = True
self.results.remove_duplicates([self.objects[2]])
self.assertEqual(0,len(g1))
self.assert_(g1 not in self.results.groups)
eq_(0,len(g1))
assert g1 not in self.results.groups
def test_make_ref(self):
g = self.results.groups[0]
d = g.dupes[0]
self.results.make_ref(d)
self.assert_(d is g.ref)
assert d is g.ref
def test_sort_groups(self):
self.results.make_ref(self.objects[1]) #We want to make the 1024 sized object to go ref.
g1,g2 = self.groups
self.results.sort_groups(2) #2 is the key for size
self.assert_(self.results.groups[0] is g2)
self.assert_(self.results.groups[1] is g1)
assert self.results.groups[0] is g2
assert self.results.groups[1] is g1
self.results.sort_groups(2,False)
self.assert_(self.results.groups[0] is g1)
self.assert_(self.results.groups[1] is g2)
assert self.results.groups[0] is g1
assert self.results.groups[1] is g2
def test_set_groups_when_sorted(self):
self.results.make_ref(self.objects[1]) #We want to make the 1024 sized object to go ref.
@@ -137,24 +140,24 @@ class TCResultsWithSomeGroups(TestCase):
g1,g2 = groups
g1.switch_ref(objects[1])
self.results.groups = groups
self.assert_(self.results.groups[0] is g2)
self.assert_(self.results.groups[1] is g1)
assert self.results.groups[0] is g2
assert self.results.groups[1] is g1
def test_get_dupe_list(self):
self.assertEqual([self.objects[1],self.objects[2],self.objects[4]],self.results.dupes)
eq_([self.objects[1],self.objects[2],self.objects[4]],self.results.dupes)
def test_dupe_list_is_cached(self):
self.assert_(self.results.dupes is self.results.dupes)
assert self.results.dupes is self.results.dupes
def test_dupe_list_cache_is_invalidated_when_needed(self):
o1,o2,o3,o4,o5 = self.objects
self.assertEqual([o2,o3,o5],self.results.dupes)
eq_([o2,o3,o5],self.results.dupes)
self.results.make_ref(o2)
self.assertEqual([o1,o3,o5],self.results.dupes)
eq_([o1,o3,o5],self.results.dupes)
objects,matches,groups = GetTestGroups()
o1,o2,o3,o4,o5 = objects
self.results.groups = groups
self.assertEqual([o2,o3,o5],self.results.dupes)
eq_([o2,o3,o5],self.results.dupes)
def test_dupe_list_sort(self):
o1,o2,o3,o4,o5 = self.objects
@@ -164,9 +167,9 @@ class TCResultsWithSomeGroups(TestCase):
o4.size = 2
o5.size = 1
self.results.sort_dupes(2)
self.assertEqual([o5,o3,o2],self.results.dupes)
eq_([o5,o3,o2],self.results.dupes)
self.results.sort_dupes(2,False)
self.assertEqual([o2,o3,o5],self.results.dupes)
eq_([o2,o3,o5],self.results.dupes)
def test_dupe_list_remember_sort(self):
o1,o2,o3,o4,o5 = self.objects
@@ -177,7 +180,7 @@ class TCResultsWithSomeGroups(TestCase):
o5.size = 1
self.results.sort_dupes(2)
self.results.make_ref(o2)
self.assertEqual([o5,o3,o1],self.results.dupes)
eq_([o5,o3,o1],self.results.dupes)
def test_dupe_list_sort_delta_values(self):
o1,o2,o3,o4,o5 = self.objects
@@ -187,19 +190,69 @@ class TCResultsWithSomeGroups(TestCase):
o4.size = 20
o5.size = 1 #-19
self.results.sort_dupes(2,delta=True)
self.assertEqual([o5,o2,o3],self.results.dupes)
eq_([o5,o2,o3],self.results.dupes)
def test_sort_empty_list(self):
#There was an infinite loop when sorting an empty list.
r = Results(data)
r.sort_dupes(0)
self.assertEqual([],r.dupes)
eq_([],r.dupes)
def test_dupe_list_update_on_remove_duplicates(self):
o1,o2,o3,o4,o5 = self.objects
self.assertEqual(3,len(self.results.dupes))
eq_(3,len(self.results.dupes))
self.results.remove_duplicates([o2])
self.assertEqual(2,len(self.results.dupes))
eq_(2,len(self.results.dupes))
def test_is_modified(self):
# Changing the groups sets the modified flag
assert self.results.is_modified
def test_is_modified_after_save_and_load(self):
# Saving/Loading a file sets the modified flag back to False
def get_file(path):
return [f for f in self.objects if str(f.path) == path][0]
f = io.BytesIO()
self.results.save_to_xml(f)
assert not self.results.is_modified
self.results.groups = self.groups # sets the flag back
f.seek(0)
self.results.load_from_xml(f, get_file)
assert not self.results.is_modified
class ResultsWithSavedResults(TestCase):
def setUp(self):
self.results = Results(data)
self.objects,self.matches,self.groups = GetTestGroups()
self.results.groups = self.groups
self.f = io.BytesIO()
self.results.save_to_xml(self.f)
self.f.seek(0)
def test_is_modified(self):
# Saving a file sets the modified flag back to False
assert not self.results.is_modified
def test_is_modified_after_load(self):
# Loading a file sets the modified flag back to False
def get_file(path):
return [f for f in self.objects if str(f.path) == path][0]
self.results.groups = self.groups # sets the flag back
self.results.load_from_xml(self.f, get_file)
assert not self.results.is_modified
def test_is_modified_after_remove(self):
# Removing dupes sets the modified flag
self.results.remove_duplicates([self.results.groups[0].dupes[0]])
assert self.results.is_modified
def test_is_modified_after_make_ref(self):
# Making a dupe ref sets the modified flag
self.results.make_ref(self.results.groups[0].dupes[0])
assert self.results.is_modified
class TCResultsMarkings(TestCase):
@@ -209,27 +262,27 @@ class TCResultsMarkings(TestCase):
self.results.groups = self.groups
def test_stat_line(self):
self.assertEqual("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
eq_("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
self.results.mark(self.objects[1])
self.assertEqual("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
eq_("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
self.results.mark_invert()
self.assertEqual("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
eq_("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
self.results.mark_invert()
self.results.unmark(self.objects[1])
self.results.mark(self.objects[2])
self.results.mark(self.objects[4])
self.assertEqual("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
eq_("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
self.results.mark(self.objects[0]) #this is a ref, it can't be counted
self.assertEqual("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
eq_("2 / 3 (2.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
self.results.groups = self.groups
self.assertEqual("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
eq_("0 / 3 (0.00 B / 1.01 KB) duplicates marked.",self.results.stat_line)
def test_with_ref_duplicate(self):
self.objects[1].is_ref = True
self.results.groups = self.groups
self.assert_(not self.results.mark(self.objects[1]))
assert not self.results.mark(self.objects[1])
self.results.mark(self.objects[2])
self.assertEqual("1 / 2 (1.00 B / 2.00 B) duplicates marked.",self.results.stat_line)
eq_("1 / 2 (1.00 B / 2.00 B) duplicates marked.",self.results.stat_line)
def test_perform_on_marked(self):
def log_object(o):
@@ -239,17 +292,17 @@ class TCResultsMarkings(TestCase):
log = []
self.results.mark_all()
self.results.perform_on_marked(log_object,False)
self.assert_(self.objects[1] in log)
self.assert_(self.objects[2] in log)
self.assert_(self.objects[4] in log)
self.assertEqual(3,len(log))
assert self.objects[1] in log
assert self.objects[2] in log
assert self.objects[4] in log
eq_(3,len(log))
log = []
self.results.mark_none()
self.results.mark(self.objects[4])
self.results.perform_on_marked(log_object,True)
self.assertEqual(1,len(log))
self.assert_(self.objects[4] in log)
self.assertEqual(1,len(self.results.groups))
eq_(1,len(log))
assert self.objects[4] in log
eq_(1,len(self.results.groups))
def test_perform_on_marked_with_problems(self):
def log_object(o):
@@ -282,61 +335,61 @@ class TCResultsMarkings(TestCase):
self.objects[1].is_ref = True
self.results.mark_all()
self.results.perform_on_marked(log_object,True)
self.assert_(self.objects[1] not in log)
self.assert_(self.objects[2] in log)
self.assert_(self.objects[4] in log)
self.assertEqual(2,len(log))
self.assertEqual(0,len(self.results.groups))
assert self.objects[1] not in log
assert self.objects[2] in log
assert self.objects[4] in log
eq_(2,len(log))
eq_(0,len(self.results.groups))
def test_perform_on_marked_remove_objects_only_at_the_end(self):
def check_groups(o):
self.assertEqual(3,len(g1))
self.assertEqual(2,len(g2))
eq_(3,len(g1))
eq_(2,len(g2))
return True
g1,g2 = self.results.groups
self.results.mark_all()
self.results.perform_on_marked(check_groups,True)
self.assertEqual(0,len(g1))
self.assertEqual(0,len(g2))
self.assertEqual(0,len(self.results.groups))
eq_(0,len(g1))
eq_(0,len(g2))
eq_(0,len(self.results.groups))
def test_remove_duplicates(self):
g1 = self.results.groups[0]
g2 = self.results.groups[1]
self.results.mark(g1.dupes[0])
self.assertEqual("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
eq_("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
self.results.remove_duplicates([g1.dupes[1]])
self.assertEqual("1 / 2 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
eq_("1 / 2 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
self.results.remove_duplicates([g1.dupes[0]])
self.assertEqual("0 / 1 (0.00 B / 1.00 B) duplicates marked.",self.results.stat_line)
eq_("0 / 1 (0.00 B / 1.00 B) duplicates marked.",self.results.stat_line)
def test_make_ref(self):
g = self.results.groups[0]
d = g.dupes[0]
self.results.mark(d)
self.assertEqual("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
eq_("1 / 3 (1.00 KB / 1.01 KB) duplicates marked.",self.results.stat_line)
self.results.make_ref(d)
self.assertEqual("0 / 3 (0.00 B / 3.00 B) duplicates marked.",self.results.stat_line)
eq_("0 / 3 (0.00 B / 3.00 B) duplicates marked.",self.results.stat_line)
self.results.make_ref(d)
self.assertEqual("0 / 3 (0.00 B / 3.00 B) duplicates marked.",self.results.stat_line)
eq_("0 / 3 (0.00 B / 3.00 B) duplicates marked.",self.results.stat_line)
def test_SaveXML(self):
self.results.mark(self.objects[1])
self.results.mark_invert()
f = StringIO.StringIO()
f = io.BytesIO()
self.results.save_to_xml(f)
f.seek(0)
doc = etree.parse(f)
doc = ET.parse(f)
root = doc.getroot()
g1, g2 = root.iterchildren('group')
d1, d2, d3 = g1.iterchildren('file')
self.assertEqual('n', d1.get('marked'))
self.assertEqual('n', d2.get('marked'))
self.assertEqual('y', d3.get('marked'))
d1, d2 = g2.iterchildren('file')
self.assertEqual('n', d1.get('marked'))
self.assertEqual('y', d2.get('marked'))
g1, g2 = root.getiterator('group')
d1, d2, d3 = g1.getiterator('file')
eq_('n', d1.get('marked'))
eq_('n', d2.get('marked'))
eq_('y', d3.get('marked'))
d1, d2 = g2.getiterator('file')
eq_('n', d1.get('marked'))
eq_('y', d2.get('marked'))
def test_LoadXML(self):
def get_file(path):
@@ -345,16 +398,16 @@ class TCResultsMarkings(TestCase):
self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path
self.results.mark(self.objects[1])
self.results.mark_invert()
f = StringIO.StringIO()
f = io.BytesIO()
self.results.save_to_xml(f)
f.seek(0)
r = Results(data)
r.load_from_xml(f,get_file)
self.assert_(not r.is_marked(self.objects[0]))
self.assert_(not r.is_marked(self.objects[1]))
self.assert_(r.is_marked(self.objects[2]))
self.assert_(not r.is_marked(self.objects[3]))
self.assert_(r.is_marked(self.objects[4]))
assert not r.is_marked(self.objects[0])
assert not r.is_marked(self.objects[1])
assert r.is_marked(self.objects[2])
assert not r.is_marked(self.objects[3])
assert r.is_marked(self.objects[4])
class TCResultsXML(TestCase):
@@ -369,38 +422,38 @@ class TCResultsXML(TestCase):
def test_save_to_xml(self):
self.objects[0].is_ref = True
self.objects[0].words = [['foo','bar']]
f = StringIO.StringIO()
f = io.BytesIO()
self.results.save_to_xml(f)
f.seek(0)
doc = etree.parse(f)
doc = ET.parse(f)
root = doc.getroot()
self.assertEqual('results', root.tag)
self.assertEqual(2, len(root))
self.assertEqual(2, len([c for c in root if c.tag == 'group']))
eq_('results', root.tag)
eq_(2, len(root))
eq_(2, len([c for c in root if c.tag == 'group']))
g1, g2 = root
self.assertEqual(6,len(g1))
self.assertEqual(3,len([c for c in g1 if c.tag == 'file']))
self.assertEqual(3,len([c for c in g1 if c.tag == 'match']))
eq_(6,len(g1))
eq_(3,len([c for c in g1 if c.tag == 'file']))
eq_(3,len([c for c in g1 if c.tag == 'match']))
d1, d2, d3 = [c for c in g1 if c.tag == 'file']
self.assertEqual(op.join('basepath','foo bar'),d1.get('path'))
self.assertEqual(op.join('basepath','bar bleh'),d2.get('path'))
self.assertEqual(op.join('basepath','foo bleh'),d3.get('path'))
self.assertEqual('y',d1.get('is_ref'))
self.assertEqual('n',d2.get('is_ref'))
self.assertEqual('n',d3.get('is_ref'))
self.assertEqual('foo,bar',d1.get('words'))
self.assertEqual('bar,bleh',d2.get('words'))
self.assertEqual('foo,bleh',d3.get('words'))
self.assertEqual(3,len(g2))
self.assertEqual(2,len([c for c in g2 if c.tag == 'file']))
self.assertEqual(1,len([c for c in g2 if c.tag == 'match']))
eq_(op.join('basepath','foo bar'),d1.get('path'))
eq_(op.join('basepath','bar bleh'),d2.get('path'))
eq_(op.join('basepath','foo bleh'),d3.get('path'))
eq_('y',d1.get('is_ref'))
eq_('n',d2.get('is_ref'))
eq_('n',d3.get('is_ref'))
eq_('foo,bar',d1.get('words'))
eq_('bar,bleh',d2.get('words'))
eq_('foo,bleh',d3.get('words'))
eq_(3,len(g2))
eq_(2,len([c for c in g2 if c.tag == 'file']))
eq_(1,len([c for c in g2 if c.tag == 'match']))
d1, d2 = [c for c in g2 if c.tag == 'file']
self.assertEqual(op.join('basepath','ibabtu'),d1.get('path'))
self.assertEqual(op.join('basepath','ibabtu'),d2.get('path'))
self.assertEqual('n',d1.get('is_ref'))
self.assertEqual('n',d2.get('is_ref'))
self.assertEqual('ibabtu',d1.get('words'))
self.assertEqual('ibabtu',d2.get('words'))
eq_(op.join('basepath','ibabtu'),d1.get('path'))
eq_(op.join('basepath','ibabtu'),d2.get('path'))
eq_('n',d1.get('is_ref'))
eq_('n',d2.get('is_ref'))
eq_('ibabtu',d1.get('words'))
eq_('ibabtu',d2.get('words'))
def test_LoadXML(self):
def get_file(path):
@@ -408,30 +461,30 @@ class TCResultsXML(TestCase):
self.objects[0].is_ref = True
self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path
f = StringIO.StringIO()
f = io.BytesIO()
self.results.save_to_xml(f)
f.seek(0)
r = Results(data)
r.load_from_xml(f,get_file)
self.assertEqual(2,len(r.groups))
eq_(2,len(r.groups))
g1,g2 = r.groups
self.assertEqual(3,len(g1))
self.assert_(g1[0].is_ref)
self.assert_(not g1[1].is_ref)
self.assert_(not g1[2].is_ref)
self.assert_(g1[0] is self.objects[0])
self.assert_(g1[1] is self.objects[1])
self.assert_(g1[2] is self.objects[2])
self.assertEqual(['foo','bar'],g1[0].words)
self.assertEqual(['bar','bleh'],g1[1].words)
self.assertEqual(['foo','bleh'],g1[2].words)
self.assertEqual(2,len(g2))
self.assert_(not g2[0].is_ref)
self.assert_(not g2[1].is_ref)
self.assert_(g2[0] is self.objects[3])
self.assert_(g2[1] is self.objects[4])
self.assertEqual(['ibabtu'],g2[0].words)
self.assertEqual(['ibabtu'],g2[1].words)
eq_(3,len(g1))
assert g1[0].is_ref
assert not g1[1].is_ref
assert not g1[2].is_ref
assert g1[0] is self.objects[0]
assert g1[1] is self.objects[1]
assert g1[2] is self.objects[2]
eq_(['foo','bar'],g1[0].words)
eq_(['bar','bleh'],g1[1].words)
eq_(['foo','bleh'],g1[2].words)
eq_(2,len(g2))
assert not g2[0].is_ref
assert not g2[1].is_ref
assert g2[0] is self.objects[3]
assert g2[1] is self.objects[4]
eq_(['ibabtu'],g2[0].words)
eq_(['ibabtu'],g2[1].words)
def test_LoadXML_with_filename(self):
def get_file(path):
@@ -442,7 +495,7 @@ class TCResultsXML(TestCase):
self.results.save_to_xml(filename)
r = Results(data)
r.load_from_xml(filename,get_file)
self.assertEqual(2,len(r.groups))
eq_(2,len(r.groups))
def test_LoadXML_with_some_files_that_dont_exist_anymore(self):
def get_file(path):
@@ -451,84 +504,84 @@ class TCResultsXML(TestCase):
return [f for f in self.objects if str(f.path) == path][0]
self.objects[4].name = 'ibabtu 2' #we can't have 2 files with the same path
f = StringIO.StringIO()
f = io.BytesIO()
self.results.save_to_xml(f)
f.seek(0)
r = Results(data)
r.load_from_xml(f,get_file)
self.assertEqual(1,len(r.groups))
self.assertEqual(3,len(r.groups[0]))
eq_(1,len(r.groups))
eq_(3,len(r.groups[0]))
def test_LoadXML_missing_attributes_and_bogus_elements(self):
def get_file(path):
return [f for f in self.objects if str(f.path) == path][0]
root = etree.Element('foobar') #The root element shouldn't matter, really.
group_node = etree.SubElement(root, 'group')
dupe_node = etree.SubElement(group_node, 'file') #Perfectly correct file
root = ET.Element('foobar') #The root element shouldn't matter, really.
group_node = ET.SubElement(root, 'group')
dupe_node = ET.SubElement(group_node, 'file') #Perfectly correct file
dupe_node.set('path', op.join('basepath','foo bar'))
dupe_node.set('is_ref', 'y')
dupe_node.set('words', 'foo,bar')
dupe_node = etree.SubElement(group_node, 'file') #is_ref missing, default to 'n'
dupe_node = ET.SubElement(group_node, 'file') #is_ref missing, default to 'n'
dupe_node.set('path',op.join('basepath','foo bleh'))
dupe_node.set('words','foo,bleh')
dupe_node = etree.SubElement(group_node, 'file') #words are missing, valid.
dupe_node = ET.SubElement(group_node, 'file') #words are missing, valid.
dupe_node.set('path',op.join('basepath','bar bleh'))
dupe_node = etree.SubElement(group_node, 'file') #path is missing, invalid.
dupe_node = ET.SubElement(group_node, 'file') #path is missing, invalid.
dupe_node.set('words','foo,bleh')
dupe_node = etree.SubElement(group_node, 'foobar') #Invalid element name
dupe_node = ET.SubElement(group_node, 'foobar') #Invalid element name
dupe_node.set('path',op.join('basepath','bar bleh'))
dupe_node.set('is_ref','y')
dupe_node.set('words','bar,bleh')
match_node = etree.SubElement(group_node, 'match') # match pointing to a bad index
match_node = ET.SubElement(group_node, 'match') # match pointing to a bad index
match_node.set('first', '42')
match_node.set('second', '45')
match_node = etree.SubElement(group_node, 'match') # match with missing attrs
match_node = etree.SubElement(group_node, 'match') # match with non-int values
match_node = ET.SubElement(group_node, 'match') # match with missing attrs
match_node = ET.SubElement(group_node, 'match') # match with non-int values
match_node.set('first', 'foo')
match_node.set('second', 'bar')
match_node.set('percentage', 'baz')
group_node = etree.SubElement(root, 'foobar') #invalid group
group_node = etree.SubElement(root, 'group') #empty group
f = StringIO.StringIO()
tree = etree.ElementTree(root)
group_node = ET.SubElement(root, 'foobar') #invalid group
group_node = ET.SubElement(root, 'group') #empty group
f = io.BytesIO()
tree = ET.ElementTree(root)
tree.write(f, encoding='utf-8')
f.seek(0)
r = Results(data)
r.load_from_xml(f, get_file)
self.assertEqual(1,len(r.groups))
self.assertEqual(3,len(r.groups[0]))
eq_(1,len(r.groups))
eq_(3,len(r.groups[0]))
def test_xml_non_ascii(self):
def get_file(path):
if path == op.join('basepath',u'\xe9foo bar'):
if path == op.join('basepath','\xe9foo bar'):
return objects[0]
if path == op.join('basepath',u'bar bleh'):
if path == op.join('basepath','bar bleh'):
return objects[1]
objects = [NamedObject(u"\xe9foo bar",True),NamedObject("bar bleh",True)]
objects = [NamedObject("\xe9foo bar",True),NamedObject("bar bleh",True)]
matches = engine.getmatches(objects) #we should have 5 matches
groups = engine.get_groups(matches) #We should have 2 groups
for g in groups:
g.prioritize(lambda x:objects.index(x)) #We want the dupes to be in the same order as the list is
results = Results(data)
results.groups = groups
f = StringIO.StringIO()
f = io.BytesIO()
results.save_to_xml(f)
f.seek(0)
r = Results(data)
r.load_from_xml(f,get_file)
g = r.groups[0]
self.assertEqual(u"\xe9foo bar",g[0].name)
self.assertEqual(['efoo','bar'],g[0].words)
eq_("\xe9foo bar",g[0].name)
eq_(['efoo','bar'],g[0].words)
def test_load_invalid_xml(self):
f = StringIO.StringIO()
f.write('<this is invalid')
f = io.BytesIO()
f.write(b'<this is invalid')
f.seek(0)
r = Results(data)
r.load_from_xml(f,None)
self.assertEqual(0,len(r.groups))
eq_(0,len(r.groups))
def test_load_non_existant_xml(self):
r = Results(data)
@@ -536,7 +589,7 @@ class TCResultsXML(TestCase):
r.load_from_xml('does_not_exist.xml', None)
except IOError:
self.fail()
self.assertEqual(0,len(r.groups))
eq_(0,len(r.groups))
def test_remember_match_percentage(self):
group = self.groups[0]
@@ -546,7 +599,7 @@ class TCResultsXML(TestCase):
fake_matches.add(engine.Match(d1, d3, 43))
fake_matches.add(engine.Match(d2, d3, 46))
group.matches = fake_matches
f = StringIO.StringIO()
f = io.BytesIO()
results = self.results
results.save_to_xml(f)
f.seek(0)
@@ -555,16 +608,16 @@ class TCResultsXML(TestCase):
group = results.groups[0]
d1, d2, d3 = group
match = group.get_match_of(d2) #d1 - d2
self.assertEqual(42, match[2])
eq_(42, match[2])
match = group.get_match_of(d3) #d1 - d3
self.assertEqual(43, match[2])
eq_(43, match[2])
group.switch_ref(d2)
match = group.get_match_of(d3) #d2 - d3
self.assertEqual(46, match[2])
eq_(46, match[2])
def test_save_and_load(self):
# previously, when reloading matches, they wouldn't be reloaded as namedtuples
f = StringIO.StringIO()
f = io.BytesIO()
self.results.save_to_xml(f)
f.seek(0)
self.results.load_from_xml(f, self.get_file)
@@ -572,13 +625,13 @@ class TCResultsXML(TestCase):
def test_apply_filter_works_on_paths(self):
# apply_filter() searches on the whole path, not just on the filename.
self.results.apply_filter(u'basepath')
self.results.apply_filter('basepath')
eq_(len(self.results.groups), 2)
def test_save_xml_with_invalid_characters(self):
# Don't crash when saving files that have invalid xml characters in their path
self.objects[0].name = u'foo\x19'
self.results.save_to_xml(StringIO.StringIO()) # don't crash
self.objects[0].name = 'foo\x19'
self.results.save_to_xml(io.BytesIO()) # don't crash
class TCResultsFilter(TestCase):
@@ -589,47 +642,47 @@ class TCResultsFilter(TestCase):
self.results.apply_filter(r'foo')
def test_groups(self):
self.assertEqual(1, len(self.results.groups))
self.assert_(self.results.groups[0] is self.groups[0])
eq_(1, len(self.results.groups))
assert self.results.groups[0] is self.groups[0]
def test_dupes(self):
# There are 2 objects matching. The first one is ref. Only the 3rd one is supposed to be in dupes.
self.assertEqual(1, len(self.results.dupes))
self.assert_(self.results.dupes[0] is self.objects[2])
eq_(1, len(self.results.dupes))
assert self.results.dupes[0] is self.objects[2]
def test_cancel_filter(self):
self.results.apply_filter(None)
self.assertEqual(3, len(self.results.dupes))
self.assertEqual(2, len(self.results.groups))
eq_(3, len(self.results.dupes))
eq_(2, len(self.results.groups))
def test_dupes_reconstructed_filtered(self):
# make_ref resets self.__dupes to None. When it's reconstructed, we want it filtered
dupe = self.results.dupes[0] #3rd object
self.results.make_ref(dupe)
self.assertEqual(1, len(self.results.dupes))
self.assert_(self.results.dupes[0] is self.objects[0])
eq_(1, len(self.results.dupes))
assert self.results.dupes[0] is self.objects[0]
def test_include_ref_dupes_in_filter(self):
# When only the ref of a group match the filter, include it in the group
self.results.apply_filter(None)
self.results.apply_filter(r'foo bar')
self.assertEqual(1, len(self.results.groups))
self.assertEqual(0, len(self.results.dupes))
eq_(1, len(self.results.groups))
eq_(0, len(self.results.dupes))
def test_filters_build_on_one_another(self):
self.results.apply_filter(r'bar')
self.assertEqual(1, len(self.results.groups))
self.assertEqual(0, len(self.results.dupes))
eq_(1, len(self.results.groups))
eq_(0, len(self.results.dupes))
def test_stat_line(self):
expected = '0 / 1 (0.00 B / 1.00 B) duplicates marked. filter: foo'
self.assertEqual(expected, self.results.stat_line)
eq_(expected, self.results.stat_line)
self.results.apply_filter(r'bar')
expected = '0 / 0 (0.00 B / 0.00 B) duplicates marked. filter: foo --> bar'
self.assertEqual(expected, self.results.stat_line)
eq_(expected, self.results.stat_line)
self.results.apply_filter(None)
expected = '0 / 3 (0.00 B / 1.01 KB) duplicates marked.'
self.assertEqual(expected, self.results.stat_line)
eq_(expected, self.results.stat_line)
def test_mark_count_is_filtered_as_well(self):
self.results.apply_filter(None)
@@ -638,7 +691,7 @@ class TCResultsFilter(TestCase):
self.results.mark(dupe)
self.results.apply_filter(r'foo')
expected = '1 / 1 (1.00 B / 1.00 B) duplicates marked. filter: foo'
self.assertEqual(expected, self.results.stat_line)
eq_(expected, self.results.stat_line)
def test_sort_groups(self):
self.results.apply_filter(None)
@@ -646,22 +699,22 @@ class TCResultsFilter(TestCase):
g1,g2 = self.groups
self.results.apply_filter('a') # Matches both group
self.results.sort_groups(2) #2 is the key for size
self.assert_(self.results.groups[0] is g2)
self.assert_(self.results.groups[1] is g1)
assert self.results.groups[0] is g2
assert self.results.groups[1] is g1
self.results.apply_filter(None)
self.assert_(self.results.groups[0] is g2)
self.assert_(self.results.groups[1] is g1)
assert self.results.groups[0] is g2
assert self.results.groups[1] is g1
self.results.sort_groups(2, False)
self.results.apply_filter('a')
self.assert_(self.results.groups[1] is g2)
self.assert_(self.results.groups[0] is g1)
assert self.results.groups[1] is g2
assert self.results.groups[0] is g1
def test_set_group(self):
#We want the new group to be filtered
self.objects, self.matches, self.groups = GetTestGroups()
self.results.groups = self.groups
self.assertEqual(1, len(self.results.groups))
self.assert_(self.results.groups[0] is self.groups[0])
eq_(1, len(self.results.groups))
assert self.results.groups[0] is self.groups[0]
def test_load_cancels_filter(self):
def get_file(path):
@@ -673,23 +726,23 @@ class TCResultsFilter(TestCase):
r = Results(data)
r.apply_filter('foo')
r.load_from_xml(filename,get_file)
self.assertEqual(2,len(r.groups))
eq_(2,len(r.groups))
def test_remove_dupe(self):
self.results.remove_duplicates([self.results.dupes[0]])
self.results.apply_filter(None)
self.assertEqual(2,len(self.results.groups))
self.assertEqual(2,len(self.results.dupes))
eq_(2,len(self.results.groups))
eq_(2,len(self.results.dupes))
self.results.apply_filter('ibabtu')
self.results.remove_duplicates([self.results.dupes[0]])
self.results.apply_filter(None)
self.assertEqual(1,len(self.results.groups))
self.assertEqual(1,len(self.results.dupes))
eq_(1,len(self.results.groups))
eq_(1,len(self.results.dupes))
def test_filter_is_case_insensitive(self):
self.results.apply_filter(None)
self.results.apply_filter('FOO')
self.assertEqual(1, len(self.results.dupes))
eq_(1, len(self.results.dupes))
def test_make_ref_on_filtered_out_doesnt_mess_stats(self):
# When filtered, a group containing filtered out dupes will display them as being reference.
@@ -700,10 +753,10 @@ class TCResultsFilter(TestCase):
self.results.make_ref(bar_bleh)
# Now the stats should display *2* markable dupes (instead of 1)
expected = '0 / 2 (0.00 B / 2.00 B) duplicates marked. filter: foo'
self.assertEqual(expected, self.results.stat_line)
eq_(expected, self.results.stat_line)
self.results.apply_filter(None) # Now let's make sure our unfiltered results aren't fucked up
expected = '0 / 3 (0.00 B / 3.00 B) duplicates marked.'
self.assertEqual(expected, self.results.stat_line)
eq_(expected, self.results.stat_line)
class TCResultsRefFile(TestCase):
@@ -716,15 +769,15 @@ class TCResultsRefFile(TestCase):
def test_stat_line(self):
expected = '0 / 2 (0.00 B / 2.00 B) duplicates marked.'
self.assertEqual(expected, self.results.stat_line)
eq_(expected, self.results.stat_line)
def test_make_ref(self):
d = self.results.groups[0].dupes[1] #non-ref
r = self.results.groups[0].ref
self.results.make_ref(d)
expected = '0 / 1 (0.00 B / 1.00 B) duplicates marked.'
self.assertEqual(expected, self.results.stat_line)
eq_(expected, self.results.stat_line)
self.results.make_ref(r)
expected = '0 / 2 (0.00 B / 2.00 B) duplicates marked.'
self.assertEqual(expected, self.results.stat_line)
eq_(expected, self.results.stat_line)

View File

@@ -2,12 +2,11 @@
# Created On: 2006/03/03
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from hscommon import job
from jobprogress import job
from hsutil import io
from hsutil.path import Path
from hsutil.testutil import eq_
@@ -25,6 +24,9 @@ class NamedObject(object):
self.path = Path('')
self.words = getwords(name)
def __repr__(self):
return '<NamedObject %r>' % self.name
no = NamedObject
@@ -43,7 +45,7 @@ class ScannerTestFakeFiles(TestCase):
def test_default_settings(self):
s = Scanner()
eq_(s.min_match_percentage, 80)
eq_(s.scan_type, SCAN_TYPE_FILENAME)
eq_(s.scan_type, ScanType.Filename)
eq_(s.mix_file_kind, True)
eq_(s.word_weighting, False)
eq_(s.match_similar_words, False)
@@ -95,7 +97,7 @@ class ScannerTestFakeFiles(TestCase):
def test_content_scan(self):
s = Scanner()
s.scan_type = SCAN_TYPE_CONTENT
s.scan_type = ScanType.Contents
f = [no('foo'), no('bar'), no('bleh')]
f[0].md5 = f[0].md5partial = 'foobar'
f[1].md5 = f[1].md5partial = 'foobar'
@@ -112,13 +114,13 @@ class ScannerTestFakeFiles(TestCase):
raise AssertionError()
s = Scanner()
s.scan_type = SCAN_TYPE_CONTENT
s.scan_type = ScanType.Contents
f = [MyFile('foo', 1), MyFile('bar', 2)]
eq_(len(s.GetDupeGroups(f)), 0)
def test_min_match_perc_doesnt_matter_for_content_scan(self):
s = Scanner()
s.scan_type = SCAN_TYPE_CONTENT
s.scan_type = ScanType.Contents
f = [no('foo'), no('bar'), no('bleh')]
f[0].md5 = f[0].md5partial = 'foobar'
f[1].md5 = f[1].md5partial = 'foobar'
@@ -134,7 +136,7 @@ class ScannerTestFakeFiles(TestCase):
def test_content_scan_doesnt_put_md5_in_words_at_the_end(self):
s = Scanner()
s.scan_type = SCAN_TYPE_CONTENT
s.scan_type = ScanType.Contents
f = [no('foo'),no('bar')]
f[0].md5 = f[0].md5partial = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
f[1].md5 = f[1].md5partial = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
@@ -188,21 +190,21 @@ class ScannerTestFakeFiles(TestCase):
def test_fields(self):
s = Scanner()
s.scan_type = SCAN_TYPE_FIELDS
s.scan_type = ScanType.Fields
f = [no('The White Stripes - Little Ghost'), no('The White Stripes - Little Acorn')]
r = s.GetDupeGroups(f)
eq_(len(r), 0)
def test_fields_no_order(self):
s = Scanner()
s.scan_type = SCAN_TYPE_FIELDS_NO_ORDER
s.scan_type = ScanType.FieldsNoOrder
f = [no('The White Stripes - Little Ghost'), no('Little Ghost - The White Stripes')]
r = s.GetDupeGroups(f)
eq_(len(r), 1)
def test_tag_scan(self):
s = Scanner()
s.scan_type = SCAN_TYPE_TAG
s.scan_type = ScanType.Tag
o1 = no('foo')
o2 = no('bar')
o1.artist = 'The White Stripes'
@@ -214,7 +216,7 @@ class ScannerTestFakeFiles(TestCase):
def test_tag_with_album_scan(self):
s = Scanner()
s.scan_type = SCAN_TYPE_TAG
s.scan_type = ScanType.Tag
s.scanned_tags = set(['artist', 'album', 'title'])
o1 = no('foo')
o2 = no('bar')
@@ -233,7 +235,7 @@ class ScannerTestFakeFiles(TestCase):
def test_that_dash_in_tags_dont_create_new_fields(self):
s = Scanner()
s.scan_type = SCAN_TYPE_TAG
s.scan_type = ScanType.Tag
s.scanned_tags = set(['artist', 'album', 'title'])
s.min_match_percentage = 50
o1 = no('foo')
@@ -249,7 +251,7 @@ class ScannerTestFakeFiles(TestCase):
def test_tag_scan_with_different_scanned(self):
s = Scanner()
s.scan_type = SCAN_TYPE_TAG
s.scan_type = ScanType.Tag
s.scanned_tags = set(['track', 'year'])
o1 = no('foo')
o2 = no('bar')
@@ -266,7 +268,7 @@ class ScannerTestFakeFiles(TestCase):
def test_tag_scan_only_scans_existing_tags(self):
s = Scanner()
s.scan_type = SCAN_TYPE_TAG
s.scan_type = ScanType.Tag
s.scanned_tags = set(['artist', 'foo'])
o1 = no('foo')
o2 = no('bar')
@@ -279,7 +281,7 @@ class ScannerTestFakeFiles(TestCase):
def test_tag_scan_converts_to_str(self):
s = Scanner()
s.scan_type = SCAN_TYPE_TAG
s.scan_type = ScanType.Tag
s.scanned_tags = set(['track'])
o1 = no('foo')
o2 = no('bar')
@@ -293,12 +295,12 @@ class ScannerTestFakeFiles(TestCase):
def test_tag_scan_non_ascii(self):
s = Scanner()
s.scan_type = SCAN_TYPE_TAG
s.scan_type = ScanType.Tag
s.scanned_tags = set(['title'])
o1 = no('foo')
o2 = no('bar')
o1.title = u'foobar\u00e9'
o2.title = u'foobar\u00e9'
o1.title = 'foobar\u00e9'
o2.title = 'foobar\u00e9'
try:
r = s.GetDupeGroups([o1, o2])
except UnicodeEncodeError:
@@ -307,7 +309,7 @@ class ScannerTestFakeFiles(TestCase):
def test_audio_content_scan(self):
s = Scanner()
s.scan_type = SCAN_TYPE_CONTENT_AUDIO
s.scan_type = ScanType.ContentsAudio
f = [no('foo'), no('bar'), no('bleh')]
f[0].md5 = 'foo'
f[1].md5 = 'bar'
@@ -329,7 +331,7 @@ class ScannerTestFakeFiles(TestCase):
raise AssertionError()
s = Scanner()
s.scan_type = SCAN_TYPE_CONTENT_AUDIO
s.scan_type = ScanType.ContentsAudio
f = [MyFile('foo'), MyFile('bar')]
f[0].audiosize = 1
f[1].audiosize = 2
@@ -362,11 +364,11 @@ class ScannerTestFakeFiles(TestCase):
f1 = no('foobar')
f2 = no('foobar')
f3 = no('foobar')
f1.path = Path(u'foo1\u00e9')
f2.path = Path(u'foo2\u00e9')
f3.path = Path(u'foo3\u00e9')
s.ignore_list.Ignore(unicode(f1.path),unicode(f2.path))
s.ignore_list.Ignore(unicode(f1.path),unicode(f3.path))
f1.path = Path('foo1\u00e9')
f2.path = Path('foo2\u00e9')
f3.path = Path('foo3\u00e9')
s.ignore_list.Ignore(str(f1.path),str(f2.path))
s.ignore_list.Ignore(str(f1.path),str(f3.path))
r = s.GetDupeGroups([f1,f2,f3])
eq_(len(r), 1)
g = r[0]
@@ -379,7 +381,7 @@ class ScannerTestFakeFiles(TestCase):
# A very wrong way to use any() was added at some point, causing resulting group list
# to be empty.
class FalseNamedObject(NamedObject):
def __nonzero__(self):
def __bool__(self):
return False
@@ -426,11 +428,20 @@ class ScannerTestFakeFiles(TestCase):
# if ref has the same words as dupe, but has some just one extra word which is a digit, it
# becomes a dupe
s = Scanner()
o1, o2 = no('foo bar 42'), no('foo bar')
o1 = no('foo bar 42')
o2 = no('foo bar [42]')
o3 = no('foo bar (42)')
o4 = no('foo bar {42}')
o5 = no('foo bar')
# all numbered names have deeper paths, so they'll end up ref if the digits aren't correctly
# used as tie breakers
o1.path = Path('deeper/path')
o2.path = Path('foo')
[group] = s.GetDupeGroups([o1, o2])
assert group.ref is o2
o2.path = Path('deeper/path')
o3.path = Path('deeper/path')
o4.path = Path('deeper/path')
o5.path = Path('foo')
[group] = s.GetDupeGroups([o1, o2, o3, o4, o5])
assert group.ref is o5
def test_partial_group_match(self):
# Count the number od discarded matches (when a file doesn't match all other dupes of the
@@ -453,7 +464,7 @@ class ScannerTest(TestCase):
# In this test, we have to delete one of the files between the get_matches() part and the
# get_groups() part.
s = Scanner()
s.scan_type = SCAN_TYPE_CONTENT
s.scan_type = ScanType.Contents
p = self.tmppath()
io.open(p + 'file1', 'w').write('foo')
io.open(p + 'file2', 'w').write('foo')

View File

@@ -2,9 +2,9 @@
# Created On: 2006/11/16
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
import logging
from appscript import app, k, CommandError
@@ -26,9 +26,9 @@ JOBID2TITLE.update({
class DupeGuruME(DupeGuruBase):
def __init__(self):
DupeGuruBase.__init__(self, data, 'dupeGuru Music Edition', appid=1)
DupeGuruBase.__init__(self, data, 'dupeGuru Music Edition')
self.scanner = scanner.ScannerME()
self.directories.fileclasses = [fs.Mp3File, fs.Mp4File, fs.WmaFile, fs.OggFile, fs.FlacFile, fs.AiffFile]
self.directories.fileclasses = [fs.MusicFile]
self.dead_tracks = []
def remove_dead_tracks(self):
@@ -41,7 +41,7 @@ class DupeGuruME(DupeGuruBase):
try:
track.delete(timeout=0)
except CommandError as e:
logging.warning('Error while trying to remove a track from iTunes: %s' % unicode(e))
logging.warning('Error while trying to remove a track from iTunes: %s' % str(e))
self._start_job(JOB_REMOVE_DEAD_TRACKS, do)

View File

@@ -2,9 +2,9 @@
# Created On: 2006/03/15
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# 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/hs_license
# http://www.hardcoded.net/licenses/bsd_license
from hsutil.str import format_time, FT_MINUTES, format_size
from core.data import (format_path, format_timestamp, format_words, format_perc,
@@ -18,7 +18,6 @@ COLUMNS = [
{'attr':'bitrate','display':'Bitrate'},
{'attr':'samplerate','display':'Sample Rate'},
{'attr':'extension','display':'Kind'},
{'attr':'ctime','display':'Creation'},
{'attr':'mtime','display':'Modification'},
{'attr':'title','display':'Title'},
{'attr':'artist','display':'Artist'},
@@ -32,7 +31,10 @@ COLUMNS = [
{'attr':'dupe_count','display':'Dupe Count'},
]
METADATA_TO_READ = ['size', 'ctime', 'mtime', 'duration', 'bitrate', 'samplerate', 'title', 'artist',
MATCHPERC_COL = 15
DUPECOUNT_COL = 17
METADATA_TO_READ = ['size', 'mtime', 'duration', 'bitrate', 'samplerate', 'title', 'artist',
'album', 'genre', 'year', 'track', 'comment']
def GetDisplayInfo(dupe, group, delta):
@@ -40,7 +42,6 @@ def GetDisplayInfo(dupe, group, delta):
duration = dupe.duration
bitrate = dupe.bitrate
samplerate = dupe.samplerate
ctime = dupe.ctime
mtime = dupe.mtime
m = group.get_match_of(dupe)
if m:
@@ -52,7 +53,6 @@ def GetDisplayInfo(dupe, group, delta):
duration -= r.duration
bitrate -= r.bitrate
samplerate -= r.samplerate
ctime -= r.ctime
mtime -= r.mtime
else:
percentage = group.percentage
@@ -65,7 +65,6 @@ def GetDisplayInfo(dupe, group, delta):
str(bitrate),
str(samplerate),
dupe.extension,
format_timestamp(ctime,delta and m),
format_timestamp(mtime,delta and m),
dupe.title,
dupe.artist,
@@ -80,19 +79,19 @@ def GetDisplayInfo(dupe, group, delta):
]
def GetDupeSortKey(dupe, get_group, key, delta):
if key == 16:
if key == MATCHPERC_COL:
m = get_group().get_match_of(dupe)
return m.percentage
if key == 18:
if key == DUPECOUNT_COL:
return 0
r = cmp_value(getattr(dupe, COLUMNS[key]['attr'], ''))
if delta and (key in (2, 3, 4, 7, 8)):
if delta and (key in {2, 3, 4, 7}):
r -= cmp_value(getattr(get_group().ref, COLUMNS[key]['attr'], ''))
return r
def GetGroupSortKey(group, key):
if key == 16:
if key == MATCHPERC_COL:
return group.percentage
if key == 18:
if key == DUPECOUNT_COL:
return len(group)
return cmp_value(getattr(group.ref, COLUMNS[key]['attr'], ''))

Some files were not shown because too many files have changed in this diff Show More