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

Compare commits

..

163 Commits

Author SHA1 Message Date
Virgil Dupras
ee96d5f88c Fixed Windows packaging for dgse. 2010-04-13 14:04:15 +01:00
Virgil Dupras
e96a917bef Fixed the problem dialog under cocoa, which was visible at launch. 2010-04-13 14:22:24 +02:00
Virgil Dupras
769b816998 se v2.10.0 2010-04-13 11:58:53 +02:00
Virgil Dupras
ff891c210c [#4 state:fixed] Filters are now applied on the whole file path. 2010-04-13 11:40:20 +02:00
Virgil Dupras
3ed5e1bf95 [#12 state:fixed] Added custom command help. 2010-04-13 11:05:42 +02:00
Virgil Dupras
5bc8581389 [#12] Tweaked the custom command feature under Cocoa. 2010-04-13 10:52:44 +02:00
Virgil Dupras
7346b422d5 [#12] Added the Custom Command preference on the Qt side. 2010-04-13 09:02:09 +01:00
Virgil Dupras
5c80ac1c74 [#12] dgse cocoa: Added custom command invocation. 2010-04-12 17:43:24 +02:00
Virgil Dupras
699023992c Added the problem dialog to the Qt side. 2010-04-12 15:29:56 +02:00
Virgil Dupras
454ce604ad Merged hsgui heads. 2010-04-12 12:22:18 +02:00
Virgil Dupras
1e0f6bfecb Added a dialog giving more information about the causes of problems during operations. 2010-04-12 12:21:01 +02:00
Virgil Dupras
7f10aa3de2 Merged heads. 2010-04-08 07:07:45 -07:00
Virgil Dupras
f8764ab85e dgme qt: Fixed visual glitch in preference panel under Linux. 2010-04-08 07:06:32 -07:00
Virgil Dupras
aa8544308e Added tag pe1.8.6 for changeset 556baf4a4107 2010-04-08 15:01:27 +02:00
Virgil Dupras
31fc70e0f8 Updated dependencies to include cx_Freeze. 2010-04-08 15:01:21 +02:00
Virgil Dupras
a16af4560b dgse qt: fixed visual glitch in the preference dialog under linux. 2010-04-08 04:26:11 -07:00
Virgil Dupras
0782ba0dab Only do cxfreeze workarounds under Windows. 2010-04-08 04:12:29 -07:00
Virgil Dupras
83725667a4 Made the windows packaging copy qt plugins in the dist package. PyInstaller did this, but cxfreeze doesn't. 2010-04-08 11:17:03 +01:00
Virgil Dupras
f4b3163b04 Merged heads. 2010-04-08 11:14:42 +02:00
Virgil Dupras
6cd745f429 Added hsutil.files.find_in_path() 2010-04-08 11:14:01 +02:00
Virgil Dupras
6131f7f6bf Merge heads. 2010-04-08 07:55:03 +01:00
Virgil Dupras
dd4faa030f Changed the installer project so that we make sure that the executable is always overwritten.
Previously, (probably because the exe doesn't have version embedded in it anymore), we ended up, during upgrades, with executable-less installs.
2010-04-08 07:54:03 +01:00
Virgil Dupras
ab8691f5ac Changed the release date for pe 1.8.6, which has been delayed by packaging problems. 2010-04-08 08:35:17 +02:00
Virgil Dupras
77ab073cdb Added a missing python-lxml dep to the debian packages. 2010-04-07 09:32:49 -07:00
Virgil Dupras
87e0011525 Under Linux, don't show the "Check for Update" action and correctly open the help file. 2010-04-07 09:04:58 -07:00
Virgil Dupras
7af3bb7226 Merged heads. 2010-04-07 08:50:56 -07:00
Virgil Dupras
5573352ce6 PyInstaller is fucked up. Moved to cxFreeze. 2010-04-07 16:30:04 +01:00
Virgil Dupras
e6486e08ab Qt: fixed help packaging. 2010-04-07 15:04:09 +01:00
Virgil Dupras
48badaa927 pe v1.8.6 2010-04-07 13:59:40 +02:00
Virgil Dupras
2f13bf677e Adjusted details table height by 2 pixels so that it doesn't show a scrollbar under Linux. 2010-04-07 04:02:18 -07:00
Virgil Dupras
e63abc1b4b Added debian packaging support. 2010-04-07 03:56:43 -07:00
Virgil Dupras
88334acdef [#90 state:fixed] Fixed a rare crash on results loading. 2010-04-07 10:29:00 +02:00
Virgil Dupras
0491aa9f6e Updated dependencies in README. 2010-04-07 09:14:10 +02:00
Virgil Dupras
5be76d7c0f Use the send2trash lib in _do_delete_dupe(). 2010-04-07 09:11:36 +02:00
Virgil Dupras
3b510389fc cocoa: Removed obsolete refreshStats calls. 2010-04-07 09:09:19 +02:00
Virgil Dupras
32d88e9249 Limit the size of arguments sent to multiprocessing because it could cause crashes. 2010-04-05 10:15:33 +02:00
Virgil Dupras
7b1a1ff4bb Added tag pe1.8.5 for changeset 0a71306434bc 2010-03-01 17:15:23 +01:00
Virgil Dupras
19beb919d0 Fixed the automatic update check option on the Cocoa side. 2010-03-01 16:09:59 +01:00
Virgil Dupras
ba09e8bf4d Updated the readme file to add the lxml dependency. 2010-03-01 14:35:58 +01:00
Virgil Dupras
26dd2d0e8e Updated py2app workaround in dg_cocoa for lxml. 2010-03-01 04:15:27 -08:00
Virgil Dupras
69b15d58a2 Updated hsutil subrepo. 2010-03-01 12:33:16 +01:00
Virgil Dupras
ba68789fb9 pe v1.8.5 2010-03-01 12:31:34 +01:00
Virgil Dupras
47a6ceffbc Use lxml everywhere for xml save/load (instead of ElementTree and minidom). 2010-03-01 12:21:43 +01:00
Virgil Dupras
b17ca66f73 Fixed crashes when reading invalid iPhoto AlbumData file. This time, I used lxml's "recover" feature to filter out crap in the XML, so it should cover most cases of invalid stuff in iPhoto data files. 2010-03-01 12:20:21 +01:00
Virgil Dupras
93bc609026 Updated the SE cocoa project so that it includes the lastest changes in dgbase and cocoalib. 2010-03-01 12:14:49 +01:00
Virgil Dupras
3ea51c2e15 Added tag pe1.8.4 for changeset 4c3cb1e671a3 2010-02-18 15:31:59 +01:00
Virgil Dupras
1d9897ea60 (Forgot to commit). Updated the ME installer project for Advanced Installer 7.5. 2010-02-18 09:49:28 +00:00
Virgil Dupras
b6cb00bc79 pe 1.8.4 2010-02-18 10:31:24 +01:00
Virgil Dupras
6dd53c6bfd Removing duplicates now preserve selected paths. 2010-02-17 18:05:19 +01:00
Virgil Dupras
07df5126b3 Adapted the PE edition to the latest refactorings and fixed a (very) minor memory leak in ME. 2010-02-17 17:37:42 +01:00
Virgil Dupras
47b38c7d45 Preliminary linux support (it starts up, at least...). 2010-02-13 12:22:34 -08:00
Virgil Dupras
0e97bec7b2 Added tag me5.7.2 for changeset 90ed56ee6026 2010-02-13 18:36:54 +01:00
Virgil Dupras
b182585d46 Fixed column reloading which was broken since the mark-->marked rename. 2010-02-13 14:08:37 +01:00
Virgil Dupras
e8f92535d3 me v5.7.2 2010-02-13 13:00:41 +01:00
Virgil Dupras
d62c3663e9 qt: scroll to selection on results refresh. 2010-02-13 12:34:36 +01:00
Virgil Dupras
6b0bfda9fb During Make Selected Reference, it's now the selection *paths* that are restored rather than the selected *dupes* 2010-02-13 10:39:54 +01:00
Virgil Dupras
7477330961 Fixed ResultOutline.selectedDupeCount(). 2010-02-12 21:58:50 +01:00
Virgil Dupras
1f71157063 Updated cocoalib subrepo. 2010-02-12 20:10:50 +01:00
Virgil Dupras
905988c592 Removed MatchesView and took advantage of HSOutlineView's delete and space triggered delegate methods. 2010-02-12 17:15:48 +01:00
Virgil Dupras
310951bfa8 Removed getSelectedPaths() from ResultsWindow. 2010-02-12 16:30:32 +01:00
Virgil Dupras
64c1087856 Fixed app_test which was broken since connext() calls aren't made by the gui themselves. 2010-02-12 16:28:15 +01:00
Virgil Dupras
cab6d924aa Adapted the Qt codebase to the addition of core.gui.result_tree and core.gui.stats_label. 2010-02-12 15:39:29 +01:00
Virgil Dupras
c3a972d39b Fixed renaming in results. 2010-02-12 13:52:40 +01:00
Virgil Dupras
33d44d4d24 Remove Marked now correctly updates the results. 2010-02-12 13:39:50 +01:00
Virgil Dupras
fd89cf2482 Pushed some code down from app_cocoa to app and re-organized test units. 2010-02-12 12:43:50 +01:00
Virgil Dupras
112ffb981f Cleaned up some cruft. 2010-02-12 12:30:00 +01:00
Virgil Dupras
514426b980 Re-added the root children count optimization in the results outline. 2010-02-12 11:34:00 +01:00
Virgil Dupras
a4bf1c8be6 Made marking changes much faster and also made data fetching lazy in dupe nodes. 2010-02-12 11:21:39 +01:00
Virgil Dupras
9b82e1478f Re-added multiple selection support in the results. 2010-02-12 11:07:33 +01:00
Virgil Dupras
d5f145d57e Fixed sorting. 2010-02-11 21:03:22 +01:00
Virgil Dupras
bab891ee74 Added the StatsLabel. 2010-02-11 20:54:06 +01:00
Virgil Dupras
a65fd7d0d0 Brought back delta values. 2010-02-11 19:22:31 +01:00
Virgil Dupras
46836cc805 Pushed down some result refresh calls to the core code. 2010-02-11 18:47:45 +01:00
Virgil Dupras
42559f13d8 Began the transition to a HSOutline based result outline. There's still a lot of glitches, the most glaring one being the lack of support for multiple selection. 2010-02-11 17:52:18 +01:00
Virgil Dupras
87351b5920 Removed Table from cocoalib and fixed the license of the newly added units. 2010-02-11 13:38:34 +01:00
Virgil Dupras
e68dcf189c Adapted the ME project to the latest structural changes. 2010-02-11 13:35:14 +01:00
Virgil Dupras
5d62b8389c Added tag pe1.8.3 for changeset 1cef6d39855f 2010-02-11 12:37:15 +01:00
Virgil Dupras
c50aebe76d pe v1.8.3 2010-02-11 10:04:54 +01:00
Virgil Dupras
a610f3fde7 Adapted the PE project to the latest structural changes. 2010-02-10 12:07:31 +01:00
Virgil Dupras
626391a1d9 [#94 state:fixed] Fixed bug in block_osx causing blocks containing nil values to be created. 2010-02-10 11:58:05 +01:00
Virgil Dupras
1bedfe75ea Added tag se2.9.2 for changeset 7b7c5a66ebee 2010-02-10 10:50:05 +01:00
Virgil Dupras
86ecc8d4d5 Fixed build script 2010-02-10 00:18:25 -08:00
Virgil Dupras
9eca84efe1 se v2.9.2 2010-02-10 08:48:01 +01:00
Virgil Dupras
8a6fb6dcba Updated Andvanced Installer project file for 7.5. 2010-02-09 15:03:36 +00:00
Virgil Dupras
e3706fa923 Fixed qt packaging. 2010-02-09 14:52:09 +00:00
Virgil Dupras
8193bc5f60 build.add_to_pythonpath() now also adds the path to sys.path. 2010-02-09 15:47:22 +01:00
Virgil Dupras
504ecaee5e Straightened out qt's packaging process. 2010-02-09 15:42:48 +01:00
Virgil Dupras
7c9e836572 Straightened out qt's build process. 2010-02-09 15:32:52 +01:00
Virgil Dupras
5db0f09b43 Fixed Reveal File on Qt. 2010-02-09 15:24:57 +01:00
Virgil Dupras
195bc4ef21 Eliminated code duplication in ResultsWindow. 2010-02-09 14:59:35 +01:00
Virgil Dupras
6b190bc184 Fixed a bug where double clicking a column would open the selected file. 2010-02-09 14:55:51 +01:00
Virgil Dupras
39f1cac2c8 Eliminated code duplication in ResultsWindow's awakeFromNib. 2010-02-09 14:50:27 +01:00
Virgil Dupras
d193eed519 [#93 state:fixed] Straightened out selection and matches reloading. 2010-02-09 14:45:14 +01:00
Virgil Dupras
2d80b0e12f Updated hsutil subrepo. 2010-02-08 08:37:40 +01:00
Virgil Dupras
b50d99be9c Added the PyRegistrable cocoa interface. 2010-02-07 16:29:39 +01:00
Virgil Dupras
af41876a5e DetailsPanel is now a subclass of HGWindowController. 2010-02-07 16:19:14 +01:00
Virgil Dupras
76d351d8be Adapted th qt part to core.gui.directory_tree. 2010-02-07 16:00:58 +01:00
Virgil Dupras
b5dd9651c3 Huge refactoring. I moved MGOutline from moneyGuru (as well as everything that comes with it) and used it to create DirectoryOutline for the directories panel. 2010-02-07 15:26:50 +01:00
Virgil Dupras
3e34502014 Added the hsgui subrepo. 2010-02-06 15:35:51 +01:00
Virgil Dupras
5e57f9cbd6 Removed logic duplication across toolkit code in "Reveal Selected" action. 2010-02-06 15:31:35 +01:00
Virgil Dupras
8edb869fdc Removed logic duplication across toolkit code in "Remove Selected" action. 2010-02-06 12:44:21 +01:00
Virgil Dupras
37238c7f57 Removed logic duplication across toolkit code in "Open Selected" action. 2010-02-06 12:36:43 +01:00
Virgil Dupras
9edee82fa1 Removed logic duplication across toolkit code in "Make Reference" action. 2010-02-06 12:27:11 +01:00
Virgil Dupras
f7aaea79af Removed useless add_to_ignore_list() 2010-02-06 12:14:33 +01:00
Virgil Dupras
3c75d2f8b7 Removed logic duplication across toolkit code in "Add to Ignore List" action. 2010-02-06 12:12:20 +01:00
Virgil Dupras
64c67e19d2 Reduced code duplication among editions in ResultsWindow. 2010-02-06 11:40:10 +01:00
Virgil Dupras
d4db8faad8 Added tag pe1.8.2 for changeset 19e40bab2052 2010-02-06 10:49:13 +01:00
Virgil Dupras
7957b73b4a Tweaked PE installer project. 2010-02-06 09:30:33 +00:00
Virgil Dupras
69838c44af pe 1.8.2 2010-02-06 09:09:40 +01:00
Virgil Dupras
8e2953aef6 Updated PE installer for Advanced Installer 7.5 and changed build scripts so they use the Advanced Installer command present in the PATH. 2010-02-06 07:58:37 +00:00
Virgil Dupras
8dda616502 The Qt side now makes use of core.gui.details_panel. 2010-02-05 21:09:04 +01:00
Virgil Dupras
484512e35b Removed refreshDetailsWithSelected which wasn't needed anymore. 2010-02-05 20:32:57 +01:00
Virgil Dupras
c8cd05c07d Removed code duplication among editions in ResultWindow. 2010-02-05 20:16:56 +01:00
Virgil Dupras
7ffefe6259 Created gui.details_panel and moved all details panel related logic in there (cocoa only, for now). 2010-02-05 20:10:54 +01:00
Virgil Dupras
cd9b7f2f11 [#86 state:fixed] Fixed a crash in GetOutlineViewValues. 2010-02-05 18:16:05 +01:00
Virgil Dupras
b372974437 [#84 state:hold] Added debug logging to fs.get_files() to eventually figure out the cause of this bug. 2010-02-05 17:55:47 +01:00
Virgil Dupras
7464e0f799 [#85 state:fixed] Fixed crash when sorting by Words Used after a Contents scan. 2010-02-05 17:47:17 +01:00
Virgil Dupras
25e12f1775 [#83 state:fixed] Fixed crash on quitting if the appdata dir has been removed. 2010-02-05 17:24:20 +01:00
Virgil Dupras
6416469f78 Re-organized DetailsPanel across editions in a saner way. 2010-02-05 17:15:45 +01:00
Virgil Dupras
922ce5ae36 Re-organized DirectoryPanel across editions in a saner way. 2010-02-05 17:05:00 +01:00
Virgil Dupras
9ca8a199c0 Re-implemented the fix for utf-8 lookup error during auto-update in a more graceful way. 2010-02-05 16:51:00 +01:00
Virgil Dupras
a570406ac8 Reduced code duplication among editions in AppDelegate.m. 2010-02-05 16:31:09 +01:00
Virgil Dupras
719edb6b6e Use hsutil.cocoa.objcmin instead of Foundation and AppKit. 2010-02-04 17:12:58 +01:00
Virgil Dupras
d075218621 Removed a </li> tag in preferences help pages which had nothing to do there. 2010-02-04 15:25:29 +01:00
Virgil Dupras
7509943938 Added _block_osx to py2app workaround in dg_cocoa. 2010-02-04 06:13:40 -08:00
Virgil Dupras
774da9d2f8 Fixed XCode projects' target build settings. 2010-02-04 15:00:06 +01:00
Virgil Dupras
978fd383e8 Fixed package.py which was broken since the xcode template change. 2010-02-04 14:36:02 +01:00
Virgil Dupras
8551fc23fe Removed the 'build64' option and added a 'dev' configuration to all xcode projects. 2010-02-04 13:45:35 +01:00
Virgil Dupras
fb711edeeb Use hsutil.cocoa.signature instead of objc.signature in dgse.dg_cocoa. 2010-02-04 13:20:38 +01:00
Virgil Dupras
352a21acaa Converted PictureBlocks to a Python extension and created a "common" C unit for common code among extensions. 2010-02-04 13:13:08 +01:00
Virgil Dupras
0b9d936317 Optimized qt/pe/modules/block.c 2010-02-03 15:44:15 +01:00
Virgil Dupras
dc500243e9 Updated cocoalib subrepo. 2010-02-03 12:19:51 +01:00
Virgil Dupras
21203b8341 Adapted to NIB --> XIB conversion in cocoalib. 2010-02-03 12:17:39 +01:00
Virgil Dupras
5b03447640 Updated subrepos to improve the registration process. 2010-02-03 11:58:58 +01:00
Virgil Dupras
d34158db2c Merged dg_cocoa files from all editions into core/app_cocoa_inter to eliminate annoying code duplication. 2010-02-03 11:55:53 +01:00
Virgil Dupras
65a17390c7 Corrected grammatical mistake in preferences panels. 2010-02-02 11:50:47 +01:00
Virgil Dupras
0e96f0917c core_pe.modules.block: Converted inttuple() to a vararg based function. 2010-01-31 12:41:28 +01:00
Virgil Dupras
3d62a7e64a Reorganized qt/pe/modules
--HG--
rename : qt/pe/modules/block/block.c => qt/pe/modules/block.c
rename : qt/pe/modules/block/setup.py => qt/pe/modules/setup.py
2010-01-31 12:25:34 +01:00
Virgil Dupras
962805936e ifdef'd min/max functions when compiled under VC. It seems that VC already defines them. 2010-01-31 11:05:13 +00:00
Virgil Dupras
967aeecf5b Removed "inline" directive from C modules (doesn't work in VC). 2010-01-31 11:33:26 +01:00
Virgil Dupras
348b039fa3 Removed references to Cython. 2010-01-31 11:25:47 +01:00
Virgil Dupras
6e9b1f4fa3 Converted qt/modules/block from Cython to C. 2010-01-31 11:24:51 +01:00
Virgil Dupras
f1d447d1aa Fixed core_pe's c modules licence notices. 2010-01-31 11:23:23 +01:00
Virgil Dupras
a7c6f47dbe Reorganized core_pe's module folder.
--HG--
rename : core_pe/modules/block/block.c => core_pe/modules/block.c
rename : core_pe/modules/cache/cache.c => core_pe/modules/cache.c
rename : core_pe/modules/cache/setup.py => core_pe/modules/setup.py
2010-01-31 10:32:02 +01:00
Virgil Dupras
0446e89bfe Converted core_pe's "block" module from Cython to C. 2010-01-31 10:27:59 +01:00
Virgil Dupras
e41457913f Fixed a memory leak in the cache module. 2010-01-31 10:12:26 +01:00
Virgil Dupras
cea1ec7641 core_pe: Aah, got it. Performance from the new cache module are now comparable to the old Cython based one. 2010-01-30 17:19:40 +01:00
Virgil Dupras
cc362deb87 core_pe: Tried to improve speed of that newly converted cache module. 2010-01-30 16:50:49 +01:00
Virgil Dupras
7ec64e8a3d core_pe: Re-implemented the "cache" cython module in C. I like coding in C from time to time... 2010-01-30 16:29:18 +01:00
Virgil Dupras
ff2461df9d Fix the configure script so that the --64bit flag works. 2010-01-20 15:22:25 +01:00
Virgil Dupras
192cd2733c Added tag me5.7.1 for changeset 2c454eca9ebe 2010-01-19 17:59:11 +01:00
Virgil Dupras
ecef95469d me v5.7.1 2010-01-19 12:38:30 +01:00
Virgil Dupras
55d30d5e4b Use hsutil.cocoa.signature in me/dg_cocoa.py, Added the Remove Dead Tracks menu item which was lost since 5.7 (how did I not notice that?) 2010-01-19 12:28:15 +01:00
Virgil Dupras
2d5502cc2f Explicited pyobjc imports. 2010-01-18 08:48:44 +01:00
Virgil Dupras
5cda4a1eb4 Updated the cocoalib subrepo 2010-01-17 12:41:19 +01:00
Virgil Dupras
812b914b70 Added support for 64 bit in the build script. 2010-01-15 11:47:40 +01:00
Virgil Dupras
9b870ad863 Converted int to NSInteger 2010-01-15 11:19:24 +01:00
Virgil Dupras
0f250ac92d Added tag pe1.8.1 for changeset 0e923897a338 2010-01-15 09:00:36 +01:00
Virgil Dupras
552e6b7836 pe v1.8.1 2010-01-15 07:24:40 +01:00
Virgil Dupras
28f70b281b [#80 state:fixed] Removed some old references to the hsfs system. 2010-01-14 16:33:27 +01:00
Virgil Dupras
32d9b573c0 Removed the test for threading support in Cache. That feature has been removed in the previous commit. 2010-01-14 16:17:38 +01:00
Virgil Dupras
fc76a843d5 Straightened out the blocks cache. Instead of having a single global threaded block cache in the app, there's just a cache path, and non-threaded caches are created when needed. Also, made Cache.clear() more robust (it will clear the cache even if the db is corrupted). 2010-01-14 16:14:26 +01:00
Virgil Dupras
06607aabb2 Added tag se2.9.1 for changeset 61c4101851bd 2010-01-13 18:03:05 +01:00
185 changed files with 8081 additions and 6197 deletions

View File

@@ -6,16 +6,20 @@ syntax: glob
*.mode1v3 *.mode1v3
*.pbxuser *.pbxuser
*.tm_build_errors *.tm_build_errors
*.pyd
conf.yaml conf.yaml
build build
core_pe/modules/block/block.c
core_pe/modules/cache/cache.c
cocoa/*/Info.plist cocoa/*/Info.plist
cocoa/*/build cocoa/*/build
cocoa/*/dg_cocoa.plugin cocoa/*/dg_cocoa.plugin
qt/base/*_rc.py qt/base/*_rc.py
qt/base/*_ui.py qt/base/*_ui.py
qt/se/*_ui.py qt/*/*_ui.py
qt/*/build
qt/*/dist
qt/*/install
qt/*/logdict*.log
qt/*/warn*.txt
help_se/dupeguru_help help_se/dupeguru_help
help_me/dupeguru_me_help help_me/dupeguru_me_help
help_pe/dupeguru_pe_help help_pe/dupeguru_pe_help

10
.hgtags
View File

@@ -5,3 +5,13 @@ a8f232f880b6f9ada565d472996a627ebf69b6e9 before-tiger-drop
321d15e818cf9a3f1fc037543090bb2fca2cccd7 me5.7.0 321d15e818cf9a3f1fc037543090bb2fca2cccd7 me5.7.0
adc73ccd14b1386cb04dee773c53a2d126800e31 se2.9.0 adc73ccd14b1386cb04dee773c53a2d126800e31 se2.9.0
cbcf9c80fee4c908ef2efbf1c143c9e47676c9b2 pe1.8.0 cbcf9c80fee4c908ef2efbf1c143c9e47676c9b2 pe1.8.0
61c4101851bdea3cb37dfb76f0d404c78c7c594c se2.9.1
0e923897a3389331d4ab3debbc40b8dd616199d9 pe1.8.1
2c454eca9ebe93b6cf34916068f828a6a39e3eaf me5.7.1
19e40bab20521d4256acf325dba9b32e95e135c5 pe1.8.2
7b7c5a66ebee4e4b8125330d24fe9ce1a070ff25 se2.9.2
1cef6d39855f85d4be728646bc78b860e6d4e398 pe1.8.3
90ed56ee602666db2f267f73eac6f824347039b5 me5.7.2
4c3cb1e671a333eabde1151c7c6ffb3609cab025 pe1.8.4
0a71306434bca51bea9a5d5ae54fe1bf0e4900d8 pe1.8.5
556baf4a410779e9bbf43129de133e4c4b26d679 pe1.8.6

18
README
View File

@@ -27,26 +27,27 @@ General dependencies
----- -----
- Python 2.6 (http://www.python.org) - Python 2.6 (http://www.python.org)
- Send2Trash (http://hg.hardcoded.net/send2trash)
- lxml, to read and write XML files. (http://codespeak.net/lxml/)
- Mako, to generate help files. (http://www.makotemplates.org/) - Mako, to generate help files. (http://www.makotemplates.org/)
- PyYaml, for help files and the build system. (http://pyyaml.org/) - PyYaml, for help files and the build system. (http://pyyaml.org/)
- Nose, to run unit tests. (http://somethingaboutorange.com/mrl/projects/nose/) - Nose, to run unit tests. (http://somethingaboutorange.com/mrl/projects/nose/)
- Cython to compile a few optimized bottlenecks. (http://www.cython.org/)
- Python Imaging Library for dupeGuru PE. (http://www.pythonware.com/products/pil/)
OS X prerequisites OS X prerequisites
----- -----
- XCode 3.1 (http://developer.apple.com/TOOLS/xcode/) - XCode 3.1 (http://developer.apple.com/TOOLS/xcode/)
- Sparkle (http://sparkle.andymatuschak.org/) - Sparkle (http://sparkle.andymatuschak.org/)
- PyObjC. Although Tiger support has been dropped with dupeGuru 1.7, I still use PyObjC 1.4 because funky stuff happens with newer releases. However, it's mostly related to packaging with py2app. (http://pyobjc.sourceforge.net/) - PyObjC 2.2. (http://pyobjc.sourceforge.net/)
- py2app (http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html) - py2app (http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html)
Windows prerequisites Windows prerequisites
--- ---
- Visual Studio 2008 (Express is enough) is needed to build the Cython extensions. (http://www.microsoft.com/Express/) - Visual Studio 2008 (Express is enough) is needed to build C extensions. (http://www.microsoft.com/Express/)
- PyQt 4.6 (http://www.riverbankcomputing.co.uk/news) - PyQt 4.6 (http://www.riverbankcomputing.co.uk/news)
- PyInstaller, if you want to build a exe. You don't need it if you just want to run dupeGuru. (http://www.pyinstaller.org/) - Python Imaging Library for dupeGuru PE. (http://www.pythonware.com/products/pil/)
- cx_Freeze, if you want to build a exe. You don't need it if you just want to run dupeGuru. (http://cx-freeze.sourceforge.net/)
- Advanced Installer, if you want to build the installer file. (http://www.advancedinstaller.com/) - Advanced Installer, if you want to build the installer file. (http://www.advancedinstaller.com/)
Building dupeGuru Building dupeGuru
@@ -56,7 +57,7 @@ First, make sure you meet the dependencies listed in the section above. Then you
python configure.py python configure.py
If you want, you can specify a UI to use with the `--ui` option. So, if you want to build dupeGuru with Qt on OS X, then you have to type `python configure.py --ui=qt`. You can also use the `--dev` flag to indicate a dev build (it will build `mg_cocoa.plugin` in alias mode). If you want, you can specify a UI to use with the `--ui` option. So, if you want to build dupeGuru with Qt on OS X, then you have to type `python configure.py --ui=qt`. You can also use the `--dev` flag to indicate a dev build (it will build `dg_cocoa.plugin` in alias mode and use the "dev" config in XCode).
Then, just build the thing and then run it with: Then, just build the thing and then run it with:
@@ -66,3 +67,8 @@ Then, just build the thing and then run it with:
If you want to create ready-to-upload package, run: If you want to create ready-to-upload package, run:
python package.py python package.py
64-bit on OS X
---
The "release" configuration of dupeGuru's XCode project build with archs "i386 x86_64 ppc". However there are currently problems with py2app and 64 bit. If you want to correctly build 64-bit apps, refer to http://www.hardcoded.net/articles/building-64-bit-pyobjc-applications-with-py2app.htm .

View File

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

View File

@@ -11,6 +11,7 @@ http://www.hardcoded.net/licenses/hs_license
#import "PyDupeGuru.h" #import "PyDupeGuru.h"
#import "ResultWindow.h" #import "ResultWindow.h"
#import "DetailsPanel.h" #import "DetailsPanel.h"
#import "DirectoryPanel.h"
@interface AppDelegateBase : NSObject @interface AppDelegateBase : NSObject
{ {
@@ -19,11 +20,15 @@ http://www.hardcoded.net/licenses/hs_license
IBOutlet NSMenuItem *unlockMenuItem; IBOutlet NSMenuItem *unlockMenuItem;
IBOutlet ResultWindowBase *result; IBOutlet ResultWindowBase *result;
DetailsPanelBase *_detailsPanel; DirectoryPanel *_directoryPanel;
DetailsPanel *_detailsPanel;
BOOL _savedResults;
} }
- (IBAction)unlockApp:(id)sender; - (IBAction)unlockApp:(id)sender;
- (PyDupeGuruBase *)py; - (PyDupeGuruBase *)py;
- (RecentDirectories *)recentDirectories; - (RecentDirectories *)recentDirectories;
- (DetailsPanelBase *)detailsPanel; // Virtual - (DirectoryPanel *)directoryPanel;
- (DetailsPanel *)detailsPanel;
- (void)saveResults;
@end @end

View File

@@ -11,6 +11,7 @@ http://www.hardcoded.net/licenses/hs_license
#import "RegistrationInterface.h" #import "RegistrationInterface.h"
#import "Utils.h" #import "Utils.h"
#import "Consts.h" #import "Consts.h"
#import <Sparkle/SUUpdater.h>
@implementation AppDelegateBase @implementation AppDelegateBase
- (IBAction)unlockApp:(id)sender - (IBAction)unlockApp:(id)sender
@@ -28,7 +29,29 @@ http://www.hardcoded.net/licenses/hs_license
- (PyDupeGuruBase *)py { return py; } - (PyDupeGuruBase *)py { return py; }
- (RecentDirectories *)recentDirectories { return recentDirectories; } - (RecentDirectories *)recentDirectories { return recentDirectories; }
- (DetailsPanelBase *)detailsPanel { return nil; } // Virtual - (DirectoryPanel *)directoryPanel
{
if (!_directoryPanel)
_directoryPanel = [[DirectoryPanel alloc] initWithParentApp:self];
return _directoryPanel;
}
- (DetailsPanel *)detailsPanel
{
if (!_detailsPanel)
_detailsPanel = [[DetailsPanel alloc] initWithPy:py];
return _detailsPanel;
}
- (void)saveResults
{
if (_savedResults) {
return;
}
[py saveIgnoreList];
[py saveResults];
_savedResults = YES;
}
/* Delegate */ /* Delegate */
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
@@ -48,5 +71,47 @@ http://www.hardcoded.net/licenses/hs_license
//Restore results //Restore results
[py loadIgnoreList]; [py loadIgnoreList];
[py loadResults]; [py loadResults];
_savedResults = NO;
}
- (void)applicationWillBecomeActive:(NSNotification *)aNotification
{
if (![[result window] isVisible])
[result showWindow:NSApp];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[ud setObject: [result getColumnsOrder] forKey:@"columnsOrder"];
[ud setObject: [result getColumnsWidth] forKey:@"columnsWidth"];
[self saveResults];
NSInteger sc = [ud integerForKey:@"sessionCountSinceLastIgnorePurge"];
if (sc >= 10)
{
sc = -1;
[py purgeIgnoreList];
}
sc++;
[ud setInteger:sc forKey:@"sessionCountSinceLastIgnorePurge"];
// NSApplication does not release nib instances objects, we must do it manually
// Well, it isn't needed because the memory is freed anyway (we are quitting the application
// But I need to release RecentDirectories so it saves the user defaults
[recentDirectories release];
}
- (void)recentDirecoryClicked:(NSString *)directory
{
[[self directoryPanel] addDirectory:directory];
}
/* SUUpdater delegate */
- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update untilInvoking:(NSInvocation *)invocation;
{
/* If results aren't saved now, we might get a weird utf-8 lookup error when saving later.
**/
[self saveResults];
return NO;
} }
@end @end

View File

@@ -8,12 +8,6 @@ http://www.hardcoded.net/licenses/hs_license
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#define DuplicateSelectionChangedNotification @"DuplicateSelectionChangedNotification"
/* ResultsChangedNotification happens on major changes, which requires a complete reload of the data*/
#define ResultsChangedNotification @"ResultsChangedNotification"
/* ResultsChangedNotification happens on minor changes, which requires buffer flush*/
#define ResultsUpdatedNotification @"ResultsUpdatedNotification"
#define ResultsMarkingChangedNotification @"ResultsMarkingChangedNotification"
#define RegistrationRequired @"RegistrationRequired" #define RegistrationRequired @"RegistrationRequired"
#define JobStarted @"JobStarted" #define JobStarted @"JobStarted"
#define JobInProgress @"JobInProgress" #define JobInProgress @"JobInProgress"
@@ -23,5 +17,3 @@ http://www.hardcoded.net/licenses/hs_license
#define jobCopy @"job_copy" #define jobCopy @"job_copy"
#define jobMove @"job_move" #define jobMove @"job_move"
#define jobDelete @"job_delete" #define jobDelete @"job_delete"
#define DEMO_MAX_ACTION_COUNT 10

View File

@@ -7,19 +7,19 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "HSWindowController.h"
#import "PyApp.h" #import "PyApp.h"
#import "Table.h" #import "PyDetailsPanel.h"
@interface DetailsPanel : HSWindowController
@interface DetailsPanelBase : NSWindowController
{ {
IBOutlet TableView *detailsTable; IBOutlet NSTableView *detailsTable;
} }
- (id)initWithPy:(PyApp *)aPy; - (id)initWithPy:(PyApp *)aPy;
- (PyDetailsPanel *)py;
- (void)refresh;
- (void)toggleVisibility; - (void)toggleVisibility;
/* Notifications */ /* Python --> Cocoa */
- (void)duplicateSelectionChanged:(NSNotification *)aNotification; - (void)refresh;
@end @end

View File

@@ -7,38 +7,60 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import "DetailsPanel.h" #import "DetailsPanel.h"
#import "Consts.h" #import "Utils.h"
@implementation DetailsPanelBase @implementation DetailsPanel
- (id)initWithPy:(PyApp *)aPy - (id)initWithPy:(PyApp *)aPy
{ {
self = [super initWithWindowNibName:@"DetailsPanel"]; self = [super initWithNibName:@"DetailsPanel" pyClassName:@"PyDetailsPanel" pyParent:aPy];
[self window]; //So the detailsTable is initialized. [self window]; //So the detailsTable is initialized.
[detailsTable setPy:aPy]; [self connect];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(duplicateSelectionChanged:) name:DuplicateSelectionChangedNotification object:nil];
return self; return self;
} }
- (void)refresh - (void)dealloc
{
[self disconnect];
[super dealloc];
}
- (PyDetailsPanel *)py
{
return (PyDetailsPanel *)py;
}
- (void)refreshDetails
{ {
[detailsTable reloadData]; [detailsTable reloadData];
} }
- (void)toggleVisibility - (void)toggleVisibility
{ {
if ([[self window] isVisible]) if ([[self window] isVisible]) {
[[self window] close]; [[self window] close];
else }
{ else {
[self refresh]; // selection might have changed since last time [self refreshDetails]; // selection might have changed since last time
[[self window] orderFront:nil]; [[self window] orderFront:nil];
} }
} }
/* Notifications */ /* NSTableView Delegate */
- (void)duplicateSelectionChanged:(NSNotification *)aNotification - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{ {
if ([[self window] isVisible]) return [[self py] numberOfRows];
[self refresh]; }
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)row
{
return [[self py] valueForColumn:[column identifier] row:row];
}
/* Python --> Cocoa */
- (void)refresh
{
if ([[self window] isVisible]) {
[self refreshDetails];
}
} }
@end @end

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,25 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
which should be included with this package. The terms are also available at
http://www.hardcoded.net/licenses/hs_license
*/
#import <Cocoa/Cocoa.h>
#import "HSWindowController.h"
#import "PyApp.h"
#import "PyProblemDialog.h"
#import "HSTable.h"
@interface ProblemDialog : HSWindowController
{
IBOutlet NSTableView *problemTableView;
HSTable *problemTable;
}
- (id)initWithPy:(PyApp *)aPy;
- (PyProblemDialog *)py;
- (IBAction)revealSelected:(id)sender;
@end

View File

@@ -0,0 +1,40 @@
/*
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 "ProblemDialog.h"
#import "Utils.h"
@implementation ProblemDialog
- (id)initWithPy:(PyApp *)aPy
{
self = [super initWithNibName:@"ProblemDialog" pyClassName:@"PyProblemDialog" pyParent:aPy];
[self window]; //So the detailsTable is initialized.
problemTable = [[HSTable alloc] initWithPyClassName:@"PyProblemTable" pyParent:[self py] view:problemTableView];
[self connect];
[problemTable connect];
return self;
}
- (void)dealloc
{
[problemTable disconnect];
[self disconnect];
[problemTable release];
[super dealloc];
}
- (PyProblemDialog *)py
{
return (PyProblemDialog *)py;
}
- (IBAction)revealSelected:(id)sender
{
[[self py] revealSelected];
}
@end

View File

@@ -6,7 +6,10 @@ which should be included with this package. The terms are also available at
http://www.hardcoded.net/licenses/hs_license http://www.hardcoded.net/licenses/hs_license
*/ */
#import "DetailsPanel.h" #import <Cocoa/Cocoa.h>
#import "PyGUI.h"
@implementation DetailsPanel @interface PyDetailsPanel : PyGUI
- (NSInteger)numberOfRows;
- (id)valueForColumn:(NSString *)column row:(NSInteger)row;
@end @end

View File

@@ -7,9 +7,8 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "../base/DirectoryPanel.h" #import "PyOutline.h"
@interface DirectoryPanel : DirectoryPanelBase @interface PyDirectoryOutline : PyOutline
{ - (void)addDirectory:(NSString *)directoryPath;
}
@end @end

View File

@@ -13,7 +13,6 @@ http://www.hardcoded.net/licenses/hs_license
//Actions //Actions
- (NSNumber *)addDirectory:(NSString *)name; - (NSNumber *)addDirectory:(NSString *)name;
- (void)removeDirectory:(NSNumber *)index; - (void)removeDirectory:(NSNumber *)index;
- (void)setDirectory:(NSArray *)indexPath state:(NSNumber *)state;
- (void)loadResults; - (void)loadResults;
- (void)saveResults; - (void)saveResults;
- (void)loadIgnoreList; - (void)loadIgnoreList;
@@ -21,31 +20,21 @@ http://www.hardcoded.net/licenses/hs_license
- (void)clearIgnoreList; - (void)clearIgnoreList;
- (void)purgeIgnoreList; - (void)purgeIgnoreList;
- (NSString *)exportToXHTMLwithColumns:(NSArray *)aColIds; - (NSString *)exportToXHTMLwithColumns:(NSArray *)aColIds;
- (void)invokeCommand:(NSString *)cmd;
- (NSNumber *)doScan; - (NSNumber *)doScan;
- (NSArray *)selectedPowerMarkerNodePaths;
- (void)selectPowerMarkerNodePaths:(NSArray *)aIndexPaths;
- (NSArray *)selectedResultNodePaths;
- (void)selectResultNodePaths:(NSArray *)aIndexPaths;
- (void)toggleSelectedMark; - (void)toggleSelectedMark;
- (void)markAll; - (void)markAll;
- (void)markInvert; - (void)markInvert;
- (void)markNone; - (void)markNone;
- (void)addSelectedToIgnoreList; - (void)addSelectedToIgnoreList;
- (void)refreshDetailsWithSelected;
- (void)removeSelected;
- (void)openSelected; - (void)openSelected;
- (NSNumber *)renameSelected:(NSString *)aNewName;
- (void)revealSelected; - (void)revealSelected;
- (void)makeSelectedReference; - (void)makeSelectedReference;
- (void)applyFilter:(NSString *)filter; - (void)applyFilter:(NSString *)filter;
- (void)sortGroupsBy:(NSNumber *)aIdentifier ascending:(NSNumber *)aAscending;
- (void)sortDupesBy:(NSNumber *)aIdentifier ascending:(NSNumber *)aAscending;
- (void)copyOrMove:(NSNumber *)aCopy markedTo:(NSString *)destination recreatePath:(NSNumber *)aRecreateType; - (void)copyOrMove:(NSNumber *)aCopy markedTo:(NSString *)destination recreatePath:(NSNumber *)aRecreateType;
- (void)deleteMarked; - (void)deleteMarked;
- (void)removeMarked; - (void)removeMarked;
@@ -53,14 +42,12 @@ http://www.hardcoded.net/licenses/hs_license
//Data //Data
- (NSNumber *)getIgnoreListCount; - (NSNumber *)getIgnoreListCount;
- (NSNumber *)getMarkCount; - (NSNumber *)getMarkCount;
- (NSString *)getStatLine; - (BOOL)scanWasProblematic;
- (NSNumber *)getOperationalErrorCount;
//Scanning options //Scanning options
- (void)setMinMatchPercentage:(NSNumber *)percentage; - (void)setMinMatchPercentage:(NSNumber *)percentage;
- (void)setMixFileKind:(NSNumber *)mix_file_kind; - (void)setMixFileKind:(NSNumber *)mix_file_kind;
- (void)setDisplayDeltaValues:(NSNumber *)display_delta_values;
- (void)setEscapeFilterRegexp:(NSNumber *)escape_filter_regexp; - (void)setEscapeFilterRegexp:(NSNumber *)escape_filter_regexp;
- (void)setRemoveEmptyFolders:(NSNumber *)remove_empty_folders; - (void)setRemoveEmptyFolders:(NSNumber *)remove_empty_folders;
- (void)setSizeThreshold:(int)size_threshold; - (void)setSizeThreshold:(NSInteger)size_threshold;
@end @end

View File

@@ -7,8 +7,8 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "../base/DetailsPanel.h" #import "PyGUI.h"
@interface PyProblemDialog : PyGUI
@interface DetailsPanel : DetailsPanelBase - (void)revealSelected;
@end @end

24
cocoa/base/PyResultTree.h Normal file
View File

@@ -0,0 +1,24 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
which should be included with this package. The terms are also available at
http://www.hardcoded.net/licenses/hs_license
*/
#import <Cocoa/Cocoa.h>
#import "PyOutline.h"
@interface PyResultTree : PyOutline
- (BOOL)powerMarkerMode;
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode;
- (BOOL)deltaValuesMode;
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode;
- (NSString *)valueForPath:(NSArray *)aPath column:(NSInteger)aColumn;
- (BOOL)renameSelected:(NSString *)aNewName;
- (void)sortBy:(NSInteger)aIdentifier ascending:(BOOL)aAscending;
- (void)markSelected;
- (void)removeSelected;
- (NSArray *)rootChildrenCounts;
@end

View File

@@ -7,7 +7,8 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "../base/DetailsPanel.h" #import "PyGUI.h"
@interface DetailsPanel : DetailsPanelBase @interface PyStatsLabel : PyGUI
- (NSString *)display;
@end @end

View File

@@ -0,0 +1,26 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
which should be included with this package. The terms are also available at
http://www.hardcoded.net/licenses/hs_license
*/
#import <Cocoa/Cocoa.h>
#import "HSOutline.h"
#import "PyResultTree.h"
@interface ResultOutline : HSOutline
{
NSIndexSet *_deltaColumns;
NSArray *_rootChildrenCounts;
}
- (PyResultTree *)py;
- (BOOL)powerMarkerMode;
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode;
- (BOOL)deltaValuesMode;
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode;
- (void)setDeltaColumns:(NSIndexSet *)aDeltaColumns;
- (NSInteger)selectedDupeCount;
- (void)removeSelected;
@end;

207
cocoa/base/ResultOutline.m Normal file
View File

@@ -0,0 +1,207 @@
/*
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

@@ -7,54 +7,65 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "Outline.h" #import "HSOutlineView.h"
#import "StatsLabel.h"
#import "ResultOutline.h"
#import "ProblemDialog.h"
#import "PyDupeGuru.h" #import "PyDupeGuru.h"
@interface MatchesView : OutlineView
- (void)keyDown:(NSEvent *)theEvent;
@end
@interface ResultWindowBase : NSWindowController @interface ResultWindowBase : NSWindowController
{ {
@protected @protected
IBOutlet PyDupeGuruBase *py; IBOutlet PyDupeGuruBase *py;
IBOutlet id app; IBOutlet id app;
IBOutlet NSSegmentedControl *deltaSwitch; IBOutlet NSSegmentedControl *deltaSwitch;
IBOutlet MatchesView *matches; IBOutlet HSOutlineView *matches;
IBOutlet NSSegmentedControl *pmSwitch; IBOutlet NSSegmentedControl *pmSwitch;
IBOutlet NSTextField *stats; IBOutlet NSTextField *stats;
IBOutlet NSMenu *columnsMenu; IBOutlet NSMenu *columnsMenu;
IBOutlet NSSearchField *filterField;
BOOL _powerMode;
BOOL _displayDelta;
NSMutableArray *_resultColumns; NSMutableArray *_resultColumns;
NSWindowController *preferencesPanel; NSWindowController *preferencesPanel;
ResultOutline *outline;
StatsLabel *statsLabel;
ProblemDialog *problemDialog;
} }
/* Helpers */ /* Helpers */
- (void)fillColumnsMenu; - (void)fillColumnsMenu;
- (NSTableColumn *)getColumnForIdentifier:(int)aIdentifier title:(NSString *)aTitle width:(int)aWidth refCol:(NSTableColumn *)aColumn; - (NSTableColumn *)getColumnForIdentifier:(NSInteger)aIdentifier title:(NSString *)aTitle width:(NSInteger)aWidth refCol:(NSTableColumn *)aColumn;
- (NSArray *)getColumnsOrder; - (NSArray *)getColumnsOrder;
- (NSDictionary *)getColumnsWidth; - (NSDictionary *)getColumnsWidth;
- (NSArray *)getSelected:(BOOL)aDupesOnly;
- (NSArray *)getSelectedPaths:(BOOL)aDupesOnly;
- (void)initResultColumns; - (void)initResultColumns;
- (void)updatePySelection;
- (void)performPySelection:(NSArray *)aIndexPaths;
- (void)refreshStats;
- (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth; - (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth;
/* Actions */ /* Actions */
- (IBAction)clearIgnoreList:(id)sender;
- (IBAction)changeDelta:(id)sender; - (IBAction)changeDelta:(id)sender;
- (IBAction)changePowerMarker:(id)sender; - (IBAction)changePowerMarker:(id)sender;
- (IBAction)copyMarked:(id)sender; - (IBAction)copyMarked:(id)sender;
- (IBAction)deleteMarked:(id)sender; - (IBAction)deleteMarked:(id)sender;
- (IBAction)expandAll:(id)sender;
- (IBAction)exportToXHTML:(id)sender; - (IBAction)exportToXHTML:(id)sender;
- (IBAction)filter:(id)sender;
- (IBAction)ignoreSelected:(id)sender;
- (IBAction)invokeCustomCommand:(id)sender;
- (IBAction)markAll:(id)sender;
- (IBAction)markInvert:(id)sender;
- (IBAction)markNone:(id)sender;
- (IBAction)markSelected:(id)sender;
- (IBAction)moveMarked:(id)sender; - (IBAction)moveMarked:(id)sender;
- (IBAction)openClicked:(id)sender;
- (IBAction)openSelected:(id)sender;
- (IBAction)removeMarked:(id)sender;
- (IBAction)removeSelected:(id)sender;
- (IBAction)renameSelected:(id)sender;
- (IBAction)resetColumnsToDefault:(id)sender; - (IBAction)resetColumnsToDefault:(id)sender;
- (IBAction)revealSelected:(id)sender;
- (IBAction)showPreferencesPanel:(id)sender; - (IBAction)showPreferencesPanel:(id)sender;
- (IBAction)startDuplicateScan:(id)sender;
- (IBAction)switchSelected:(id)sender; - (IBAction)switchSelected:(id)sender;
- (IBAction)toggleColumn:(id)sender; - (IBAction)toggleColumn:(id)sender;
- (IBAction)toggleDelta:(id)sender;
- (IBAction)toggleDetailsPanel:(id)sender; - (IBAction)toggleDetailsPanel:(id)sender;
- (IBAction)togglePowerMarker:(id)sender; - (IBAction)togglePowerMarker:(id)sender;

View File

@@ -14,57 +14,33 @@ http://www.hardcoded.net/licenses/hs_license
#import "AppDelegate.h" #import "AppDelegate.h"
#import "Consts.h" #import "Consts.h"
@implementation MatchesView
- (void)keyDown:(NSEvent *)theEvent
{
unichar key = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
// get flags and strip the lower 16 (device dependant) bits
unsigned int flags = ( [theEvent modifierFlags] & 0x00FF );
if (((key == NSDeleteFunctionKey) || (key == NSDeleteCharacter)) && (flags == 0))
[self sendAction:@selector(removeSelected:) to:[self delegate]];
else
if ((key == 0x20) && (flags == 0)) // Space
[self sendAction:@selector(markSelected:) to:[self delegate]];
else
[super keyDown:theEvent];
}
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
if (![[tableColumn identifier] isEqual:@"0"])
return; //We only want to cover renames.
OVNode *node = item;
NSString *oldName = [[node buffer] objectAtIndex:0];
NSString *newName = object;
if (![newName isEqual:oldName])
{
BOOL renamed = n2b([(PyDupeGuruBase *)py renameSelected:newName]);
if (renamed)
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
else
[Dialogs showMessage:[NSString stringWithFormat:@"The name '%@' already exists.",newName]];
}
}
@end
@implementation ResultWindowBase @implementation ResultWindowBase
- (void)awakeFromNib - (void)awakeFromNib
{ {
[self window]; [self window];
preferencesPanel = [[NSWindowController alloc] initWithWindowNibName:@"Preferences"]; preferencesPanel = [[NSWindowController alloc] initWithWindowNibName:@"Preferences"];
outline = [[ResultOutline alloc] initWithPyParent:py view:matches];
statsLabel = [[StatsLabel alloc] initWithPyParent:py labelView:stats];
problemDialog = [[ProblemDialog alloc] initWithPy:py];
[self initResultColumns]; [self initResultColumns];
[self fillColumnsMenu]; [self fillColumnsMenu];
[deltaSwitch setSelectedSegment:0];
[pmSwitch setSelectedSegment:0];
[matches setTarget:self];
[matches setDoubleAction:@selector(openClicked:)];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registrationRequired:) name:RegistrationRequired object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registrationRequired:) name:RegistrationRequired object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobCompleted:) name:JobCompletedNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobCompleted:) name:JobCompletedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobStarted:) name:JobStarted object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobStarted:) name:JobStarted object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobInProgress:) name:JobInProgress object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobInProgress:) name:JobInProgress object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsChanged:) name:ResultsChangedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resultsUpdated:) name:ResultsUpdatedNotification object:nil];
} }
- (void)dealloc - (void)dealloc
{ {
[outline release];
[preferencesPanel release]; [preferencesPanel release];
[statsLabel release];
[problemDialog release];
[super dealloc]; [super dealloc];
} }
@@ -85,9 +61,9 @@ http://www.hardcoded.net/licenses/hs_license
[mi setTarget:self]; [mi setTarget:self];
} }
- (NSTableColumn *)getColumnForIdentifier:(int)aIdentifier title:(NSString *)aTitle width:(int)aWidth refCol:(NSTableColumn *)aColumn - (NSTableColumn *)getColumnForIdentifier:(NSInteger)aIdentifier title:(NSString *)aTitle width:(NSInteger)aWidth refCol:(NSTableColumn *)aColumn
{ {
NSNumber *n = [NSNumber numberWithInt:aIdentifier]; NSNumber *n = [NSNumber numberWithInteger:aIdentifier];
NSTableColumn *col = [[NSTableColumn alloc] initWithIdentifier:[n stringValue]]; NSTableColumn *col = [[NSTableColumn alloc] initWithIdentifier:[n stringValue]];
[col setWidth:aWidth]; [col setWidth:aWidth];
[col setEditable:NO]; [col setEditable:NO];
@@ -101,13 +77,9 @@ http://www.hardcoded.net/licenses/hs_license
//Returns an array of identifiers, in order. //Returns an array of identifiers, in order.
- (NSArray *)getColumnsOrder - (NSArray *)getColumnsOrder
{ {
NSTableColumn *col;
NSString *colId;
NSMutableArray *result = [NSMutableArray array]; NSMutableArray *result = [NSMutableArray array];
NSEnumerator *e = [[matches tableColumns] objectEnumerator]; for (NSTableColumn *col in [matches tableColumns]) {
while (col = [e nextObject]) NSString *colId = [col identifier];
{
colId = [col identifier];
[result addObject:colId]; [result addObject:colId];
} }
return result; return result;
@@ -116,48 +88,14 @@ http://www.hardcoded.net/licenses/hs_license
- (NSDictionary *)getColumnsWidth - (NSDictionary *)getColumnsWidth
{ {
NSMutableDictionary *result = [NSMutableDictionary dictionary]; NSMutableDictionary *result = [NSMutableDictionary dictionary];
NSTableColumn *col; for (NSTableColumn *col in [matches tableColumns]) {
NSString *colId; NSString *colId = [col identifier];
NSNumber *width; NSNumber *width = [NSNumber numberWithDouble:[col width]];
NSEnumerator *e = [[matches tableColumns] objectEnumerator];
while (col = [e nextObject])
{
colId = [col identifier];
width = [NSNumber numberWithFloat:[col width]];
[result setObject:width forKey:colId]; [result setObject:width forKey:colId];
} }
return result; return result;
} }
- (NSArray *)getSelected:(BOOL)aDupesOnly
{
if (_powerMode)
aDupesOnly = NO;
NSIndexSet *indexes = [matches selectedRowIndexes];
NSMutableArray *nodeList = [NSMutableArray array];
OVNode *node;
int i = [indexes firstIndex];
while (i != NSNotFound)
{
node = [matches itemAtRow:i];
if (!aDupesOnly || ([node level] > 1))
[nodeList addObject:node];
i = [indexes indexGreaterThanIndex:i];
}
return nodeList;
}
- (NSArray *)getSelectedPaths:(BOOL)aDupesOnly
{
NSMutableArray *r = [NSMutableArray array];
NSArray *selected = [self getSelected:aDupesOnly];
NSEnumerator *e = [selected objectEnumerator];
OVNode *node;
while (node = [e nextObject])
[r addObject:p2a([node indexPath])];
return r;
}
- (void)initResultColumns - (void)initResultColumns
{ {
// Virtual // Virtual
@@ -165,80 +103,51 @@ http://www.hardcoded.net/licenses/hs_license
- (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth - (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth
{ {
NSTableColumn *col; for (NSMenuItem *mi in [columnsMenu itemArray]) {
NSString *colId; if ([mi state] == NSOnState) {
NSNumber *width;
NSMenuItem *mi;
//Remove all columns
NSEnumerator *e = [[columnsMenu itemArray] objectEnumerator];
while (mi = [e nextObject])
{
if ([mi state] == NSOnState)
[self toggleColumn:mi]; [self toggleColumn:mi];
} }
}
//Add columns and set widths //Add columns and set widths
e = [aColumnsOrder objectEnumerator]; for (NSString *colId in aColumnsOrder) {
while (colId = [e nextObject]) NSInteger colIndex = [colId integerValue];
{ if ((colIndex == 0) && (![colId isEqual:@"0"])) {
if (![colId isEqual:@"mark"]) continue;
{ }
col = [_resultColumns objectAtIndex:[colId intValue]]; NSTableColumn *col = [_resultColumns objectAtIndex:colIndex];
width = [aColumnsWidth objectForKey:[col identifier]]; NSNumber *width = [aColumnsWidth objectForKey:[col identifier]];
mi = [columnsMenu itemWithTag:[colId intValue]]; NSMenuItem *mi = [columnsMenu itemWithTag:colIndex];
if (width) if (width) {
[col setWidth:[width floatValue]]; [col setWidth:[width floatValue]];
}
[self toggleColumn:mi]; [self toggleColumn:mi];
} }
}
}
- (void)updatePySelection
{
NSArray *selection;
if (_powerMode)
selection = [py selectedPowerMarkerNodePaths];
else
selection = [py selectedResultNodePaths];
[matches selectNodePaths:selection];
}
- (void)performPySelection:(NSArray *)aIndexPaths
{
if (_powerMode)
[py selectPowerMarkerNodePaths:aIndexPaths];
else
[py selectResultNodePaths:aIndexPaths];
}
- (void)refreshStats
{
[stats setStringValue:[py getStatLine]];
} }
/* Actions */ /* Actions */
- (IBAction)clearIgnoreList:(id)sender
{
NSInteger i = n2i([py getIgnoreListCount]);
if (!i)
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"Do you really want to remove all %d items from the ignore list?",i]] == NSAlertSecondButtonReturn) // NO
return;
[py clearIgnoreList];
}
- (IBAction)changeDelta:(id)sender - (IBAction)changeDelta:(id)sender
{ {
_displayDelta = [deltaSwitch selectedSegment] == 1; [outline setDeltaValuesMode:[deltaSwitch selectedSegment] == 1];
[py setDisplayDeltaValues:b2n(_displayDelta)];
[matches reloadData];
[self expandAll:nil];
} }
- (IBAction)changePowerMarker:(id)sender - (IBAction)changePowerMarker:(id)sender
{ {
_powerMode = [pmSwitch selectedSegment] == 1; [outline setPowerMarkerMode:[pmSwitch selectedSegment] == 1];
if (_powerMode)
[matches setTag:2];
else
[matches setTag:0];
[self expandAll:nil];
[self outlineView:matches didClickTableColumn:nil];
[self updatePySelection];
} }
- (IBAction)copyMarked:(id)sender - (IBAction)copyMarked:(id)sender
{ {
int mark_count = [[py getMarkCount] intValue]; NSInteger mark_count = [[py getMarkCount] intValue];
if (!mark_count) if (!mark_count)
return; return;
NSOpenPanel *op = [NSOpenPanel openPanel]; NSOpenPanel *op = [NSOpenPanel openPanel];
@@ -257,7 +166,7 @@ http://www.hardcoded.net/licenses/hs_license
- (IBAction)deleteMarked:(id)sender - (IBAction)deleteMarked:(id)sender
{ {
int mark_count = [[py getMarkCount] intValue]; NSInteger mark_count = [[py getMarkCount] intValue];
if (!mark_count) if (!mark_count)
return; return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to send %d files to Trash. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to send %d files to Trash. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
@@ -267,21 +176,65 @@ http://www.hardcoded.net/licenses/hs_license
[py deleteMarked]; [py deleteMarked];
} }
- (IBAction)expandAll:(id)sender
{
for (int i=0;i < [matches numberOfRows];i++)
[matches expandItem:[matches itemAtRow:i]];
}
- (IBAction)exportToXHTML:(id)sender - (IBAction)exportToXHTML:(id)sender
{ {
NSString *exported = [py exportToXHTMLwithColumns:[self getColumnsOrder]]; NSString *exported = [py exportToXHTMLwithColumns:[self getColumnsOrder]];
[[NSWorkspace sharedWorkspace] openFile:exported]; [[NSWorkspace sharedWorkspace] openFile:exported];
} }
- (IBAction)filter:(id)sender
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[py setEscapeFilterRegexp:b2n(!n2b([ud objectForKey:@"useRegexpFilter"]))];
[py applyFilter:[filterField stringValue]];
}
- (IBAction)ignoreSelected:(id)sender
{
NSInteger selectedDupeCount = [outline selectedDupeCount];
if (!selectedDupeCount)
return;
NSString *msg = [NSString stringWithFormat:@"All selected %d matches are going to be ignored in all subsequent scans. Continue?",selectedDupeCount];
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) // NO
return;
[py addSelectedToIgnoreList];
}
- (IBAction)invokeCustomCommand:(id)sender
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *cmd = [ud stringForKey:@"CustomCommand"];
if ((cmd != nil) && ([cmd length] > 0)) {
[py invokeCommand:cmd];
}
else {
[Dialogs showMessage:@"You have no custom command set up. Set it up in your preferences."];
}
}
- (IBAction)markAll:(id)sender
{
[py markAll];
}
- (IBAction)markInvert:(id)sender
{
[py markInvert];
}
- (IBAction)markNone:(id)sender
{
[py markNone];
}
- (IBAction)markSelected:(id)sender
{
[py toggleSelectedMark];
}
- (IBAction)moveMarked:(id)sender - (IBAction)moveMarked:(id)sender
{ {
int mark_count = [[py getMarkCount] intValue]; NSInteger mark_count = [[py getMarkCount] intValue];
if (!mark_count) if (!mark_count)
return; return;
NSOpenPanel *op = [NSOpenPanel openPanel]; NSOpenPanel *op = [NSOpenPanel openPanel];
@@ -299,31 +252,65 @@ http://www.hardcoded.net/licenses/hs_license
} }
} }
- (IBAction)openClicked:(id)sender
{
if ([matches clickedRow] < 0) {
return;
}
[matches selectRowIndexes:[NSIndexSet indexSetWithIndex:[matches clickedRow]] byExtendingSelection:NO];
[py openSelected];
}
- (IBAction)openSelected:(id)sender
{
[py openSelected];
}
- (IBAction)removeMarked:(id)sender
{
int mark_count = [[py getMarkCount] intValue];
if (!mark_count)
return;
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
return;
[py removeMarked];
}
- (IBAction)removeSelected:(id)sender
{
[outline removeSelected];
}
- (IBAction)renameSelected:(id)sender
{
NSInteger col = [matches columnWithIdentifier:@"0"];
NSInteger row = [matches selectedRow];
[matches editColumn:col row:row withEvent:[NSApp currentEvent] select:YES];
}
- (IBAction)resetColumnsToDefault:(id)sender - (IBAction)resetColumnsToDefault:(id)sender
{ {
// Virtual // Virtual
} }
- (IBAction)revealSelected:(id)sender
{
[py revealSelected];
}
- (IBAction)showPreferencesPanel:(id)sender - (IBAction)showPreferencesPanel:(id)sender
{ {
[preferencesPanel showWindow:sender]; [preferencesPanel showWindow:sender];
} }
- (IBAction)startDuplicateScan:(id)sender
{
// Virtual
}
- (IBAction)switchSelected:(id)sender - (IBAction)switchSelected:(id)sender
{ {
// It might look like a complicated way to get the length of the current dupe list on the py side
// but after a lot of fussing around, believe it or not, it actually is.
int matchesTag = _powerMode ? 2 : 0;
int startLen = [[py getOutlineView:matchesTag childCountsForPath:[NSArray array]] count];
[self performPySelection:[self getSelectedPaths:YES]];
[py makeSelectedReference]; [py makeSelectedReference];
// In some cases (when in a filtered view in Power Marker mode, it's possible that the demoted
// ref is not a part of the filter, making the table smaller. In those cases, we want to do a
// complete reload of the table to avoid a crash.
if ([[py getOutlineView:matchesTag childCountsForPath:[NSArray array]] count] == startLen)
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsUpdatedNotification object:self];
else
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
} }
- (IBAction)toggleColumn:(id)sender - (IBAction)toggleColumn:(id)sender
@@ -346,6 +333,15 @@ http://www.hardcoded.net/licenses/hs_license
} }
} }
- (IBAction)toggleDelta:(id)sender
{
if ([deltaSwitch selectedSegment] == 1)
[deltaSwitch setSelectedSegment:0];
else
[deltaSwitch setSelectedSegment:1];
[self changeDelta:sender];
}
- (IBAction)toggleDetailsPanel:(id)sender - (IBAction)toggleDetailsPanel:(id)sender
{ {
[[(AppDelegateBase *)app detailsPanel] toggleVisibility]; [[(AppDelegateBase *)app detailsPanel] toggleVisibility];
@@ -360,21 +356,6 @@ http://www.hardcoded.net/licenses/hs_license
[self changePowerMarker:sender]; [self changePowerMarker:sender];
} }
/* Delegate */
- (void)outlineView:(NSOutlineView *)outlineView didClickTableColumn:(NSTableColumn *)tableColumn
{
if ([[outlineView sortDescriptors] count] < 1)
return;
NSSortDescriptor *sd = [[outlineView sortDescriptors] objectAtIndex:0];
if (_powerMode)
[py sortDupesBy:i2n([[sd key] intValue]) ascending:b2n([sd ascending])];
else
[py sortGroupsBy:i2n([[sd key] intValue]) ascending:b2n([sd ascending])];
[matches reloadData];
[self expandAll:nil];
}
/* Notifications */ /* Notifications */
- (void)windowWillClose:(NSNotification *)aNotification - (void)windowWillClose:(NSNotification *)aNotification
{ {
@@ -383,32 +364,33 @@ http://www.hardcoded.net/licenses/hs_license
- (void)jobCompleted:(NSNotification *)aNotification - (void)jobCompleted:(NSNotification *)aNotification
{ {
[[NSNotificationCenter defaultCenter] postNotificationName:ResultsChangedNotification object:self];
int r = n2i([py getOperationalErrorCount]);
id lastAction = [[ProgressController mainProgressController] jobId]; id lastAction = [[ProgressController mainProgressController] jobId];
if ([lastAction isEqualTo:jobCopy]) { if ([lastAction isEqualTo:jobCopy]) {
if (r > 0) if ([py scanWasProblematic]) {
[Dialogs showMessage:[NSString stringWithFormat:@"%d file(s) couldn't be copied.",r]]; [problemDialog showWindow:self];
else }
else {
[Dialogs showMessage:@"All marked files were copied sucessfully."]; [Dialogs showMessage:@"All marked files were copied sucessfully."];
} }
}
else if ([lastAction isEqualTo:jobMove]) { else if ([lastAction isEqualTo:jobMove]) {
if (r > 0) if ([py scanWasProblematic]) {
[Dialogs showMessage:[NSString stringWithFormat:@"%d file(s) couldn't be moved. They were kept in the results, and still are marked.",r]]; [problemDialog showWindow:self];
else }
else {
[Dialogs showMessage:@"All marked files were moved sucessfully."]; [Dialogs showMessage:@"All marked files were moved sucessfully."];
} }
else if ([lastAction isEqualTo:jobDelete]) {
if (r > 0) {
NSString *msg = @"%d file(s) couldn't be sent to Trash. They were kept in the results, "\
"and still are marked. See the F.A.Q. section in the help file for details.";
[Dialogs showMessage:[NSString stringWithFormat:msg,r]];
} }
else else if ([lastAction isEqualTo:jobDelete]) {
if ([py scanWasProblematic]) {
[problemDialog showWindow:self];
}
else {
[Dialogs showMessage:@"All marked files were sucessfully sent to Trash."]; [Dialogs showMessage:@"All marked files were sucessfully sent to Trash."];
} }
}
else if ([lastAction isEqualTo:jobScan]) { else if ([lastAction isEqualTo:jobScan]) {
NSInteger groupCount = [[py getOutlineView:0 childCountsForPath:[NSArray array]] count]; NSInteger groupCount = [outline intProperty:@"children_count" valueAtPath:nil];
if (groupCount == 0) if (groupCount == 0)
[Dialogs showMessage:@"No duplicates found."]; [Dialogs showMessage:@"No duplicates found."];
} }
@@ -429,7 +411,6 @@ http://www.hardcoded.net/licenses/hs_license
NSString *desc = [ui valueForKey:@"desc"]; NSString *desc = [ui valueForKey:@"desc"];
[[ProgressController mainProgressController] setJobDesc:desc]; [[ProgressController mainProgressController] setJobDesc:desc];
NSString *jobid = [ui valueForKey:@"jobid"]; NSString *jobid = [ui valueForKey:@"jobid"];
// NSLog(jobid);
[[ProgressController mainProgressController] setJobId:jobid]; [[ProgressController mainProgressController] setJobId:jobid];
[[ProgressController mainProgressController] showSheetForParent:[self window]]; [[ProgressController mainProgressController] showSheetForParent:[self window]];
} }
@@ -440,20 +421,6 @@ http://www.hardcoded.net/licenses/hs_license
[Dialogs showMessage:msg]; [Dialogs showMessage:msg];
} }
- (void)resultsChanged:(NSNotification *)aNotification
{
[matches reloadData];
[self expandAll:nil];
[self outlineViewSelectionDidChange:nil];
[self refreshStats];
}
- (void)resultsUpdated:(NSNotification *)aNotification
{
[matches invalidateBuffers];
[matches invalidateMarkings];
}
- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem - (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
{ {
return ![[ProgressController mainProgressController] isShown]; return ![[ProgressController mainProgressController] isShown];

View File

@@ -7,13 +7,13 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "HSGUIController.h"
#import "PyStatsLabel.h"
@interface StatsLabel : HSGUIController
@interface PictureBlocks : NSObject { {
NSTextField *labelView;
} }
+ (NSString *)getBlocksFromImagePath:(NSString *)imagePath blockCount:(NSNumber *)blockCount; - (id)initWithPyParent:(id)aPyParent labelView:(NSTextField *)aLabelView;
+ (NSSize)getImageSize:(NSString *)imagePath; - (PyStatsLabel *)py;
@end @end
NSString* GetBlocks(NSString *filePath, int blockCount);

38
cocoa/base/StatsLabel.m Normal file
View File

@@ -0,0 +1,38 @@
/*
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 "StatsLabel.h"
#import "Utils.h"
@implementation StatsLabel
- (id)initWithPyParent:(id)aPyParent labelView:(NSTextField *)aLabelView
{
self = [super initWithPyClassName:@"PyStatsLabel" pyParent:aPyParent];
labelView = [aLabelView retain];
[self connect];
return self;
}
- (void)dealloc
{
[self disconnect];
[labelView release];
[super dealloc];
}
- (PyStatsLabel *)py
{
return (PyStatsLabel *)py;
}
/* Python --> Cocoa */
- (void)refresh
{
[labelView setStringValue:[[self py] display]];
}
@end

View File

@@ -2,17 +2,17 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10"> <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data> <data>
<int key="IBDocument.SystemTarget">1050</int> <int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">10B504</string> <string key="IBDocument.SystemVersion">10C540</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string> <string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.AppKitVersion">1038.2</string> <string key="IBDocument.AppKitVersion">1038.25</string>
<string key="IBDocument.HIToolboxVersion">437.00</string> <string key="IBDocument.HIToolboxVersion">458.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions"> <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">740</string> <string key="NS.object.0">740</string>
</object> </object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<integer value="5"/> <integer value="6"/>
</object> </object>
<object class="NSArray" key="IBDocument.PluginDependencies"> <object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
@@ -297,13 +297,21 @@
</object> </object>
<int key="connectionID">12</int> <int key="connectionID">12</int>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">dataSource</string>
<reference key="source" ref="251969872"/>
<reference key="destination" ref="449947658"/>
</object>
<int key="connectionID">21</int>
</object>
<object class="IBConnectionRecord"> <object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection"> <object class="IBOutletConnection" key="connection">
<string key="label">detailsTable</string> <string key="label">detailsTable</string>
<reference key="source" ref="449947658"/> <reference key="source" ref="449947658"/>
<reference key="destination" ref="251969872"/> <reference key="destination" ref="251969872"/>
</object> </object>
<int key="connectionID">13</int> <int key="connectionID">22</int>
</object> </object>
</object> </object>
<object class="IBMutableOrderedSet" key="objectRecords"> <object class="IBMutableOrderedSet" key="objectRecords">
@@ -438,15 +446,22 @@
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys"> <object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<string>-3.IBPluginDependency</string>
<string>10.IBPluginDependency</string> <string>10.IBPluginDependency</string>
<string>10.ImportedFromIB2</string> <string>10.ImportedFromIB2</string>
<string>11.IBPluginDependency</string> <string>11.IBPluginDependency</string>
<string>11.ImportedFromIB2</string> <string>11.ImportedFromIB2</string>
<string>15.IBPluginDependency</string>
<string>15.IBShouldRemoveOnLegacySave</string> <string>15.IBShouldRemoveOnLegacySave</string>
<string>16.IBPluginDependency</string>
<string>16.IBShouldRemoveOnLegacySave</string> <string>16.IBShouldRemoveOnLegacySave</string>
<string>17.IBPluginDependency</string>
<string>17.IBShouldRemoveOnLegacySave</string> <string>17.IBShouldRemoveOnLegacySave</string>
<string>18.IBPluginDependency</string>
<string>18.IBShouldRemoveOnLegacySave</string> <string>18.IBShouldRemoveOnLegacySave</string>
<string>19.IBPluginDependency</string>
<string>19.IBShouldRemoveOnLegacySave</string> <string>19.IBShouldRemoveOnLegacySave</string>
<string>20.IBPluginDependency</string>
<string>20.IBShouldRemoveOnLegacySave</string> <string>20.IBShouldRemoveOnLegacySave</string>
<string>5.IBEditorWindowLastContentRect</string> <string>5.IBEditorWindowLastContentRect</string>
<string>5.IBPluginDependency</string> <string>5.IBPluginDependency</string>
@@ -458,7 +473,6 @@
<string>6.ImportedFromIB2</string> <string>6.ImportedFromIB2</string>
<string>7.IBPluginDependency</string> <string>7.IBPluginDependency</string>
<string>7.ImportedFromIB2</string> <string>7.ImportedFromIB2</string>
<string>8.CustomClassName</string>
<string>8.IBPluginDependency</string> <string>8.IBPluginDependency</string>
<string>8.ImportedFromIB2</string> <string>8.ImportedFromIB2</string>
<string>9.IBPluginDependency</string> <string>9.IBPluginDependency</string>
@@ -467,14 +481,21 @@
<object class="NSMutableArray" key="dict.values"> <object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>{{109, 656}, {451, 161}}</string> <string>{{109, 656}, {451, 161}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -486,7 +507,6 @@
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>TableView</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -509,81 +529,34 @@
</object> </object>
</object> </object>
<nil key="sourceID"/> <nil key="sourceID"/>
<int key="maxID">20</int> <int key="maxID">22</int>
</object> </object>
<object class="IBClassDescriber" key="IBDocument.Classes"> <object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions"> <object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">DetailsPanel</string> <string key="className">DetailsPanel</string>
<string key="superclassName">DetailsPanelBase</string> <string key="superclassName">NSWindowController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">DetailsPanel.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">DetailsPanel</string>
<string key="superclassName">DetailsPanelBase</string>
<object class="NSMutableDictionary" key="outlets"> <object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">detailsTable</string> <string key="NS.key.0">detailsTable</string>
<string key="NS.object.0">NSTableView</string> <string key="NS.object.0">NSTableView</string>
</object> </object>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey"/> <string key="minorKey">../base/DetailsPanel.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">DetailsPanelBase</string> <string key="className">DetailsPanel</string>
<string key="superclassName">NSWindowController</string> <string key="superclassName">NSWindowController</string>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">detailsTable</string>
<string key="NS.object.0">TableView</string>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBUserSource</string>
<string key="minorKey">dgbase/DetailsPanel.h</string> <string key="minorKey"/>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">FirstResponder</string> <string key="className">FirstResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string>
<string key="minorKey"/>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">PyApp</string>
<string key="superclassName">PyRegistrable</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/PyApp.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">PyRegistrable</string>
<string key="superclassName">NSObject</string> <string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/PyRegistrable.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">TableView</string>
<string key="superclassName">NSTableView</string>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">py</string>
<string key="NS.object.0">PyApp</string>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/Table.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">TableView</string>
<string key="superclassName">NSTableView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string> <string key="majorKey">IBUserSource</string>
<string key="minorKey"/> <string key="minorKey"/>

View File

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

View File

@@ -2,18 +2,18 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10"> <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data> <data>
<int key="IBDocument.SystemTarget">1050</int> <int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">10B504</string> <string key="IBDocument.SystemVersion">10D573</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string> <string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.AppKitVersion">1038.2</string> <string key="IBDocument.AppKitVersion">1038.29</string>
<string key="IBDocument.HIToolboxVersion">437.00</string> <string key="IBDocument.HIToolboxVersion">460.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions"> <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">740</string> <string key="NS.object.0">740</string>
</object> </object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<integer value="29"/> <integer value="598"/>
<integer value="21"/> <integer value="219"/>
</object> </object>
<object class="NSArray" key="IBDocument.PluginDependencies"> <object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
@@ -83,11 +83,9 @@
<string key="NSToolbarItemPaletteLabel">Power Marker</string> <string key="NSToolbarItemPaletteLabel">Power Marker</string>
<nil key="NSToolbarItemToolTip"/> <nil key="NSToolbarItemToolTip"/>
<object class="NSSegmentedControl" key="NSToolbarItemView" id="35398541"> <object class="NSSegmentedControl" key="NSToolbarItemView" id="35398541">
<reference key="NSNextResponder"/> <nil key="NSNextResponder"/>
<int key="NSvFlags">256</int> <int key="NSvFlags">256</int>
<string key="NSFrame">{{7, 14}, {67, 24}}</string> <string key="NSFrame">{{7, 14}, {67, 24}}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool> <bool key="NSEnabled">YES</bool>
<object class="NSSegmentedCell" key="NSCell" id="431579725"> <object class="NSSegmentedCell" key="NSCell" id="431579725">
<int key="NSCellFlags">67239424</int> <int key="NSCellFlags">67239424</int>
@@ -178,11 +176,9 @@
<string key="NSToolbarItemPaletteLabel">Filter</string> <string key="NSToolbarItemPaletteLabel">Filter</string>
<nil key="NSToolbarItemToolTip"/> <nil key="NSToolbarItemToolTip"/>
<object class="NSSearchField" key="NSToolbarItemView" id="1013657232"> <object class="NSSearchField" key="NSToolbarItemView" id="1013657232">
<reference key="NSNextResponder"/> <nil key="NSNextResponder"/>
<int key="NSvFlags">258</int> <int key="NSvFlags">258</int>
<string key="NSFrame">{{0, 14}, {81, 22}}</string> <string key="NSFrame">{{0, 14}, {81, 22}}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool> <bool key="NSEnabled">YES</bool>
<object class="NSSearchFieldCell" key="NSCell" id="484816507"> <object class="NSSearchFieldCell" key="NSCell" id="484816507">
<int key="NSCellFlags">343014976</int> <int key="NSCellFlags">343014976</int>
@@ -324,11 +320,9 @@
<string key="NSToolbarItemPaletteLabel">Action</string> <string key="NSToolbarItemPaletteLabel">Action</string>
<nil key="NSToolbarItemToolTip"/> <nil key="NSToolbarItemToolTip"/>
<object class="NSPopUpButton" key="NSToolbarItemView" id="165812138"> <object class="NSPopUpButton" key="NSToolbarItemView" id="165812138">
<reference key="NSNextResponder"/> <nil key="NSNextResponder"/>
<int key="NSvFlags">256</int> <int key="NSvFlags">256</int>
<string key="NSFrame">{{0, 14}, {58, 26}}</string> <string key="NSFrame">{{0, 14}, {58, 26}}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool> <bool key="NSEnabled">YES</bool>
<object class="NSPopUpButtonCell" key="NSCell" id="436420677"> <object class="NSPopUpButtonCell" key="NSCell" id="436420677">
<int key="NSCellFlags">-2076049856</int> <int key="NSCellFlags">-2076049856</int>
@@ -531,11 +525,9 @@
<string key="NSToolbarItemPaletteLabel">Delta Values</string> <string key="NSToolbarItemPaletteLabel">Delta Values</string>
<nil key="NSToolbarItemToolTip"/> <nil key="NSToolbarItemToolTip"/>
<object class="NSSegmentedControl" key="NSToolbarItemView" id="311230297"> <object class="NSSegmentedControl" key="NSToolbarItemView" id="311230297">
<reference key="NSNextResponder"/> <nil key="NSNextResponder"/>
<int key="NSvFlags">256</int> <int key="NSvFlags">256</int>
<string key="NSFrame">{{4, 14}, {67, 24}}</string> <string key="NSFrame">{{4, 14}, {67, 24}}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<bool key="NSEnabled">YES</bool> <bool key="NSEnabled">YES</bool>
<object class="NSSegmentedCell" key="NSCell" id="211272396"> <object class="NSSegmentedCell" key="NSCell" id="211272396">
<int key="NSCellFlags">67239424</int> <int key="NSCellFlags">67239424</int>
@@ -718,7 +710,7 @@
<object class="NSMutableArray" key="NSTableColumns"> <object class="NSMutableArray" key="NSTableColumns">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSTableColumn" id="430098394"> <object class="NSTableColumn" id="430098394">
<string key="NSIdentifier">mark</string> <string key="NSIdentifier">marked</string>
<double key="NSWidth">47</double> <double key="NSWidth">47</double>
<double key="NSMinWidth">16</double> <double key="NSMinWidth">16</double>
<double key="NSMaxWidth">1000</double> <double key="NSMaxWidth">1000</double>
@@ -760,6 +752,7 @@
<int key="NSPeriodicDelay">400</int> <int key="NSPeriodicDelay">400</int>
<int key="NSPeriodicInterval">75</int> <int key="NSPeriodicInterval">75</int>
</object> </object>
<bool key="NSIsEditable">YES</bool>
<reference key="NSTableView" ref="40047569"/> <reference key="NSTableView" ref="40047569"/>
</object> </object>
<object class="NSTableColumn" id="932540235"> <object class="NSTableColumn" id="932540235">
@@ -1153,7 +1146,7 @@
<object class="NSMenuItem" id="578499792"> <object class="NSMenuItem" id="578499792">
<reference key="NSMenu" ref="600111647"/> <reference key="NSMenu" ref="600111647"/>
<string key="NSTitle">Clear Ignore List</string> <string key="NSTitle">Clear Ignore List</string>
<string key="NSKeyEquiv">I</string> <string key="NSKeyEquiv">G</string>
<int key="NSKeyEquivModMask">1048576</int> <int key="NSKeyEquivModMask">1048576</int>
<int key="NSMnemonicLoc">2147483647</int> <int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="852972005"/> <reference key="NSOnImage" ref="852972005"/>
@@ -1238,7 +1231,7 @@
<object class="NSMenuItem" id="904423169"> <object class="NSMenuItem" id="904423169">
<reference key="NSMenu" ref="600111647"/> <reference key="NSMenu" ref="600111647"/>
<string key="NSTitle">Add Selected to Ignore List</string> <string key="NSTitle">Add Selected to Ignore List</string>
<string key="NSKeyEquiv">i</string> <string key="NSKeyEquiv">g</string>
<int key="NSKeyEquivModMask">1048576</int> <int key="NSKeyEquivModMask">1048576</int>
<int key="NSMnemonicLoc">2147483647</int> <int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="852972005"/> <reference key="NSOnImage" ref="852972005"/>
@@ -1282,6 +1275,15 @@
<reference key="NSOnImage" ref="852972005"/> <reference key="NSOnImage" ref="852972005"/>
<reference key="NSMixedImage" ref="218295580"/> <reference key="NSMixedImage" ref="218295580"/>
</object> </object>
<object class="NSMenuItem" id="517397504">
<reference key="NSMenu" ref="600111647"/>
<string key="NSTitle">Invoke Custom Command</string>
<string key="NSKeyEquiv">i</string>
<int key="NSKeyEquivModMask">1048576</int>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="852972005"/>
<reference key="NSMixedImage" ref="218295580"/>
</object>
<object class="NSMenuItem" id="564101661"> <object class="NSMenuItem" id="564101661">
<reference key="NSMenu" ref="600111647"/> <reference key="NSMenu" ref="600111647"/>
<string key="NSTitle">Rename Selected</string> <string key="NSTitle">Rename Selected</string>
@@ -1675,14 +1677,6 @@
</object> </object>
<int key="connectionID">212</int> <int key="connectionID">212</int>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">matches</string>
<reference key="source" ref="339936126"/>
<reference key="destination" ref="40047569"/>
</object>
<int key="connectionID">245</int>
</object>
<object class="IBConnectionRecord"> <object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection"> <object class="IBOutletConnection" key="connection">
<string key="label">initialFirstResponder</string> <string key="label">initialFirstResponder</string>
@@ -1691,22 +1685,6 @@
</object> </object>
<int key="connectionID">279</int> <int key="connectionID">279</int>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="40047569"/>
<reference key="destination" ref="339936126"/>
</object>
<int key="connectionID">410</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">markToggle:</string>
<reference key="source" ref="339936126"/>
<reference key="destination" ref="705360835"/>
</object>
<int key="connectionID">414</int>
</object>
<object class="IBConnectionRecord"> <object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection"> <object class="IBOutletConnection" key="connection">
<string key="label">stats</string> <string key="label">stats</string>
@@ -1947,14 +1925,6 @@
</object> </object>
<int key="connectionID">758</int> <int key="connectionID">758</int>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">py</string>
<reference key="source" ref="40047569"/>
<reference key="destination" ref="875360857"/>
</object>
<int key="connectionID">764</int>
</object>
<object class="IBConnectionRecord"> <object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection"> <object class="IBActionConnection" key="connection">
<string key="label">removeSelected:</string> <string key="label">removeSelected:</string>
@@ -2227,6 +2197,30 @@
</object> </object>
<int key="connectionID">1174</int> <int key="connectionID">1174</int>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="23220930"/>
<reference key="destination" ref="91622651"/>
</object>
<int key="connectionID">1175</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">matches</string>
<reference key="source" ref="339936126"/>
<reference key="destination" ref="40047569"/>
</object>
<int key="connectionID">1176</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">invokeCustomCommand:</string>
<reference key="source" ref="339936126"/>
<reference key="destination" ref="517397504"/>
</object>
<int key="connectionID">1178</int>
</object>
</object> </object>
<object class="IBMutableOrderedSet" key="objectRecords"> <object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects"> <object class="NSArray" key="orderedObjects">
@@ -2553,6 +2547,7 @@
<reference ref="564101661"/> <reference ref="564101661"/>
<reference ref="630362403"/> <reference ref="630362403"/>
<reference ref="747820446"/> <reference ref="747820446"/>
<reference ref="517397504"/>
</object> </object>
<reference key="parent" ref="528113253"/> <reference key="parent" ref="528113253"/>
</object> </object>
@@ -3089,6 +3084,11 @@
<reference key="object" ref="747820446"/> <reference key="object" ref="747820446"/>
<reference key="parent" ref="600111647"/> <reference key="parent" ref="600111647"/>
</object> </object>
<object class="IBObjectRecord">
<int key="objectID">1177</int>
<reference key="object" ref="517397504"/>
<reference key="parent" ref="600111647"/>
</object>
</object> </object>
</object> </object>
<object class="NSMutableDictionary" key="flattenedProperties"> <object class="NSMutableDictionary" key="flattenedProperties">
@@ -3140,6 +3140,7 @@
<string>1171.IBPluginDependency</string> <string>1171.IBPluginDependency</string>
<string>1172.IBPluginDependency</string> <string>1172.IBPluginDependency</string>
<string>1173.IBPluginDependency</string> <string>1173.IBPluginDependency</string>
<string>1177.IBPluginDependency</string>
<string>134.IBPluginDependency</string> <string>134.IBPluginDependency</string>
<string>134.ImportedFromIB2</string> <string>134.ImportedFromIB2</string>
<string>136.IBPluginDependency</string> <string>136.IBPluginDependency</string>
@@ -3378,6 +3379,7 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
@@ -3397,16 +3399,16 @@
<boolean value="YES"/> <boolean value="YES"/>
<boolean value="YES"/> <boolean value="YES"/>
<boolean value="YES"/> <boolean value="YES"/>
<string>{{109, 366}, {557, 400}}</string> <string>{{439, 345}, {557, 400}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{109, 366}, {557, 400}}</string> <string>{{439, 345}, {557, 400}}</string>
<boolean value="YES"/> <boolean value="YES"/>
<boolean value="YES"/> <boolean value="YES"/>
<boolean value="YES"/> <boolean value="YES"/>
<string>{340, 340}</string> <string>{340, 340}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>MatchesView</string> <string>HSOutlineView</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -3446,7 +3448,7 @@
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>{{286, 475}, {361, 293}}</string> <string>{{286, 455}, {361, 313}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -3584,7 +3586,7 @@
</object> </object>
</object> </object>
<nil key="sourceID"/> <nil key="sourceID"/>
<int key="maxID">1174</int> <int key="maxID">1178</int>
</object> </object>
<object class="IBClassDescriber" key="IBDocument.Classes"> <object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions"> <object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -3663,7 +3665,7 @@
</object> </object>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">dgbase/AppDelegate.h</string> <string key="minorKey">../base/AppDelegate.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
@@ -3675,19 +3677,22 @@
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">MatchesView</string> <string key="className">HSOutlineView</string>
<string key="superclassName">OutlineView</string> <string key="superclassName">NSOutlineView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="417275989"> <object class="IBClassDescriptionSource" key="sourceIdentifier" id="384069338">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">dgbase/ResultWindow.h</string> <string key="minorKey">../views/HSOutlineView.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">MatchesView</string> <string key="className">NSObject</string>
<string key="superclassName">OutlineView</string> <reference key="sourceIdentifier" ref="384069338"/>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> </object>
<string key="majorKey">IBUserSource</string> <object class="IBPartialClassDescription">
<string key="minorKey"/> <string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="653924221">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../views/NSTableViewAdditions.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
@@ -3699,31 +3704,15 @@
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">OutlineView</string> <string key="className">NSTableView</string>
<string key="superclassName">NSOutlineView</string> <reference key="sourceIdentifier" ref="653924221"/>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">py</string>
<string key="NS.object.0">PyApp</string>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/Outline.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">OutlineView</string>
<string key="superclassName">NSOutlineView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string>
<string key="minorKey"/>
</object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">PyApp</string> <string key="className">PyApp</string>
<string key="superclassName">PyRegistrable</string> <string key="superclassName">PyRegistrable</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/PyApp.h</string> <string key="minorKey">../PyApp.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
@@ -3755,7 +3744,7 @@
<string key="superclassName">PyApp</string> <string key="superclassName">PyApp</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">dgbase/PyDupeGuru.h</string> <string key="minorKey">../base/PyDupeGuru.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
@@ -3763,7 +3752,7 @@
<string key="superclassName">NSObject</string> <string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/PyRegistrable.h</string> <string key="minorKey">../PyRegistrable.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
@@ -3797,7 +3786,7 @@
</object> </object>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/RecentDirectories.h</string> <string key="minorKey">../RecentDirectories.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
@@ -3815,120 +3804,20 @@
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys"> <object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<string>clearIgnoreList:</string>
<string>filter:</string>
<string>ignoreSelected:</string>
<string>markAll:</string>
<string>markInvert:</string>
<string>markNone:</string>
<string>markSelected:</string>
<string>markToggle:</string>
<string>openSelected:</string>
<string>refresh:</string>
<string>removeMarked:</string>
<string>removeSelected:</string>
<string>renameSelected:</string>
<string>resetColumnsToDefault:</string> <string>resetColumnsToDefault:</string>
<string>revealSelected:</string>
<string>startDuplicateScan:</string> <string>startDuplicateScan:</string>
<string>toggleDelta:</string>
</object> </object>
<object class="NSMutableArray" key="dict.values"> <object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<string>id</string> <string>id</string>
<string>id</string> <string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
</object> </object>
</object> </object>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">filterField</string>
<string key="NS.object.0">NSSearchField</string>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
<string key="minorKey">ResultWindow.h</string> <string key="minorKey">ResultWindow.h</string>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription">
<string key="className">ResultWindow</string>
<string key="superclassName">ResultWindowBase</string>
<object class="NSMutableDictionary" key="actions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>changeDelta:</string>
<string>changePowerMarker:</string>
<string>collapseAll:</string>
<string>copyMarked:</string>
<string>deleteMarked:</string>
<string>expandAll:</string>
<string>exportToXHTML:</string>
<string>moveMarked:</string>
<string>switchSelected:</string>
<string>togglePowerMarker:</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>actionMenuView</string>
<string>app</string>
<string>deltaSwitch</string>
<string>deltaSwitchView</string>
<string>filterFieldView</string>
<string>matches</string>
<string>pmSwitch</string>
<string>pmSwitchView</string>
<string>py</string>
<string>stats</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSView</string>
<string>id</string>
<string>NSSegmentedControl</string>
<string>NSView</string>
<string>NSView</string>
<string>MatchesView</string>
<string>NSSegmentedControl</string>
<string>NSView</string>
<string>PyDupeGuru</string>
<string>NSTextField</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string>
<string key="minorKey"/>
</object>
</object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">ResultWindowBase</string> <string key="className">ResultWindowBase</string>
<string key="superclassName">NSWindowController</string> <string key="superclassName">NSWindowController</string>
@@ -3938,15 +3827,30 @@
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<string>changeDelta:</string> <string>changeDelta:</string>
<string>changePowerMarker:</string> <string>changePowerMarker:</string>
<string>clearIgnoreList:</string>
<string>copyMarked:</string> <string>copyMarked:</string>
<string>deleteMarked:</string> <string>deleteMarked:</string>
<string>expandAll:</string>
<string>exportToXHTML:</string> <string>exportToXHTML:</string>
<string>filter:</string>
<string>ignoreSelected:</string>
<string>invokeCustomCommand:</string>
<string>markAll:</string>
<string>markInvert:</string>
<string>markNone:</string>
<string>markSelected:</string>
<string>moveMarked:</string> <string>moveMarked:</string>
<string>openClicked:</string>
<string>openSelected:</string>
<string>removeMarked:</string>
<string>removeSelected:</string>
<string>renameSelected:</string>
<string>resetColumnsToDefault:</string> <string>resetColumnsToDefault:</string>
<string>revealSelected:</string>
<string>showPreferencesPanel:</string> <string>showPreferencesPanel:</string>
<string>startDuplicateScan:</string>
<string>switchSelected:</string> <string>switchSelected:</string>
<string>toggleColumn:</string> <string>toggleColumn:</string>
<string>toggleDelta:</string>
<string>toggleDetailsPanel:</string> <string>toggleDetailsPanel:</string>
<string>togglePowerMarker:</string> <string>togglePowerMarker:</string>
</object> </object>
@@ -3965,6 +3869,21 @@
<string>id</string> <string>id</string>
<string>id</string> <string>id</string>
<string>id</string> <string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
</object> </object>
</object> </object>
<object class="NSMutableDictionary" key="outlets"> <object class="NSMutableDictionary" key="outlets">
@@ -3974,6 +3893,7 @@
<string>app</string> <string>app</string>
<string>columnsMenu</string> <string>columnsMenu</string>
<string>deltaSwitch</string> <string>deltaSwitch</string>
<string>filterField</string>
<string>matches</string> <string>matches</string>
<string>pmSwitch</string> <string>pmSwitch</string>
<string>py</string> <string>py</string>
@@ -3984,13 +3904,17 @@
<string>id</string> <string>id</string>
<string>NSMenu</string> <string>NSMenu</string>
<string>NSSegmentedControl</string> <string>NSSegmentedControl</string>
<string>MatchesView</string> <string>NSSearchField</string>
<string>HSOutlineView</string>
<string>NSSegmentedControl</string> <string>NSSegmentedControl</string>
<string>PyDupeGuruBase</string> <string>PyDupeGuruBase</string>
<string>NSTextField</string> <string>NSTextField</string>
</object> </object>
</object> </object>
<reference key="sourceIdentifier" ref="417275989"/> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../base/ResultWindow.h</string>
</object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">SUUpdater</string> <string key="className">SUUpdater</string>
@@ -4612,7 +4536,7 @@
<integer value="3000" key="NS.object.0"/> <integer value="3000" key="NS.object.0"/>
</object> </object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool> <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">../../dupeguru.xcodeproj</string> <string key="IBDocument.LastKnownRelativeProjectPath">../../se/dupeguru.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int> <int key="IBDocument.defaultPropertyAccessControl">3</int>
</data> </data>
</archive> </archive>

File diff suppressed because it is too large Load Diff

View File

@@ -9,16 +9,11 @@ http://www.hardcoded.net/licenses/hs_license
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "../base/AppDelegate.h" #import "../base/AppDelegate.h"
#import "ResultWindow.h" #import "ResultWindow.h"
#import "DirectoryPanel.h"
#import "PyDupeGuru.h" #import "PyDupeGuru.h"
@interface AppDelegate : AppDelegateBase @interface AppDelegate : AppDelegateBase {}
{
DirectoryPanel *_directoryPanel;
}
- (IBAction)openWebsite:(id)sender; - (IBAction)openWebsite:(id)sender;
- (IBAction)toggleDirectories:(id)sender; - (IBAction)toggleDirectories:(id)sender;
- (DirectoryPanel *)directoryPanel;
- (PyDupeGuru *)py; - (PyDupeGuru *)py;
@end @end

View File

@@ -13,6 +13,7 @@ http://www.hardcoded.net/licenses/hs_license
#import "../../cocoalib/ValueTransformers.h" #import "../../cocoalib/ValueTransformers.h"
#import "../../cocoalib/Dialogs.h" #import "../../cocoalib/Dialogs.h"
#import "DetailsPanel.h" #import "DetailsPanel.h"
#import "DirectoryPanel.h"
#import "Consts.h" #import "Consts.h"
@implementation AppDelegate @implementation AppDelegate
@@ -65,52 +66,22 @@ http://www.hardcoded.net/licenses/hs_license
[[self directoryPanel] toggleVisible:sender]; [[self directoryPanel] toggleVisible:sender];
} }
- (DetailsPanelBase *)detailsPanel
{
if (!_detailsPanel)
_detailsPanel = [[DetailsPanel alloc] initWithPy:py];
return _detailsPanel;
}
- (DirectoryPanel *)directoryPanel - (DirectoryPanel *)directoryPanel
{ {
if (!_directoryPanel) if (!_directoryPanel)
_directoryPanel = [[DirectoryPanel alloc] initWithParentApp:self]; _directoryPanel = [[DirectoryPanelME alloc] initWithParentApp:self];
return _directoryPanel; return _directoryPanel;
} }
- (PyDupeGuru *)py { return (PyDupeGuru *)py; } - (PyDupeGuru *)py { return (PyDupeGuru *)py; }
//Delegate //Delegate
- (void)applicationWillBecomeActive:(NSNotification *)aNotification - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{ {
if (![[result window] isVisible]) NSMenu *actionsMenu = [[[NSApp mainMenu] itemWithTitle:@"Actions"] submenu];
[result showWindow:NSApp]; // index 3 is just after "Export Results to XHTML"
} NSMenuItem *mi = [actionsMenu insertItemWithTitle:@"Remove Dead Tracks in iTunes"
action:@selector(removeDeadTracks:) keyEquivalent:@"" atIndex:3];
- (void)applicationWillTerminate:(NSNotification *)aNotification [mi setTarget:result];
{ [super applicationDidFinishLaunching:aNotification];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[ud setObject: [result getColumnsOrder] forKey:@"columnsOrder"];
[ud setObject: [result getColumnsWidth] forKey:@"columnsWidth"];
[py saveIgnoreList];
[py saveResults];
int sc = [ud integerForKey:@"sessionCountSinceLastIgnorePurge"];
if (sc >= 10)
{
sc = -1;
[py purgeIgnoreList];
}
sc++;
[ud setInteger:sc forKey:@"sessionCountSinceLastIgnorePurge"];
// NSApplication does not release nib instances objects, we must do it manually
// Well, it isn't needed because the memory is freed anyway (we are quitting the application
// But I need to release RecentDirectories so it saves the user defaults
[recentDirectories release];
}
- (void)recentDirecoryClicked:(NSString *)directory
{
[[self directoryPanel] addDirectory:directory];
} }
@end @end

View File

@@ -1,12 +0,0 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
which should be included with this package. The terms are also available at
http://www.hardcoded.net/licenses/hs_license
*/
#import "DetailsPanel.h"
@implementation DetailsPanel
@end

View File

@@ -9,7 +9,7 @@ http://www.hardcoded.net/licenses/hs_license
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "../base/DirectoryPanel.h" #import "../base/DirectoryPanel.h"
@interface DirectoryPanel : DirectoryPanelBase @interface DirectoryPanelME : DirectoryPanel
{ {
} }
- (IBAction)addiTunes:(id)sender; - (IBAction)addiTunes:(id)sender;

View File

@@ -8,7 +8,7 @@ http://www.hardcoded.net/licenses/hs_license
#import "DirectoryPanel.h" #import "DirectoryPanel.h"
@implementation DirectoryPanel @implementation DirectoryPanelME
- (IBAction)addiTunes:(id)sender - (IBAction)addiTunes:(id)sender
{ {
[self addDirectory:[@"~/Music/iTunes/iTunes Music" stringByExpandingTildeInPath]]; [self addDirectory:[@"~/Music/iTunes/iTunes Music" stringByExpandingTildeInPath]];

View File

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

View File

@@ -19,5 +19,5 @@ http://www.hardcoded.net/licenses/hs_license
- (void)enable:(NSNumber *)enable scanForTag:(NSString *)tag; - (void)enable:(NSNumber *)enable scanForTag:(NSString *)tag;
- (void)scanDeadTracks; - (void)scanDeadTracks;
- (void)removeDeadTracks; - (void)removeDeadTracks;
- (int)deadTrackCount; - (NSInteger)deadTrackCount;
@end @end

View File

@@ -7,32 +7,9 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "../../cocoalib/Outline.h"
#import "../base/ResultWindow.h" #import "../base/ResultWindow.h"
#import "DirectoryPanel.h" #import "DirectoryPanel.h"
@interface ResultWindow : ResultWindowBase @interface ResultWindow : ResultWindowBase {}
{
IBOutlet NSSearchField *filterField;
NSString *_lastAction;
NSMutableIndexSet *_deltaColumns;
}
- (IBAction)clearIgnoreList:(id)sender;
- (IBAction)filter:(id)sender;
- (IBAction)ignoreSelected:(id)sender;
- (IBAction)markAll:(id)sender;
- (IBAction)markInvert:(id)sender;
- (IBAction)markNone:(id)sender;
- (IBAction)markSelected:(id)sender;
- (IBAction)markToggle:(id)sender;
- (IBAction)openSelected:(id)sender;
- (IBAction)refresh:(id)sender;
- (IBAction)removeDeadTracks:(id)sender; - (IBAction)removeDeadTracks:(id)sender;
- (IBAction)removeMarked:(id)sender;
- (IBAction)removeSelected:(id)sender;
- (IBAction)renameSelected:(id)sender;
- (IBAction)revealSelected:(id)sender;
- (IBAction)startDuplicateScan:(id)sender;
- (IBAction)toggleDelta:(id)sender;
@end @end

View File

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

View File

@@ -4,182 +4,43 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license # http://www.hardcoded.net/licenses/hs_license
import objc from hsutil.cocoa import signature
from AppKit import *
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
from core_me.app_cocoa import DupeGuruME from core_me.app_cocoa import DupeGuruME
from core.scanner import (SCAN_TYPE_FILENAME, SCAN_TYPE_FIELDS, SCAN_TYPE_FIELDS_NO_ORDER, from core.scanner import (SCAN_TYPE_FILENAME, SCAN_TYPE_FIELDS, SCAN_TYPE_FIELDS_NO_ORDER,
SCAN_TYPE_TAG, SCAN_TYPE_CONTENT, SCAN_TYPE_CONTENT_AUDIO) SCAN_TYPE_TAG, SCAN_TYPE_CONTENT, SCAN_TYPE_CONTENT_AUDIO)
# Fix py2app imports which chokes on relative imports # Fix py2app imports which chokes on relative imports and other stuff
from core_me import app_cocoa, data, fs, scanner from core_me import app_cocoa, data, fs, scanner
from core import app, app_cocoa, data, directories, engine, export, ignore, results, scanner, fs
from hsmedia import aiff, flac, genres, id3v1, id3v2, mp4, mpeg, ogg, wma from hsmedia import aiff, flac, genres, id3v1, id3v2, mp4, mpeg, ogg, wma
from hsutil import conflict from lxml import etree, _elementpath
import gzip
class PyApp(NSObject): class PyDupeGuru(PyDupeGuruBase):
pass #fake class
class PyDupeGuru(PyApp):
def init(self): def init(self):
self = super(PyDupeGuru,self).init() self = super(PyDupeGuru,self).init()
self.app = DupeGuruME() self.py = DupeGuruME()
return self return self
#---Directories
def addDirectory_(self,directory):
return self.app.add_directory(directory)
def removeDirectory_(self,index):
self.app.RemoveDirectory(index)
def setDirectory_state_(self,node_path,state):
self.app.SetDirectoryState(node_path,state)
#---Results
def clearIgnoreList(self):
self.app.scanner.ignore_list.Clear()
def doScan(self):
return self.app.start_scanning()
def exportToXHTMLwithColumns_(self, column_ids):
return self.app.export_to_xhtml(column_ids)
def loadIgnoreList(self):
self.app.load_ignore_list()
def loadResults(self):
self.app.load()
def markAll(self):
self.app.results.mark_all()
def markNone(self):
self.app.results.mark_none()
def markInvert(self):
self.app.results.mark_invert()
def purgeIgnoreList(self):
self.app.PurgeIgnoreList()
def toggleSelectedMark(self):
self.app.ToggleSelectedMarkState()
def saveIgnoreList(self):
self.app.save_ignore_list()
def saveResults(self):
self.app.save()
def refreshDetailsWithSelected(self):
self.app.RefreshDetailsWithSelected()
def selectedResultNodePaths(self):
return self.app.selected_result_node_paths()
def selectResultNodePaths_(self,node_paths):
self.app.SelectResultNodePaths(node_paths)
def selectedPowerMarkerNodePaths(self):
return self.app.selected_powermarker_node_paths()
def selectPowerMarkerNodePaths_(self,node_paths):
self.app.SelectPowerMarkerNodePaths(node_paths)
#---Actions
def addSelectedToIgnoreList(self):
self.app.AddSelectedToIgnoreList()
def applyFilter_(self, filter):
self.app.apply_filter(filter)
def deleteMarked(self):
self.app.delete_marked()
def makeSelectedReference(self):
self.app.MakeSelectedReference()
def copyOrMove_markedTo_recreatePath_(self,copy,destination,recreate_path):
self.app.copy_or_move_marked(copy, destination, recreate_path)
def openSelected(self):
self.app.OpenSelected()
def removeDeadTracks(self): def removeDeadTracks(self):
self.app.remove_dead_tracks() self.py.remove_dead_tracks()
def removeMarked(self):
self.app.results.perform_on_marked(lambda x:True, True)
def removeSelected(self):
self.app.RemoveSelected()
def renameSelected_(self,newname):
return self.app.RenameSelected(newname)
def revealSelected(self):
self.app.RevealSelected()
def scanDeadTracks(self): def scanDeadTracks(self):
self.app.scan_dead_tracks() self.py.scan_dead_tracks()
#---Misc
def sortDupesBy_ascending_(self,key,asc):
self.app.sort_dupes(key,asc)
def sortGroupsBy_ascending_(self,key,asc):
self.app.sort_groups(key,asc)
#---Information #---Information
@objc.signature('i@:') @signature('i@:')
def deadTrackCount(self): def deadTrackCount(self):
return len(self.app.dead_tracks) return len(self.py.dead_tracks)
def getIgnoreListCount(self):
return len(self.app.scanner.ignore_list)
def getMarkCount(self):
return self.app.results.mark_count
def getStatLine(self):
return self.app.stat_line
def getOperationalErrorCount(self):
return self.app.last_op_error_count
#---Data
@objc.signature('i@:i')
def getOutlineViewMaxLevel_(self, tag):
return self.app.GetOutlineViewMaxLevel(tag)
@objc.signature('@@:i@')
def getOutlineView_childCountsForPath_(self, tag, node_path):
return self.app.GetOutlineViewChildCounts(tag, node_path)
def getOutlineView_valuesForIndexes_(self,tag,node_path):
return self.app.GetOutlineViewValues(tag,node_path)
def getOutlineView_markedAtIndexes_(self,tag,node_path):
return self.app.GetOutlineViewMarked(tag,node_path)
def getTableViewCount_(self,tag):
return self.app.GetTableViewCount(tag)
def getTableViewMarkedIndexes_(self,tag):
return self.app.GetTableViewMarkedIndexes(tag)
def getTableView_valuesForRow_(self,tag,row):
return self.app.GetTableViewValues(tag,row)
#---Properties #---Properties
def setMinMatchPercentage_(self, percentage): def setMinMatchPercentage_(self, percentage):
self.app.scanner.min_match_percentage = int(percentage) self.py.scanner.min_match_percentage = int(percentage)
def setScanType_(self, scan_type): def setScanType_(self, scan_type):
try: try:
self.app.scanner.scan_type = [ self.py.scanner.scan_type = [
SCAN_TYPE_FILENAME, SCAN_TYPE_FILENAME,
SCAN_TYPE_FIELDS, SCAN_TYPE_FIELDS,
SCAN_TYPE_FIELDS_NO_ORDER, SCAN_TYPE_FIELDS_NO_ORDER,
@@ -191,54 +52,18 @@ class PyDupeGuru(PyApp):
pass pass
def setWordWeighting_(self, words_are_weighted): def setWordWeighting_(self, words_are_weighted):
self.app.scanner.word_weighting = words_are_weighted self.py.scanner.word_weighting = words_are_weighted
def setMixFileKind_(self, mix_file_kind):
self.app.scanner.mix_file_kind = mix_file_kind
def setDisplayDeltaValues_(self, display_delta_values):
self.app.display_delta_values = display_delta_values
def setMatchSimilarWords_(self, match_similar_words): def setMatchSimilarWords_(self, match_similar_words):
self.app.scanner.match_similar_words = match_similar_words self.py.scanner.match_similar_words = match_similar_words
def setEscapeFilterRegexp_(self, escape_filter_regexp):
self.app.options['escape_filter_regexp'] = escape_filter_regexp
def setRemoveEmptyFolders_(self, remove_empty_folders):
self.app.options['clean_empty_dirs'] = remove_empty_folders
def enable_scanForTag_(self, enable, scan_tag): def enable_scanForTag_(self, enable, scan_tag):
if enable: if enable:
self.app.scanner.scanned_tags.add(scan_tag) self.py.scanner.scanned_tags.add(scan_tag)
else: else:
self.app.scanner.scanned_tags.discard(scan_tag) self.py.scanner.scanned_tags.discard(scan_tag)
#---Worker
def getJobProgress(self):
return self.app.progress.last_progress
def getJobDesc(self):
return self.app.progress.last_desc
def cancelJob(self):
self.app.progress.job_cancelled = True
#---Registration #---Registration
def appName(self): def appName(self):
return "dupeGuru Music Edition" return "dupeGuru Music Edition"
def demoLimitDescription(self):
return self.app.DEMO_LIMIT_DESC
@objc.signature('i@:')
def isRegistered(self):
return self.app.registered
@objc.signature('i@:@@')
def isCodeValid_withEmail_(self, code, email):
return self.app.is_code_valid(code, email)
def setRegisteredCode_andEmail_(self, code, email):
self.app.set_registration(code, email)

View File

@@ -21,7 +21,16 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
CE003CC611242D00004B0AA7 /* HSGUIController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CB411242D00004B0AA7 /* HSGUIController.m */; };
CE003CC711242D00004B0AA7 /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CB611242D00004B0AA7 /* HSOutline.m */; };
CE003CC811242D00004B0AA7 /* HSWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CB811242D00004B0AA7 /* HSWindowController.m */; };
CE003CC911242D00004B0AA7 /* NSEventAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CBA11242D00004B0AA7 /* NSEventAdditions.m */; };
CE003CCA11242D00004B0AA7 /* HSOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CC111242D00004B0AA7 /* HSOutlineView.m */; };
CE003CCB11242D00004B0AA7 /* NSIndexPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CC311242D00004B0AA7 /* NSIndexPathAdditions.m */; };
CE003CCC11242D00004B0AA7 /* NSTableViewAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CC511242D00004B0AA7 /* NSTableViewAdditions.m */; };
CE003CD011242D2C004B0AA7 /* DirectoryOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE003CCE11242D2C004B0AA7 /* DirectoryOutline.m */; };
CE073F6309CAE1A3005C1D2F /* dupeguru_me_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_me_help */; }; CE073F6309CAE1A3005C1D2F /* dupeguru_me_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_me_help */; };
CE0B3D6711243F83009A7A30 /* ResultOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0B3D6611243F83009A7A30 /* ResultOutline.m */; };
CE1425890AFB718500BD5167 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; }; CE1425890AFB718500BD5167 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; }; CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; }; CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
@@ -30,18 +39,16 @@
CE3FBDD31094637800B72D77 /* DetailsPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3FBDD11094637800B72D77 /* DetailsPanel.xib */; }; CE3FBDD31094637800B72D77 /* DetailsPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3FBDD11094637800B72D77 /* DetailsPanel.xib */; };
CE3FBDD41094637800B72D77 /* DirectoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3FBDD21094637800B72D77 /* DirectoryPanel.xib */; }; CE3FBDD41094637800B72D77 /* DirectoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3FBDD21094637800B72D77 /* DirectoryPanel.xib */; };
CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */; }; CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */; };
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */; };
CE4B59C91119919700C06C9E /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C61119919700C06C9E /* progress.xib */; };
CE4B59CA1119919700C06C9E /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C71119919700C06C9E /* registration.xib */; };
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE10FC6C12E00EC695D /* Dialogs.m */; }; CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE10FC6C12E00EC695D /* Dialogs.m */; };
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */; }; CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */; };
CE515DF50FC6C12E00EC695D /* Outline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE50FC6C12E00EC695D /* Outline.m */; };
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE70FC6C12E00EC695D /* ProgressController.m */; }; CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE70FC6C12E00EC695D /* ProgressController.m */; };
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */; }; CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */; };
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */; }; CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */; };
CE515DF90FC6C12E00EC695D /* Table.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEE0FC6C12E00EC695D /* Table.m */; };
CE515DFA0FC6C12E00EC695D /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DF00FC6C12E00EC695D /* Utils.m */; }; CE515DFA0FC6C12E00EC695D /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DF00FC6C12E00EC695D /* Utils.m */; };
CE515DFB0FC6C12E00EC695D /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DF20FC6C12E00EC695D /* ValueTransformers.m */; }; CE515DFB0FC6C12E00EC695D /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DF20FC6C12E00EC695D /* ValueTransformers.m */; };
CE515E020FC6C13E00EC695D /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE515DFC0FC6C13E00EC695D /* ErrorReportWindow.xib */; };
CE515E030FC6C13E00EC695D /* progress.nib in Resources */ = {isa = PBXBuildFile; fileRef = CE515DFE0FC6C13E00EC695D /* progress.nib */; };
CE515E040FC6C13E00EC695D /* registration.nib in Resources */ = {isa = PBXBuildFile; fileRef = CE515E000FC6C13E00EC695D /* registration.nib */; };
CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E160FC6C19300EC695D /* AppDelegate.m */; }; CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E160FC6C19300EC695D /* AppDelegate.m */; };
CE515E1E0FC6C19300EC695D /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E190FC6C19300EC695D /* DirectoryPanel.m */; }; CE515E1E0FC6C19300EC695D /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E190FC6C19300EC695D /* DirectoryPanel.m */; };
CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E1C0FC6C19300EC695D /* ResultWindow.m */; }; CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E1C0FC6C19300EC695D /* ResultWindow.m */; };
@@ -51,8 +58,7 @@
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; }; CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
CE900AD2109B238600754048 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD1109B238600754048 /* Preferences.xib */; }; CE900AD2109B238600754048 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD1109B238600754048 /* Preferences.xib */; };
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD6109B2A9B00754048 /* MainMenu.xib */; }; CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD6109B2A9B00754048 /* MainMenu.xib */; };
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECA899A09DB132E00A3D774 /* DetailsPanel.h */; }; CEDF07A3112493B200EE5BC0 /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDF07A2112493B200EE5BC0 /* StatsLabel.m */; };
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CECA899B09DB132E00A3D774 /* DetailsPanel.m */; };
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; }; CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; }; CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; };
CEFC295509C89FF200D9F998 /* details32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295309C89FF200D9F998 /* details32.png */; }; CEFC295509C89FF200D9F998 /* details32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295309C89FF200D9F998 /* details32.png */; };
@@ -67,7 +73,6 @@
dstSubfolderSpec = 10; dstSubfolderSpec = 10;
files = ( files = (
CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */, CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */,
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */,
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */, CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -82,23 +87,47 @@
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; };
8D1107320486CEB800E47090 /* dupeGuru ME.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "dupeGuru ME.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 8D1107320486CEB800E47090 /* dupeGuru ME.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "dupeGuru ME.app"; sourceTree = BUILT_PRODUCTS_DIR; };
CE003CB311242D00004B0AA7 /* HSGUIController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSGUIController.h; sourceTree = "<group>"; };
CE003CB411242D00004B0AA7 /* HSGUIController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSGUIController.m; sourceTree = "<group>"; };
CE003CB511242D00004B0AA7 /* HSOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutline.h; sourceTree = "<group>"; };
CE003CB611242D00004B0AA7 /* HSOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutline.m; sourceTree = "<group>"; };
CE003CB711242D00004B0AA7 /* HSWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSWindowController.h; sourceTree = "<group>"; };
CE003CB811242D00004B0AA7 /* HSWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSWindowController.m; sourceTree = "<group>"; };
CE003CB911242D00004B0AA7 /* NSEventAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSEventAdditions.h; path = ../../cocoalib/NSEventAdditions.h; sourceTree = SOURCE_ROOT; };
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>"; };
CE003CC311242D00004B0AA7 /* NSIndexPathAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSIndexPathAdditions.m; sourceTree = "<group>"; };
CE003CC411242D00004B0AA7 /* NSTableViewAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSTableViewAdditions.h; sourceTree = "<group>"; };
CE003CC511242D00004B0AA7 /* NSTableViewAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSTableViewAdditions.m; sourceTree = "<group>"; };
CE003CCD11242D2C004B0AA7 /* DirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DirectoryOutline.h; path = ../base/DirectoryOutline.h; sourceTree = SOURCE_ROOT; };
CE003CCE11242D2C004B0AA7 /* DirectoryOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DirectoryOutline.m; path = ../base/DirectoryOutline.m; sourceTree = SOURCE_ROOT; };
CE003CCF11242D2C004B0AA7 /* PyDirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDirectoryOutline.h; path = ../base/PyDirectoryOutline.h; sourceTree = SOURCE_ROOT; };
CE073F5409CAE1A3005C1D2F /* dupeguru_me_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_me_help; path = ../../help_me/dupeguru_me_help; sourceTree = "<group>"; }; CE073F5409CAE1A3005C1D2F /* dupeguru_me_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_me_help; path = ../../help_me/dupeguru_me_help; sourceTree = "<group>"; };
CE0B3D6411243F83009A7A30 /* PyResultTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTree.h; path = ../base/PyResultTree.h; sourceTree = SOURCE_ROOT; };
CE0B3D6511243F83009A7A30 /* ResultOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultOutline.h; path = ../base/ResultOutline.h; sourceTree = SOURCE_ROOT; };
CE0B3D6611243F83009A7A30 /* ResultOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultOutline.m; path = ../base/ResultOutline.m; sourceTree = SOURCE_ROOT; };
CE1425880AFB718500BD5167 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; }; CE1425880AFB718500BD5167 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; }; CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; }; CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; }; CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; };
CE381C9B09914ADF003581CE /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = ResultWindow.h; sourceTree = SOURCE_ROOT; }; CE381C9B09914ADF003581CE /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = ResultWindow.h; sourceTree = SOURCE_ROOT; };
CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dg_cocoa.plugin; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; }; CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; };
CE3FBDD11094637800B72D77 /* DetailsPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = DetailsPanel.xib; path = ../../base/xib/DetailsPanel.xib; sourceTree = "<group>"; }; CE3FBDD11094637800B72D77 /* DetailsPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = DetailsPanel.xib; path = ../../base/xib/DetailsPanel.xib; sourceTree = "<group>"; };
CE3FBDD21094637800B72D77 /* DirectoryPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = DirectoryPanel.xib; path = ../../base/xib/DirectoryPanel.xib; sourceTree = "<group>"; }; CE3FBDD21094637800B72D77 /* DirectoryPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = DirectoryPanel.xib; path = ../../base/xib/DirectoryPanel.xib; sourceTree = "<group>"; };
CE49DEF20FDFEB810098617B /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; }; CE49DEF20FDFEB810098617B /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; };
CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; }; CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; };
CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ErrorReportWindow.xib; sourceTree = "<group>"; };
CE4B59C61119919700C06C9E /* progress.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = progress.xib; sourceTree = "<group>"; };
CE4B59C71119919700C06C9E /* registration.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = registration.xib; sourceTree = "<group>"; };
CE515DE00FC6C12E00EC695D /* Dialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Dialogs.h; path = ../../cocoalib/Dialogs.h; sourceTree = SOURCE_ROOT; }; CE515DE00FC6C12E00EC695D /* Dialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Dialogs.h; path = ../../cocoalib/Dialogs.h; sourceTree = SOURCE_ROOT; };
CE515DE10FC6C12E00EC695D /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; }; CE515DE10FC6C12E00EC695D /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; };
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; }; CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSErrorReportWindow.m; path = ../../cocoalib/HSErrorReportWindow.m; sourceTree = SOURCE_ROOT; }; CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSErrorReportWindow.m; path = ../../cocoalib/HSErrorReportWindow.m; sourceTree = SOURCE_ROOT; };
CE515DE40FC6C12E00EC695D /* Outline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Outline.h; path = ../../cocoalib/Outline.h; sourceTree = SOURCE_ROOT; };
CE515DE50FC6C12E00EC695D /* Outline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Outline.m; path = ../../cocoalib/Outline.m; sourceTree = SOURCE_ROOT; };
CE515DE60FC6C12E00EC695D /* ProgressController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressController.h; path = ../../cocoalib/ProgressController.h; sourceTree = SOURCE_ROOT; }; CE515DE60FC6C12E00EC695D /* ProgressController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressController.h; path = ../../cocoalib/ProgressController.h; sourceTree = SOURCE_ROOT; };
CE515DE70FC6C12E00EC695D /* ProgressController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProgressController.m; path = ../../cocoalib/ProgressController.m; sourceTree = SOURCE_ROOT; }; CE515DE70FC6C12E00EC695D /* ProgressController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProgressController.m; path = ../../cocoalib/ProgressController.m; sourceTree = SOURCE_ROOT; };
CE515DE80FC6C12E00EC695D /* PyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyApp.h; path = ../../cocoalib/PyApp.h; sourceTree = SOURCE_ROOT; }; CE515DE80FC6C12E00EC695D /* PyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyApp.h; path = ../../cocoalib/PyApp.h; sourceTree = SOURCE_ROOT; };
@@ -106,15 +135,10 @@
CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RecentDirectories.m; path = ../../cocoalib/RecentDirectories.m; 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; }; 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; }; CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegistrationInterface.m; path = ../../cocoalib/RegistrationInterface.m; sourceTree = SOURCE_ROOT; };
CE515DED0FC6C12E00EC695D /* Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Table.h; path = ../../cocoalib/Table.h; sourceTree = SOURCE_ROOT; };
CE515DEE0FC6C12E00EC695D /* Table.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Table.m; path = ../../cocoalib/Table.m; sourceTree = SOURCE_ROOT; };
CE515DEF0FC6C12E00EC695D /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = ../../cocoalib/Utils.h; sourceTree = SOURCE_ROOT; }; 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; }; 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; }; CE515DF10FC6C12E00EC695D /* ValueTransformers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueTransformers.h; path = ../../cocoalib/ValueTransformers.h; sourceTree = SOURCE_ROOT; };
CE515DF20FC6C12E00EC695D /* ValueTransformers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ValueTransformers.m; path = ../../cocoalib/ValueTransformers.m; sourceTree = SOURCE_ROOT; }; CE515DF20FC6C12E00EC695D /* ValueTransformers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ValueTransformers.m; path = ../../cocoalib/ValueTransformers.m; sourceTree = SOURCE_ROOT; };
CE515DFD0FC6C13E00EC695D /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = ../../cocoalib/English.lproj/ErrorReportWindow.xib; sourceTree = "<group>"; };
CE515DFF0FC6C13E00EC695D /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/progress.nib; sourceTree = "<group>"; };
CE515E010FC6C13E00EC695D /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/registration.nib; sourceTree = "<group>"; };
CE515E150FC6C19300EC695D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ../base/AppDelegate.h; sourceTree = SOURCE_ROOT; }; CE515E150FC6C19300EC695D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ../base/AppDelegate.h; sourceTree = SOURCE_ROOT; };
CE515E160FC6C19300EC695D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ../base/AppDelegate.m; sourceTree = SOURCE_ROOT; }; CE515E160FC6C19300EC695D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ../base/AppDelegate.m; sourceTree = SOURCE_ROOT; };
CE515E170FC6C19300EC695D /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = ../base/Consts.h; sourceTree = SOURCE_ROOT; }; CE515E170FC6C19300EC695D /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = ../base/Consts.h; sourceTree = SOURCE_ROOT; };
@@ -133,6 +157,10 @@
CE900AD6109B2A9B00754048 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; }; CE900AD6109B2A9B00754048 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; };
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; }; CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; };
CECA899B09DB132E00A3D774 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DetailsPanel.m; sourceTree = "<group>"; }; CECA899B09DB132E00A3D774 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DetailsPanel.m; sourceTree = "<group>"; };
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; };
CEDF07A2112493B200EE5BC0 /* StatsLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StatsLabel.m; path = ../base/StatsLabel.m; sourceTree = SOURCE_ROOT; };
CEEB135109C837A2004D2330 /* dupeguru.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = dupeguru.icns; sourceTree = "<group>"; }; CEEB135109C837A2004D2330 /* dupeguru.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = dupeguru.icns; sourceTree = "<group>"; };
CEFC294509C89E3D00D9F998 /* folder32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = folder32.png; path = ../../images/folder32.png; sourceTree = SOURCE_ROOT; }; CEFC294509C89E3D00D9F998 /* folder32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = folder32.png; path = ../../images/folder32.png; sourceTree = SOURCE_ROOT; };
CEFC295309C89FF200D9F998 /* details32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = details32.png; path = ../../images/details32.png; sourceTree = SOURCE_ROOT; }; CEFC295309C89FF200D9F998 /* details32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = details32.png; path = ../../images/details32.png; sourceTree = SOURCE_ROOT; };
@@ -242,6 +270,44 @@
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
CE003CB211242D00004B0AA7 /* controllers */ = {
isa = PBXGroup;
children = (
CE003CB311242D00004B0AA7 /* HSGUIController.h */,
CE003CB411242D00004B0AA7 /* HSGUIController.m */,
CE003CB511242D00004B0AA7 /* HSOutline.h */,
CE003CB611242D00004B0AA7 /* HSOutline.m */,
CE003CB711242D00004B0AA7 /* HSWindowController.h */,
CE003CB811242D00004B0AA7 /* HSWindowController.m */,
);
name = controllers;
path = ../../cocoalib/controllers;
sourceTree = SOURCE_ROOT;
};
CE003CBB11242D00004B0AA7 /* proxies */ = {
isa = PBXGroup;
children = (
CE003CBC11242D00004B0AA7 /* PyGUI.h */,
CE003CBD11242D00004B0AA7 /* PyOutline.h */,
);
name = proxies;
path = ../../cocoalib/proxies;
sourceTree = SOURCE_ROOT;
};
CE003CBF11242D00004B0AA7 /* views */ = {
isa = PBXGroup;
children = (
CE003CC011242D00004B0AA7 /* HSOutlineView.h */,
CE003CC111242D00004B0AA7 /* HSOutlineView.m */,
CE003CC211242D00004B0AA7 /* NSIndexPathAdditions.h */,
CE003CC311242D00004B0AA7 /* NSIndexPathAdditions.m */,
CE003CC411242D00004B0AA7 /* NSTableViewAdditions.h */,
CE003CC511242D00004B0AA7 /* NSTableViewAdditions.m */,
);
name = views;
path = ../../cocoalib/views;
sourceTree = SOURCE_ROOT;
};
CE3FBDD01094637800B72D77 /* xib */ = { CE3FBDD01094637800B72D77 /* xib */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -263,28 +329,39 @@
path = ../../cocoalib/brsinglelineformatter; path = ../../cocoalib/brsinglelineformatter;
sourceTree = SOURCE_ROOT; sourceTree = SOURCE_ROOT;
}; };
CE4B59C41119919700C06C9E /* xib */ = {
isa = PBXGroup;
children = (
CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */,
CE4B59C61119919700C06C9E /* progress.xib */,
CE4B59C71119919700C06C9E /* registration.xib */,
);
name = xib;
path = ../../cocoalib/xib;
sourceTree = SOURCE_ROOT;
};
CE515DDD0FC6C09400EC695D /* cocoalib */ = { CE515DDD0FC6C09400EC695D /* cocoalib */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CE003CB211242D00004B0AA7 /* controllers */,
CE003CBB11242D00004B0AA7 /* proxies */,
CE003CBF11242D00004B0AA7 /* views */,
CE4B59C41119919700C06C9E /* xib */,
CE49DEF10FDFEB810098617B /* brsinglelineformatter */, CE49DEF10FDFEB810098617B /* brsinglelineformatter */,
CE515DFC0FC6C13E00EC695D /* ErrorReportWindow.xib */,
CE515DFE0FC6C13E00EC695D /* progress.nib */,
CE515E000FC6C13E00EC695D /* registration.nib */,
CE515DE00FC6C12E00EC695D /* Dialogs.h */, CE515DE00FC6C12E00EC695D /* Dialogs.h */,
CE515DE10FC6C12E00EC695D /* Dialogs.m */, CE515DE10FC6C12E00EC695D /* Dialogs.m */,
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */, CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */,
CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */, CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */,
CE515DE40FC6C12E00EC695D /* Outline.h */, CE003CB911242D00004B0AA7 /* NSEventAdditions.h */,
CE515DE50FC6C12E00EC695D /* Outline.m */, CE003CBA11242D00004B0AA7 /* NSEventAdditions.m */,
CE515DE60FC6C12E00EC695D /* ProgressController.h */, CE515DE60FC6C12E00EC695D /* ProgressController.h */,
CE515DE70FC6C12E00EC695D /* ProgressController.m */, CE515DE70FC6C12E00EC695D /* ProgressController.m */,
CE515DE80FC6C12E00EC695D /* PyApp.h */, CE515DE80FC6C12E00EC695D /* PyApp.h */,
CE003CBE11242D00004B0AA7 /* PyRegistrable.h */,
CE515DE90FC6C12E00EC695D /* RecentDirectories.h */, CE515DE90FC6C12E00EC695D /* RecentDirectories.h */,
CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */, CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */,
CE515DEB0FC6C12E00EC695D /* RegistrationInterface.h */, CE515DEB0FC6C12E00EC695D /* RegistrationInterface.h */,
CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */, CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */,
CE515DED0FC6C12E00EC695D /* Table.h */,
CE515DEE0FC6C12E00EC695D /* Table.m */,
CE515DEF0FC6C12E00EC695D /* Utils.h */, CE515DEF0FC6C12E00EC695D /* Utils.h */,
CE515DF00FC6C12E00EC695D /* Utils.m */, CE515DF00FC6C12E00EC695D /* Utils.m */,
CE515DF10FC6C12E00EC695D /* ValueTransformers.h */, CE515DF10FC6C12E00EC695D /* ValueTransformers.h */,
@@ -296,6 +373,9 @@
CE515E140FC6C17900EC695D /* dgbase */ = { CE515E140FC6C17900EC695D /* dgbase */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CE003CCD11242D2C004B0AA7 /* DirectoryOutline.h */,
CE003CCE11242D2C004B0AA7 /* DirectoryOutline.m */,
CE003CCF11242D2C004B0AA7 /* PyDirectoryOutline.h */,
CE515E150FC6C19300EC695D /* AppDelegate.h */, CE515E150FC6C19300EC695D /* AppDelegate.h */,
CE515E160FC6C19300EC695D /* AppDelegate.m */, CE515E160FC6C19300EC695D /* AppDelegate.m */,
CE515E170FC6C19300EC695D /* Consts.h */, CE515E170FC6C19300EC695D /* Consts.h */,
@@ -303,9 +383,16 @@
CE6032BF0FE6784C007E33FF /* DetailsPanel.m */, CE6032BF0FE6784C007E33FF /* DetailsPanel.m */,
CE515E180FC6C19300EC695D /* DirectoryPanel.h */, CE515E180FC6C19300EC695D /* DirectoryPanel.h */,
CE515E190FC6C19300EC695D /* DirectoryPanel.m */, CE515E190FC6C19300EC695D /* DirectoryPanel.m */,
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */,
CE515E1B0FC6C19300EC695D /* ResultWindow.h */, CE515E1B0FC6C19300EC695D /* ResultWindow.h */,
CE515E1C0FC6C19300EC695D /* ResultWindow.m */, CE515E1C0FC6C19300EC695D /* ResultWindow.m */,
CE0B3D6511243F83009A7A30 /* ResultOutline.h */,
CE0B3D6611243F83009A7A30 /* ResultOutline.m */,
CEDF07A1112493B200EE5BC0 /* StatsLabel.h */,
CEDF07A2112493B200EE5BC0 /* StatsLabel.m */,
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */,
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */,
CE0B3D6411243F83009A7A30 /* PyResultTree.h */,
CEDF07A0112493B200EE5BC0 /* PyStatsLabel.h */,
); );
name = dgbase; name = dgbase;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -371,14 +458,14 @@
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */, CEFC294609C89E3D00D9F998 /* folder32.png in Resources */,
CEFC295509C89FF200D9F998 /* details32.png in Resources */, CEFC295509C89FF200D9F998 /* details32.png in Resources */,
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */, CEFC295609C89FF200D9F998 /* preferences32.png in Resources */,
CE515E020FC6C13E00EC695D /* ErrorReportWindow.xib in Resources */,
CE515E030FC6C13E00EC695D /* progress.nib in Resources */,
CE515E040FC6C13E00EC695D /* registration.nib in Resources */,
CE6E0E9F1054EB97008D9390 /* dsa_pub.pem in Resources */, CE6E0E9F1054EB97008D9390 /* dsa_pub.pem in Resources */,
CE3FBDD31094637800B72D77 /* DetailsPanel.xib in Resources */, CE3FBDD31094637800B72D77 /* DetailsPanel.xib in Resources */,
CE3FBDD41094637800B72D77 /* DirectoryPanel.xib in Resources */, CE3FBDD41094637800B72D77 /* DirectoryPanel.xib in Resources */,
CE900AD2109B238600754048 /* Preferences.xib in Resources */, CE900AD2109B238600754048 /* Preferences.xib in Resources */,
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */, CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */,
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */,
CE4B59C91119919700C06C9E /* progress.xib in Resources */,
CE4B59CA1119919700C06C9E /* registration.xib in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -393,14 +480,11 @@
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */, CE381C9609914ACE003581CE /* AppDelegate.m in Sources */,
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */, CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */,
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */, CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */,
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */,
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */, CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */,
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */, CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */,
CE515DF50FC6C12E00EC695D /* Outline.m in Sources */,
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */, CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */,
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */, CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */,
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */, CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */,
CE515DF90FC6C12E00EC695D /* Table.m in Sources */,
CE515DFA0FC6C12E00EC695D /* Utils.m in Sources */, CE515DFA0FC6C12E00EC695D /* Utils.m in Sources */,
CE515DFB0FC6C12E00EC695D /* ValueTransformers.m in Sources */, CE515DFB0FC6C12E00EC695D /* ValueTransformers.m in Sources */,
CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */, CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */,
@@ -408,63 +492,70 @@
CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */, CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */,
CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.m in Sources */, CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.m in Sources */,
CE6032C00FE6784C007E33FF /* DetailsPanel.m in Sources */, CE6032C00FE6784C007E33FF /* DetailsPanel.m in Sources */,
CE003CC611242D00004B0AA7 /* HSGUIController.m in Sources */,
CE003CC711242D00004B0AA7 /* HSOutline.m in Sources */,
CE003CC811242D00004B0AA7 /* HSWindowController.m in Sources */,
CE003CC911242D00004B0AA7 /* NSEventAdditions.m in Sources */,
CE003CCA11242D00004B0AA7 /* HSOutlineView.m in Sources */,
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 */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
CE515DFC0FC6C13E00EC695D /* ErrorReportWindow.xib */ = {
isa = PBXVariantGroup;
children = (
CE515DFD0FC6C13E00EC695D /* English */,
);
name = ErrorReportWindow.xib;
sourceTree = SOURCE_ROOT;
};
CE515DFE0FC6C13E00EC695D /* progress.nib */ = {
isa = PBXVariantGroup;
children = (
CE515DFF0FC6C13E00EC695D /* English */,
);
name = progress.nib;
sourceTree = SOURCE_ROOT;
};
CE515E000FC6C13E00EC695D /* registration.nib */ = {
isa = PBXVariantGroup;
children = (
CE515E010FC6C13E00EC695D /* English */,
);
name = registration.nib;
sourceTree = SOURCE_ROOT;
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
C01FCF4C08A954540054247B /* Release */ = { C01FCF4C08A954540054247B /* release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_MODEL_TUNING = G5;
INFOPLIST_FILE = Info.plist; INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Applications"; INSTALL_PATH = "$(HOME)/Applications";
PRODUCT_NAME = "dupeGuru ME"; PRODUCT_NAME = "dupeGuru ME";
WRAPPER_EXTENSION = app; WRAPPER_EXTENSION = app;
}; };
name = Release; name = release;
}; };
C01FCF5008A954540054247B /* Release */ = { C01FCF5008A954540054247B /* release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; ARCHS = (
ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; i386,
x86_64,
ppc,
);
GCC_C_LANGUAGE_STANDARD = c99; GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.5; MACOSX_DEPLOYMENT_TARGET = 10.5;
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
}; };
name = Release; name = release;
};
CED596C5111AF56D00C0CF2B /* dev */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(NATIVE_ARCH_ACTUAL)";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.5;
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
};
name = dev;
};
CED596C6111AF56D00C0CF2B /* dev */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Applications";
PRODUCT_NAME = "dupeGuru ME";
WRAPPER_EXTENSION = app;
};
name = dev;
}; };
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
@@ -472,18 +563,20 @@
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "dupeguru" */ = { C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "dupeguru" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
C01FCF4C08A954540054247B /* Release */, C01FCF4C08A954540054247B /* release */,
CED596C6111AF56D00C0CF2B /* dev */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = release;
}; };
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */ = { C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
C01FCF5008A954540054247B /* Release */, C01FCF5008A954540054247B /* release */,
CED596C5111AF56D00C0CF2B /* dev */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };

View File

@@ -7,10 +7,12 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "Utils.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[Utils setPluginName:@"dg_cocoa"];
NSString *pluginPath = [[NSBundle mainBundle] NSString *pluginPath = [[NSBundle mainBundle]
pathForResource:@"dg_cocoa" pathForResource:@"dg_cocoa"
ofType:@"plugin"]; ofType:@"plugin"];

View File

@@ -2,10 +2,10 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10"> <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data> <data>
<int key="IBDocument.SystemTarget">1050</int> <int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">10B504</string> <string key="IBDocument.SystemVersion">10C540</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string> <string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.AppKitVersion">1038.2</string> <string key="IBDocument.AppKitVersion">1038.25</string>
<string key="IBDocument.HIToolboxVersion">437.00</string> <string key="IBDocument.HIToolboxVersion">458.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions"> <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">740</string> <string key="NS.object.0">740</string>
@@ -39,6 +39,10 @@
<string key="NSClassName">NSApplication</string> <string key="NSClassName">NSApplication</string>
</object> </object>
<object class="NSUserDefaultsController" id="579641073"> <object class="NSUserDefaultsController" id="579641073">
<object class="NSMutableArray" key="NSDeclaredKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>SUEnableAutomaticChecks</string>
</object>
<bool key="NSSharedInstance">YES</bool> <bool key="NSSharedInstance">YES</bool>
</object> </object>
<object class="NSWindowTemplate" id="793317856"> <object class="NSWindowTemplate" id="793317856">
@@ -133,7 +137,7 @@
<object class="NSTextFieldCell" key="NSCell" id="602722407"> <object class="NSTextFieldCell" key="NSCell" id="602722407">
<int key="NSCellFlags">67239424</int> <int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">71303168</int> <int key="NSCellFlags2">71303168</int>
<string key="NSContents">Less results</string> <string key="NSContents">Fewer results</string>
<reference key="NSSupport" ref="262032469"/> <reference key="NSSupport" ref="262032469"/>
<reference key="NSControlView" ref="735544762"/> <reference key="NSControlView" ref="735544762"/>
<reference key="NSBackgroundColor" ref="221998487"/> <reference key="NSBackgroundColor" ref="221998487"/>
@@ -552,7 +556,7 @@
<object class="NSButtonCell" key="NSCell" id="58676792"> <object class="NSButtonCell" key="NSCell" id="58676792">
<int key="NSCellFlags">67239424</int> <int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">0</int> <int key="NSCellFlags2">0</int>
<string key="NSContents">Check for update on startup</string> <string key="NSContents">Automatically check for updates</string>
<reference key="NSSupport" ref="26"/> <reference key="NSSupport" ref="26"/>
<reference key="NSControlView" ref="147113892"/> <reference key="NSControlView" ref="147113892"/>
<int key="NSButtonFlags">1211912703</int> <int key="NSButtonFlags">1211912703</int>
@@ -980,22 +984,6 @@
</object> </object>
<int key="connectionID">84</int> <int key="connectionID">84</int>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: values.SUCheckAtStartup</string>
<reference key="source" ref="147113892"/>
<reference key="destination" ref="579641073"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="147113892"/>
<reference key="NSDestination" ref="579641073"/>
<string key="NSLabel">value: values.SUCheckAtStartup</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">values.SUCheckAtStartup</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">85</int>
</object>
<object class="IBConnectionRecord"> <object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection"> <object class="IBOutletConnection" key="connection">
<string key="label">nextKeyView</string> <string key="label">nextKeyView</string>
@@ -1348,6 +1336,22 @@
</object> </object>
<int key="connectionID">113</int> <int key="connectionID">113</int>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: values.SUEnableAutomaticChecks</string>
<reference key="source" ref="147113892"/>
<reference key="destination" ref="579641073"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="147113892"/>
<reference key="NSDestination" ref="579641073"/>
<string key="NSLabel">value: values.SUEnableAutomaticChecks</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">values.SUEnableAutomaticChecks</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">114</int>
</object>
</object> </object>
<object class="IBMutableOrderedSet" key="objectRecords"> <object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects"> <object class="NSArray" key="orderedObjects">
@@ -1838,8 +1842,6 @@
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys"> <object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<string>-1.IBPluginDependency</string>
<string>-2.IBPluginDependency</string>
<string>-3.IBPluginDependency</string> <string>-3.IBPluginDependency</string>
<string>1.IBPluginDependency</string> <string>1.IBPluginDependency</string>
<string>1.ImportedFromIB2</string> <string>1.ImportedFromIB2</string>
@@ -1949,8 +1951,6 @@
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
@@ -2071,9 +2071,26 @@
</object> </object>
</object> </object>
<nil key="sourceID"/> <nil key="sourceID"/>
<int key="maxID">113</int> <int key="maxID">114</int>
</object> </object>
<object class="IBClassDescriber" key="IBDocument.Classes"> <object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool>
<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">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../views/NSTableViewAdditions.h</string>
</object>
</object>
</object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+"> <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">

View File

@@ -8,16 +8,11 @@ http://www.hardcoded.net/licenses/hs_license
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "../base/AppDelegate.h" #import "../base/AppDelegate.h"
#import "DirectoryPanel.h"
#import "PyDupeGuru.h" #import "PyDupeGuru.h"
@interface AppDelegate : AppDelegateBase @interface AppDelegate : AppDelegateBase {}
{
DirectoryPanel *_directoryPanel;
}
- (IBAction)openWebsite:(id)sender; - (IBAction)openWebsite:(id)sender;
- (IBAction)toggleDirectories:(id)sender; - (IBAction)toggleDirectories:(id)sender;
- (DirectoryPanel *)directoryPanel;
- (PyDupeGuru *)py; - (PyDupeGuru *)py;
@end @end

View File

@@ -13,6 +13,7 @@ http://www.hardcoded.net/licenses/hs_license
#import "ValueTransformers.h" #import "ValueTransformers.h"
#import "Consts.h" #import "Consts.h"
#import "DetailsPanel.h" #import "DetailsPanel.h"
#import "DirectoryPanel.h"
@implementation AppDelegate @implementation AppDelegate
+ (void)initialize + (void)initialize
@@ -40,10 +41,10 @@ http://www.hardcoded.net/licenses/hs_license
return self; return self;
} }
- (DetailsPanelBase *)detailsPanel - (DetailsPanel *)detailsPanel
{ {
if (!_detailsPanel) if (!_detailsPanel)
_detailsPanel = [[DetailsPanel alloc] initWithPy:py]; _detailsPanel = [[DetailsPanelPE alloc] initWithPy:py];
return _detailsPanel; return _detailsPanel;
} }
@@ -60,7 +61,7 @@ http://www.hardcoded.net/licenses/hs_license
- (DirectoryPanel *)directoryPanel - (DirectoryPanel *)directoryPanel
{ {
if (!_directoryPanel) if (!_directoryPanel)
_directoryPanel = [[DirectoryPanel alloc] initWithParentApp:self]; _directoryPanel = [[DirectoryPanelPE alloc] initWithParentApp:self];
return _directoryPanel; return _directoryPanel;
} }
- (PyDupeGuru *)py { return (PyDupeGuru *)py; } - (PyDupeGuru *)py { return (PyDupeGuru *)py; }
@@ -75,36 +76,4 @@ http://www.hardcoded.net/licenses/hs_license
[mi setKeyEquivalentModifierMask:NSCommandKeyMask|NSShiftKeyMask]; [mi setKeyEquivalentModifierMask:NSCommandKeyMask|NSShiftKeyMask];
[super applicationDidFinishLaunching:aNotification]; [super applicationDidFinishLaunching:aNotification];
} }
- (void)applicationWillBecomeActive:(NSNotification *)aNotification
{
if (![[result window] isVisible])
[result showWindow:NSApp];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[ud setObject: [result getColumnsOrder] forKey:@"columnsOrder"];
[ud setObject: [result getColumnsWidth] forKey:@"columnsWidth"];
[py saveIgnoreList];
[py saveResults];
int sc = [ud integerForKey:@"sessionCountSinceLastIgnorePurge"];
if (sc >= 10)
{
sc = -1;
[py purgeIgnoreList];
}
sc++;
[ud setInteger:sc forKey:@"sessionCountSinceLastIgnorePurge"];
// NSApplication does not release nib instances objects, we must do it manually
// Well, it isn't needed because the memory is freed anyway (we are quitting the application
// But I need to release RecentDirectories so it saves the user defaults
[recentDirectories release];
}
- (void)recentDirecoryClicked:(NSString *)directory
{
[[self directoryPanel] addDirectory:directory];
}
@end @end

View File

@@ -9,14 +9,14 @@ http://www.hardcoded.net/licenses/hs_license
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "../base/DetailsPanel.h" #import "../base/DetailsPanel.h"
@interface DetailsPanel : DetailsPanelBase @interface DetailsPanelPE : DetailsPanel
{ {
IBOutlet NSImageView *dupeImage; IBOutlet NSImageView *dupeImage;
IBOutlet NSProgressIndicator *dupeProgressIndicator; IBOutlet NSProgressIndicator *dupeProgressIndicator;
IBOutlet NSImageView *refImage; IBOutlet NSImageView *refImage;
IBOutlet NSProgressIndicator *refProgressIndicator; IBOutlet NSProgressIndicator *refProgressIndicator;
PyApp *py; PyApp *pyApp;
BOOL _needsRefresh; BOOL _needsRefresh;
NSString *_dupePath; NSString *_dupePath;
NSString *_refPath; NSString *_refPath;

View File

@@ -13,11 +13,11 @@ http://www.hardcoded.net/licenses/hs_license
#import "DetailsPanel.h" #import "DetailsPanel.h"
#import "Consts.h" #import "Consts.h"
@implementation DetailsPanel @implementation DetailsPanelPE
- (id)initWithPy:(PyApp *)aPy - (id)initWithPy:(PyApp *)aPy
{ {
self = [super initWithPy:aPy]; self = [super initWithPy:aPy];
py = aPy; pyApp = aPy;
_needsRefresh = YES; _needsRefresh = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imageLoaded:) name:ImageLoadedNotification object:self]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imageLoaded:) name:ImageLoadedNotification object:self];
return self; return self;
@@ -36,18 +36,18 @@ http://www.hardcoded.net/licenses/hs_license
[pool release]; [pool release];
} }
- (void)refresh - (void)refreshDetails
{ {
if (!_needsRefresh) if (!_needsRefresh)
return; return;
[detailsTable reloadData]; [detailsTable reloadData];
NSString *refPath = [(PyDupeGuru *)py getSelectedDupeRefPath]; NSString *refPath = [(PyDupeGuru *)pyApp getSelectedDupeRefPath];
if (_refPath != nil) if (_refPath != nil)
[_refPath autorelease]; [_refPath autorelease];
_refPath = [refPath retain]; _refPath = [refPath retain];
[NSThread detachNewThreadSelector:@selector(loadImageAsync:) toTarget:self withObject:refPath]; [NSThread detachNewThreadSelector:@selector(loadImageAsync:) toTarget:self withObject:refPath];
NSString *dupePath = [(PyDupeGuru *)py getSelectedDupePath]; NSString *dupePath = [(PyDupeGuru *)pyApp getSelectedDupePath];
if (_dupePath != nil) if (_dupePath != nil)
[_dupePath autorelease]; [_dupePath autorelease];
_dupePath = [dupePath retain]; _dupePath = [dupePath retain];
@@ -59,12 +59,6 @@ http://www.hardcoded.net/licenses/hs_license
} }
/* Notifications */ /* Notifications */
- (void)duplicateSelectionChanged:(NSNotification *)aNotification
{
_needsRefresh = YES;
[super duplicateSelectionChanged:aNotification];
}
- (void)imageLoaded:(NSNotification *)aNotification - (void)imageLoaded:(NSNotification *)aNotification
{ {
NSString *imagePath = [[aNotification userInfo] valueForKey:@"imagePath"]; NSString *imagePath = [[aNotification userInfo] valueForKey:@"imagePath"];
@@ -80,4 +74,11 @@ http://www.hardcoded.net/licenses/hs_license
[dupeProgressIndicator stopAnimation:nil]; [dupeProgressIndicator stopAnimation:nil];
} }
} }
/* Python --> Cocoa */
- (void)refresh
{
_needsRefresh = YES;
[super refresh];
}
@end @end

View File

@@ -9,7 +9,7 @@ http://www.hardcoded.net/licenses/hs_license
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "../base/DirectoryPanel.h" #import "../base/DirectoryPanel.h"
@interface DirectoryPanel : DirectoryPanelBase @interface DirectoryPanelPE : DirectoryPanel
{ {
} }
- (IBAction)addiPhoto:(id)sender; - (IBAction)addiPhoto:(id)sender;

View File

@@ -11,7 +11,7 @@ http://www.hardcoded.net/licenses/hs_license
static NSString* jobAddIPhoto = @"jobAddIPhoto"; static NSString* jobAddIPhoto = @"jobAddIPhoto";
@implementation DirectoryPanel @implementation DirectoryPanelPE
- (id)initWithParentApp:(id)aParentApp - (id)initWithParentApp:(id)aParentApp
{ {
self = [super initWithParentApp:aParentApp]; self = [super initWithParentApp:aParentApp];
@@ -44,9 +44,8 @@ static NSString* jobAddIPhoto = @"jobAddIPhoto";
- (void)jobCompleted:(NSNotification *)aNotification - (void)jobCompleted:(NSNotification *)aNotification
{ {
if ([[ProgressController mainProgressController] jobId] == jobAddIPhoto) if ([[ProgressController mainProgressController] jobId] == jobAddIPhoto) {
{ [outlineView reloadData];
[directories reloadData];
} }
} }
@end @end

View File

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

View File

@@ -1,146 +0,0 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
which should be included with this package. The terms are also available at
http://www.hardcoded.net/licenses/hs_license
*/
#import "PictureBlocks.h"
#import "Utils.h"
@implementation PictureBlocks
+ (NSString *)getBlocksFromImagePath:(NSString *)imagePath blockCount:(NSNumber *)blockCount
{
return GetBlocks(imagePath, n2i(blockCount));
}
+ (NSSize)getImageSize:(NSString *)imagePath
{
CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)imagePath, kCFURLPOSIXPathStyle, FALSE);
CGImageSourceRef source = CGImageSourceCreateWithURL(fileURL, NULL);
if (source == NULL)
return NSMakeSize(0, 0);
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
if (image == NULL)
return NSMakeSize(0, 0);
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
CGImageRelease(image);
CFRelease(source);
CFRelease(fileURL);
return NSMakeSize(width, height);
}
@end
CGContextRef MyCreateBitmapContext (int width, int height)
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
bitmapBytesPerRow = (width * 4);
bitmapByteCount = (bitmapBytesPerRow * height);
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
// calloc() must be used to allocate bitmapData here because the buffer has to be zeroed.
// If it's not zeroes, when images with transparency are drawn in the context, this buffer
// will stay with undefined pixels, which means that two pictures with the same pixels will
// most likely have different blocks (which is not supposed to happen).
bitmapData = calloc(bitmapByteCount, 1);
if (bitmapData == NULL)
{
fprintf (stderr, "Memory not allocated!");
return NULL;
}
context = CGBitmapContextCreate (bitmapData,width,height,8,bitmapBytesPerRow,colorSpace,kCGImageAlphaNoneSkipLast);
if (context== NULL)
{
free (bitmapData);
fprintf (stderr, "Context not created!");
return NULL;
}
CGColorSpaceRelease( colorSpace );
return context;
}
// returns 0x00RRGGBB
int GetBlock(unsigned char *imageData, int imageWidth, int imageHeight, int boxX, int boxY, int boxW, int boxH)
{
int i,j;
int totalR = 0;
int totalG = 0;
int totalB = 0;
for(i = boxY; i < boxY + boxH; i++)
{
for(j = boxX; j < boxX + boxW; j++)
{
int offset = (i * imageWidth * 4) + (j * 4);
totalR += *(imageData + offset);
totalG += *(imageData + offset + 1);
totalB += *(imageData + offset + 2);
}
}
int pixelCount = boxH * boxW;
int result = 0;
result += (totalR / pixelCount) << 16;
result += (totalG / pixelCount) << 8;
result += (totalB / pixelCount);
return result;
}
NSString* GetBlocks (NSString* filePath, int blockCount)
{
CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)filePath, kCFURLPOSIXPathStyle, FALSE);
CGImageSourceRef source = CGImageSourceCreateWithURL(fileURL, NULL);
if (source == NULL)
return NULL;
CGImageRef image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
if (image == NULL)
return NULL;
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
CGContextRef myContext = MyCreateBitmapContext(width, height);
CGRect myBoundingBox = CGRectMake (0, 0, width, height);
CGContextDrawImage(myContext, myBoundingBox, image);
unsigned char *bitmapData = CGBitmapContextGetData(myContext);
if (bitmapData == NULL)
return NULL;
int blockHeight = height / blockCount;
if (blockHeight < 1)
blockHeight = 1;
int blockWidth = width / blockCount;
if (blockWidth < 1)
blockWidth = 1;
//blockCount might have changed
int blockXCount = (width / blockWidth);
int blockYCount = (height / blockHeight);
CFMutableArrayRef blocks = CFArrayCreateMutable(NULL, blockXCount * blockYCount, &kCFTypeArrayCallBacks);
int i,j;
for(i = 0; i < blockYCount; i++)
{
for(j = 0; j < blockXCount; j++)
{
int block = GetBlock(bitmapData, width, height, j * blockWidth, i * blockHeight, blockWidth, blockHeight);
CFStringRef strBlock = CFStringCreateWithFormat(NULL, NULL, CFSTR("%06x"), block);
CFArrayAppendValue(blocks, strBlock);
CFRelease(strBlock);
}
}
CGContextRelease (myContext);
if (bitmapData) free(bitmapData);
CGImageRelease(image);
CFRelease(source);
CFRelease(fileURL);
CFStringRef result = CFStringCreateByCombiningStrings(NULL, blocks, CFSTR(""));
CFRelease(blocks);
return (NSString *)result;
}

View File

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

View File

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

View File

@@ -4,215 +4,38 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license # http://www.hardcoded.net/licenses/hs_license
import objc from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
from AppKit import *
from core_pe import app_cocoa as app_pe_cocoa from core_pe import app_cocoa as app_pe_cocoa
# Fix py2app imports which chokes on relative imports # Fix py2app imports which chokes on relative imports and other stuff
from core import app, app_cocoa, data, directories, engine, export, ignore, results, scanner from core_pe import block, cache, matchbase, data, _block_osx
from core_pe import block, cache, matchbase, data from lxml import etree, _elementpath
from hsutil import conflict import gzip
class PyApp(NSObject): class PyDupeGuru(PyDupeGuruBase):
pass #fake class
class PyDupeGuru(PyApp):
def init(self): def init(self):
self = super(PyDupeGuru,self).init() self = super(PyDupeGuru, self).init()
self.app = app_pe_cocoa.DupeGuruPE() self.py = app_pe_cocoa.DupeGuruPE()
return self return self
#---Directories
def addDirectory_(self,directory):
return self.app.add_directory(directory)
def removeDirectory_(self,index):
self.app.RemoveDirectory(index)
def setDirectory_state_(self,node_path,state):
self.app.SetDirectoryState(node_path,state)
#---Results
def clearIgnoreList(self):
self.app.scanner.ignore_list.Clear()
def clearPictureCache(self): def clearPictureCache(self):
self.app.scanner.cached_blocks.clear() self.py.scanner.clear_picture_cache()
def doScan(self):
return self.app.start_scanning()
def exportToXHTMLwithColumns_(self, column_ids):
return self.app.export_to_xhtml(column_ids)
def loadIgnoreList(self):
self.app.load_ignore_list()
def loadResults(self):
self.app.load()
def markAll(self):
self.app.results.mark_all()
def markNone(self):
self.app.results.mark_none()
def markInvert(self):
self.app.results.mark_invert()
def purgeIgnoreList(self):
self.app.PurgeIgnoreList()
def toggleSelectedMark(self):
self.app.ToggleSelectedMarkState()
def saveIgnoreList(self):
self.app.save_ignore_list()
def saveResults(self):
self.app.save()
def refreshDetailsWithSelected(self):
self.app.RefreshDetailsWithSelected()
def selectedResultNodePaths(self):
return self.app.selected_result_node_paths()
def selectResultNodePaths_(self,node_paths):
self.app.SelectResultNodePaths(node_paths)
def selectedPowerMarkerNodePaths(self):
return self.app.selected_powermarker_node_paths()
def selectPowerMarkerNodePaths_(self,node_paths):
self.app.SelectPowerMarkerNodePaths(node_paths)
#---Actions
def addSelectedToIgnoreList(self):
self.app.AddSelectedToIgnoreList()
def deleteMarked(self):
self.app.delete_marked()
def applyFilter_(self, filter):
self.app.apply_filter(filter)
def makeSelectedReference(self):
self.app.MakeSelectedReference()
def copyOrMove_markedTo_recreatePath_(self,copy,destination,recreate_path):
self.app.copy_or_move_marked(copy, destination, recreate_path)
def openSelected(self):
self.app.OpenSelected()
def removeMarked(self):
self.app.results.perform_on_marked(lambda x:True,True)
def removeSelected(self):
self.app.RemoveSelected()
def renameSelected_(self,newname):
return self.app.RenameSelected(newname)
def revealSelected(self):
self.app.RevealSelected()
#---Misc
def sortDupesBy_ascending_(self,key,asc):
self.app.sort_dupes(key,asc)
def sortGroupsBy_ascending_(self,key,asc):
self.app.sort_groups(key,asc)
#---Information #---Information
def getIgnoreListCount(self):
return len(self.app.scanner.ignore_list)
def getMarkCount(self):
return self.app.results.mark_count
def getStatLine(self):
return self.app.stat_line
def getOperationalErrorCount(self):
return self.app.last_op_error_count
def getSelectedDupePath(self): def getSelectedDupePath(self):
return unicode(self.app.selected_dupe_path()) return unicode(self.py.selected_dupe_path())
def getSelectedDupeRefPath(self): def getSelectedDupeRefPath(self):
return unicode(self.app.selected_dupe_ref_path()) return unicode(self.py.selected_dupe_ref_path())
#---Data
@objc.signature('i@:i')
def getOutlineViewMaxLevel_(self, tag):
return self.app.GetOutlineViewMaxLevel(tag)
@objc.signature('@@:i@')
def getOutlineView_childCountsForPath_(self, tag, node_path):
return self.app.GetOutlineViewChildCounts(tag, node_path)
def getOutlineView_valuesForIndexes_(self,tag,node_path):
return self.app.GetOutlineViewValues(tag,node_path)
def getOutlineView_markedAtIndexes_(self,tag,node_path):
return self.app.GetOutlineViewMarked(tag,node_path)
def getTableViewCount_(self,tag):
return self.app.GetTableViewCount(tag)
def getTableViewMarkedIndexes_(self,tag):
return self.app.GetTableViewMarkedIndexes(tag)
def getTableView_valuesForRow_(self,tag,row):
return self.app.GetTableViewValues(tag,row)
#---Properties #---Properties
def setMatchScaled_(self,match_scaled): def setMatchScaled_(self,match_scaled):
self.app.scanner.match_scaled = match_scaled self.py.scanner.match_scaled = match_scaled
def setMinMatchPercentage_(self,percentage): def setMinMatchPercentage_(self,percentage):
self.app.scanner.threshold = int(percentage) self.py.scanner.threshold = int(percentage)
def setMixFileKind_(self,mix_file_kind):
self.app.scanner.mix_file_kind = mix_file_kind
def setDisplayDeltaValues_(self,display_delta_values):
self.app.display_delta_values= display_delta_values
def setEscapeFilterRegexp_(self, escape_filter_regexp):
self.app.options['escape_filter_regexp'] = escape_filter_regexp
def setRemoveEmptyFolders_(self, remove_empty_folders):
self.app.options['clean_empty_dirs'] = remove_empty_folders
#---Worker
def getJobProgress(self):
return self.app.progress.last_progress
def getJobDesc(self):
return self.app.progress.last_desc
def cancelJob(self):
self.app.progress.job_cancelled = True
#---Registration #---Registration
def appName(self): def appName(self):
return "dupeGuru Picture Edition" return "dupeGuru Picture Edition"
def demoLimitDescription(self):
return self.app.DEMO_LIMIT_DESC
@objc.signature('i@:')
def isRegistered(self):
return self.app.registered
@objc.signature('i@:@@')
def isCodeValid_withEmail_(self, code, email):
return self.app.is_code_valid(code, email)
def setRegisteredCode_andEmail_(self, code, email):
self.app.set_registration(code, email)

View File

@@ -12,7 +12,6 @@
CE031751109B340A00517EE6 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE031750109B340A00517EE6 /* Preferences.xib */; }; CE031751109B340A00517EE6 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE031750109B340A00517EE6 /* Preferences.xib */; };
CE031754109B345200517EE6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE031753109B345200517EE6 /* MainMenu.xib */; }; CE031754109B345200517EE6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE031753109B345200517EE6 /* MainMenu.xib */; };
CE073F6309CAE1A3005C1D2F /* dupeguru_pe_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_pe_help */; }; CE073F6309CAE1A3005C1D2F /* dupeguru_pe_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_pe_help */; };
CE0C46AA0FA0647E000BE99B /* PictureBlocks.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0C46A90FA0647E000BE99B /* PictureBlocks.m */; };
CE15C8A80ADEB8B50061D4A5 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; }; CE15C8A80ADEB8B50061D4A5 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; };
CE15C8C00ADEB8D40061D4A5 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; }; CE15C8C00ADEB8D40061D4A5 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */; };
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; }; CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
@@ -23,24 +22,32 @@
CE6E0F3D1054EC62008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0F3C1054EC62008D9390 /* dsa_pub.pem */; }; CE6E0F3D1054EC62008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0F3C1054EC62008D9390 /* dsa_pub.pem */; };
CE77C89E10946C6D0078B0DB /* DirectoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE77C89C10946C6D0078B0DB /* DirectoryPanel.xib */; }; CE77C89E10946C6D0078B0DB /* DirectoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE77C89C10946C6D0078B0DB /* DirectoryPanel.xib */; };
CE77C8A810946CE20078B0DB /* DetailsPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE77C8A710946CE20078B0DB /* DetailsPanel.xib */; }; CE77C8A810946CE20078B0DB /* DetailsPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE77C8A710946CE20078B0DB /* DetailsPanel.xib */; };
CE7AC9181119911200D02F6C /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */; };
CE7AC9191119911200D02F6C /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9161119911200D02F6C /* progress.xib */; };
CE7AC91A1119911200D02F6C /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE7AC9171119911200D02F6C /* registration.xib */; };
CE80DB2E0FC192D60086DCA6 /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB1C0FC192D60086DCA6 /* Dialogs.m */; }; CE80DB2E0FC192D60086DCA6 /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB1C0FC192D60086DCA6 /* Dialogs.m */; };
CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB1E0FC192D60086DCA6 /* HSErrorReportWindow.m */; }; CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB1E0FC192D60086DCA6 /* HSErrorReportWindow.m */; };
CE80DB300FC192D60086DCA6 /* Outline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB200FC192D60086DCA6 /* Outline.m */; };
CE80DB310FC192D60086DCA6 /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB220FC192D60086DCA6 /* ProgressController.m */; }; CE80DB310FC192D60086DCA6 /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB220FC192D60086DCA6 /* ProgressController.m */; };
CE80DB320FC192D60086DCA6 /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB250FC192D60086DCA6 /* RecentDirectories.m */; }; CE80DB320FC192D60086DCA6 /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB250FC192D60086DCA6 /* RecentDirectories.m */; };
CE80DB330FC192D60086DCA6 /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB270FC192D60086DCA6 /* RegistrationInterface.m */; }; CE80DB330FC192D60086DCA6 /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB270FC192D60086DCA6 /* RegistrationInterface.m */; };
CE80DB340FC192D60086DCA6 /* Table.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB290FC192D60086DCA6 /* Table.m */; };
CE80DB350FC192D60086DCA6 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB2B0FC192D60086DCA6 /* Utils.m */; }; CE80DB350FC192D60086DCA6 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB2B0FC192D60086DCA6 /* Utils.m */; };
CE80DB360FC192D60086DCA6 /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB2D0FC192D60086DCA6 /* ValueTransformers.m */; }; CE80DB360FC192D60086DCA6 /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB2D0FC192D60086DCA6 /* ValueTransformers.m */; };
CE80DB470FC193650086DCA6 /* NSNotificationAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB460FC193650086DCA6 /* NSNotificationAdditions.m */; }; CE80DB470FC193650086DCA6 /* NSNotificationAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB460FC193650086DCA6 /* NSNotificationAdditions.m */; };
CE80DB4A0FC193770086DCA6 /* NSImageAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB490FC193770086DCA6 /* NSImageAdditions.m */; }; CE80DB4A0FC193770086DCA6 /* NSImageAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB490FC193770086DCA6 /* NSImageAdditions.m */; };
CE80DB760FC194760086DCA6 /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE80DB700FC194760086DCA6 /* ErrorReportWindow.xib */; };
CE80DB770FC194760086DCA6 /* progress.nib in Resources */ = {isa = PBXBuildFile; fileRef = CE80DB720FC194760086DCA6 /* progress.nib */; };
CE80DB780FC194760086DCA6 /* registration.nib in Resources */ = {isa = PBXBuildFile; fileRef = CE80DB740FC194760086DCA6 /* registration.nib */; };
CE80DB8A0FC1951C0086DCA6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB830FC1951C0086DCA6 /* AppDelegate.m */; }; CE80DB8A0FC1951C0086DCA6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB830FC1951C0086DCA6 /* AppDelegate.m */; };
CE80DB8B0FC1951C0086DCA6 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */; }; CE80DB8B0FC1951C0086DCA6 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */; };
CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB890FC1951C0086DCA6 /* ResultWindow.m */; }; CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE80DB890FC1951C0086DCA6 /* ResultWindow.m */; };
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; }; CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
CE95865E112C516400F95FD2 /* ResultOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE95865B112C516400F95FD2 /* ResultOutline.m */; };
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 */; };
CE9EA7581122C96C008CD2BC /* HSWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7481122C96C008CD2BC /* HSWindowController.m */; };
CE9EA7591122C96C008CD2BC /* NSEventAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA74A1122C96C008CD2BC /* NSEventAdditions.m */; };
CE9EA75A1122C96C008CD2BC /* HSOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7511122C96C008CD2BC /* HSOutlineView.m */; };
CE9EA75B1122C96C008CD2BC /* NSIndexPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9EA7531122C96C008CD2BC /* NSIndexPathAdditions.m */; };
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 */; }; CEBAE4270FDA97E000B7887D /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CEBAE4240FDA97E000B7887D /* BRSingleLineFormatter.m */; };
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECA899A09DB132E00A3D774 /* DetailsPanel.h */; }; CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECA899A09DB132E00A3D774 /* DetailsPanel.h */; };
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CECA899B09DB132E00A3D774 /* DetailsPanel.m */; }; CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CECA899B09DB132E00A3D774 /* DetailsPanel.m */; };
@@ -77,14 +84,13 @@
CE031750109B340A00517EE6 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; }; CE031750109B340A00517EE6 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; };
CE031753109B345200517EE6 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; }; CE031753109B345200517EE6 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; };
CE073F5409CAE1A3005C1D2F /* dupeguru_pe_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_pe_help; path = ../../help_pe/dupeguru_pe_help; sourceTree = SOURCE_ROOT; }; CE073F5409CAE1A3005C1D2F /* dupeguru_pe_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_pe_help; path = ../../help_pe/dupeguru_pe_help; sourceTree = SOURCE_ROOT; };
CE0C46A80FA0647E000BE99B /* PictureBlocks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PictureBlocks.h; sourceTree = "<group>"; };
CE0C46A90FA0647E000BE99B /* PictureBlocks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PictureBlocks.m; sourceTree = "<group>"; };
CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; }; CE15C8A70ADEB8B50061D4A5 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
CE18126F111C9D5100E49FCE /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; }; CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; }; CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; }; CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; };
CE381C9B09914ADF003581CE /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = ResultWindow.h; sourceTree = SOURCE_ROOT; }; CE381C9B09914ADF003581CE /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = ResultWindow.h; sourceTree = SOURCE_ROOT; };
CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dg_cocoa.plugin; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; }; CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; };
CE6044EA0FE6796200B71262 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DetailsPanel.h; path = ../base/DetailsPanel.h; sourceTree = SOURCE_ROOT; }; CE6044EA0FE6796200B71262 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DetailsPanel.h; path = ../base/DetailsPanel.h; sourceTree = SOURCE_ROOT; };
CE6044EB0FE6796200B71262 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DetailsPanel.m; path = ../base/DetailsPanel.m; sourceTree = SOURCE_ROOT; }; CE6044EB0FE6796200B71262 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DetailsPanel.m; path = ../base/DetailsPanel.m; sourceTree = SOURCE_ROOT; };
CE68EE6509ABC48000971085 /* DirectoryPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DirectoryPanel.h; sourceTree = SOURCE_ROOT; }; CE68EE6509ABC48000971085 /* DirectoryPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DirectoryPanel.h; sourceTree = SOURCE_ROOT; };
@@ -92,12 +98,13 @@
CE6E0F3C1054EC62008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = "<group>"; }; CE6E0F3C1054EC62008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = "<group>"; };
CE77C89C10946C6D0078B0DB /* DirectoryPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = DirectoryPanel.xib; path = ../../base/xib/DirectoryPanel.xib; sourceTree = "<group>"; }; CE77C89C10946C6D0078B0DB /* DirectoryPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = DirectoryPanel.xib; path = ../../base/xib/DirectoryPanel.xib; sourceTree = "<group>"; };
CE77C8A710946CE20078B0DB /* DetailsPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DetailsPanel.xib; sourceTree = "<group>"; }; 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; }; CE80DB1B0FC192D60086DCA6 /* Dialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Dialogs.h; path = ../../cocoalib/Dialogs.h; sourceTree = SOURCE_ROOT; };
CE80DB1C0FC192D60086DCA6 /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; }; CE80DB1C0FC192D60086DCA6 /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; };
CE80DB1D0FC192D60086DCA6 /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; }; CE80DB1D0FC192D60086DCA6 /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
CE80DB1E0FC192D60086DCA6 /* HSErrorReportWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSErrorReportWindow.m; path = ../../cocoalib/HSErrorReportWindow.m; sourceTree = SOURCE_ROOT; }; CE80DB1E0FC192D60086DCA6 /* HSErrorReportWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSErrorReportWindow.m; path = ../../cocoalib/HSErrorReportWindow.m; sourceTree = SOURCE_ROOT; };
CE80DB1F0FC192D60086DCA6 /* Outline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Outline.h; path = ../../cocoalib/Outline.h; sourceTree = SOURCE_ROOT; };
CE80DB200FC192D60086DCA6 /* Outline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Outline.m; path = ../../cocoalib/Outline.m; sourceTree = SOURCE_ROOT; };
CE80DB210FC192D60086DCA6 /* ProgressController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressController.h; path = ../../cocoalib/ProgressController.h; sourceTree = SOURCE_ROOT; }; CE80DB210FC192D60086DCA6 /* ProgressController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressController.h; path = ../../cocoalib/ProgressController.h; sourceTree = SOURCE_ROOT; };
CE80DB220FC192D60086DCA6 /* ProgressController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProgressController.m; path = ../../cocoalib/ProgressController.m; sourceTree = SOURCE_ROOT; }; CE80DB220FC192D60086DCA6 /* ProgressController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProgressController.m; path = ../../cocoalib/ProgressController.m; sourceTree = SOURCE_ROOT; };
CE80DB230FC192D60086DCA6 /* PyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyApp.h; path = ../../cocoalib/PyApp.h; sourceTree = SOURCE_ROOT; }; CE80DB230FC192D60086DCA6 /* PyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyApp.h; path = ../../cocoalib/PyApp.h; sourceTree = SOURCE_ROOT; };
@@ -105,8 +112,6 @@
CE80DB250FC192D60086DCA6 /* RecentDirectories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RecentDirectories.m; path = ../../cocoalib/RecentDirectories.m; 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; }; 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; }; CE80DB270FC192D60086DCA6 /* RegistrationInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegistrationInterface.m; path = ../../cocoalib/RegistrationInterface.m; sourceTree = SOURCE_ROOT; };
CE80DB280FC192D60086DCA6 /* Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Table.h; path = ../../cocoalib/Table.h; sourceTree = SOURCE_ROOT; };
CE80DB290FC192D60086DCA6 /* Table.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Table.m; path = ../../cocoalib/Table.m; sourceTree = SOURCE_ROOT; };
CE80DB2A0FC192D60086DCA6 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = ../../cocoalib/Utils.h; sourceTree = SOURCE_ROOT; }; 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; }; 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; }; CE80DB2C0FC192D60086DCA6 /* ValueTransformers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueTransformers.h; path = ../../cocoalib/ValueTransformers.h; sourceTree = SOURCE_ROOT; };
@@ -115,9 +120,6 @@
CE80DB460FC193650086DCA6 /* NSNotificationAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSNotificationAdditions.m; path = ../../cocoalib/NSNotificationAdditions.m; sourceTree = SOURCE_ROOT; }; CE80DB460FC193650086DCA6 /* NSNotificationAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSNotificationAdditions.m; path = ../../cocoalib/NSNotificationAdditions.m; sourceTree = SOURCE_ROOT; };
CE80DB480FC193770086DCA6 /* NSImageAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSImageAdditions.h; path = ../../cocoalib/NSImageAdditions.h; sourceTree = SOURCE_ROOT; }; CE80DB480FC193770086DCA6 /* NSImageAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSImageAdditions.h; path = ../../cocoalib/NSImageAdditions.h; sourceTree = SOURCE_ROOT; };
CE80DB490FC193770086DCA6 /* NSImageAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSImageAdditions.m; path = ../../cocoalib/NSImageAdditions.m; sourceTree = SOURCE_ROOT; }; CE80DB490FC193770086DCA6 /* NSImageAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSImageAdditions.m; path = ../../cocoalib/NSImageAdditions.m; sourceTree = SOURCE_ROOT; };
CE80DB710FC194760086DCA6 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = ../../cocoalib/English.lproj/ErrorReportWindow.xib; sourceTree = SOURCE_ROOT; };
CE80DB730FC194760086DCA6 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/progress.nib; sourceTree = SOURCE_ROOT; };
CE80DB750FC194760086DCA6 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/registration.nib; sourceTree = SOURCE_ROOT; };
CE80DB820FC1951C0086DCA6 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ../base/AppDelegate.h; sourceTree = SOURCE_ROOT; }; CE80DB820FC1951C0086DCA6 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ../base/AppDelegate.h; sourceTree = SOURCE_ROOT; };
CE80DB830FC1951C0086DCA6 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ../base/AppDelegate.m; sourceTree = SOURCE_ROOT; }; CE80DB830FC1951C0086DCA6 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ../base/AppDelegate.m; sourceTree = SOURCE_ROOT; };
CE80DB840FC1951C0086DCA6 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = ../base/Consts.h; sourceTree = SOURCE_ROOT; }; CE80DB840FC1951C0086DCA6 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = ../base/Consts.h; sourceTree = SOURCE_ROOT; };
@@ -127,6 +129,32 @@
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultWindow.h; path = ../base/ResultWindow.h; sourceTree = SOURCE_ROOT; }; CE80DB880FC1951C0086DCA6 /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultWindow.h; path = ../base/ResultWindow.h; sourceTree = SOURCE_ROOT; };
CE80DB890FC1951C0086DCA6 /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultWindow.m; path = ../base/ResultWindow.m; sourceTree = SOURCE_ROOT; }; CE80DB890FC1951C0086DCA6 /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultWindow.m; path = ../base/ResultWindow.m; sourceTree = SOURCE_ROOT; };
CE848A1809DD85810004CB44 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; }; CE848A1809DD85810004CB44 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
CE958658112C516400F95FD2 /* PyResultTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTree.h; path = ../base/PyResultTree.h; sourceTree = SOURCE_ROOT; };
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>"; };
CE9EA7441122C96C008CD2BC /* HSGUIController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSGUIController.m; sourceTree = "<group>"; };
CE9EA7451122C96C008CD2BC /* HSOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutline.h; sourceTree = "<group>"; };
CE9EA7461122C96C008CD2BC /* HSOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutline.m; sourceTree = "<group>"; };
CE9EA7471122C96C008CD2BC /* HSWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSWindowController.h; sourceTree = "<group>"; };
CE9EA7481122C96C008CD2BC /* HSWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSWindowController.m; sourceTree = "<group>"; };
CE9EA7491122C96C008CD2BC /* NSEventAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSEventAdditions.h; path = ../../cocoalib/NSEventAdditions.h; sourceTree = SOURCE_ROOT; };
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>"; };
CE9EA7531122C96C008CD2BC /* NSIndexPathAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSIndexPathAdditions.m; sourceTree = "<group>"; };
CE9EA7541122C96C008CD2BC /* NSTableViewAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSTableViewAdditions.h; sourceTree = "<group>"; };
CE9EA7551122C96C008CD2BC /* NSTableViewAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSTableViewAdditions.m; sourceTree = "<group>"; };
CE9EA76F1122CA0B008CD2BC /* DirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DirectoryOutline.h; path = ../base/DirectoryOutline.h; sourceTree = SOURCE_ROOT; };
CE9EA7701122CA0B008CD2BC /* DirectoryOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DirectoryOutline.m; path = ../base/DirectoryOutline.m; sourceTree = SOURCE_ROOT; };
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; }; 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; }; CEBAE4240FDA97E000B7887D /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; };
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; }; CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; };
@@ -155,8 +183,6 @@
080E96DDFE201D6D7F000001 /* Classes */ = { 080E96DDFE201D6D7F000001 /* Classes */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CE0C46A80FA0647E000BE99B /* PictureBlocks.h */,
CE0C46A90FA0647E000BE99B /* PictureBlocks.m */,
CE381C9509914ACE003581CE /* AppDelegate.h */, CE381C9509914ACE003581CE /* AppDelegate.h */,
CE381C9409914ACE003581CE /* AppDelegate.m */, CE381C9409914ACE003581CE /* AppDelegate.m */,
CE848A1809DD85810004CB44 /* Consts.h */, CE848A1809DD85810004CB44 /* Consts.h */,
@@ -254,32 +280,43 @@
path = xib; path = xib;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
CE7AC9141119911200D02F6C /* xib */ = {
isa = PBXGroup;
children = (
CE7AC9151119911200D02F6C /* ErrorReportWindow.xib */,
CE7AC9161119911200D02F6C /* progress.xib */,
CE7AC9171119911200D02F6C /* registration.xib */,
);
name = xib;
path = ../../cocoalib/xib;
sourceTree = SOURCE_ROOT;
};
CE80DB1A0FC192AB0086DCA6 /* cocoalib */ = { CE80DB1A0FC192AB0086DCA6 /* cocoalib */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CE9EA7421122C96C008CD2BC /* controllers */,
CE9EA74B1122C96C008CD2BC /* proxies */,
CE9EA74F1122C96C008CD2BC /* views */,
CE7AC9141119911200D02F6C /* xib */,
CEBAE4220FDA97E000B7887D /* brsinglelineformatter */, CEBAE4220FDA97E000B7887D /* brsinglelineformatter */,
CE80DB700FC194760086DCA6 /* ErrorReportWindow.xib */,
CE80DB720FC194760086DCA6 /* progress.nib */,
CE80DB740FC194760086DCA6 /* registration.nib */,
CE80DB480FC193770086DCA6 /* NSImageAdditions.h */, CE80DB480FC193770086DCA6 /* NSImageAdditions.h */,
CE80DB490FC193770086DCA6 /* NSImageAdditions.m */, CE80DB490FC193770086DCA6 /* NSImageAdditions.m */,
CE80DB450FC193650086DCA6 /* NSNotificationAdditions.h */, CE80DB450FC193650086DCA6 /* NSNotificationAdditions.h */,
CE80DB460FC193650086DCA6 /* NSNotificationAdditions.m */, CE80DB460FC193650086DCA6 /* NSNotificationAdditions.m */,
CE9EA7491122C96C008CD2BC /* NSEventAdditions.h */,
CE9EA74A1122C96C008CD2BC /* NSEventAdditions.m */,
CE80DB1B0FC192D60086DCA6 /* Dialogs.h */, CE80DB1B0FC192D60086DCA6 /* Dialogs.h */,
CE80DB1C0FC192D60086DCA6 /* Dialogs.m */, CE80DB1C0FC192D60086DCA6 /* Dialogs.m */,
CE80DB1D0FC192D60086DCA6 /* HSErrorReportWindow.h */, CE80DB1D0FC192D60086DCA6 /* HSErrorReportWindow.h */,
CE80DB1E0FC192D60086DCA6 /* HSErrorReportWindow.m */, CE80DB1E0FC192D60086DCA6 /* HSErrorReportWindow.m */,
CE80DB1F0FC192D60086DCA6 /* Outline.h */,
CE80DB200FC192D60086DCA6 /* Outline.m */,
CE80DB210FC192D60086DCA6 /* ProgressController.h */, CE80DB210FC192D60086DCA6 /* ProgressController.h */,
CE80DB220FC192D60086DCA6 /* ProgressController.m */, CE80DB220FC192D60086DCA6 /* ProgressController.m */,
CE80DB230FC192D60086DCA6 /* PyApp.h */, CE80DB230FC192D60086DCA6 /* PyApp.h */,
CE9EA74E1122C96C008CD2BC /* PyRegistrable.h */,
CE80DB240FC192D60086DCA6 /* RecentDirectories.h */, CE80DB240FC192D60086DCA6 /* RecentDirectories.h */,
CE80DB250FC192D60086DCA6 /* RecentDirectories.m */, CE80DB250FC192D60086DCA6 /* RecentDirectories.m */,
CE80DB260FC192D60086DCA6 /* RegistrationInterface.h */, CE80DB260FC192D60086DCA6 /* RegistrationInterface.h */,
CE80DB270FC192D60086DCA6 /* RegistrationInterface.m */, CE80DB270FC192D60086DCA6 /* RegistrationInterface.m */,
CE80DB280FC192D60086DCA6 /* Table.h */,
CE80DB290FC192D60086DCA6 /* Table.m */,
CE80DB2A0FC192D60086DCA6 /* Utils.h */, CE80DB2A0FC192D60086DCA6 /* Utils.h */,
CE80DB2B0FC192D60086DCA6 /* Utils.m */, CE80DB2B0FC192D60086DCA6 /* Utils.m */,
CE80DB2C0FC192D60086DCA6 /* ValueTransformers.h */, CE80DB2C0FC192D60086DCA6 /* ValueTransformers.h */,
@@ -298,13 +335,61 @@
CE6044EB0FE6796200B71262 /* DetailsPanel.m */, CE6044EB0FE6796200B71262 /* DetailsPanel.m */,
CE80DB850FC1951C0086DCA6 /* DirectoryPanel.h */, CE80DB850FC1951C0086DCA6 /* DirectoryPanel.h */,
CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */, CE80DB860FC1951C0086DCA6 /* DirectoryPanel.m */,
CE9EA7711122CA0B008CD2BC /* PyDirectoryOutline.h */,
CE9EA76F1122CA0B008CD2BC /* DirectoryOutline.h */,
CE9EA7701122CA0B008CD2BC /* DirectoryOutline.m */,
CE80DB870FC1951C0086DCA6 /* PyDupeGuru.h */, CE80DB870FC1951C0086DCA6 /* PyDupeGuru.h */,
CE18126F111C9D5100E49FCE /* PyDetailsPanel.h */,
CE80DB880FC1951C0086DCA6 /* ResultWindow.h */, CE80DB880FC1951C0086DCA6 /* ResultWindow.h */,
CE80DB890FC1951C0086DCA6 /* ResultWindow.m */, CE80DB890FC1951C0086DCA6 /* ResultWindow.m */,
CE958658112C516400F95FD2 /* PyResultTree.h */,
CE95865A112C516400F95FD2 /* ResultOutline.h */,
CE95865B112C516400F95FD2 /* ResultOutline.m */,
CE958659112C516400F95FD2 /* PyStatsLabel.h */,
CE95865C112C516400F95FD2 /* StatsLabel.h */,
CE95865D112C516400F95FD2 /* StatsLabel.m */,
); );
name = dgbase; name = dgbase;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
CE9EA7421122C96C008CD2BC /* controllers */ = {
isa = PBXGroup;
children = (
CE9EA7431122C96C008CD2BC /* HSGUIController.h */,
CE9EA7441122C96C008CD2BC /* HSGUIController.m */,
CE9EA7451122C96C008CD2BC /* HSOutline.h */,
CE9EA7461122C96C008CD2BC /* HSOutline.m */,
CE9EA7471122C96C008CD2BC /* HSWindowController.h */,
CE9EA7481122C96C008CD2BC /* HSWindowController.m */,
);
name = controllers;
path = ../../cocoalib/controllers;
sourceTree = SOURCE_ROOT;
};
CE9EA74B1122C96C008CD2BC /* proxies */ = {
isa = PBXGroup;
children = (
CE9EA74C1122C96C008CD2BC /* PyGUI.h */,
CE9EA74D1122C96C008CD2BC /* PyOutline.h */,
);
name = proxies;
path = ../../cocoalib/proxies;
sourceTree = SOURCE_ROOT;
};
CE9EA74F1122C96C008CD2BC /* views */ = {
isa = PBXGroup;
children = (
CE9EA7501122C96C008CD2BC /* HSOutlineView.h */,
CE9EA7511122C96C008CD2BC /* HSOutlineView.m */,
CE9EA7521122C96C008CD2BC /* NSIndexPathAdditions.h */,
CE9EA7531122C96C008CD2BC /* NSIndexPathAdditions.m */,
CE9EA7541122C96C008CD2BC /* NSTableViewAdditions.h */,
CE9EA7551122C96C008CD2BC /* NSTableViewAdditions.m */,
);
name = views;
path = ../../cocoalib/views;
sourceTree = SOURCE_ROOT;
};
CEBAE4220FDA97E000B7887D /* brsinglelineformatter */ = { CEBAE4220FDA97E000B7887D /* brsinglelineformatter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -377,14 +462,14 @@
CEFC295509C89FF200D9F998 /* details32.png in Resources */, CEFC295509C89FF200D9F998 /* details32.png in Resources */,
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */, CEFC295609C89FF200D9F998 /* preferences32.png in Resources */,
CEFCDE2D0AB0418600C33A93 /* dgpe_logo_32.png in Resources */, CEFCDE2D0AB0418600C33A93 /* dgpe_logo_32.png in Resources */,
CE80DB760FC194760086DCA6 /* ErrorReportWindow.xib in Resources */,
CE80DB770FC194760086DCA6 /* progress.nib in Resources */,
CE80DB780FC194760086DCA6 /* registration.nib in Resources */,
CE6E0F3D1054EC62008D9390 /* dsa_pub.pem in Resources */, CE6E0F3D1054EC62008D9390 /* dsa_pub.pem in Resources */,
CE77C89E10946C6D0078B0DB /* DirectoryPanel.xib in Resources */, CE77C89E10946C6D0078B0DB /* DirectoryPanel.xib in Resources */,
CE77C8A810946CE20078B0DB /* DetailsPanel.xib in Resources */, CE77C8A810946CE20078B0DB /* DetailsPanel.xib in Resources */,
CE031751109B340A00517EE6 /* Preferences.xib in Resources */, CE031751109B340A00517EE6 /* Preferences.xib in Resources */,
CE031754109B345200517EE6 /* MainMenu.xib in Resources */, CE031754109B345200517EE6 /* MainMenu.xib in Resources */,
CE7AC9181119911200D02F6C /* ErrorReportWindow.xib in Resources */,
CE7AC9191119911200D02F6C /* progress.xib in Resources */,
CE7AC91A1119911200D02F6C /* registration.xib in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -400,14 +485,11 @@
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */, CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */,
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */, CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */,
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */, CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */,
CE0C46AA0FA0647E000BE99B /* PictureBlocks.m in Sources */,
CE80DB2E0FC192D60086DCA6 /* Dialogs.m in Sources */, CE80DB2E0FC192D60086DCA6 /* Dialogs.m in Sources */,
CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */, CE80DB2F0FC192D60086DCA6 /* HSErrorReportWindow.m in Sources */,
CE80DB300FC192D60086DCA6 /* Outline.m in Sources */,
CE80DB310FC192D60086DCA6 /* ProgressController.m in Sources */, CE80DB310FC192D60086DCA6 /* ProgressController.m in Sources */,
CE80DB320FC192D60086DCA6 /* RecentDirectories.m in Sources */, CE80DB320FC192D60086DCA6 /* RecentDirectories.m in Sources */,
CE80DB330FC192D60086DCA6 /* RegistrationInterface.m in Sources */, CE80DB330FC192D60086DCA6 /* RegistrationInterface.m in Sources */,
CE80DB340FC192D60086DCA6 /* Table.m in Sources */,
CE80DB350FC192D60086DCA6 /* Utils.m in Sources */, CE80DB350FC192D60086DCA6 /* Utils.m in Sources */,
CE80DB360FC192D60086DCA6 /* ValueTransformers.m in Sources */, CE80DB360FC192D60086DCA6 /* ValueTransformers.m in Sources */,
CE80DB470FC193650086DCA6 /* NSNotificationAdditions.m in Sources */, CE80DB470FC193650086DCA6 /* NSNotificationAdditions.m in Sources */,
@@ -417,71 +499,70 @@
CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */, CE80DB8C0FC1951C0086DCA6 /* ResultWindow.m in Sources */,
CEBAE4270FDA97E000B7887D /* BRSingleLineFormatter.m in Sources */, CEBAE4270FDA97E000B7887D /* BRSingleLineFormatter.m in Sources */,
CE6044EC0FE6796200B71262 /* DetailsPanel.m in Sources */, CE6044EC0FE6796200B71262 /* DetailsPanel.m in Sources */,
CE9EA7561122C96C008CD2BC /* HSGUIController.m in Sources */,
CE9EA7571122C96C008CD2BC /* HSOutline.m in Sources */,
CE9EA7581122C96C008CD2BC /* HSWindowController.m in Sources */,
CE9EA7591122C96C008CD2BC /* NSEventAdditions.m in Sources */,
CE9EA75A1122C96C008CD2BC /* HSOutlineView.m in Sources */,
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 */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
CE80DB700FC194760086DCA6 /* ErrorReportWindow.xib */ = {
isa = PBXVariantGroup;
children = (
CE80DB710FC194760086DCA6 /* English */,
);
name = ErrorReportWindow.xib;
sourceTree = SOURCE_ROOT;
};
CE80DB720FC194760086DCA6 /* progress.nib */ = {
isa = PBXVariantGroup;
children = (
CE80DB730FC194760086DCA6 /* English */,
);
name = progress.nib;
sourceTree = SOURCE_ROOT;
};
CE80DB740FC194760086DCA6 /* registration.nib */ = {
isa = PBXVariantGroup;
children = (
CE80DB750FC194760086DCA6 /* English */,
);
name = registration.nib;
sourceTree = SOURCE_ROOT;
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
C01FCF4C08A954540054247B /* Release */ = { C01FCF4C08A954540054247B /* release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ARCHS = (
ppc,
i386,
);
FRAMEWORK_SEARCH_PATHS = (
"$(FRAMEWORK_SEARCH_PATHS)",
"$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
);
GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_MODEL_TUNING = G5;
INFOPLIST_FILE = Info.plist; INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Applications"; INSTALL_PATH = "$(HOME)/Applications";
PRODUCT_NAME = "dupeGuru PE"; PRODUCT_NAME = "dupeGuru PE";
WRAPPER_EXTENSION = app; WRAPPER_EXTENSION = app;
}; };
name = Release; name = release;
}; };
C01FCF5008A954540054247B /* Release */ = { C01FCF5008A954540054247B /* release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; ARCHS = (
ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; i386,
x86_64,
ppc,
);
GCC_C_LANGUAGE_STANDARD = c99; GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.5; MACOSX_DEPLOYMENT_TARGET = 10.5;
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
}; };
name = Release; name = release;
};
CEE00FF0111AF37400BC1A77 /* dev */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(NATIVE_ARCH_ACTUAL)";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.5;
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
};
name = dev;
};
CEE00FF1111AF37400BC1A77 /* dev */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Applications";
PRODUCT_NAME = "dupeGuru PE";
WRAPPER_EXTENSION = app;
};
name = dev;
}; };
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
@@ -489,18 +570,20 @@
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "dupeguru" */ = { C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "dupeguru" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
C01FCF4C08A954540054247B /* Release */, C01FCF4C08A954540054247B /* release */,
CEE00FF1111AF37400BC1A77 /* dev */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = release;
}; };
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */ = { C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
C01FCF5008A954540054247B /* Release */, C01FCF5008A954540054247B /* release */,
CEE00FF0111AF37400BC1A77 /* dev */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };

View File

@@ -7,10 +7,12 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "Utils.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[Utils setPluginName:@"dg_cocoa"];
NSString *pluginPath = [[NSBundle mainBundle] NSString *pluginPath = [[NSBundle mainBundle]
pathForResource:@"dg_cocoa" pathForResource:@"dg_cocoa"
ofType:@"plugin"]; ofType:@"plugin"];

View File

@@ -2,17 +2,17 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10"> <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data> <data>
<int key="IBDocument.SystemTarget">1050</int> <int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">10B504</string> <string key="IBDocument.SystemVersion">10C540</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string> <string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.AppKitVersion">1038.2</string> <string key="IBDocument.AppKitVersion">1038.25</string>
<string key="IBDocument.HIToolboxVersion">437.00</string> <string key="IBDocument.HIToolboxVersion">458.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions"> <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">740</string> <string key="NS.object.0">740</string>
</object> </object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<integer value="18"/> <integer value="7"/>
</object> </object>
<object class="NSArray" key="IBDocument.PluginDependencies"> <object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
@@ -30,7 +30,7 @@
<object class="NSMutableArray" key="IBDocument.RootObjects" id="433298071"> <object class="NSMutableArray" key="IBDocument.RootObjects" id="433298071">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSCustomObject" id="449950342"> <object class="NSCustomObject" id="449950342">
<string key="NSClassName">DetailsPanel</string> <string key="NSClassName">DetailsPanelPE</string>
</object> </object>
<object class="NSCustomObject" id="175405098"> <object class="NSCustomObject" id="175405098">
<string key="NSClassName">FirstResponder</string> <string key="NSClassName">FirstResponder</string>
@@ -487,6 +487,14 @@
</object> </object>
<int key="connectionID">31</int> <int key="connectionID">31</int>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">dataSource</string>
<reference key="source" ref="1061505056"/>
<reference key="destination" ref="449950342"/>
</object>
<int key="connectionID">43</int>
</object>
</object> </object>
<object class="IBMutableOrderedSet" key="objectRecords"> <object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects"> <object class="NSArray" key="orderedObjects">
@@ -768,7 +776,6 @@
<string>6.ImportedFromIB2</string> <string>6.ImportedFromIB2</string>
<string>7.IBPluginDependency</string> <string>7.IBPluginDependency</string>
<string>7.ImportedFromIB2</string> <string>7.ImportedFromIB2</string>
<string>8.CustomClassName</string>
<string>8.IBPluginDependency</string> <string>8.IBPluginDependency</string>
<string>8.ImportedFromIB2</string> <string>8.ImportedFromIB2</string>
<string>9.IBPluginDependency</string> <string>9.IBPluginDependency</string>
@@ -825,7 +832,6 @@
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>TableView</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<boolean value="YES"/> <boolean value="YES"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string> <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -848,14 +854,26 @@
</object> </object>
</object> </object>
<nil key="sourceID"/> <nil key="sourceID"/>
<int key="maxID">42</int> <int key="maxID">43</int>
</object> </object>
<object class="IBClassDescriber" key="IBDocument.Classes"> <object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions"> <object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">DetailsPanel</string> <string key="className">DetailsPanel</string>
<string key="superclassName">DetailsPanelBase</string> <string key="superclassName">NSWindowController</string>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">detailsTable</string>
<string key="NS.object.0">NSTableView</string>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../base/DetailsPanel.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">DetailsPanelPE</string>
<string key="superclassName">DetailsPanel</string>
<object class="NSMutableDictionary" key="outlets"> <object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys"> <object class="NSArray" key="dict.sortedKeys">
@@ -879,8 +897,8 @@
</object> </object>
</object> </object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">DetailsPanel</string> <string key="className">DetailsPanelPE</string>
<string key="superclassName">DetailsPanelBase</string> <string key="superclassName">DetailsPanel</string>
<object class="NSMutableDictionary" key="outlets"> <object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">detailsTable</string> <string key="NS.key.0">detailsTable</string>
<string key="NS.object.0">NSTableView</string> <string key="NS.object.0">NSTableView</string>
@@ -890,18 +908,6 @@
<string key="minorKey"/> <string key="minorKey"/>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription">
<string key="className">DetailsPanelBase</string>
<string key="superclassName">NSWindowController</string>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">detailsTable</string>
<string key="NS.object.0">TableView</string>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">dgbase/DetailsPanel.h</string>
</object>
</object>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">
<string key="className">FirstResponder</string> <string key="className">FirstResponder</string>
<string key="superclassName">NSObject</string> <string key="superclassName">NSObject</string>
@@ -910,34 +916,6 @@
<string key="minorKey"/> <string key="minorKey"/>
</object> </object>
</object> </object>
<object class="IBPartialClassDescription">
<string key="className">PyApp</string>
<string key="superclassName">PyRegistrable</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/PyApp.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">TableView</string>
<string key="superclassName">NSTableView</string>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">py</string>
<string key="NS.object.0">PyApp</string>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">cocoalib/Table.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">TableView</string>
<string key="superclassName">NSTableView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBUserSource</string>
<string key="minorKey"/>
</object>
</object>
</object> </object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+"> <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>

View File

@@ -2,17 +2,17 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10"> <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data> <data>
<int key="IBDocument.SystemTarget">1050</int> <int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">10B504</string> <string key="IBDocument.SystemVersion">10C540</string>
<string key="IBDocument.InterfaceBuilderVersion">740</string> <string key="IBDocument.InterfaceBuilderVersion">740</string>
<string key="IBDocument.AppKitVersion">1038.2</string> <string key="IBDocument.AppKitVersion">1038.25</string>
<string key="IBDocument.HIToolboxVersion">437.00</string> <string key="IBDocument.HIToolboxVersion">458.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions"> <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string> <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">740</string> <string key="NS.object.0">740</string>
</object> </object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs"> <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<integer value="2"/> <integer value="3"/>
</object> </object>
<object class="NSArray" key="IBDocument.PluginDependencies"> <object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
@@ -39,6 +39,10 @@
<string key="NSClassName">NSApplication</string> <string key="NSClassName">NSApplication</string>
</object> </object>
<object class="NSUserDefaultsController" id="455472712"> <object class="NSUserDefaultsController" id="455472712">
<object class="NSMutableArray" key="NSDeclaredKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>SUEnableAutomaticChecks</string>
</object>
<bool key="NSSharedInstance">YES</bool> <bool key="NSSharedInstance">YES</bool>
</object> </object>
<object class="NSWindowTemplate" id="809668081"> <object class="NSWindowTemplate" id="809668081">
@@ -133,7 +137,7 @@
<object class="NSTextFieldCell" key="NSCell" id="397705219"> <object class="NSTextFieldCell" key="NSCell" id="397705219">
<int key="NSCellFlags">67239424</int> <int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">71303168</int> <int key="NSCellFlags2">71303168</int>
<string key="NSContents">Less results</string> <string key="NSContents">Fewer results</string>
<reference key="NSSupport" ref="649492068"/> <reference key="NSSupport" ref="649492068"/>
<reference key="NSControlView" ref="171701149"/> <reference key="NSControlView" ref="171701149"/>
<reference key="NSBackgroundColor" ref="71910056"/> <reference key="NSBackgroundColor" ref="71910056"/>
@@ -411,7 +415,7 @@
<object class="NSButtonCell" key="NSCell" id="2297113"> <object class="NSButtonCell" key="NSCell" id="2297113">
<int key="NSCellFlags">67239424</int> <int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">0</int> <int key="NSCellFlags2">0</int>
<string key="NSContents">Check for update on startup</string> <string key="NSContents">Automatically check for updates</string>
<reference key="NSSupport" ref="26"/> <reference key="NSSupport" ref="26"/>
<reference key="NSControlView" ref="472028782"/> <reference key="NSControlView" ref="472028782"/>
<int key="NSButtonFlags">1211912703</int> <int key="NSButtonFlags">1211912703</int>
@@ -541,22 +545,6 @@
</object> </object>
<int key="connectionID">39</int> <int key="connectionID">39</int>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: values.SUCheckAtStartup</string>
<reference key="source" ref="472028782"/>
<reference key="destination" ref="455472712"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="472028782"/>
<reference key="NSDestination" ref="455472712"/>
<string key="NSLabel">value: values.SUCheckAtStartup</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">values.SUCheckAtStartup</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">40</int>
</object>
<object class="IBConnectionRecord"> <object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection"> <object class="IBActionConnection" key="connection">
<string key="label">revertToInitialValues:</string> <string key="label">revertToInitialValues:</string>
@@ -677,6 +665,22 @@
</object> </object>
<int key="connectionID">51</int> <int key="connectionID">51</int>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: values.SUEnableAutomaticChecks</string>
<reference key="source" ref="472028782"/>
<reference key="destination" ref="455472712"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="472028782"/>
<reference key="NSDestination" ref="455472712"/>
<string key="NSLabel">value: values.SUEnableAutomaticChecks</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">values.SUEnableAutomaticChecks</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">58</int>
</object>
</object> </object>
<object class="IBMutableOrderedSet" key="objectRecords"> <object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects"> <object class="NSArray" key="orderedObjects">
@@ -1110,9 +1114,26 @@
</object> </object>
</object> </object>
<nil key="sourceID"/> <nil key="sourceID"/>
<int key="maxID">51</int> <int key="maxID">58</int>
</object> </object>
<object class="IBClassDescriber" key="IBDocument.Classes"> <object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool>
<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">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">../views/NSTableViewAdditions.h</string>
</object>
</object>
</object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+"> <object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
<bool key="EncodedWithXMLCoder">YES</bool> <bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription"> <object class="IBPartialClassDescription">

View File

@@ -8,16 +8,11 @@ http://www.hardcoded.net/licenses/hs_license
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "../base/AppDelegate.h" #import "../base/AppDelegate.h"
#import "DirectoryPanel.h"
#import "PyDupeGuru.h" #import "PyDupeGuru.h"
@interface AppDelegate : AppDelegateBase @interface AppDelegate : AppDelegateBase {}
{
DirectoryPanel *_directoryPanel;
}
- (IBAction)openWebsite:(id)sender; - (IBAction)openWebsite:(id)sender;
- (IBAction)toggleDirectories:(id)sender; - (IBAction)toggleDirectories:(id)sender;
- (DirectoryPanel *)directoryPanel;
- (PyDupeGuru *)py; - (PyDupeGuru *)py;
@end @end

View File

@@ -12,6 +12,7 @@ http://www.hardcoded.net/licenses/hs_license
#import "../../cocoalib/Utils.h" #import "../../cocoalib/Utils.h"
#import "../../cocoalib/ValueTransformers.h" #import "../../cocoalib/ValueTransformers.h"
#import "DetailsPanel.h" #import "DetailsPanel.h"
#import "DirectoryPanel.h"
#import "Consts.h" #import "Consts.h"
@implementation AppDelegate @implementation AppDelegate
@@ -56,52 +57,5 @@ http://www.hardcoded.net/licenses/hs_license
[[self directoryPanel] toggleVisible:sender]; [[self directoryPanel] toggleVisible:sender];
} }
- (DirectoryPanel *)directoryPanel
{
if (!_directoryPanel)
_directoryPanel = [[DirectoryPanel alloc] initWithParentApp:self];
return _directoryPanel;
}
- (DetailsPanelBase *)detailsPanel
{
if (!_detailsPanel)
_detailsPanel = [[DetailsPanel alloc] initWithPy:py];
return _detailsPanel;
}
- (PyDupeGuru *)py { return (PyDupeGuru *)py; } - (PyDupeGuru *)py { return (PyDupeGuru *)py; }
//Delegate
- (void)applicationWillBecomeActive:(NSNotification *)aNotification
{
if (![[result window] isVisible])
[result showWindow:NSApp];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
[ud setObject: [result getColumnsOrder] forKey:@"columnsOrder"];
[ud setObject: [result getColumnsWidth] forKey:@"columnsWidth"];
[py saveResults];
int sc = [ud integerForKey:@"sessionCountSinceLastIgnorePurge"];
if (sc >= 10)
{
sc = -1;
[py purgeIgnoreList];
}
sc++;
[ud setInteger:sc forKey:@"sessionCountSinceLastIgnorePurge"];
[py saveIgnoreList];
// NSApplication does not release nib instances objects, we must do it manually
// Well, it isn't needed because the memory is freed anyway (we are quitting the application
// But I need to release RecentDirectories so it saves the user defaults
[recentDirectories release];
}
- (void)recentDirecoryClicked:(NSString *)directory
{
[[self directoryPanel] addDirectory:directory];
}
@end @end

View File

@@ -1,12 +0,0 @@
/*
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
This software is licensed under the "HS" License as described in the "LICENSE" file,
which should be included with this package. The terms are also available at
http://www.hardcoded.net/licenses/hs_license
*/
#import "DirectoryPanel.h"
@implementation DirectoryPanel
@end

View File

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

View File

@@ -7,32 +7,13 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "../../cocoalib/Outline.h"
#import "../base/ResultWindow.h" #import "../base/ResultWindow.h"
#import "DirectoryPanel.h" #import "DirectoryPanel.h"
@interface ResultWindow : ResultWindowBase @interface ResultWindow : ResultWindowBase
{ {
IBOutlet NSSearchField *filterField;
NSString *_lastAction; NSString *_lastAction;
NSMutableIndexSet *_deltaColumns;
} }
- (IBAction)clearIgnoreList:(id)sender;
- (IBAction)filter:(id)sender;
- (IBAction)ignoreSelected:(id)sender;
- (IBAction)markAll:(id)sender;
- (IBAction)markInvert:(id)sender;
- (IBAction)markNone:(id)sender;
- (IBAction)markSelected:(id)sender;
- (IBAction)markToggle:(id)sender;
- (IBAction)openSelected:(id)sender;
- (IBAction)refresh:(id)sender;
- (IBAction)removeMarked:(id)sender;
- (IBAction)removeSelected:(id)sender;
- (IBAction)renameSelected:(id)sender;
- (IBAction)resetColumnsToDefault:(id)sender; - (IBAction)resetColumnsToDefault:(id)sender;
- (IBAction)revealSelected:(id)sender;
- (IBAction)startDuplicateScan:(id)sender; - (IBAction)startDuplicateScan:(id)sender;
- (IBAction)toggleDelta:(id)sender;
@end @end

View File

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

View File

@@ -4,170 +4,30 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license # http://www.hardcoded.net/licenses/hs_license
import objc from hsutil.cocoa import signature
from AppKit import *
from core_se.app_cocoa import DupeGuru
from core import scanner from core import scanner
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
from core_se.app_cocoa import DupeGuru
# Fix py2app imports with chokes on relative imports # Fix py2app imports with chokes on relative imports and other stuff
from core_se import fs, data from core_se import fs, data
from core import app, app_cocoa, data, directories, engine, export, ignore, results, fs from lxml import etree, _elementpath
from hsutil import conflict import gzip
class PyApp(NSObject): class PyDupeGuru(PyDupeGuruBase):
pass #fake class
class PyDupeGuru(PyApp):
def init(self): def init(self):
self = super(PyDupeGuru,self).init() self = super(PyDupeGuru,self).init()
self.app = DupeGuru() self.py = DupeGuru()
return self return self
#---Directories
def addDirectory_(self,directory):
return self.app.add_directory(directory)
def removeDirectory_(self,index):
self.app.RemoveDirectory(index)
def setDirectory_state_(self,node_path,state):
self.app.SetDirectoryState(node_path,state)
#---Results
def clearIgnoreList(self):
self.app.scanner.ignore_list.Clear()
def doScan(self):
return self.app.start_scanning()
def exportToXHTMLwithColumns_(self, column_ids):
return self.app.export_to_xhtml(column_ids)
def loadIgnoreList(self):
self.app.load_ignore_list()
def loadResults(self):
self.app.load()
def markAll(self):
self.app.results.mark_all()
def markNone(self):
self.app.results.mark_none()
def markInvert(self):
self.app.results.mark_invert()
def purgeIgnoreList(self):
self.app.PurgeIgnoreList()
def toggleSelectedMark(self):
self.app.ToggleSelectedMarkState()
def saveIgnoreList(self):
self.app.save_ignore_list()
def saveResults(self):
self.app.save()
def refreshDetailsWithSelected(self):
self.app.RefreshDetailsWithSelected()
def selectedResultNodePaths(self):
return self.app.selected_result_node_paths()
def selectResultNodePaths_(self,node_paths):
self.app.SelectResultNodePaths(node_paths)
def selectedPowerMarkerNodePaths(self):
return self.app.selected_powermarker_node_paths()
def selectPowerMarkerNodePaths_(self,node_paths):
self.app.SelectPowerMarkerNodePaths(node_paths)
#---Actions
def addSelectedToIgnoreList(self):
self.app.AddSelectedToIgnoreList()
def deleteMarked(self):
self.app.delete_marked()
def applyFilter_(self, filter):
self.app.apply_filter(filter)
def makeSelectedReference(self):
self.app.MakeSelectedReference()
def copyOrMove_markedTo_recreatePath_(self,copy,destination,recreate_path):
self.app.copy_or_move_marked(copy, destination, recreate_path)
def openSelected(self):
self.app.OpenSelected()
def removeMarked(self):
self.app.results.perform_on_marked(lambda x:True, True)
def removeSelected(self):
self.app.RemoveSelected()
def renameSelected_(self,newname):
return self.app.RenameSelected(newname)
def revealSelected(self):
self.app.RevealSelected()
#---Misc
def sortDupesBy_ascending_(self,key,asc):
self.app.sort_dupes(key,asc)
def sortGroupsBy_ascending_(self,key,asc):
self.app.sort_groups(key,asc)
#---Information
def getIgnoreListCount(self):
return len(self.app.scanner.ignore_list)
def getMarkCount(self):
return self.app.results.mark_count
def getStatLine(self):
return self.app.stat_line
def getOperationalErrorCount(self):
return self.app.last_op_error_count
#---Data
@objc.signature('i@:i')
def getOutlineViewMaxLevel_(self, tag):
return self.app.GetOutlineViewMaxLevel(tag)
@objc.signature('@@:i@')
def getOutlineView_childCountsForPath_(self, tag, node_path):
return self.app.GetOutlineViewChildCounts(tag, node_path)
def getOutlineView_valuesForIndexes_(self,tag,node_path):
return self.app.GetOutlineViewValues(tag,node_path)
def getOutlineView_markedAtIndexes_(self,tag,node_path):
return self.app.GetOutlineViewMarked(tag,node_path)
def getTableViewCount_(self,tag):
return self.app.GetTableViewCount(tag)
def getTableViewMarkedIndexes_(self,tag):
return self.app.GetTableViewMarkedIndexes(tag)
def getTableView_valuesForRow_(self,tag,row):
return self.app.GetTableViewValues(tag,row)
#---Properties #---Properties
def setMinMatchPercentage_(self,percentage): def setMinMatchPercentage_(self,percentage):
self.app.scanner.min_match_percentage = int(percentage) self.py.scanner.min_match_percentage = int(percentage)
def setScanType_(self,scan_type): def setScanType_(self,scan_type):
try: try:
self.app.scanner.scan_type = [ self.py.scanner.scan_type = [
scanner.SCAN_TYPE_FILENAME, scanner.SCAN_TYPE_FILENAME,
scanner.SCAN_TYPE_CONTENT scanner.SCAN_TYPE_CONTENT
][scan_type] ][scan_type]
@@ -175,52 +35,16 @@ class PyDupeGuru(PyApp):
pass pass
def setWordWeighting_(self,words_are_weighted): def setWordWeighting_(self,words_are_weighted):
self.app.scanner.word_weighting = words_are_weighted self.py.scanner.word_weighting = words_are_weighted
def setMixFileKind_(self,mix_file_kind):
self.app.scanner.mix_file_kind = mix_file_kind
def setDisplayDeltaValues_(self,display_delta_values):
self.app.display_delta_values= display_delta_values
def setMatchSimilarWords_(self,match_similar_words): def setMatchSimilarWords_(self,match_similar_words):
self.app.scanner.match_similar_words = match_similar_words self.py.scanner.match_similar_words = match_similar_words
def setEscapeFilterRegexp_(self, escape_filter_regexp): @signature('v@:i')
self.app.options['escape_filter_regexp'] = escape_filter_regexp
def setRemoveEmptyFolders_(self, remove_empty_folders):
self.app.options['clean_empty_dirs'] = remove_empty_folders
@objc.signature('v@:i')
def setSizeThreshold_(self, size_threshold): def setSizeThreshold_(self, size_threshold):
self.app.scanner.size_threshold = size_threshold self.py.scanner.size_threshold = size_threshold
#---Worker
def getJobProgress(self):
return self.app.progress.last_progress
def getJobDesc(self):
return self.app.progress.last_desc
def cancelJob(self):
self.app.progress.job_cancelled = True
#---Registration #---Registration
def appName(self): def appName(self):
return "dupeGuru" return "dupeGuru"
def demoLimitDescription(self):
return self.app.DEMO_LIMIT_DESC
@objc.signature('i@:')
def isRegistered(self):
return self.app.registered
@objc.signature('i@:@@')
def isCodeValid_withEmail_(self, code, email):
return self.app.is_code_valid(code, email)
def setRegisteredCode_andEmail_(self, code, email):
self.app.set_registration(code, email)

View File

@@ -10,17 +10,30 @@
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
CE073F6309CAE1A3005C1D2F /* dupeguru_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_help */; }; CE073F6309CAE1A3005C1D2F /* dupeguru_help in Resources */ = {isa = PBXBuildFile; fileRef = CE073F5409CAE1A3005C1D2F /* dupeguru_help */; };
CE19BC6311199231007CCEB0 /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */; };
CE19BC6411199231007CCEB0 /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6111199231007CCEB0 /* progress.xib */; };
CE19BC6511199231007CCEB0 /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE19BC6211199231007CCEB0 /* registration.xib */; };
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; }; CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9A09914ADF003581CE /* ResultWindow.m */; }; CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9A09914ADF003581CE /* ResultWindow.m */; };
CE381D0509915304003581CE /* dg_cocoa.plugin in Resources */ = {isa = PBXBuildFile; fileRef = CE381CF509915304003581CE /* dg_cocoa.plugin */; }; CE381D0509915304003581CE /* dg_cocoa.plugin in Resources */ = {isa = PBXBuildFile; fileRef = CE381CF509915304003581CE /* dg_cocoa.plugin */; };
CE3A46FA109B212E002ABFD5 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3A46F9109B212E002ABFD5 /* MainMenu.xib */; }; CE3A46FA109B212E002ABFD5 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3A46F9109B212E002ABFD5 /* MainMenu.xib */; };
CE45579B0AE3BC2B005A9546 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; }; CE45579B0AE3BC2B005A9546 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; };
CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; }; CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE45579A0AE3BC2B005A9546 /* Sparkle.framework */; };
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE68EE6609ABC48000971085 /* DirectoryPanel.m */; }; CE647E571173024A006D28BA /* ProblemDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = CE647E551173024A006D28BA /* ProblemDialog.m */; };
CE647E591173026F006D28BA /* ProblemDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE647E581173026F006D28BA /* ProblemDialog.xib */; };
CE6E0DFE1054E9EF008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */; }; CE6E0DFE1054E9EF008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */; };
CE76FDC4111EE37C006618EA /* HSOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDBF111EE37C006618EA /* HSOutlineView.m */; };
CE76FDC5111EE37C006618EA /* NSIndexPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */; };
CE76FDC6111EE37C006618EA /* NSTableViewAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC3111EE37C006618EA /* NSTableViewAdditions.m */; };
CE76FDCF111EE38E006618EA /* HSGUIController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDC9111EE38E006618EA /* HSGUIController.m */; };
CE76FDD4111EE3A7006618EA /* DirectoryOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDD2111EE3A7006618EA /* DirectoryOutline.m */; };
CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDDE111EE42F006618EA /* HSOutline.m */; };
CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = CE76FDF6111EE561006618EA /* NSEventAdditions.m */; };
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 */; }; CEAC6811109B0B7E00B43C85 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEAC6810109B0B7E00B43C85 /* Preferences.xib */; };
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CECA899A09DB132E00A3D774 /* DetailsPanel.h */; }; CEBE4D74111F0EE1009AAC6D /* HSWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CEBE4D73111F0EE1009AAC6D /* HSWindowController.m */; };
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CECA899B09DB132E00A3D774 /* DetailsPanel.m */; };
CEDD92DA0FDD01640031C7B7 /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDD92D70FDD01640031C7B7 /* BRSingleLineFormatter.m */; }; CEDD92DA0FDD01640031C7B7 /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDD92D70FDD01640031C7B7 /* BRSingleLineFormatter.m */; };
CEE7EA130FE675C80004E467 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEE7EA120FE675C80004E467 /* DetailsPanel.m */; }; CEE7EA130FE675C80004E467 /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEE7EA120FE675C80004E467 /* DetailsPanel.m */; };
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; }; CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
@@ -31,16 +44,11 @@
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295409C89FF200D9F998 /* preferences32.png */; }; CEFC295609C89FF200D9F998 /* preferences32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295409C89FF200D9F998 /* preferences32.png */; };
CEFC7F9E0FC9517500CD5728 /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F8B0FC9517500CD5728 /* Dialogs.m */; }; CEFC7F9E0FC9517500CD5728 /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F8B0FC9517500CD5728 /* Dialogs.m */; };
CEFC7F9F0FC9517500CD5728 /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F8D0FC9517500CD5728 /* HSErrorReportWindow.m */; }; CEFC7F9F0FC9517500CD5728 /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F8D0FC9517500CD5728 /* HSErrorReportWindow.m */; };
CEFC7FA00FC9517500CD5728 /* Outline.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F8F0FC9517500CD5728 /* Outline.m */; };
CEFC7FA10FC9517500CD5728 /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F910FC9517500CD5728 /* ProgressController.m */; }; CEFC7FA10FC9517500CD5728 /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F910FC9517500CD5728 /* ProgressController.m */; };
CEFC7FA20FC9517500CD5728 /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F950FC9517500CD5728 /* RecentDirectories.m */; }; CEFC7FA20FC9517500CD5728 /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F950FC9517500CD5728 /* RecentDirectories.m */; };
CEFC7FA30FC9517500CD5728 /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F970FC9517500CD5728 /* RegistrationInterface.m */; }; CEFC7FA30FC9517500CD5728 /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F970FC9517500CD5728 /* RegistrationInterface.m */; };
CEFC7FA40FC9517500CD5728 /* Table.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F990FC9517500CD5728 /* Table.m */; };
CEFC7FA50FC9517500CD5728 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F9B0FC9517500CD5728 /* Utils.m */; }; CEFC7FA50FC9517500CD5728 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F9B0FC9517500CD5728 /* Utils.m */; };
CEFC7FA60FC9517500CD5728 /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F9D0FC9517500CD5728 /* ValueTransformers.m */; }; CEFC7FA60FC9517500CD5728 /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7F9D0FC9517500CD5728 /* ValueTransformers.m */; };
CEFC7FAD0FC9518A00CD5728 /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CEFC7FA70FC9518A00CD5728 /* ErrorReportWindow.xib */; };
CEFC7FAE0FC9518A00CD5728 /* progress.nib in Resources */ = {isa = PBXBuildFile; fileRef = CEFC7FA90FC9518A00CD5728 /* progress.nib */; };
CEFC7FAF0FC9518A00CD5728 /* registration.nib in Resources */ = {isa = PBXBuildFile; fileRef = CEFC7FAB0FC9518A00CD5728 /* registration.nib */; };
CEFC7FB90FC951A700CD5728 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7FB20FC951A700CD5728 /* AppDelegate.m */; }; CEFC7FB90FC951A700CD5728 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7FB20FC951A700CD5728 /* AppDelegate.m */; };
CEFC7FBA0FC951A700CD5728 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7FB50FC951A700CD5728 /* DirectoryPanel.m */; }; CEFC7FBA0FC951A700CD5728 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7FB50FC951A700CD5728 /* DirectoryPanel.m */; };
CEFC7FBB0FC951A700CD5728 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7FB80FC951A700CD5728 /* ResultWindow.m */; }; CEFC7FBB0FC951A700CD5728 /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CEFC7FB80FC951A700CD5728 /* ResultWindow.m */; };
@@ -54,7 +62,6 @@
dstSubfolderSpec = 10; dstSubfolderSpec = 10;
files = ( files = (
CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */, CE4557B40AE3BC50005A9546 /* Sparkle.framework in CopyFiles */,
CECA899C09DB132E00A3D774 /* DetailsPanel.h in CopyFiles */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -69,6 +76,9 @@
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; };
8D1107320486CEB800E47090 /* dupeGuru.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = dupeGuru.app; sourceTree = BUILT_PRODUCTS_DIR; }; 8D1107320486CEB800E47090 /* dupeGuru.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = dupeGuru.app; sourceTree = BUILT_PRODUCTS_DIR; };
CE073F5409CAE1A3005C1D2F /* dupeguru_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_help; path = ../../help_se/dupeguru_help; sourceTree = "<group>"; }; CE073F5409CAE1A3005C1D2F /* dupeguru_help */ = {isa = PBXFileReference; lastKnownFileType = folder; name = dupeguru_help; path = ../../help_se/dupeguru_help; sourceTree = "<group>"; };
CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ErrorReportWindow.xib; sourceTree = "<group>"; };
CE19BC6111199231007CCEB0 /* progress.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = progress.xib; sourceTree = "<group>"; };
CE19BC6211199231007CCEB0 /* registration.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = registration.xib; sourceTree = "<group>"; };
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; }; CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; }; CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; }; CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; };
@@ -76,12 +86,40 @@
CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; }; CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; };
CE3A46F9109B212E002ABFD5 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../base/xib/MainMenu.xib; sourceTree = "<group>"; }; CE3A46F9109B212E002ABFD5 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../base/xib/MainMenu.xib; sourceTree = "<group>"; };
CE45579A0AE3BC2B005A9546 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; }; CE45579A0AE3BC2B005A9546 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
CE68EE6509ABC48000971085 /* DirectoryPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DirectoryPanel.h; sourceTree = SOURCE_ROOT; }; CE647E541173024A006D28BA /* ProblemDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProblemDialog.h; path = ../base/ProblemDialog.h; sourceTree = SOURCE_ROOT; };
CE68EE6609ABC48000971085 /* DirectoryPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DirectoryPanel.m; sourceTree = SOURCE_ROOT; }; CE647E551173024A006D28BA /* ProblemDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ProblemDialog.m; path = ../base/ProblemDialog.m; sourceTree = SOURCE_ROOT; };
CE647E561173024A006D28BA /* PyProblemDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyProblemDialog.h; path = ../base/PyProblemDialog.h; sourceTree = SOURCE_ROOT; };
CE647E581173026F006D28BA /* ProblemDialog.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ProblemDialog.xib; path = ../base/xib/ProblemDialog.xib; sourceTree = SOURCE_ROOT; };
CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = "<group>"; }; CE6E0DFD1054E9EF008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = "<group>"; };
CE6E7407111C997500C350E3 /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
CE76FDBE111EE37C006618EA /* HSOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutlineView.h; sourceTree = "<group>"; };
CE76FDBF111EE37C006618EA /* HSOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutlineView.m; sourceTree = "<group>"; };
CE76FDC0111EE37C006618EA /* NSIndexPathAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSIndexPathAdditions.h; sourceTree = "<group>"; };
CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSIndexPathAdditions.m; sourceTree = "<group>"; };
CE76FDC2111EE37C006618EA /* NSTableViewAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSTableViewAdditions.h; sourceTree = "<group>"; };
CE76FDC3111EE37C006618EA /* NSTableViewAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSTableViewAdditions.m; sourceTree = "<group>"; };
CE76FDC8111EE38E006618EA /* HSGUIController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSGUIController.h; sourceTree = "<group>"; };
CE76FDC9111EE38E006618EA /* HSGUIController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSGUIController.m; sourceTree = "<group>"; };
CE76FDCD111EE38E006618EA /* PyGUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyGUI.h; sourceTree = "<group>"; };
CE76FDCE111EE38E006618EA /* PyOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyOutline.h; sourceTree = "<group>"; };
CE76FDD1111EE3A7006618EA /* DirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DirectoryOutline.h; path = ../base/DirectoryOutline.h; sourceTree = SOURCE_ROOT; };
CE76FDD2111EE3A7006618EA /* DirectoryOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DirectoryOutline.m; path = ../base/DirectoryOutline.m; sourceTree = SOURCE_ROOT; };
CE76FDD3111EE3A7006618EA /* PyDirectoryOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDirectoryOutline.h; path = ../base/PyDirectoryOutline.h; sourceTree = SOURCE_ROOT; };
CE76FDDD111EE42F006618EA /* HSOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSOutline.h; sourceTree = "<group>"; };
CE76FDDE111EE42F006618EA /* HSOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSOutline.m; sourceTree = "<group>"; };
CE76FDF5111EE561006618EA /* NSEventAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSEventAdditions.h; path = ../../cocoalib/NSEventAdditions.h; sourceTree = SOURCE_ROOT; };
CE76FDF6111EE561006618EA /* NSEventAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NSEventAdditions.m; path = ../../cocoalib/NSEventAdditions.m; sourceTree = SOURCE_ROOT; };
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>"; }; CEAC6810109B0B7E00B43C85 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Preferences.xib; path = xib/Preferences.xib; sourceTree = "<group>"; };
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; }; CEBE4D72111F0EE1009AAC6D /* HSWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HSWindowController.h; sourceTree = "<group>"; };
CECA899B09DB132E00A3D774 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DetailsPanel.m; sourceTree = "<group>"; }; CEBE4D73111F0EE1009AAC6D /* HSWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HSWindowController.m; sourceTree = "<group>"; };
CEDD92D60FDD01640031C7B7 /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; }; CEDD92D60FDD01640031C7B7 /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; };
CEDD92D70FDD01640031C7B7 /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; }; CEDD92D70FDD01640031C7B7 /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; };
CEE7EA110FE675C80004E467 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DetailsPanel.h; path = ../base/DetailsPanel.h; sourceTree = SOURCE_ROOT; }; CEE7EA110FE675C80004E467 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DetailsPanel.h; path = ../base/DetailsPanel.h; sourceTree = SOURCE_ROOT; };
@@ -96,8 +134,6 @@
CEFC7F8B0FC9517500CD5728 /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; }; CEFC7F8B0FC9517500CD5728 /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; };
CEFC7F8C0FC9517500CD5728 /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; }; CEFC7F8C0FC9517500CD5728 /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
CEFC7F8D0FC9517500CD5728 /* HSErrorReportWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSErrorReportWindow.m; path = ../../cocoalib/HSErrorReportWindow.m; sourceTree = SOURCE_ROOT; }; CEFC7F8D0FC9517500CD5728 /* HSErrorReportWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSErrorReportWindow.m; path = ../../cocoalib/HSErrorReportWindow.m; sourceTree = SOURCE_ROOT; };
CEFC7F8E0FC9517500CD5728 /* Outline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Outline.h; path = ../../cocoalib/Outline.h; sourceTree = SOURCE_ROOT; };
CEFC7F8F0FC9517500CD5728 /* Outline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Outline.m; path = ../../cocoalib/Outline.m; sourceTree = SOURCE_ROOT; };
CEFC7F900FC9517500CD5728 /* ProgressController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProgressController.h; path = ../../cocoalib/ProgressController.h; sourceTree = SOURCE_ROOT; }; 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; }; 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; }; CEFC7F920FC9517500CD5728 /* PyApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyApp.h; path = ../../cocoalib/PyApp.h; sourceTree = SOURCE_ROOT; };
@@ -106,15 +142,10 @@
CEFC7F950FC9517500CD5728 /* RecentDirectories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RecentDirectories.m; path = ../../cocoalib/RecentDirectories.m; 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; }; 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; }; CEFC7F970FC9517500CD5728 /* RegistrationInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegistrationInterface.m; path = ../../cocoalib/RegistrationInterface.m; sourceTree = SOURCE_ROOT; };
CEFC7F980FC9517500CD5728 /* Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Table.h; path = ../../cocoalib/Table.h; sourceTree = SOURCE_ROOT; };
CEFC7F990FC9517500CD5728 /* Table.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Table.m; path = ../../cocoalib/Table.m; sourceTree = SOURCE_ROOT; };
CEFC7F9A0FC9517500CD5728 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = ../../cocoalib/Utils.h; sourceTree = SOURCE_ROOT; }; 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; }; 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; }; CEFC7F9C0FC9517500CD5728 /* ValueTransformers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueTransformers.h; path = ../../cocoalib/ValueTransformers.h; sourceTree = SOURCE_ROOT; };
CEFC7F9D0FC9517500CD5728 /* ValueTransformers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ValueTransformers.m; path = ../../cocoalib/ValueTransformers.m; sourceTree = SOURCE_ROOT; }; CEFC7F9D0FC9517500CD5728 /* ValueTransformers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ValueTransformers.m; path = ../../cocoalib/ValueTransformers.m; sourceTree = SOURCE_ROOT; };
CEFC7FA80FC9518A00CD5728 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = ../../cocoalib/English.lproj/ErrorReportWindow.xib; sourceTree = "<group>"; };
CEFC7FAA0FC9518A00CD5728 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/progress.nib; sourceTree = "<group>"; };
CEFC7FAC0FC9518A00CD5728 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = ../../cocoalib/English.lproj/registration.nib; sourceTree = "<group>"; };
CEFC7FB10FC951A700CD5728 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ../base/AppDelegate.h; sourceTree = SOURCE_ROOT; }; CEFC7FB10FC951A700CD5728 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ../base/AppDelegate.h; sourceTree = SOURCE_ROOT; };
CEFC7FB20FC951A700CD5728 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ../base/AppDelegate.m; sourceTree = SOURCE_ROOT; }; CEFC7FB20FC951A700CD5728 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = ../base/AppDelegate.m; sourceTree = SOURCE_ROOT; };
CEFC7FB30FC951A700CD5728 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = ../base/Consts.h; sourceTree = SOURCE_ROOT; }; CEFC7FB30FC951A700CD5728 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = ../base/Consts.h; sourceTree = SOURCE_ROOT; };
@@ -144,10 +175,6 @@
children = ( children = (
CE381C9509914ACE003581CE /* AppDelegate.h */, CE381C9509914ACE003581CE /* AppDelegate.h */,
CE381C9409914ACE003581CE /* AppDelegate.m */, CE381C9409914ACE003581CE /* AppDelegate.m */,
CECA899A09DB132E00A3D774 /* DetailsPanel.h */,
CECA899B09DB132E00A3D774 /* DetailsPanel.m */,
CE68EE6509ABC48000971085 /* DirectoryPanel.h */,
CE68EE6609ABC48000971085 /* DirectoryPanel.m */,
CEFF18A009A4D387005E6321 /* PyDupeGuru.h */, CEFF18A009A4D387005E6321 /* PyDupeGuru.h */,
CE381C9B09914ADF003581CE /* ResultWindow.h */, CE381C9B09914ADF003581CE /* ResultWindow.h */,
CE381C9A09914ADF003581CE /* ResultWindow.m */, CE381C9A09914ADF003581CE /* ResultWindow.m */,
@@ -219,6 +246,57 @@
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
CE19BC5F11199231007CCEB0 /* xib */ = {
isa = PBXGroup;
children = (
CE19BC6011199231007CCEB0 /* ErrorReportWindow.xib */,
CE19BC6111199231007CCEB0 /* progress.xib */,
CE19BC6211199231007CCEB0 /* registration.xib */,
);
name = xib;
path = ../../cocoalib/xib;
sourceTree = SOURCE_ROOT;
};
CE76FDBD111EE37C006618EA /* views */ = {
isa = PBXGroup;
children = (
CE76FDBE111EE37C006618EA /* HSOutlineView.h */,
CE76FDBF111EE37C006618EA /* HSOutlineView.m */,
CE76FDC0111EE37C006618EA /* NSIndexPathAdditions.h */,
CE76FDC1111EE37C006618EA /* NSIndexPathAdditions.m */,
CE76FDC2111EE37C006618EA /* NSTableViewAdditions.h */,
CE76FDC3111EE37C006618EA /* NSTableViewAdditions.m */,
);
name = views;
path = ../../cocoalib/views;
sourceTree = SOURCE_ROOT;
};
CE76FDC7111EE38E006618EA /* controllers */ = {
isa = PBXGroup;
children = (
CEBE4D72111F0EE1009AAC6D /* HSWindowController.h */,
CEBE4D73111F0EE1009AAC6D /* HSWindowController.m */,
CE76FDDD111EE42F006618EA /* HSOutline.h */,
CE76FDDE111EE42F006618EA /* HSOutline.m */,
CE76FDC8111EE38E006618EA /* HSGUIController.h */,
CE76FDC9111EE38E006618EA /* HSGUIController.m */,
CE8C53BB117324CE0011B41F /* HSTable.m */,
);
name = controllers;
path = ../../cocoalib/controllers;
sourceTree = SOURCE_ROOT;
};
CE76FDCC111EE38E006618EA /* proxies */ = {
isa = PBXGroup;
children = (
CE76FDCD111EE38E006618EA /* PyGUI.h */,
CE76FDCE111EE38E006618EA /* PyOutline.h */,
CE8C53B61173248F0011B41F /* PyTable.h */,
);
name = proxies;
path = ../../cocoalib/proxies;
sourceTree = SOURCE_ROOT;
};
CEDD92D50FDD01640031C7B7 /* brsinglelineformatter */ = { CEDD92D50FDD01640031C7B7 /* brsinglelineformatter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -232,6 +310,7 @@
CEEFC0CA10943849001F3A39 /* xib */ = { CEEFC0CA10943849001F3A39 /* xib */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CE647E581173026F006D28BA /* ProblemDialog.xib */,
CE3A46F9109B212E002ABFD5 /* MainMenu.xib */, CE3A46F9109B212E002ABFD5 /* MainMenu.xib */,
CEAC6810109B0B7E00B43C85 /* Preferences.xib */, CEAC6810109B0B7E00B43C85 /* Preferences.xib */,
CEEFC0F710945D9F001F3A39 /* DirectoryPanel.xib */, CEEFC0F710945D9F001F3A39 /* DirectoryPanel.xib */,
@@ -253,16 +332,17 @@
CEFC7F890FC9513600CD5728 /* cocoalib */ = { CEFC7F890FC9513600CD5728 /* cocoalib */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CE76FDF5111EE561006618EA /* NSEventAdditions.h */,
CE76FDF6111EE561006618EA /* NSEventAdditions.m */,
CE76FDC7111EE38E006618EA /* controllers */,
CE76FDCC111EE38E006618EA /* proxies */,
CE76FDBD111EE37C006618EA /* views */,
CE19BC5F11199231007CCEB0 /* xib */,
CEDD92D50FDD01640031C7B7 /* brsinglelineformatter */, CEDD92D50FDD01640031C7B7 /* brsinglelineformatter */,
CEFC7FA70FC9518A00CD5728 /* ErrorReportWindow.xib */,
CEFC7FA90FC9518A00CD5728 /* progress.nib */,
CEFC7FAB0FC9518A00CD5728 /* registration.nib */,
CEFC7F8A0FC9517500CD5728 /* Dialogs.h */, CEFC7F8A0FC9517500CD5728 /* Dialogs.h */,
CEFC7F8B0FC9517500CD5728 /* Dialogs.m */, CEFC7F8B0FC9517500CD5728 /* Dialogs.m */,
CEFC7F8C0FC9517500CD5728 /* HSErrorReportWindow.h */, CEFC7F8C0FC9517500CD5728 /* HSErrorReportWindow.h */,
CEFC7F8D0FC9517500CD5728 /* HSErrorReportWindow.m */, CEFC7F8D0FC9517500CD5728 /* HSErrorReportWindow.m */,
CEFC7F8E0FC9517500CD5728 /* Outline.h */,
CEFC7F8F0FC9517500CD5728 /* Outline.m */,
CEFC7F900FC9517500CD5728 /* ProgressController.h */, CEFC7F900FC9517500CD5728 /* ProgressController.h */,
CEFC7F910FC9517500CD5728 /* ProgressController.m */, CEFC7F910FC9517500CD5728 /* ProgressController.m */,
CEFC7F920FC9517500CD5728 /* PyApp.h */, CEFC7F920FC9517500CD5728 /* PyApp.h */,
@@ -271,8 +351,6 @@
CEFC7F950FC9517500CD5728 /* RecentDirectories.m */, CEFC7F950FC9517500CD5728 /* RecentDirectories.m */,
CEFC7F960FC9517500CD5728 /* RegistrationInterface.h */, CEFC7F960FC9517500CD5728 /* RegistrationInterface.h */,
CEFC7F970FC9517500CD5728 /* RegistrationInterface.m */, CEFC7F970FC9517500CD5728 /* RegistrationInterface.m */,
CEFC7F980FC9517500CD5728 /* Table.h */,
CEFC7F990FC9517500CD5728 /* Table.m */,
CEFC7F9A0FC9517500CD5728 /* Utils.h */, CEFC7F9A0FC9517500CD5728 /* Utils.h */,
CEFC7F9B0FC9517500CD5728 /* Utils.m */, CEFC7F9B0FC9517500CD5728 /* Utils.m */,
CEFC7F9C0FC9517500CD5728 /* ValueTransformers.h */, CEFC7F9C0FC9517500CD5728 /* ValueTransformers.h */,
@@ -284,6 +362,15 @@
CEFC7FB00FC9518F00CD5728 /* dgbase */ = { CEFC7FB00FC9518F00CD5728 /* dgbase */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CE91F20F113BC22D0010360B /* PyResultTree.h */,
CE91F210113BC22D0010360B /* PyStatsLabel.h */,
CE91F211113BC22D0010360B /* ResultOutline.h */,
CE91F212113BC22D0010360B /* ResultOutline.m */,
CE91F213113BC22D0010360B /* StatsLabel.h */,
CE91F214113BC22D0010360B /* StatsLabel.m */,
CE76FDD1111EE3A7006618EA /* DirectoryOutline.h */,
CE76FDD2111EE3A7006618EA /* DirectoryOutline.m */,
CE76FDD3111EE3A7006618EA /* PyDirectoryOutline.h */,
CEFC7FB10FC951A700CD5728 /* AppDelegate.h */, CEFC7FB10FC951A700CD5728 /* AppDelegate.h */,
CEFC7FB20FC951A700CD5728 /* AppDelegate.m */, CEFC7FB20FC951A700CD5728 /* AppDelegate.m */,
CEFC7FB30FC951A700CD5728 /* Consts.h */, CEFC7FB30FC951A700CD5728 /* Consts.h */,
@@ -292,8 +379,12 @@
CEFC7FB40FC951A700CD5728 /* DirectoryPanel.h */, CEFC7FB40FC951A700CD5728 /* DirectoryPanel.h */,
CEFC7FB50FC951A700CD5728 /* DirectoryPanel.m */, CEFC7FB50FC951A700CD5728 /* DirectoryPanel.m */,
CEFC7FB60FC951A700CD5728 /* PyDupeGuru.h */, CEFC7FB60FC951A700CD5728 /* PyDupeGuru.h */,
CE6E7407111C997500C350E3 /* PyDetailsPanel.h */,
CEFC7FB70FC951A700CD5728 /* ResultWindow.h */, CEFC7FB70FC951A700CD5728 /* ResultWindow.h */,
CEFC7FB80FC951A700CD5728 /* ResultWindow.m */, CEFC7FB80FC951A700CD5728 /* ResultWindow.m */,
CE647E541173024A006D28BA /* ProblemDialog.h */,
CE647E551173024A006D28BA /* ProblemDialog.m */,
CE647E561173024A006D28BA /* PyProblemDialog.h */,
); );
name = dgbase; name = dgbase;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -351,14 +442,15 @@
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */, CEFC294609C89E3D00D9F998 /* folder32.png in Resources */,
CEFC295509C89FF200D9F998 /* details32.png in Resources */, CEFC295509C89FF200D9F998 /* details32.png in Resources */,
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */, CEFC295609C89FF200D9F998 /* preferences32.png in Resources */,
CEFC7FAD0FC9518A00CD5728 /* ErrorReportWindow.xib in Resources */,
CEFC7FAE0FC9518A00CD5728 /* progress.nib in Resources */,
CEFC7FAF0FC9518A00CD5728 /* registration.nib in Resources */,
CE6E0DFE1054E9EF008D9390 /* dsa_pub.pem in Resources */, CE6E0DFE1054E9EF008D9390 /* dsa_pub.pem in Resources */,
CEEFC0F810945D9F001F3A39 /* DirectoryPanel.xib in Resources */, CEEFC0F810945D9F001F3A39 /* DirectoryPanel.xib in Resources */,
CEEFC0FB10945E37001F3A39 /* DetailsPanel.xib in Resources */, CEEFC0FB10945E37001F3A39 /* DetailsPanel.xib in Resources */,
CEAC6811109B0B7E00B43C85 /* Preferences.xib in Resources */, CEAC6811109B0B7E00B43C85 /* Preferences.xib in Resources */,
CE3A46FA109B212E002ABFD5 /* MainMenu.xib in Resources */, 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 */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -372,15 +464,11 @@
8D11072D0486CEB800E47090 /* main.m in Sources */, 8D11072D0486CEB800E47090 /* main.m in Sources */,
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */, CE381C9609914ACE003581CE /* AppDelegate.m in Sources */,
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */, CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */,
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */,
CECA899D09DB132E00A3D774 /* DetailsPanel.m in Sources */,
CEFC7F9E0FC9517500CD5728 /* Dialogs.m in Sources */, CEFC7F9E0FC9517500CD5728 /* Dialogs.m in Sources */,
CEFC7F9F0FC9517500CD5728 /* HSErrorReportWindow.m in Sources */, CEFC7F9F0FC9517500CD5728 /* HSErrorReportWindow.m in Sources */,
CEFC7FA00FC9517500CD5728 /* Outline.m in Sources */,
CEFC7FA10FC9517500CD5728 /* ProgressController.m in Sources */, CEFC7FA10FC9517500CD5728 /* ProgressController.m in Sources */,
CEFC7FA20FC9517500CD5728 /* RecentDirectories.m in Sources */, CEFC7FA20FC9517500CD5728 /* RecentDirectories.m in Sources */,
CEFC7FA30FC9517500CD5728 /* RegistrationInterface.m in Sources */, CEFC7FA30FC9517500CD5728 /* RegistrationInterface.m in Sources */,
CEFC7FA40FC9517500CD5728 /* Table.m in Sources */,
CEFC7FA50FC9517500CD5728 /* Utils.m in Sources */, CEFC7FA50FC9517500CD5728 /* Utils.m in Sources */,
CEFC7FA60FC9517500CD5728 /* ValueTransformers.m in Sources */, CEFC7FA60FC9517500CD5728 /* ValueTransformers.m in Sources */,
CEFC7FB90FC951A700CD5728 /* AppDelegate.m in Sources */, CEFC7FB90FC951A700CD5728 /* AppDelegate.m in Sources */,
@@ -388,68 +476,72 @@
CEFC7FBB0FC951A700CD5728 /* ResultWindow.m in Sources */, CEFC7FBB0FC951A700CD5728 /* ResultWindow.m in Sources */,
CEDD92DA0FDD01640031C7B7 /* BRSingleLineFormatter.m in Sources */, CEDD92DA0FDD01640031C7B7 /* BRSingleLineFormatter.m in Sources */,
CEE7EA130FE675C80004E467 /* DetailsPanel.m in Sources */, CEE7EA130FE675C80004E467 /* DetailsPanel.m in Sources */,
CE76FDC4111EE37C006618EA /* HSOutlineView.m in Sources */,
CE76FDC5111EE37C006618EA /* NSIndexPathAdditions.m in Sources */,
CE76FDC6111EE37C006618EA /* NSTableViewAdditions.m in Sources */,
CE76FDCF111EE38E006618EA /* HSGUIController.m in Sources */,
CE76FDD4111EE3A7006618EA /* DirectoryOutline.m in Sources */,
CE76FDDF111EE42F006618EA /* HSOutline.m in Sources */,
CE76FDF7111EE561006618EA /* NSEventAdditions.m in Sources */,
CEBE4D74111F0EE1009AAC6D /* HSWindowController.m in Sources */,
CE91F215113BC22D0010360B /* ResultOutline.m in Sources */,
CE91F216113BC22D0010360B /* StatsLabel.m in Sources */,
CE647E571173024A006D28BA /* ProblemDialog.m in Sources */,
CE8C53BC117324CE0011B41F /* HSTable.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
CEFC7FA70FC9518A00CD5728 /* ErrorReportWindow.xib */ = {
isa = PBXVariantGroup;
children = (
CEFC7FA80FC9518A00CD5728 /* English */,
);
name = ErrorReportWindow.xib;
sourceTree = SOURCE_ROOT;
};
CEFC7FA90FC9518A00CD5728 /* progress.nib */ = {
isa = PBXVariantGroup;
children = (
CEFC7FAA0FC9518A00CD5728 /* English */,
);
name = progress.nib;
sourceTree = SOURCE_ROOT;
};
CEFC7FAB0FC9518A00CD5728 /* registration.nib */ = {
isa = PBXVariantGroup;
children = (
CEFC7FAC0FC9518A00CD5728 /* English */,
);
name = registration.nib;
sourceTree = SOURCE_ROOT;
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
C01FCF4C08A954540054247B /* Release */ = { C01FCF4C08A954540054247B /* release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ARCHS = (
ppc,
i386,
);
FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(SRCROOT)/../../base/cocoa/build/Release\"";
GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_MODEL_TUNING = G5;
INFOPLIST_FILE = Info.plist; INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Applications"; INSTALL_PATH = "$(HOME)/Applications";
PRODUCT_NAME = dupeGuru; PRODUCT_NAME = dupeGuru;
WRAPPER_EXTENSION = app; WRAPPER_EXTENSION = app;
}; };
name = Release; name = release;
}; };
C01FCF5008A954540054247B /* Release */ = { C01FCF5008A954540054247B /* release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; ARCHS = (
ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; i386,
x86_64,
ppc,
);
GCC_C_LANGUAGE_STANDARD = c99; GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.5; MACOSX_DEPLOYMENT_TARGET = 10.5;
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
}; };
name = Release; name = release;
};
CE85E84F111AF63D00187B0D /* dev */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(NATIVE_ARCH_ACTUAL)";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.5;
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
};
name = dev;
};
CE85E850111AF63D00187B0D /* dev */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Applications";
PRODUCT_NAME = dupeGuru;
WRAPPER_EXTENSION = app;
};
name = dev;
}; };
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
@@ -457,18 +549,20 @@
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "dupeguru" */ = { C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "dupeguru" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
C01FCF4C08A954540054247B /* Release */, C01FCF4C08A954540054247B /* release */,
CE85E850111AF63D00187B0D /* dev */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = release;
}; };
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */ = { C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
C01FCF5008A954540054247B /* Release */, C01FCF5008A954540054247B /* release */,
CE85E84F111AF63D00187B0D /* dev */,
); );
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };

View File

@@ -7,10 +7,12 @@ http://www.hardcoded.net/licenses/hs_license
*/ */
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "Utils.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[Utils setPluginName:@"dg_cocoa"];
NSString *pluginPath = [[NSBundle mainBundle] NSString *pluginPath = [[NSBundle mainBundle]
pathForResource:@"dg_cocoa" pathForResource:@"dg_cocoa"
ofType:@"plugin"]; ofType:@"plugin"];

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
#!/usr/bin/env python
# Created By: Virgil Dupras # Created By: Virgil Dupras
# Created On: 2006/11/11 # Created On: 2006/11/11
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net) # Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
@@ -12,11 +11,15 @@ from __future__ import unicode_literals
import os import os
import os.path as op import os.path as op
import logging import logging
import subprocess
import re
from send2trash import send2trash
from hsutil import io, files from hsutil import io, files
from hsutil.path import Path from hsutil.path import Path
from hsutil.reg import RegistrableApplication, RegistrationRequired from hsutil.reg import RegistrableApplication, RegistrationRequired
from hsutil.misc import flatten, first from hsutil.misc import flatten, first
from hsutil.notify import Broadcaster
from hsutil.str import escape from hsutil.str import escape
from . import directories, results, scanner, export, fs from . import directories, results, scanner, export, fs
@@ -33,11 +36,12 @@ class NoScannableFileError(Exception):
class AllFilesAreRefError(Exception): class AllFilesAreRefError(Exception):
pass pass
class DupeGuru(RegistrableApplication): class DupeGuru(RegistrableApplication, Broadcaster):
DEMO_LIMIT_DESC = "In the demo version, only 10 duplicates per session can be sent to the recycle bin, moved or copied." 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): def __init__(self, data_module, appdata, appid):
RegistrableApplication.__init__(self, appid) RegistrableApplication.__init__(self, appid)
Broadcaster.__init__(self)
self.appdata = appdata self.appdata = appdata
if not op.exists(self.appdata): if not op.exists(self.appdata):
os.makedirs(self.appdata) os.makedirs(self.appdata)
@@ -46,11 +50,11 @@ class DupeGuru(RegistrableApplication):
self.results = results.Results(data_module) self.results = results.Results(data_module)
self.scanner = scanner.Scanner() self.scanner = scanner.Scanner()
self.action_count = 0 self.action_count = 0
self.last_op_error_count = 0
self.options = { self.options = {
'escape_filter_regexp': True, 'escape_filter_regexp': True,
'clean_empty_dirs': False, 'clean_empty_dirs': False,
} }
self.selected_dupes = []
def _demo_check(self): def _demo_check(self):
if self.registered: if self.registered:
@@ -67,25 +71,28 @@ class DupeGuru(RegistrableApplication):
return self._do_delete_dupe(dupe) return self._do_delete_dupe(dupe)
j.start_job(self.results.mark_count) j.start_job(self.results.mark_count)
self.last_op_error_count = self.results.perform_on_marked(op, True) self.results.perform_on_marked(op, True)
def _do_delete_dupe(self, dupe): def _do_delete_dupe(self, dupe):
if not io.exists(dupe.path): if not io.exists(dupe.path):
return True return
self._recycle_dupe(dupe) send2trash(unicode(dupe.path)) # Raises OSError when there's a problem
self.clean_empty_dirs(dupe.path[:-1]) self.clean_empty_dirs(dupe.path[:-1])
if not io.exists(dupe.path):
return True
logging.warning("Could not send {0} to trash.".format(unicode(dupe.path)))
return False
def _do_load(self, j): def _do_load(self, j):
self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml')) self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml'))
self.notify('directories_changed')
j = j.start_subjob([1, 9]) j = j.start_subjob([1, 9])
self.results.load_from_xml(op.join(self.appdata, 'last_results.xml'), self._get_file, j) self.results.load_from_xml(op.join(self.appdata, 'last_results.xml'), self._get_file, j)
files = flatten(g[:] for g in self.results.groups) files = flatten(g[:] for g in self.results.groups)
try:
for file in j.iter_with_progress(files, 'Reading metadata %d/%d'): for file in j.iter_with_progress(files, 'Reading metadata %d/%d'):
file._read_all_info(attrnames=self.data.METADATA_TO_READ) file._read_all_info(attrnames=self.data.METADATA_TO_READ)
except OSError:
# If this error is raised, it means that a file was deleted while we were reading
# metadata. Proper handling of this rare occurrence is complex because there's no easy
# way to remove an arbitrary file from the Results. Thus, we simply clear them.
self.results.groups = []
def _get_display_info(self, dupe, group, delta=False): def _get_display_info(self, dupe, group, delta=False):
if (dupe is None) or (group is None): if (dupe is None) or (group is None):
@@ -100,10 +107,28 @@ class DupeGuru(RegistrableApplication):
path = Path(str_path) path = Path(str_path)
return fs.get_file(path, self.directories.fileclasses) return fs.get_file(path, self.directories.fileclasses)
def _job_completed(self, jobid):
# Must be called by subclasses when they detect that an async job is completed.
if jobid == JOB_SCAN:
self.notify('results_changed')
elif jobid in (JOB_LOAD, JOB_MOVE, JOB_DELETE):
self.notify('results_changed')
self.notify('problems_changed')
@staticmethod @staticmethod
def _recycle_dupe(dupe): def _open_path(path):
raise NotImplementedError() raise NotImplementedError()
@staticmethod
def _reveal_path(path):
raise NotImplementedError()
def _select_dupes(self, dupes):
if dupes == self.selected_dupes:
return
self.selected_dupes = dupes
self.notify('dupes_selected')
def _start_job(self, jobid, func): def _start_job(self, jobid, func):
# func(j) # func(j)
raise NotImplementedError() raise NotImplementedError()
@@ -111,17 +136,21 @@ class DupeGuru(RegistrableApplication):
def add_directory(self, d): def add_directory(self, d):
try: try:
self.directories.add_path(Path(d)) self.directories.add_path(Path(d))
self.notify('directories_changed')
return 0 return 0
except directories.AlreadyThereError: except directories.AlreadyThereError:
return 1 return 1
except directories.InvalidPathError: except directories.InvalidPathError:
return 2 return 2
def add_to_ignore_list(self, dupe): def add_selected_to_ignore_list(self):
dupes = self.without_ref(self.selected_dupes)
for dupe in dupes:
g = self.results.get_group_of_duplicate(dupe) g = self.results.get_group_of_duplicate(dupe)
for other in g: for other in g:
if other is not dupe: if other is not dupe:
self.scanner.ignore_list.Ignore(unicode(other.path), unicode(dupe.path)) self.scanner.ignore_list.Ignore(unicode(other.path), unicode(dupe.path))
self.remove_duplicates(dupes)
def apply_filter(self, filter): def apply_filter(self, filter):
self.results.apply_filter(None) self.results.apply_filter(None)
@@ -129,6 +158,7 @@ class DupeGuru(RegistrableApplication):
filter = escape(filter, '()[]\\.|+?^') filter = escape(filter, '()[]\\.|+?^')
filter = escape(filter, '*', '.') filter = escape(filter, '*', '.')
self.results.apply_filter(filter) self.results.apply_filter(filter)
self.notify('results_changed')
def clean_empty_dirs(self, path): def clean_empty_dirs(self, path):
if self.options['clean_empty_dirs']: if self.options['clean_empty_dirs']:
@@ -150,28 +180,23 @@ class DupeGuru(RegistrableApplication):
dest_path = dest_path + source_path[1:-1] #Remove drive letter and filename dest_path = dest_path + source_path[1:-1] #Remove drive letter and filename
elif dest_type == 1: elif dest_type == 1:
dest_path = dest_path + source_path[location_path:-1] dest_path = dest_path + source_path[location_path:-1]
try:
if not io.exists(dest_path): if not io.exists(dest_path):
io.makedirs(dest_path) io.makedirs(dest_path)
# Raises an EnvironmentError if there's a problem
if copy: if copy:
files.copy(source_path, dest_path) files.copy(source_path, dest_path)
else: else:
files.move(source_path, dest_path) files.move(source_path, dest_path)
self.clean_empty_dirs(source_path[:-1]) self.clean_empty_dirs(source_path[:-1])
except EnvironmentError as e:
operation = 'Copy' if copy else 'Move'
logging.warning('%s operation failed on %s. Error: %s' % (operation, unicode(dupe.path), unicode(e)))
return False
return True
def copy_or_move_marked(self, copy, destination, recreate_path): def copy_or_move_marked(self, copy, destination, recreate_path):
def do(j): def do(j):
def op(dupe): def op(dupe):
j.add_progress() j.add_progress()
return self.copy_or_move(dupe, copy, destination, recreate_path) self.copy_or_move(dupe, copy, destination, recreate_path)
j.start_job(self.results.mark_count) j.start_job(self.results.mark_count)
self.last_op_error_count = self.results.perform_on_marked(op, not copy) self.results.perform_on_marked(op, not copy)
self._demo_check() self._demo_check()
jobid = JOB_COPY if copy else JOB_MOVE jobid = JOB_COPY if copy else JOB_MOVE
@@ -195,6 +220,31 @@ class DupeGuru(RegistrableApplication):
rows.append(row) rows.append(row)
return export.export_to_xhtml(colnames, rows) return export.export_to_xhtml(colnames, rows)
def invoke_command(self, cmd):
"""Calls command `cmd` with %d and %r placeholders replaced.
Using the current selection, %d is replaced with the currently selected dupe and %r is
replaced with that dupe's ref file. If there's no selection, the command is not invoked.
If the dupe is a ref, %d and %r will be the same.
"""
if not self.selected_dupes:
return
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))
match = re.match(r'"([^"]+)"(.*)', cmd)
if match is not None:
# This code here is because subprocess. Popen doesn't seem to accept, under Windows,
# executable paths with spaces in it, *even* when they're enclosed in "". So this is
# a workaround to make the damn thing work.
exepath, args = match.groups()
path, exename = op.split(exepath)
subprocess.Popen(exename + args, shell=True, cwd=path)
else:
subprocess.Popen(cmd, shell=True)
def load(self): def load(self):
self._start_job(JOB_LOAD, self._do_load) self._start_job(JOB_LOAD, self._do_load)
self.load_ignore_list() self.load_ignore_list()
@@ -203,24 +253,82 @@ class DupeGuru(RegistrableApplication):
p = op.join(self.appdata, 'ignore_list.xml') p = op.join(self.appdata, 'ignore_list.xml')
self.scanner.ignore_list.load_from_xml(p) self.scanner.ignore_list.load_from_xml(p)
def make_reference(self, duplicates): def make_selected_reference(self):
dupes = self.without_ref(self.selected_dupes)
changed_groups = set() changed_groups = set()
for dupe in duplicates: for dupe in dupes:
g = self.results.get_group_of_duplicate(dupe) g = self.results.get_group_of_duplicate(dupe)
if g not in changed_groups: if g not in changed_groups:
self.results.make_ref(dupe) self.results.make_ref(dupe)
changed_groups.add(g) changed_groups.add(g)
self.notify('results_changed_but_keep_selection')
def save(self): def mark_all(self):
self.results.mark_all()
self.notify('marking_changed')
def mark_none(self):
self.results.mark_none()
self.notify('marking_changed')
def mark_invert(self):
self.results.mark_invert()
self.notify('marking_changed')
def mark_dupe(self, dupe, marked):
if marked:
self.results.mark(dupe)
else:
self.results.unmark(dupe)
self.notify('marking_changed')
def open_selected(self):
if self.selected_dupes:
self._open_path(self.selected_dupes[0].path)
def purge_ignore_list(self):
self.scanner.ignore_list.Filter(lambda f,s:op.exists(f) and op.exists(s))
def remove_directory(self,index):
try: try:
self.directories.save_to_file(op.join(self.appdata, 'last_directories.xml')) del self.directories[index]
self.results.save_to_xml(op.join(self.appdata, 'last_results.xml')) self.notify('directories_changed')
except LookupError: except IndexError:
# This is that weird issue from #39 that sometimes happens when auto-updating with
# Sparkle. Just ignore it.
pass pass
def remove_duplicates(self, duplicates):
self.results.remove_duplicates(self.without_ref(duplicates))
self.notify('results_changed_but_keep_selection')
def remove_marked(self):
self.results.perform_on_marked(lambda x:None, True)
self.notify('results_changed')
def remove_selected(self):
self.remove_duplicates(self.selected_dupes)
def rename_selected(self, newname):
try:
d = self.selected_dupes[0]
d.rename(newname)
return True
except (IndexError, fs.FSError) as e:
logging.warning("dupeGuru Warning: %s" % unicode(e))
return False
def reveal_selected(self):
if self.selected_dupes:
self._reveal_path(self.selected_dupes[0].path)
def save(self):
if not op.exists(self.appdata):
os.makedirs(self.appdata)
self.directories.save_to_file(op.join(self.appdata, 'last_directories.xml'))
self.results.save_to_xml(op.join(self.appdata, 'last_results.xml'))
def save_ignore_list(self): def save_ignore_list(self):
if not op.exists(self.appdata):
os.makedirs(self.appdata)
p = op.join(self.appdata, 'ignore_list.xml') p = op.join(self.appdata, 'ignore_list.xml')
self.scanner.ignore_list.save_to_xml(p) self.scanner.ignore_list.save_to_xml(p)
@@ -240,6 +348,14 @@ class DupeGuru(RegistrableApplication):
self.results.groups = [] self.results.groups = []
self._start_job(JOB_SCAN, do) self._start_job(JOB_SCAN, do)
def toggle_selected_mark_state(self):
for dupe in self.selected_dupes:
self.results.mark_toggle(dupe)
self.notify('marking_changed')
def without_ref(self, dupes):
return [dupe for dupe in dupes if self.results.get_group_of_duplicate(dupe).ref is not dupe]
#--- Properties #--- Properties
@property @property
def stat_line(self): def stat_line(self):

View File

@@ -6,18 +6,17 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license # http://www.hardcoded.net/licenses/hs_license
import objc
from Foundation import *
from AppKit import *
import logging import logging
import os.path as op import os.path as op
from hsutil import io, cocoa, job from hsutil import cocoa, job
from hsutil.cocoa import install_exception_hook from hsutil.cocoa import install_exception_hook
from hsutil.misc import stripnone from hsutil.cocoa.objcmin import (NSNotificationCenter, NSUserDefaults,
NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSUserDomainMask,
NSWorkspace)
from hsutil.reg import RegistrationRequired from hsutil.reg import RegistrationRequired
from . import app, fs from . import app
JOBID2TITLE = { JOBID2TITLE = {
app.JOB_SCAN: "Scanning for duplicates", app.JOB_SCAN: "Scanning for duplicates",
@@ -46,21 +45,15 @@ class DupeGuru(app.DupeGuru):
appdata = op.join(appsupport, appdata_subdir) appdata = op.join(appsupport, appdata_subdir)
app.DupeGuru.__init__(self, data_module, appdata, appid) app.DupeGuru.__init__(self, data_module, appdata, appid)
self.progress = cocoa.ThreadedJobPerformer() self.progress = cocoa.ThreadedJobPerformer()
self.display_delta_values = False
self.selected_dupes = []
self.RefreshDetailsTable(None,None)
#--- Override #--- Override
@staticmethod @staticmethod
def _recycle_dupe(dupe): def _open_path(path):
directory = unicode(dupe.path[:-1]) NSWorkspace.sharedWorkspace().openFile_(unicode(path))
filename = dupe.name
if objc.__version__ == '1.4': # For a while, we have to support this. @staticmethod
result, tag = NSWorkspace.sharedWorkspace().performFileOperation_source_destination_files_tag_( def _reveal_path(path):
NSWorkspaceRecycleOperation, directory, '', [filename]) NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(unicode(path), '')
else:
result, tag = NSWorkspace.sharedWorkspace().performFileOperation_source_destination_files_tag_(
NSWorkspaceRecycleOperation, directory, '', [filename], None)
def _start_job(self, jobid, func): def _start_job(self, jobid, func):
try: try:
@@ -72,89 +65,12 @@ class DupeGuru(app.DupeGuru):
ud = {'desc': JOBID2TITLE[jobid], 'jobid':jobid} ud = {'desc': JOBID2TITLE[jobid], 'jobid':jobid}
NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_('JobStarted', self, ud) NSNotificationCenter.defaultCenter().postNotificationName_object_userInfo_('JobStarted', self, ud)
#---Helpers
def GetObjects(self,node_path):
#returns a tuple g,d
try:
g = self.results.groups[node_path[0]]
if len(node_path) == 2:
return (g,self.results.groups[node_path[0]].dupes[node_path[1]])
else:
return (g,None)
except IndexError:
return (None,None)
def get_folder_path(self, node_path, curr_path=None):
if not node_path:
return curr_path
current_index = node_path[0]
if curr_path is None:
curr_path = self.directories[current_index]
else:
curr_path = self.directories.get_subfolders(curr_path)[current_index]
return self.get_folder_path(node_path[1:], curr_path)
def RefreshDetailsTable(self,dupe,group):
l1 = self._get_display_info(dupe, group, False)
# we don't want the two sides of the table to display the stats for the same file
ref = group.ref if group is not None and group.ref is not dupe else None
l2 = self._get_display_info(ref, group, False)
names = [c['display'] for c in self.data.COLUMNS]
self.details_table = zip(names,l1,l2)
#---Public #---Public
def AddSelectedToIgnoreList(self):
for dupe in self.selected_dupes:
self.add_to_ignore_list(dupe)
copy_or_move_marked = demo_method(app.DupeGuru.copy_or_move_marked) copy_or_move_marked = demo_method(app.DupeGuru.copy_or_move_marked)
delete_marked = demo_method(app.DupeGuru.delete_marked) delete_marked = demo_method(app.DupeGuru.delete_marked)
def MakeSelectedReference(self):
self.make_reference(self.selected_dupes)
def OpenSelected(self):
if self.selected_dupes:
path = unicode(self.selected_dupes[0].path)
NSWorkspace.sharedWorkspace().openFile_(path)
def PurgeIgnoreList(self):
self.scanner.ignore_list.Filter(lambda f,s:op.exists(f) and op.exists(s))
def RefreshDetailsWithSelected(self):
if self.selected_dupes:
self.RefreshDetailsTable(
self.selected_dupes[0],
self.results.get_group_of_duplicate(self.selected_dupes[0])
)
else:
self.RefreshDetailsTable(None,None)
def RemoveDirectory(self,index):
try:
del self.directories[index]
except IndexError:
pass
def RemoveSelected(self):
self.results.remove_duplicates(self.selected_dupes)
def RenameSelected(self, newname):
try:
d = self.selected_dupes[0]
d.rename(newname)
return True
except (IndexError, fs.FSError) as e:
logging.warning("dupeGuru Warning: %s" % unicode(e))
return False
def RevealSelected(self):
if self.selected_dupes:
path = unicode(self.selected_dupes[0].path)
NSWorkspace.sharedWorkspace().selectFile_inFileViewerRootedAtPath_(path,'')
def start_scanning(self): def start_scanning(self):
self.RefreshDetailsTable(None, None) self._select_dupes([])
try: try:
app.DupeGuru.start_scanning(self) app.DupeGuru.start_scanning(self)
return 0 return 0
@@ -163,144 +79,3 @@ class DupeGuru(app.DupeGuru):
except app.AllFilesAreRefError: except app.AllFilesAreRefError:
return 1 return 1
def selected_result_node_paths(self):
def get_path(dupe):
try:
group = self.results.get_group_of_duplicate(dupe)
groupindex = self.results.groups.index(group)
if dupe is group.ref:
return [groupindex]
dupeindex = group.dupes.index(dupe)
return [groupindex, dupeindex]
except ValueError: # dupe not in there
return None
dupes = self.selected_dupes
return stripnone(get_path(dupe) for dupe in dupes)
def selected_powermarker_node_paths(self):
def get_path(dupe):
try:
dupeindex = self.results.dupes.index(dupe)
return [dupeindex]
except ValueError: # dupe not in there
return None
dupes = self.selected_dupes
return stripnone(get_path(dupe) for dupe in dupes)
def SelectResultNodePaths(self,node_paths):
def extract_dupe(t):
g,d = t
if d is not None:
return d
else:
if g is not None:
return g.ref
selected = [extract_dupe(self.GetObjects(p)) for p in node_paths]
self.selected_dupes = [dupe for dupe in selected if dupe is not None]
def SelectPowerMarkerNodePaths(self,node_paths):
rows = [p[0] for p in node_paths]
self.selected_dupes = [
self.results.dupes[row] for row in rows if row in xrange(len(self.results.dupes))
]
def SetDirectoryState(self, node_path, state):
p = self.get_folder_path(node_path)
self.directories.set_state(p, state)
def sort_dupes(self,key,asc):
self.results.sort_dupes(key,asc,self.display_delta_values)
def sort_groups(self,key,asc):
self.results.sort_groups(key,asc)
def ToggleSelectedMarkState(self):
for dupe in self.selected_dupes:
self.results.mark_toggle(dupe)
#---Data
def GetOutlineViewMaxLevel(self, tag):
if tag == 0:
return 2
elif tag == 1:
return 0
elif tag == 2:
return 1
def GetOutlineViewChildCounts(self, tag, node_path):
if self.progress._job_running:
return []
if tag == 0: #Normal results
assert not node_path # no other value is possible
return [len(g.dupes) for g in self.results.groups]
elif tag == 1: #Directories
try:
if node_path:
path = self.get_folder_path(node_path)
subfolders = self.directories.get_subfolders(path)
else:
subfolders = self.directories
return [len(self.directories.get_subfolders(path)) for path in subfolders]
except IndexError: # node_path out of range
return []
else: #Power Marker
assert not node_path # no other value is possible
return [0 for d in self.results.dupes]
def GetOutlineViewValues(self, tag, node_path):
if self.progress._job_running:
return
if not node_path:
return
if tag in (0,2): #Normal results / Power Marker
if tag == 0:
g, d = self.GetObjects(node_path)
if d is None:
d = g.ref
else:
d = self.results.dupes[node_path[0]]
g = self.results.get_group_of_duplicate(d)
result = self._get_display_info(d, g, self.display_delta_values)
return result
elif tag == 1: #Directories
try:
path = self.get_folder_path(node_path)
name = unicode(path) if len(node_path) == 1 else path[-1]
return [name, self.directories.get_state(path)]
except IndexError: # node_path out of range
return []
def GetOutlineViewMarked(self, tag, node_path):
# 0=unmarked 1=marked 2=unmarkable
if self.progress._job_running:
return
if not node_path:
return 2
if tag == 1: #Directories
return 2
if tag == 0: #Normal results
g, d = self.GetObjects(node_path)
else: #Power Marker
d = self.results.dupes[node_path[0]]
if (d is None) or (not self.results.is_markable(d)):
return 2
elif self.results.is_marked(d):
return 1
else:
return 0
def GetTableViewCount(self, tag):
if self.progress._job_running:
return 0
return len(self.details_table)
def GetTableViewMarkedIndexes(self,tag):
return []
def GetTableViewValues(self,tag,row):
return self.details_table[row]

214
core/app_cocoa_inter.py Normal file
View File

@@ -0,0 +1,214 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2010-02-02
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
# Common interface for all editions' dg_cocoa unit.
from hsutil.cocoa.inter import signature, PyTable, PyOutline, PyGUIObject, PyRegistrable
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.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):
#---Directories
def addDirectory_(self, directory):
return self.py.add_directory(directory)
def removeDirectory_(self, index):
self.py.remove_directory(index)
#---Results
def clearIgnoreList(self):
self.py.scanner.ignore_list.Clear()
def doScan(self):
return self.py.start_scanning()
def exportToXHTMLwithColumns_(self, column_ids):
return self.py.export_to_xhtml(column_ids)
def loadIgnoreList(self):
self.py.load_ignore_list()
def loadResults(self):
self.py.load()
def markAll(self):
self.py.mark_all()
def markNone(self):
self.py.mark_none()
def markInvert(self):
self.py.mark_invert()
def purgeIgnoreList(self):
self.py.purge_ignore_list()
def toggleSelectedMark(self):
self.py.toggle_selected_mark_state()
def saveIgnoreList(self):
self.py.save_ignore_list()
def saveResults(self):
self.py.save()
#---Actions
def addSelectedToIgnoreList(self):
self.py.add_selected_to_ignore_list()
def deleteMarked(self):
self.py.delete_marked()
def applyFilter_(self, filter):
self.py.apply_filter(filter)
def makeSelectedReference(self):
self.py.make_selected_reference()
def copyOrMove_markedTo_recreatePath_(self, copy, destination, recreate_path):
self.py.copy_or_move_marked(copy, destination, recreate_path)
def openSelected(self):
self.py.open_selected()
def removeMarked(self):
self.py.remove_marked()
def renameSelected_(self,newname):
return self.py.rename_selected(newname)
def revealSelected(self):
self.py.reveal_selected()
def invokeCommand_(self, cmd):
self.py.invoke_command(cmd)
#---Information
def getIgnoreListCount(self):
return len(self.py.scanner.ignore_list)
def getMarkCount(self):
return self.py.results.mark_count
@signature('i@:')
def scanWasProblematic(self):
return bool(self.py.results.problems)
#---Properties
def setMixFileKind_(self, mix_file_kind):
self.py.scanner.mix_file_kind = mix_file_kind
def setEscapeFilterRegexp_(self, escape_filter_regexp):
self.py.options['escape_filter_regexp'] = escape_filter_regexp
def setRemoveEmptyFolders_(self, remove_empty_folders):
self.py.options['clean_empty_dirs'] = remove_empty_folders
#---Worker
def getJobProgress(self):
return self.py.progress.last_progress
def getJobDesc(self):
return self.py.progress.last_desc
def cancelJob(self):
self.py.progress.job_cancelled = True
def jobCompleted_(self, jobid):
self.py._job_completed(jobid)
class PyDetailsPanel(PyGUIObject):
py_class = DetailsPanel
@signature('i@:')
def numberOfRows(self):
return self.py.row_count()
@signature('@@:@i')
def valueForColumn_row_(self, column, row):
return self.py.row(row)[int(column)]
class PyDirectoryOutline(PyOutline):
py_class = DirectoryTree
def addDirectory_(self, path):
self.py.add_directory(path)
class PyResultOutline(PyOutline):
py_class = ResultTree
@signature('c@:')
def powerMarkerMode(self):
return self.py.power_marker
@signature('v@:c')
def setPowerMarkerMode_(self, value):
self.py.power_marker = value
@signature('c@:')
def deltaValuesMode(self):
return self.py.delta_values
@signature('v@:c')
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('c@:@')
def renameSelected_(self, newname):
return self.py.rename_selected(newname)
@signature('v@:ic')
def sortBy_ascending_(self, key, asc):
self.py.sort(key, asc)
def markSelected(self):
self.py.app.toggle_selected_mark_state()
def removeSelected(self):
self.py.app.remove_selected()
def rootChildrenCounts(self):
return self.py.root_children_counts()
# python --> cocoa
def invalidate_markings(self):
self.cocoa.invalidateMarkings()
class PyStatsLabel(PyGUIObject):
py_class = StatsLabel
def display(self):
return self.py.display
class PyProblemDialog(PyGUIObject):
py_class = ProblemDialog
def revealSelected(self):
self.py.reveal_selected_dupe()
class PyProblemTable(PyTable):
py_class = ProblemTable

View File

@@ -6,7 +6,7 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license # http://www.hardcoded.net/licenses/hs_license
import xml.dom.minidom from lxml import etree
from hsutil import io from hsutil import io
from hsutil.files import FileOrPath from hsutil.files import FileOrPath
@@ -126,38 +126,38 @@ class Directories(object):
def load_from_file(self, infile): def load_from_file(self, infile):
try: try:
doc = xml.dom.minidom.parse(infile) root = etree.parse(infile).getroot()
except: except:
return return
root_path_nodes = doc.getElementsByTagName('root_directory') for rdn in root.iterchildren('root_directory'):
for rdn in root_path_nodes: attrib = rdn.attrib
if not rdn.getAttributeNode('path'): if 'path' not in attrib:
continue continue
path = rdn.getAttributeNode('path').nodeValue path = attrib['path']
try: try:
self.add_path(Path(path)) self.add_path(Path(path))
except (AlreadyThereError, InvalidPathError): except (AlreadyThereError, InvalidPathError):
pass pass
state_nodes = doc.getElementsByTagName('state') for sn in root.iterchildren('state'):
for sn in state_nodes: attrib = sn.attrib
if not (sn.getAttributeNode('path') and sn.getAttributeNode('value')): if not ('path' in attrib and 'value' in attrib):
continue continue
path = sn.getAttributeNode('path').nodeValue path = attrib['path']
state = sn.getAttributeNode('value').nodeValue state = attrib['value']
self.set_state(Path(path), int(state)) self.set_state(Path(path), int(state))
def save_to_file(self,outfile): def save_to_file(self, outfile):
with FileOrPath(outfile, 'wb') as fp: with FileOrPath(outfile, 'wb') as fp:
doc = xml.dom.minidom.Document() root = etree.Element('directories')
root = doc.appendChild(doc.createElement('directories'))
for root_path in self: for root_path in self:
root_path_node = root.appendChild(doc.createElement('root_directory')) root_path_node = etree.SubElement(root, 'root_directory')
root_path_node.setAttribute('path', unicode(root_path).encode('utf-8')) root_path_node.set('path', unicode(root_path))
for path, state in self.states.iteritems(): for path, state in self.states.iteritems():
state_node = root.appendChild(doc.createElement('state')) state_node = etree.SubElement(root, 'state')
state_node.setAttribute('path', unicode(path).encode('utf-8')) state_node.set('path', unicode(path))
state_node.setAttribute('value', str(state)) state_node.set('value', unicode(state))
doc.writexml(fp, '\t', '\t', '\n', encoding='utf-8') tree = etree.ElementTree(root)
tree.write(fp, encoding='utf-8')
def set_state(self, path, state): def set_state(self, path, state):
if self.get_state(path) == state: if self.get_state(path) == state:

View File

@@ -160,8 +160,16 @@ def get_file(path, fileclasses=[File]):
def get_files(path, fileclasses=[File]): def get_files(path, fileclasses=[File]):
assert all(issubclass(fileclass, File) for fileclass in fileclasses) assert all(issubclass(fileclass, File) for fileclass in fileclasses)
def combine_paths(p1, p2):
try: try:
paths = [path + name for name in io.listdir(path)] return p1 + p2
except Exception:
# This is temporary debug logging for #84.
logging.warning("Failed to combine %r and %r.", p1, p2)
raise
try:
paths = [combine_paths(path, name) for name in io.listdir(path)]
result = [] result = []
for path in paths: for path in paths:
file = get_file(path, fileclasses=fileclasses) file = get_file(path, fileclasses=fileclasses)

0
core/gui/__init__.py Normal file
View File

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

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

48
core/gui/details_panel.py Normal file
View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# Created On: 2010-02-05
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
from .base import GUIObject
class DetailsPanel(GUIObject):
def __init__(self, view, app):
GUIObject.__init__(self, view, app)
self._table = []
def connect(self):
GUIObject.connect(self)
self._refresh()
self.view.refresh()
#--- Private
def _refresh(self):
if self.app.selected_dupes:
dupe = self.app.selected_dupes[0]
group = self.app.results.get_group_of_duplicate(dupe)
else:
dupe = None
group = None
l1 = self.app._get_display_info(dupe, group, False)
# we don't want the two sides of the table to display the stats for the same file
ref = group.ref if group is not None and group.ref is not dupe else None
l2 = self.app._get_display_info(ref, group, False)
names = [c['display'] for c in self.app.data.COLUMNS]
self._table = zip(names, l1, l2)
#--- Public
def row_count(self):
return len(self._table)
def row(self, row_index):
return self._table[row_index]
#--- Event Handlers
def dupes_selected(self):
self._refresh()
self.view.refresh()

View File

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

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# 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,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
from hsutil.notify import Broadcaster
from .base import GUIObject
class ProblemDialog(GUIObject, Broadcaster):
def __init__(self, view, app):
GUIObject.__init__(self, view, app)
Broadcaster.__init__(self)
self._selected_dupe = None
def reveal_selected_dupe(self):
if self._selected_dupe is not None:
self.app._reveal_path(self._selected_dupe.path)
def select_dupe(self, dupe):
self._selected_dupe = dupe
#--- Event Handlers
def problems_changed(self):
self._selected_dupe = None
self.notify('problems_changed')

43
core/gui/problem_table.py Normal file
View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# 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,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
from hsutil.notify import Listener
from hsgui.table import GUITable, Row
class ProblemTable(GUITable, Listener):
def __init__(self, view, problem_dialog):
GUITable.__init__(self)
Listener.__init__(self, problem_dialog)
self.view = view
self.dialog = problem_dialog
#--- Override
def _update_selection(self):
row = self.selected_row
dupe = row.dupe if row is not None else None
self.dialog.select_dupe(dupe)
def _fill(self):
problems = self.dialog.app.results.problems
for dupe, msg in problems:
self.append(ProblemRow(self, dupe, msg))
#--- Event handlers
def problems_changed(self):
self.refresh()
self.view.refresh()
class ProblemRow(Row):
def __init__(self, table, dupe, msg):
Row.__init__(self, table)
self.dupe = dupe
self.msg = msg
self.path = unicode(dupe.path)

159
core/gui/result_tree.py Normal file
View File

@@ -0,0 +1,159 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# 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,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
from operator import attrgetter
from hsgui.tree import Tree, Node
from .base import GUIObject
class DupeNode(Node):
def __init__(self, app, group, dupe):
Node.__init__(self, '')
self._app = app
self._group = group
self._dupe = dupe
self._data = None
self._data_delta = None
@property
def data(self):
if self._data is None:
self._data = self._app._get_display_info(self._dupe, self._group, False)
return self._data
@property
def data_delta(self):
if self._data_delta is None:
self._data_delta = self._app._get_display_info(self._dupe, self._group, True)
return self._data_delta
@property
def markable(self):
return self._app.results.is_markable(self._dupe)
@property
def marked(self):
return self._app.results.is_marked(self._dupe)
@marked.setter
def marked(self, value):
self._app.mark_dupe(self._dupe, value)
class ResultTree(GUIObject, Tree):
def __init__(self, view, app):
GUIObject.__init__(self, view, app)
Tree.__init__(self)
self._power_marker = False
self._delta_values = False
self._sort_descriptors = (0, True)
#--- Override
def connect(self):
GUIObject.connect(self)
self._refresh()
self.view.refresh()
def _select_nodes(self, nodes):
Tree._select_nodes(self, nodes)
self.app._select_dupes(map(attrgetter('_dupe'), nodes))
#--- Private
def _refresh(self):
self.clear()
if not self.power_marker:
for group in self.app.results.groups:
group_node = DupeNode(self.app, group, group.ref)
self.append(group_node)
for dupe in group.dupes:
group_node.append(DupeNode(self.app, 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
#--- Public
def get_node_value(self, path, column):
try:
node = self.get_node(path)
except IndexError:
return '---'
if self.delta_values:
return node.data_delta[column]
else:
return node.data[column]
def rename_selected(self, newname):
node = self.selected_node
node._data = None
node._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()
#--- Properties
@property
def power_marker(self):
return self._power_marker
@power_marker.setter
def power_marker(self, value):
if value == self._power_marker:
return
self._power_marker = value
key, asc = self._sort_descriptors
self.sort(key, asc)
self._refresh()
self.view.refresh()
@property
def delta_values(self):
return self._delta_values
@delta_values.setter
def delta_values(self, value):
if value == self._delta_values:
return
self._delta_values = value
self._refresh()
self.view.refresh()
#--- Event Handlers
def marking_changed(self):
self.view.invalidate_markings()
def results_changed(self):
self._refresh()
self.view.refresh()
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
self.view.refresh()

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

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Created By: Virgil Dupras
# 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,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
from .base import GUIObject
class StatsLabel(GUIObject):
def connect(self):
GUIObject.connect(self)
self.view.refresh()
@property
def display(self):
return self.app.stat_line
def results_changed(self):
self.view.refresh()
marking_changed = results_changed

View File

@@ -6,9 +6,9 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license # http://www.hardcoded.net/licenses/hs_license
from hsutil.files import FileOrPath from lxml import etree
import xml.dom.minidom from hsutil.files import FileOrPath
class IgnoreList(object): class IgnoreList(object):
"""An ignore list implementation that is iterable, filterable and exportable to XML. """An ignore list implementation that is iterable, filterable and exportable to XML.
@@ -71,45 +71,38 @@ class IgnoreList(object):
self._ignored[first] = matches self._ignored[first] = matches
self._count += 1 self._count += 1
def load_from_xml(self,infile): def load_from_xml(self, infile):
"""Loads the ignore list from a XML created with save_to_xml. """Loads the ignore list from a XML created with save_to_xml.
infile can be a file object or a filename. infile can be a file object or a filename.
""" """
try: try:
doc = xml.dom.minidom.parse(infile) root = etree.parse(infile).getroot()
except Exception: except Exception:
return return
file_nodes = doc.getElementsByTagName('file') for fn in root.iterchildren('file'):
for fn in file_nodes: file_path = fn.get('path')
if not fn.getAttributeNode('path'): if not file_path:
continue continue
file_path = fn.getAttributeNode('path').nodeValue for sfn in fn.iterchildren('file'):
subfile_nodes = fn.getElementsByTagName('file') subfile_path = sfn.get('path')
for sfn in subfile_nodes: if subfile_path:
if not sfn.getAttributeNode('path'): self.Ignore(file_path, subfile_path)
continue
subfile_path = sfn.getAttributeNode('path').nodeValue
self.Ignore(file_path,subfile_path)
def save_to_xml(self,outfile): def save_to_xml(self, outfile):
"""Create a XML file that can be used by load_from_xml. """Create a XML file that can be used by load_from_xml.
outfile can be a file object or a filename. outfile can be a file object or a filename.
""" """
doc = xml.dom.minidom.Document() root = etree.Element('ignore_list')
root = doc.appendChild(doc.createElement('ignore_list')) for filename, subfiles in self._ignored.items():
for file,subfiles in self._ignored.items(): file_node = etree.SubElement(root, 'file')
file_node = root.appendChild(doc.createElement('file')) file_node.set('path', filename)
if isinstance(file,unicode): for subfilename in subfiles:
file = file.encode('utf-8') subfile_node = etree.SubElement(file_node, 'file')
file_node.setAttribute('path',file) subfile_node.set('path', subfilename)
for subfile in subfiles: tree = etree.ElementTree(root)
subfile_node = file_node.appendChild(doc.createElement('file'))
if isinstance(subfile,unicode):
subfile = subfile.encode('utf-8')
subfile_node.setAttribute('path',subfile)
with FileOrPath(outfile, 'wb') as fp: with FileOrPath(outfile, 'wb') as fp:
doc.writexml(fp,'\t','\t','\n',encoding='utf-8') tree.write(fp, encoding='utf-8')

View File

@@ -8,16 +8,14 @@
import logging import logging
import re import re
from xml.sax import handler, make_parser, SAXException from lxml import etree
from xml.sax.saxutils import XMLGenerator
from xml.sax.xmlreader import AttributesImpl
from . import engine from . import engine
from hsutil.job import nulljob from hsutil.job import nulljob
from hsutil.markable import Markable from hsutil.markable import Markable
from hsutil.misc import flatten, cond, nonone from hsutil.misc import flatten, nonone
from hsutil.str import format_size from hsutil.str import format_size
from hsutil.files import open_if_filename from hsutil.files import FileOrPath
class Results(Markable): class Results(Markable):
#---Override #---Override
@@ -34,6 +32,7 @@ class Results(Markable):
self.__recalculate_stats() self.__recalculate_stats()
self.__marked_size = 0 self.__marked_size = 0
self.data = data_module self.data = data_module
self.problems = [] # (dupe, error_msg)
def _did_mark(self, dupe): def _did_mark(self, dupe):
self.__marked_size += dupe.size self.__marked_size += dupe.size
@@ -148,7 +147,7 @@ class Results(Markable):
self.__filters.append(filter_str) self.__filters.append(filter_str)
if self.__filtered_dupes is None: if self.__filtered_dupes is None:
self.__filtered_dupes = flatten(g[:] for g in self.groups) self.__filtered_dupes = flatten(g[:] for g in self.groups)
self.__filtered_dupes = set(dupe for dupe in self.__filtered_dupes if filter_re.search(dupe.name)) self.__filtered_dupes = set(dupe for dupe in self.__filtered_dupes if filter_re.search(unicode(dupe.path)))
filtered_groups = set() filtered_groups = set()
for dupe in self.__filtered_dupes: for dupe in self.__filtered_dupes:
filtered_groups.add(self.get_group_of_duplicate(dupe)) filtered_groups.add(self.get_group_of_duplicate(dupe))
@@ -168,42 +167,54 @@ class Results(Markable):
is_markable = _is_markable is_markable = _is_markable
def load_from_xml(self, infile, get_file, j=nulljob): def load_from_xml(self, infile, get_file, j=nulljob):
def do_match(ref_file, other_files, group):
if not other_files:
return
for other_file in other_files:
group.add_match(engine.get_match(ref_file, other_file))
do_match(other_files[0], other_files[1:], group)
self.apply_filter(None) self.apply_filter(None)
handler = _ResultsHandler(get_file)
try: try:
parser = make_parser() root = etree.parse(infile).getroot()
except Exception as e: except Exception:
# This special handling is to try to figure out the cause of #47
# We don't silently return, because we want the user to send error report.
logging.exception(e)
try:
import xml.parsers.expat
logging.warning('importing xml.parsers.expat went ok, WTF?')
except Exception as e:
# This log should give a little more details about the cause of this all
logging.exception(e)
raise
raise
parser.setContentHandler(handler)
try:
infile, must_close = open_if_filename(infile)
except IOError:
return return
BUFSIZE = 1024 * 1024 # 1mb buffer group_elems = list(root.iterchildren('group'))
infile.seek(0, 2) groups = []
j.start_job(infile.tell() // BUFSIZE) marked = set()
infile.seek(0, 0) for group_elem in j.iter_with_progress(group_elems, every=100):
group = engine.Group()
dupes = []
for file_elem in group_elem.iterchildren('file'):
path = file_elem.get('path')
words = file_elem.get('words', '')
if not path:
continue
file = get_file(path)
if file is None:
continue
file.words = words.split(',')
file.is_ref = file_elem.get('is_ref') == 'y'
dupes.append(file)
if file_elem.get('marked') == 'y':
marked.add(file)
for match_elem in group_elem.iterchildren('match'):
try: try:
while True: attrs = match_elem.attrib
data = infile.read(BUFSIZE) first_file = dupes[int(attrs['first'])]
if not data: second_file = dupes[int(attrs['second'])]
break percentage = int(attrs['percentage'])
parser.feed(data) group.add_match(engine.Match(first_file, second_file, percentage))
except (IndexError, KeyError, ValueError): # Covers missing attr, non-int values and indexes out of bounds
pass
if (not group.matches) and (len(dupes) >= 2):
do_match(dupes[0], dupes[1:], group)
group.prioritize(lambda x: dupes.index(x))
if len(group):
groups.append(group)
j.add_progress() j.add_progress()
except SAXException: self.groups = groups
return for dupe_file in marked:
self.groups = handler.groups
for dupe_file in handler.marked:
self.mark(dupe_file) self.mark(dupe_file)
def make_ref(self, dupe): def make_ref(self, dupe):
@@ -220,17 +231,22 @@ class Results(Markable):
self.__dupes = None self.__dupes = None
def perform_on_marked(self, func, remove_from_results): def perform_on_marked(self, func, remove_from_results):
problems = [] # Performs `func` on all marked dupes. If an EnvironmentError is raised during the call,
for d in self.dupes: # the problematic dupe is added to self.problems.
if self.is_marked(d) and (not func(d)): self.problems = []
problems.append(d) to_remove = []
marked = (dupe for dupe in self.dupes if self.is_marked(dupe))
for dupe in marked:
try:
func(dupe)
to_remove.append(dupe)
except EnvironmentError as e:
self.problems.append((dupe, unicode(e)))
if remove_from_results: if remove_from_results:
to_remove = [d for d in self.dupes if self.is_marked(d) and (d not in problems)]
self.remove_duplicates(to_remove) self.remove_duplicates(to_remove)
self.mark_none() self.mark_none()
for d in problems: for dupe, _ in self.problems:
self.mark(d) self.mark(dupe)
return len(problems)
def remove_duplicates(self, dupes): def remove_duplicates(self, dupes):
'''Remove 'dupes' from their respective group, and remove the group is it ends up empty. '''Remove 'dupes' from their respective group, and remove the group is it ends up empty.
@@ -256,13 +272,10 @@ class Results(Markable):
def save_to_xml(self, outfile): def save_to_xml(self, outfile):
self.apply_filter(None) self.apply_filter(None)
outfile, must_close = open_if_filename(outfile, 'wb') root = etree.Element('results')
writer = XMLGenerator(outfile, 'utf-8') # writer = XMLGenerator(outfile, 'utf-8')
writer.startDocument()
empty_attrs = AttributesImpl({})
writer.startElement('results', empty_attrs)
for g in self.groups: for g in self.groups:
writer.startElement('group', empty_attrs) group_elem = etree.SubElement(root, 'group')
dupe2index = {} dupe2index = {}
for index, d in enumerate(g): for index, d in enumerate(g):
dupe2index[d] = index dupe2index[d] = index
@@ -270,27 +283,19 @@ class Results(Markable):
words = engine.unpack_fields(d.words) words = engine.unpack_fields(d.words)
except AttributeError: except AttributeError:
words = () words = ()
attrs = AttributesImpl({ file_elem = etree.SubElement(group_elem, 'file')
'path': unicode(d.path), file_elem.set('path', unicode(d.path))
'is_ref': cond(d.is_ref, 'y', 'n'), file_elem.set('is_ref', ('y' if d.is_ref else 'n'))
'words': ','.join(words), file_elem.set('words', ','.join(words))
'marked': cond(self.is_marked(d), 'y', 'n') file_elem.set('marked', ('y' if self.is_marked(d) else 'n'))
})
writer.startElement('file', attrs)
writer.endElement('file')
for match in g.matches: for match in g.matches:
attrs = AttributesImpl({ match_elem = etree.SubElement(group_elem, 'match')
'first': str(dupe2index[match.first]), match_elem.set('first', unicode(dupe2index[match.first]))
'second': str(dupe2index[match.second]), match_elem.set('second', unicode(dupe2index[match.second]))
'percentage': str(int(match.percentage)), match_elem.set('percentage', unicode(int(match.percentage)))
}) tree = etree.ElementTree(root)
writer.startElement('match', attrs) with FileOrPath(outfile, 'wb') as fp:
writer.endElement('match') tree.write(fp, encoding='utf-8')
writer.endElement('group')
writer.endElement('results')
writer.endDocument()
if must_close:
outfile.close()
def sort_dupes(self, key, asc=True, delta=False): def sort_dupes(self, key, asc=True, delta=False):
if not self.__dupes: if not self.__dupes:
@@ -310,60 +315,3 @@ class Results(Markable):
dupes = property(__get_dupe_list) dupes = property(__get_dupe_list)
groups = property(__get_groups, __set_groups) groups = property(__get_groups, __set_groups)
stat_line = property(__get_stat_line) stat_line = property(__get_stat_line)
class _ResultsHandler(handler.ContentHandler):
def __init__(self, get_file):
self.group = None
self.dupes = None
self.marked = set()
self.groups = []
self.get_file = get_file
def startElement(self, name, attrs):
if name == 'group':
self.group = engine.Group()
self.dupes = []
return
if (name == 'file') and (self.group is not None):
if not (('path' in attrs) and ('words' in attrs)):
return
path = attrs['path']
file = self.get_file(path)
if file is None:
return
file.words = attrs['words'].split(',')
file.is_ref = attrs.get('is_ref') == 'y'
self.dupes.append(file)
if attrs.get('marked') == 'y':
self.marked.add(file)
if (name == 'match') and (self.group is not None):
try:
first_file = self.dupes[int(attrs['first'])]
second_file = self.dupes[int(attrs['second'])]
percentage = int(attrs['percentage'])
self.group.add_match(engine.Match(first_file, second_file, percentage))
except (IndexError, KeyError, ValueError): # Covers missing attr, non-int values and indexes out of bounds
pass
def endElement(self, name):
def do_match(ref_file, other_files, group):
if not other_files:
return
for other_file in other_files:
group.add_match(engine.get_match(ref_file, other_file))
do_match(other_files[0], other_files[1:], group)
if name == 'group':
group = self.group
self.group = None
dupes = self.dupes
self.dupes = []
if group is None:
return
if len(dupes) < 2:
return
if not group.matches: # <match> elements not present, do it manually, without %
do_match(dupes[0], dupes[1:], group)
group.prioritize(lambda x: dupes.index(x))
self.groups.append(group)

View File

@@ -1,365 +0,0 @@
# Created By: Virgil Dupras
# 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,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
import tempfile
import shutil
import logging
import os.path as op
from nose.tools import eq_
from hsutil.path import Path
from hsutil.testcase import TestCase
from hsutil.decorators import log_calls
from hsutil import io
from . import data
from .results_test import GetTestGroups
from .. import engine, fs
try:
from ..app_cocoa import DupeGuru as DupeGuruBase
except ImportError:
from nose.plugins.skip import SkipTest
raise SkipTest("These tests can only be run on OS X")
class DupeGuru(DupeGuruBase):
def __init__(self):
DupeGuruBase.__init__(self, data, '/tmp', appid=4)
def _start_job(self, jobid, func):
func(nulljob)
def r2np(rows):
#Transforms a list of rows [1,2,3] into a list of node paths [[1],[2],[3]]
return [[i] for i in rows]
class TCDupeGuru(TestCase):
def setUp(self):
self.app = DupeGuru()
self.objects,self.matches,self.groups = GetTestGroups()
self.app.results.groups = self.groups
tmppath = self.tmppath()
io.mkdir(tmppath + 'foo')
io.mkdir(tmppath + 'bar')
self.app.directories.add_path(tmppath)
def test_GetObjects(self):
app = self.app
objects = self.objects
groups = self.groups
g,d = app.GetObjects([0])
self.assert_(g is groups[0])
self.assert_(d is None)
g,d = app.GetObjects([0,0])
self.assert_(g is groups[0])
self.assert_(d is objects[1])
g,d = app.GetObjects([1,0])
self.assert_(g is groups[1])
self.assert_(d is objects[4])
def test_GetObjects_after_sort(self):
app = self.app
objects = self.objects
groups = self.groups[:] #To keep the old order in memory
app.sort_groups(0,False) #0 = Filename
#Now, the group order is supposed to be reversed
g,d = app.GetObjects([0,0])
self.assert_(g is groups[1])
self.assert_(d is objects[4])
def test_GetObjects_out_of_range(self):
app = self.app
self.assertEqual((None,None),app.GetObjects([2]))
self.assertEqual((None,None),app.GetObjects([]))
self.assertEqual((None,None),app.GetObjects([1,2]))
def test_selected_result_node_paths(self):
# app.selected_dupes is correctly converted into node paths
app = self.app
objects = self.objects
paths = [[0, 0], [0, 1], [1]]
app.SelectResultNodePaths(paths)
eq_(app.selected_result_node_paths(), paths)
def test_selected_result_node_paths_after_deletion(self):
# cases where the selected dupes aren't there are correctly handled
app = self.app
objects = self.objects
paths = [[0, 0], [0, 1], [1]]
app.SelectResultNodePaths(paths)
app.RemoveSelected()
# The first 2 dupes have been removed. The 3rd one is a ref. it stays there, in first pos.
eq_(app.selected_result_node_paths(), [[0]]) # no exception
def test_selectResultNodePaths(self):
app = self.app
objects = self.objects
app.SelectResultNodePaths([[0,0],[0,1]])
self.assertEqual(2,len(app.selected_dupes))
self.assert_(app.selected_dupes[0] is objects[1])
self.assert_(app.selected_dupes[1] is objects[2])
def test_selectResultNodePaths_with_ref(self):
app = self.app
objects = self.objects
app.SelectResultNodePaths([[0,0],[0,1],[1]])
self.assertEqual(3,len(app.selected_dupes))
self.assert_(app.selected_dupes[0] is objects[1])
self.assert_(app.selected_dupes[1] is objects[2])
self.assert_(app.selected_dupes[2] is self.groups[1].ref)
def test_selectResultNodePaths_empty(self):
self.app.SelectResultNodePaths([])
self.assertEqual(0,len(self.app.selected_dupes))
def test_selectResultNodePaths_after_sort(self):
app = self.app
objects = self.objects
groups = self.groups[:] #To keep the old order in memory
app.sort_groups(0,False) #0 = Filename
#Now, the group order is supposed to be reversed
app.SelectResultNodePaths([[0,0],[1],[1,0]])
self.assertEqual(3,len(app.selected_dupes))
self.assert_(app.selected_dupes[0] is objects[4])
self.assert_(app.selected_dupes[1] is groups[0].ref)
self.assert_(app.selected_dupes[2] is objects[1])
def test_selectResultNodePaths_out_of_range(self):
app = self.app
app.SelectResultNodePaths([[0,0],[0,1],[1],[1,1],[2]])
self.assertEqual(3,len(app.selected_dupes))
def test_selected_powermarker_node_paths(self):
# app.selected_dupes is correctly converted into paths
app = self.app
objects = self.objects
paths = r2np([0, 1, 2])
app.SelectPowerMarkerNodePaths(paths)
eq_(app.selected_powermarker_node_paths(), paths)
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
paths = r2np([0, 1, 2])
app.SelectPowerMarkerNodePaths(paths)
app.RemoveSelected()
eq_(app.selected_powermarker_node_paths(), []) # no exception
def test_selectPowerMarkerRows(self):
app = self.app
objects = self.objects
app.SelectPowerMarkerNodePaths(r2np([0,1,2]))
self.assertEqual(3,len(app.selected_dupes))
self.assert_(app.selected_dupes[0] is objects[1])
self.assert_(app.selected_dupes[1] is objects[2])
self.assert_(app.selected_dupes[2] is objects[4])
def test_selectPowerMarkerRows_empty(self):
self.app.SelectPowerMarkerNodePaths([])
self.assertEqual(0,len(self.app.selected_dupes))
def test_selectPowerMarkerRows_after_sort(self):
app = self.app
objects = self.objects
app.sort_dupes(0,False) #0 = Filename
app.SelectPowerMarkerNodePaths(r2np([0,1,2]))
self.assertEqual(3,len(app.selected_dupes))
self.assert_(app.selected_dupes[0] is objects[4])
self.assert_(app.selected_dupes[1] is objects[2])
self.assert_(app.selected_dupes[2] is objects[1])
def test_selectPowerMarkerRows_out_of_range(self):
app = self.app
app.SelectPowerMarkerNodePaths(r2np([0,1,2,3]))
self.assertEqual(3,len(app.selected_dupes))
def test_toggleSelectedMark(self):
app = self.app
objects = self.objects
app.ToggleSelectedMarkState()
self.assertEqual(0,app.results.mark_count)
app.SelectPowerMarkerNodePaths(r2np([0,2]))
app.ToggleSelectedMarkState()
self.assertEqual(2,app.results.mark_count)
self.assert_(not app.results.is_marked(objects[0]))
self.assert_(app.results.is_marked(objects[1]))
self.assert_(not app.results.is_marked(objects[2]))
self.assert_(not app.results.is_marked(objects[3]))
self.assert_(app.results.is_marked(objects[4]))
def test_refreshDetailsWithSelected(self):
def mock_refresh(dupe,group):
self.called = True
if self.app.selected_dupes:
self.assert_(dupe is self.app.selected_dupes[0])
self.assert_(group is self.app.results.get_group_of_duplicate(dupe))
else:
self.assert_(dupe is None)
self.assert_(group is None)
self.app.RefreshDetailsTable = mock_refresh
self.called = False
self.app.SelectPowerMarkerNodePaths(r2np([0,2]))
self.app.RefreshDetailsWithSelected()
self.assert_(self.called)
self.called = False
self.app.SelectPowerMarkerNodePaths([])
self.app.RefreshDetailsWithSelected()
self.assert_(self.called)
def test_makeSelectedReference(self):
app = self.app
objects = self.objects
groups = self.groups
app.SelectPowerMarkerNodePaths(r2np([0,2]))
app.MakeSelectedReference()
self.assert_(groups[0].ref is objects[1])
self.assert_(groups[1].ref is objects[4])
def test_makeSelectedReference_by_selecting_two_dupes_in_the_same_group(self):
app = self.app
objects = self.objects
groups = self.groups
app.SelectPowerMarkerNodePaths(r2np([0,1,2]))
#Only 0 and 2 must go ref, not 1 because it is a part of the same group
app.MakeSelectedReference()
self.assert_(groups[0].ref is objects[1])
self.assert_(groups[1].ref is objects[4])
def test_removeSelected(self):
app = self.app
app.SelectPowerMarkerNodePaths(r2np([0,2]))
app.RemoveSelected()
self.assertEqual(1,len(app.results.dupes))
app.RemoveSelected()
self.assertEqual(1,len(app.results.dupes))
app.SelectPowerMarkerNodePaths(r2np([0,2]))
app.RemoveSelected()
self.assertEqual(0,len(app.results.dupes))
def test_addDirectory_simple(self):
# There's already a directory in self.app, so adding another once makes 2 of em
app = self.app
eq_(app.add_directory(self.datadirpath()), 0)
eq_(len(app.directories), 2)
def test_addDirectory_already_there(self):
app = self.app
self.assertEqual(0,app.add_directory(self.datadirpath()))
self.assertEqual(1,app.add_directory(self.datadirpath()))
def test_addDirectory_does_not_exist(self):
app = self.app
self.assertEqual(2,app.add_directory('/does_not_exist'))
def test_ignore(self):
app = self.app
app.SelectPowerMarkerNodePaths(r2np([2])) #The dupe of the second, 2 sized group
app.AddSelectedToIgnoreList()
self.assertEqual(1,len(app.scanner.ignore_list))
app.SelectPowerMarkerNodePaths(r2np([0])) #first dupe of the 3 dupes group
app.AddSelectedToIgnoreList()
#BOTH the ref and the other dupe should have been added
self.assertEqual(3,len(app.scanner.ignore_list))
def test_purgeIgnoreList(self):
app = self.app
p1 = self.filepath('zerofile')
p2 = self.filepath('zerofill')
dne = '/does_not_exist'
app.scanner.ignore_list.Ignore(dne,p1)
app.scanner.ignore_list.Ignore(p2,dne)
app.scanner.ignore_list.Ignore(p1,p2)
app.PurgeIgnoreList()
self.assertEqual(1,len(app.scanner.ignore_list))
self.assert_(app.scanner.ignore_list.AreIgnored(p1,p2))
self.assert_(not app.scanner.ignore_list.AreIgnored(dne,p1))
def test_only_unicode_is_added_to_ignore_list(self):
def FakeIgnore(first,second):
if not isinstance(first,unicode):
self.fail()
if not isinstance(second,unicode):
self.fail()
app = self.app
app.scanner.ignore_list.Ignore = FakeIgnore
app.SelectPowerMarkerNodePaths(r2np([2])) #The dupe of the second, 2 sized group
app.AddSelectedToIgnoreList()
def test_GetOutlineViewChildCounts_out_of_range(self):
# Out of range requests don't crash and return an empty value
app = self.app
# [0, 2] is out of range
eq_(app.GetOutlineViewChildCounts(1, [0, 2]), []) # no crash
def test_GetOutlineViewValues_out_of_range(self):
# Out of range requests don't crash and return an empty value
app = self.app
# [0, 2] is out of range
eq_(app.GetOutlineViewValues(1, [0, 2]), []) # no crash
class TCDupeGuru_renameSelected(TestCase):
def setUp(self):
p = self.tmppath()
fp = open(unicode(p + 'foo bar 1'),mode='w')
fp.close()
fp = open(unicode(p + 'foo bar 2'),mode='w')
fp.close()
fp = open(unicode(p + 'foo bar 3'),mode='w')
fp.close()
files = fs.get_files(p)
matches = engine.getmatches(files)
groups = engine.get_groups(matches)
g = groups[0]
g.prioritize(lambda x:x.name)
app = DupeGuru()
app.results.groups = groups
self.app = app
self.groups = groups
self.p = p
self.files = files
def test_simple(self):
app = self.app
g = self.groups[0]
app.SelectPowerMarkerNodePaths(r2np([0]))
assert app.RenameSelected('renamed')
names = io.listdir(self.p)
assert 'renamed' in names
assert 'foo bar 2' not in names
eq_(g.dupes[0].name, 'renamed')
def test_none_selected(self):
app = self.app
g = self.groups[0]
app.SelectPowerMarkerNodePaths([])
self.mock(logging, 'warning', log_calls(lambda msg: None))
assert not app.RenameSelected('renamed')
msg = logging.warning.calls[0]['msg']
eq_('dupeGuru Warning: list index out of range', msg)
names = io.listdir(self.p)
assert 'renamed' not in names
assert 'foo bar 2' in names
eq_(g.dupes[0].name, 'foo bar 2')
def test_name_already_exists(self):
app = self.app
g = self.groups[0]
app.SelectPowerMarkerNodePaths(r2np([0]))
self.mock(logging, 'warning', log_calls(lambda msg: None))
assert not app.RenameSelected('foo bar 1')
msg = logging.warning.calls[0]['msg']
assert msg.startswith('dupeGuru Warning: \'foo bar 1\' already exists in')
names = io.listdir(self.p)
assert 'foo bar 1' in names
assert 'foo bar 2' in names
eq_(g.dupes[0].name, 'foo bar 2')

View File

@@ -7,6 +7,9 @@
# http://www.hardcoded.net/licenses/hs_license # http://www.hardcoded.net/licenses/hs_license
import os import os
import logging
from nose.tools import eq_
from hsutil.testcase import TestCase from hsutil.testcase import TestCase
from hsutil import io from hsutil import io
@@ -16,8 +19,12 @@ import hsutil.files
from hsutil.job import nulljob from hsutil.job import nulljob
from . import data from . import data
from .. import app, fs from .results_test import GetTestGroups
from .. import app, fs, engine
from ..app import DupeGuru as DupeGuruBase from ..app import DupeGuru as DupeGuruBase
from ..gui.details_panel import DetailsPanel
from ..gui.directory_tree import DirectoryTree
from ..gui.result_tree import ResultTree
class DupeGuru(DupeGuruBase): class DupeGuru(DupeGuruBase):
def __init__(self): def __init__(self):
@@ -27,6 +34,23 @@ class DupeGuru(DupeGuruBase):
func(nulljob) func(nulljob)
class CallLogger(object):
"""This is a dummy object that logs all calls made to it.
It is used to simulate the GUI layer.
"""
def __init__(self):
self.calls = []
def __getattr__(self, func_name):
def func(*args, **kw):
self.calls.append(func_name)
return func
def clear_calls(self):
del self.calls[:]
class TCDupeGuru(TestCase): class TCDupeGuru(TestCase):
cls_tested_module = app cls_tested_module = app
def test_apply_filter_calls_results_apply_filter(self): def test_apply_filter_calls_results_apply_filter(self):
@@ -133,3 +157,327 @@ class TCDupeGuru_clean_empty_dirs(TestCase):
self.assertEqual(Path('not-empty/empty'), calls[1]['path']) self.assertEqual(Path('not-empty/empty'), calls[1]['path'])
self.assertEqual(Path('not-empty'), calls[2]['path']) self.assertEqual(Path('not-empty'), calls[2]['path'])
class TCDupeGuruWithResults(TestCase):
def setUp(self):
self.app = DupeGuru()
self.objects,self.matches,self.groups = GetTestGroups()
self.app.results.groups = self.groups
self.dpanel_gui = CallLogger()
self.dpanel = DetailsPanel(self.dpanel_gui, self.app)
self.dtree_gui = CallLogger()
self.dtree = DirectoryTree(self.dtree_gui, self.app)
self.rtree_gui = CallLogger()
self.rtree = ResultTree(self.rtree_gui, self.app)
self.dpanel.connect()
self.dtree.connect()
self.rtree.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]
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)
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.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
def test_selectResultNodePaths(self):
app = self.app
objects = self.objects
self.rtree.selected_paths = [[0, 0], [0, 1]]
eq_(len(app.selected_dupes), 2)
assert app.selected_dupes[0] is objects[1]
assert app.selected_dupes[1] is objects[2]
def test_selectResultNodePaths_with_ref(self):
app = self.app
objects = self.objects
self.rtree.selected_paths = [[0, 0], [0, 1], [1]]
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 self.groups[1].ref
def test_selectResultNodePaths_after_sort(self):
app = self.app
objects = self.objects
groups = self.groups[:] #To keep the old order in memory
self.rtree.sort(0, False) #0 = Filename
#Now, the group order is supposed to be reversed
self.rtree.selected_paths = [[0, 0], [1], [1, 0]]
eq_(len(app.selected_dupes), 3)
assert app.selected_dupes[0] is objects[4]
assert app.selected_dupes[1] is groups[0].ref
assert app.selected_dupes[2] is objects[1]
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]])
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]]
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)
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]]
eq_(len(app.selected_dupes), 3)
assert app.selected_dupes[0] is objects[4]
assert app.selected_dupes[1] is objects[2]
assert app.selected_dupes[2] is objects[1]
def test_toggleSelectedMark(self):
app = self.app
objects = self.objects
app.toggle_selected_mark_state()
eq_(app.results.mark_count, 0)
self.rtree.selected_paths = [[0, 0], [1, 0]]
app.toggle_selected_mark_state()
eq_(app.results.mark_count, 2)
assert not app.results.is_marked(objects[0])
assert app.results.is_marked(objects[1])
assert not app.results.is_marked(objects[2])
assert not app.results.is_marked(objects[3])
assert app.results.is_marked(objects[4])
def test_refreshDetailsWithSelected(self):
self.rtree.selected_paths = [[0, 0], [1, 0]]
eq_(self.dpanel.row(0), ('Filename', 'bar bleh', 'foo bar'))
self.check_gui_calls(self.dpanel_gui, ['refresh'])
self.rtree.selected_paths = []
eq_(self.dpanel.row(0), ('Filename', '---', '---'))
self.check_gui_calls(self.dpanel_gui, ['refresh'])
def test_makeSelectedReference(self):
app = self.app
objects = self.objects
groups = self.groups
self.rtree.selected_paths = [[0, 0], [1, 0]]
app.make_selected_reference()
assert groups[0].ref is objects[1]
assert groups[1].ref is objects[4]
def test_makeSelectedReference_by_selecting_two_dupes_in_the_same_group(self):
app = self.app
objects = self.objects
groups = self.groups
self.rtree.selected_paths = [[0, 0], [0, 1], [1, 0]]
#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]
assert groups[1].ref is objects[4]
def test_removeSelected(self):
app = self.app
self.rtree.selected_paths = [[0, 0], [1, 0]]
app.remove_selected()
eq_(len(app.results.dupes), 1) # the first path is now selected
app.remove_selected()
eq_(len(app.results.dupes), 0)
def test_addDirectory_simple(self):
# There's already a directory in self.app, so adding another once makes 2 of em
app = self.app
eq_(app.add_directory(self.datadirpath()), 0)
eq_(len(app.directories), 2)
def test_addDirectory_already_there(self):
app = self.app
self.assertEqual(0,app.add_directory(self.datadirpath()))
self.assertEqual(1,app.add_directory(self.datadirpath()))
def test_addDirectory_does_not_exist(self):
app = self.app
self.assertEqual(2,app.add_directory('/does_not_exist'))
def test_ignore(self):
app = self.app
self.rtree.selected_path = [1, 0] #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
app.add_selected_to_ignore_list()
#BOTH the ref and the other dupe should have been added
eq_(len(app.scanner.ignore_list), 3)
def test_purgeIgnoreList(self):
app = self.app
p1 = self.filepath('zerofile')
p2 = self.filepath('zerofill')
dne = '/does_not_exist'
app.scanner.ignore_list.Ignore(dne,p1)
app.scanner.ignore_list.Ignore(p2,dne)
app.scanner.ignore_list.Ignore(p1,p2)
app.purge_ignore_list()
self.assertEqual(1,len(app.scanner.ignore_list))
self.assert_(app.scanner.ignore_list.AreIgnored(p1,p2))
self.assert_(not app.scanner.ignore_list.AreIgnored(dne,p1))
def test_only_unicode_is_added_to_ignore_list(self):
def FakeIgnore(first,second):
if not isinstance(first,unicode):
self.fail()
if not isinstance(second,unicode):
self.fail()
app = self.app
app.scanner.ignore_list.Ignore = FakeIgnore
self.rtree.selected_path = [1, 0]
app.add_selected_to_ignore_list()
class TCDupeGuru_renameSelected(TestCase):
def setUp(self):
p = self.tmppath()
fp = open(unicode(p + 'foo bar 1'),mode='w')
fp.close()
fp = open(unicode(p + 'foo bar 2'),mode='w')
fp.close()
fp = open(unicode(p + 'foo bar 3'),mode='w')
fp.close()
files = fs.get_files(p)
matches = engine.getmatches(files)
groups = engine.get_groups(matches)
g = groups[0]
g.prioritize(lambda x:x.name)
app = DupeGuru()
app.results.groups = groups
self.app = app
self.groups = groups
self.p = p
self.files = files
self.rtree_gui = CallLogger()
self.rtree = ResultTree(self.rtree_gui, self.app)
self.rtree.connect()
def test_simple(self):
app = self.app
g = self.groups[0]
self.rtree.selected_path = [0, 0]
assert app.rename_selected('renamed')
names = io.listdir(self.p)
assert 'renamed' in names
assert 'foo bar 2' not in names
eq_(g.dupes[0].name, 'renamed')
def test_none_selected(self):
app = self.app
g = self.groups[0]
self.rtree.selected_paths = []
self.mock(logging, 'warning', log_calls(lambda msg: None))
assert not app.rename_selected('renamed')
msg = logging.warning.calls[0]['msg']
eq_('dupeGuru Warning: list index out of range', msg)
names = io.listdir(self.p)
assert 'renamed' not in names
assert 'foo bar 2' in names
eq_(g.dupes[0].name, 'foo bar 2')
def test_name_already_exists(self):
app = self.app
g = self.groups[0]
self.rtree.selected_path = [0, 0]
self.mock(logging, 'warning', log_calls(lambda msg: None))
assert not app.rename_selected('foo bar 1')
msg = logging.warning.calls[0]['msg']
assert msg.startswith('dupeGuru Warning: \'foo bar 1\' already exists in')
names = io.listdir(self.p)
assert 'foo bar 1' in names
assert 'foo bar 2' in names
eq_(g.dupes[0].name, 'foo bar 2')

View File

@@ -31,7 +31,7 @@ def GetDisplayInfo(dupe, group, delta):
dupe.name, dupe.name,
format_path(dupe.path), format_path(dupe.path),
format_size(size, 0, 1, False), format_size(size, 0, 1, False),
dupe.extension, dupe.extension if hasattr(dupe, 'extension') else '---',
] ]
def GetDupeSortKey(dupe, get_group, key, delta): def GetDupeSortKey(dupe, get_group, key, delta):

View File

@@ -229,10 +229,9 @@ class TCbuild_word_dict(TestCase):
self.log = [] self.log = []
s = "foo bar" s = "foo bar"
build_word_dict([NamedObject(s, True), NamedObject(s, True), NamedObject(s, True)], j) build_word_dict([NamedObject(s, True), NamedObject(s, True), NamedObject(s, True)], j)
# We don't have intermediate log because iter_with_progress is called with every > 1
self.assertEqual(0,self.log[0]) self.assertEqual(0,self.log[0])
self.assertEqual(33,self.log[1]) self.assertEqual(100,self.log[1])
self.assertEqual(66,self.log[2])
self.assertEqual(100,self.log[3])
class TCmerge_similar_words(TestCase): class TCmerge_similar_words(TestCase):

View File

@@ -7,7 +7,7 @@
# http://www.hardcoded.net/licenses/hs_license # http://www.hardcoded.net/licenses/hs_license
import cStringIO import cStringIO
import xml.dom.minidom from lxml import etree
from nose.tools import eq_ from nose.tools import eq_
@@ -62,26 +62,25 @@ def test_save_to_xml():
f = cStringIO.StringIO() f = cStringIO.StringIO()
il.save_to_xml(f) il.save_to_xml(f)
f.seek(0) f.seek(0)
doc = xml.dom.minidom.parse(f) doc = etree.parse(f)
root = doc.documentElement root = doc.getroot()
eq_('ignore_list',root.nodeName) eq_(root.tag, 'ignore_list')
children = [c for c in root.childNodes if c.localName] eq_(len(root), 2)
eq_(2,len(children)) eq_(len([c for c in root if c.tag == 'file']), 2)
eq_(2,len([c for c in children if c.nodeName == 'file'])) f1, f2 = root[:]
f1,f2 = children subchildren = [c for c in f1 if c.tag == 'file'] + [c for c in f2 if c.tag == 'file']
subchildren = [c for c in f1.childNodes if c.localName == 'file'] +\ eq_(len(subchildren), 3)
[c for c in f2.childNodes if c.localName == 'file']
eq_(3,len(subchildren))
def test_SaveThenLoad(): def test_SaveThenLoad():
il = IgnoreList() il = IgnoreList()
il.Ignore('foo','bar') il.Ignore('foo', 'bar')
il.Ignore('foo','bleh') il.Ignore('foo', 'bleh')
il.Ignore('bleh','bar') il.Ignore('bleh', 'bar')
il.Ignore(u'\u00e9','bar') il.Ignore(u'\u00e9', 'bar')
f = cStringIO.StringIO() f = cStringIO.StringIO()
il.save_to_xml(f) il.save_to_xml(f)
f.seek(0) f.seek(0)
f.seek(0)
il = IgnoreList() il = IgnoreList()
il.load_from_xml(f) il.load_from_xml(f)
eq_(4,len(il)) eq_(4,len(il))
@@ -129,9 +128,9 @@ def test_filter():
assert not il.AreIgnored('foo','bar') assert not il.AreIgnored('foo','bar')
assert il.AreIgnored('bar','baz') assert il.AreIgnored('bar','baz')
def test_save_with_non_ascii_non_unicode_items(): def test_save_with_non_ascii_items():
il = IgnoreList() il = IgnoreList()
il.Ignore('\xac','\xbf') il.Ignore(u'\xac', u'\xbf')
f = cStringIO.StringIO() f = cStringIO.StringIO()
try: try:
il.save_to_xml(f) il.save_to_xml(f)

View File

@@ -7,18 +7,19 @@
# which should be included with this package. The terms are also available at # which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license # http://www.hardcoded.net/licenses/hs_license
import unittest
import StringIO import StringIO
import xml.dom.minidom
import os.path as op import os.path as op
from lxml import etree
from nose.tools import eq_
from hsutil.path import Path from hsutil.path import Path
from hsutil.testcase import TestCase from hsutil.testcase import TestCase
from hsutil.misc import first from hsutil.misc import first
from . import engine_test, data from . import engine_test, data
from .. import engine from .. import engine
from ..results import * from ..results import Results
class NamedObject(engine_test.NamedObject): class NamedObject(engine_test.NamedObject):
path = property(lambda x:Path('basepath') + x.name) path = property(lambda x:Path('basepath') + x.name)
@@ -65,9 +66,9 @@ class TCResultsEmpty(TestCase):
f = StringIO.StringIO() f = StringIO.StringIO()
self.results.save_to_xml(f) self.results.save_to_xml(f)
f.seek(0) f.seek(0)
doc = xml.dom.minidom.parse(f) doc = etree.parse(f)
root = doc.documentElement root = doc.getroot()
self.assertEqual('results',root.nodeName) self.assertEqual('results', root.tag)
class TCResultsWithSomeGroups(TestCase): class TCResultsWithSomeGroups(TestCase):
@@ -253,18 +254,23 @@ class TCResultsMarkings(TestCase):
def test_perform_on_marked_with_problems(self): def test_perform_on_marked_with_problems(self):
def log_object(o): def log_object(o):
log.append(o) log.append(o)
return o is not self.objects[1] if o is self.objects[1]:
raise EnvironmentError('foobar')
log = [] log = []
self.results.mark_all() self.results.mark_all()
self.assert_(self.results.is_marked(self.objects[1])) assert self.results.is_marked(self.objects[1])
self.assertEqual(1,self.results.perform_on_marked(log_object, True)) self.results.perform_on_marked(log_object, True)
self.assertEqual(3,len(log)) eq_(len(log), 3)
self.assertEqual(1,len(self.results.groups)) eq_(len(self.results.groups), 1)
self.assertEqual(2,len(self.results.groups[0])) eq_(len(self.results.groups[0]), 2)
self.assert_(self.objects[1] in self.results.groups[0]) assert self.objects[1] in self.results.groups[0]
self.assert_(not self.results.is_marked(self.objects[2])) assert not self.results.is_marked(self.objects[2])
self.assert_(self.results.is_marked(self.objects[1])) assert self.results.is_marked(self.objects[1])
eq_(len(self.results.problems), 1)
dupe, msg = self.results.problems[0]
assert dupe is self.objects[1]
eq_(msg, 'foobar')
def test_perform_on_marked_with_ref(self): def test_perform_on_marked_with_ref(self):
def log_object(o): def log_object(o):
@@ -321,16 +327,16 @@ class TCResultsMarkings(TestCase):
f = StringIO.StringIO() f = StringIO.StringIO()
self.results.save_to_xml(f) self.results.save_to_xml(f)
f.seek(0) f.seek(0)
doc = xml.dom.minidom.parse(f) doc = etree.parse(f)
root = doc.documentElement root = doc.getroot()
g1,g2 = root.getElementsByTagName('group') g1, g2 = root.iterchildren('group')
d1,d2,d3 = g1.getElementsByTagName('file') d1, d2, d3 = g1.iterchildren('file')
self.assertEqual('n',d1.getAttributeNode('marked').nodeValue) self.assertEqual('n', d1.get('marked'))
self.assertEqual('n',d2.getAttributeNode('marked').nodeValue) self.assertEqual('n', d2.get('marked'))
self.assertEqual('y',d3.getAttributeNode('marked').nodeValue) self.assertEqual('y', d3.get('marked'))
d1,d2 = g2.getElementsByTagName('file') d1, d2 = g2.iterchildren('file')
self.assertEqual('n',d1.getAttributeNode('marked').nodeValue) self.assertEqual('n', d1.get('marked'))
self.assertEqual('y',d2.getAttributeNode('marked').nodeValue) self.assertEqual('y', d2.get('marked'))
def test_LoadXML(self): def test_LoadXML(self):
def get_file(path): def get_file(path):
@@ -366,38 +372,35 @@ class TCResultsXML(TestCase):
f = StringIO.StringIO() f = StringIO.StringIO()
self.results.save_to_xml(f) self.results.save_to_xml(f)
f.seek(0) f.seek(0)
doc = xml.dom.minidom.parse(f) doc = etree.parse(f)
root = doc.documentElement root = doc.getroot()
self.assertEqual('results',root.nodeName) self.assertEqual('results', root.tag)
children = [c for c in root.childNodes if c.localName] self.assertEqual(2, len(root))
self.assertEqual(2,len(children)) self.assertEqual(2, len([c for c in root if c.tag == 'group']))
self.assertEqual(2,len([c for c in children if c.nodeName == 'group'])) g1, g2 = root
g1,g2 = children self.assertEqual(6,len(g1))
children = [c for c in g1.childNodes if c.localName] self.assertEqual(3,len([c for c in g1 if c.tag == 'file']))
self.assertEqual(6,len(children)) self.assertEqual(3,len([c for c in g1 if c.tag == 'match']))
self.assertEqual(3,len([c for c in children if c.nodeName == 'file'])) d1, d2, d3 = [c for c in g1 if c.tag == 'file']
self.assertEqual(3,len([c for c in children if c.nodeName == 'match'])) self.assertEqual(op.join('basepath','foo bar'),d1.get('path'))
d1,d2,d3 = [c for c in children if c.nodeName == 'file'] self.assertEqual(op.join('basepath','bar bleh'),d2.get('path'))
self.assertEqual(op.join('basepath','foo bar'),d1.getAttributeNode('path').nodeValue) self.assertEqual(op.join('basepath','foo bleh'),d3.get('path'))
self.assertEqual(op.join('basepath','bar bleh'),d2.getAttributeNode('path').nodeValue) self.assertEqual('y',d1.get('is_ref'))
self.assertEqual(op.join('basepath','foo bleh'),d3.getAttributeNode('path').nodeValue) self.assertEqual('n',d2.get('is_ref'))
self.assertEqual('y',d1.getAttributeNode('is_ref').nodeValue) self.assertEqual('n',d3.get('is_ref'))
self.assertEqual('n',d2.getAttributeNode('is_ref').nodeValue) self.assertEqual('foo,bar',d1.get('words'))
self.assertEqual('n',d3.getAttributeNode('is_ref').nodeValue) self.assertEqual('bar,bleh',d2.get('words'))
self.assertEqual('foo,bar',d1.getAttributeNode('words').nodeValue) self.assertEqual('foo,bleh',d3.get('words'))
self.assertEqual('bar,bleh',d2.getAttributeNode('words').nodeValue) self.assertEqual(3,len(g2))
self.assertEqual('foo,bleh',d3.getAttributeNode('words').nodeValue) self.assertEqual(2,len([c for c in g2 if c.tag == 'file']))
children = [c for c in g2.childNodes if c.localName] self.assertEqual(1,len([c for c in g2 if c.tag == 'match']))
self.assertEqual(3,len(children)) d1, d2 = [c for c in g2 if c.tag == 'file']
self.assertEqual(2,len([c for c in children if c.nodeName == 'file'])) self.assertEqual(op.join('basepath','ibabtu'),d1.get('path'))
self.assertEqual(1,len([c for c in children if c.nodeName == 'match'])) self.assertEqual(op.join('basepath','ibabtu'),d2.get('path'))
d1,d2 = [c for c in children if c.nodeName == 'file'] self.assertEqual('n',d1.get('is_ref'))
self.assertEqual(op.join('basepath','ibabtu'),d1.getAttributeNode('path').nodeValue) self.assertEqual('n',d2.get('is_ref'))
self.assertEqual(op.join('basepath','ibabtu'),d2.getAttributeNode('path').nodeValue) self.assertEqual('ibabtu',d1.get('words'))
self.assertEqual('n',d1.getAttributeNode('is_ref').nodeValue) self.assertEqual('ibabtu',d2.get('words'))
self.assertEqual('n',d2.getAttributeNode('is_ref').nodeValue)
self.assertEqual('ibabtu',d1.getAttributeNode('words').nodeValue)
self.assertEqual('ibabtu',d2.getAttributeNode('words').nodeValue)
def test_LoadXML(self): def test_LoadXML(self):
def get_file(path): def get_file(path):
@@ -460,41 +463,41 @@ class TCResultsXML(TestCase):
def get_file(path): def get_file(path):
return [f for f in self.objects if str(f.path) == path][0] return [f for f in self.objects if str(f.path) == path][0]
doc = xml.dom.minidom.Document() root = etree.Element('foobar') #The root element shouldn't matter, really.
root = doc.appendChild(doc.createElement('foobar')) #The root element shouldn't matter, really. group_node = etree.SubElement(root, 'group')
group_node = root.appendChild(doc.createElement('group')) dupe_node = etree.SubElement(group_node, 'file') #Perfectly correct file
dupe_node = group_node.appendChild(doc.createElement('file')) #Perfectly correct file dupe_node.set('path', op.join('basepath','foo bar'))
dupe_node.setAttribute('path',op.join('basepath','foo bar')) dupe_node.set('is_ref', 'y')
dupe_node.setAttribute('is_ref','y') dupe_node.set('words', 'foo,bar')
dupe_node.setAttribute('words','foo,bar') dupe_node = etree.SubElement(group_node, 'file') #is_ref missing, default to 'n'
dupe_node = group_node.appendChild(doc.createElement('file')) #is_ref missing, default to 'n' dupe_node.set('path',op.join('basepath','foo bleh'))
dupe_node.setAttribute('path',op.join('basepath','foo bleh')) dupe_node.set('words','foo,bleh')
dupe_node.setAttribute('words','foo,bleh') dupe_node = etree.SubElement(group_node, 'file') #words are missing, valid.
dupe_node = group_node.appendChild(doc.createElement('file')) #words are missing, invalid. dupe_node.set('path',op.join('basepath','bar bleh'))
dupe_node.setAttribute('path',op.join('basepath','bar bleh')) dupe_node = etree.SubElement(group_node, 'file') #path is missing, invalid.
dupe_node = group_node.appendChild(doc.createElement('file')) #path is missing, invalid. dupe_node.set('words','foo,bleh')
dupe_node.setAttribute('words','foo,bleh') dupe_node = etree.SubElement(group_node, 'foobar') #Invalid element name
dupe_node = group_node.appendChild(doc.createElement('foobar')) #Invalid element name dupe_node.set('path',op.join('basepath','bar bleh'))
dupe_node.setAttribute('path',op.join('basepath','bar bleh')) dupe_node.set('is_ref','y')
dupe_node.setAttribute('is_ref','y') dupe_node.set('words','bar,bleh')
dupe_node.setAttribute('words','bar,bleh') match_node = etree.SubElement(group_node, 'match') # match pointing to a bad index
match_node = group_node.appendChild(doc.createElement('match')) # match pointing to a bad index match_node.set('first', '42')
match_node.setAttribute('first', '42') match_node.set('second', '45')
match_node.setAttribute('second', '45') match_node = etree.SubElement(group_node, 'match') # match with missing attrs
match_node = group_node.appendChild(doc.createElement('match')) # match with missing attrs match_node = etree.SubElement(group_node, 'match') # match with non-int values
match_node = group_node.appendChild(doc.createElement('match')) # match with non-int values match_node.set('first', 'foo')
match_node.setAttribute('first', 'foo') match_node.set('second', 'bar')
match_node.setAttribute('second', 'bar') match_node.set('percentage', 'baz')
match_node.setAttribute('percentage', 'baz') group_node = etree.SubElement(root, 'foobar') #invalid group
group_node = root.appendChild(doc.createElement('foobar')) #invalid group group_node = etree.SubElement(root, 'group') #empty group
group_node = root.appendChild(doc.createElement('group')) #empty group
f = StringIO.StringIO() f = StringIO.StringIO()
doc.writexml(f,'\t','\t','\n',encoding='utf-8') tree = etree.ElementTree(root)
tree.write(f, encoding='utf-8')
f.seek(0) f.seek(0)
r = Results(data) r = Results(data)
r.load_from_xml(f,get_file) r.load_from_xml(f, get_file)
self.assertEqual(1,len(r.groups)) self.assertEqual(1,len(r.groups))
self.assertEqual(2,len(r.groups[0])) self.assertEqual(3,len(r.groups[0]))
def test_xml_non_ascii(self): def test_xml_non_ascii(self):
def get_file(path): def get_file(path):
@@ -567,6 +570,10 @@ class TCResultsXML(TestCase):
self.results.load_from_xml(f, self.get_file) self.results.load_from_xml(f, self.get_file)
first(self.results.groups[0].matches).percentage first(self.results.groups[0].matches).percentage
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')
eq_(len(self.results.groups), 2)
class TCResultsFilter(TestCase): class TCResultsFilter(TestCase):
def setUp(self): def setUp(self):

View File

@@ -85,9 +85,9 @@ def GetDupeSortKey(dupe, get_group, key, delta):
return m.percentage return m.percentage
if key == 18: if key == 18:
return 0 return 0
r = cmp_value(getattr(dupe, COLUMNS[key]['attr'])) r = cmp_value(getattr(dupe, COLUMNS[key]['attr'], ''))
if delta and (key in (2, 3, 4, 7, 8)): if delta and (key in (2, 3, 4, 7, 8)):
r -= cmp_value(getattr(get_group().ref, COLUMNS[key]['attr'])) r -= cmp_value(getattr(get_group().ref, COLUMNS[key]['attr'], ''))
return r return r
def GetGroupSortKey(group, key): def GetGroupSortKey(group, key):
@@ -95,4 +95,4 @@ def GetGroupSortKey(group, key):
return group.percentage return group.percentage
if key == 18: if key == 18:
return len(group) return len(group)
return cmp_value(getattr(group.ref, COLUMNS[key]['attr'])) return cmp_value(getattr(group.ref, COLUMNS[key]['attr'], ''))

View File

@@ -7,29 +7,22 @@
# http://www.hardcoded.net/licenses/hs_license # http://www.hardcoded.net/licenses/hs_license
import os.path as op import os.path as op
import logging
import plistlib import plistlib
import re
from Foundation import * from lxml import etree
from AppKit import *
from appscript import app, k, CommandError from appscript import app, k, CommandError
from hsutil import io from hsutil import io
from hsutil.str import get_file_ext from hsutil.str import get_file_ext
from hsutil.path import Path from hsutil.path import Path
from hsutil.cocoa import as_fetch from hsutil.cocoa import as_fetch
from hsutil.cocoa.objcmin import NSUserDefaults, NSURL
from core import fs from core import fs
from core import app_cocoa, directories from core import app_cocoa, directories
from . import data from . import data, _block_osx
from .cache import string_to_colors, Cache
from .scanner import ScannerPE from .scanner import ScannerPE
mainBundle = NSBundle.mainBundle()
PictureBlocks = mainBundle.classNamed_('PictureBlocks')
assert PictureBlocks is not None
class Photo(fs.File): class Photo(fs.File):
INITIAL_INFO = fs.File.INITIAL_INFO.copy() INITIAL_INFO = fs.File.INITIAL_INFO.copy()
INITIAL_INFO.update({ INITIAL_INFO.update({
@@ -44,17 +37,16 @@ class Photo(fs.File):
def _read_info(self, field): def _read_info(self, field):
fs.File._read_info(self, field) fs.File._read_info(self, field)
if field == 'dimensions': if field == 'dimensions':
size = PictureBlocks.getImageSize_(unicode(self.path)) self.dimensions = _block_osx.get_image_size(unicode(self.path))
self.dimensions = (size.width, size.height)
def get_blocks(self, block_count_per_side): def get_blocks(self, block_count_per_side):
try: try:
blocks = PictureBlocks.getBlocksFromImagePath_blockCount_(unicode(self.path), block_count_per_side) blocks = _block_osx.getblocks(unicode(self.path), block_count_per_side)
except Exception as e: except Exception as e:
raise IOError('The reading of "%s" failed with "%s"' % (unicode(self.path), unicode(e))) raise IOError('The reading of "%s" failed with "%s"' % (unicode(self.path), unicode(e)))
if not blocks: if not blocks:
raise IOError('The picture %s could not be read' % unicode(self.path)) raise IOError('The picture %s could not be read' % unicode(self.path))
return string_to_colors(blocks) return blocks
class IPhoto(Photo): class IPhoto(Photo):
@@ -74,16 +66,11 @@ def get_iphoto_database_path():
def get_iphoto_pictures(plistpath): def get_iphoto_pictures(plistpath):
if not io.exists(plistpath): if not io.exists(plistpath):
raise InvalidPath(self) return []
s = io.open(plistpath).read() # We make the xml go through lxml so that it can fix broken xml which iPhoto sometimes produces.
# There was a case where a guy had 0x10 chars in his plist, causing expat errors on loading parser = etree.XMLParser(recover=True)
s = s.replace('\x10', '') root = etree.parse(io.open(plistpath), parser=parser).getroot()
# It seems that iPhoto sometimes doesn't properly escape & chars. The regexp below is to find s = etree.tostring(root)
# any & char that is not a &-based entity (&amp;, &quot;, etc.). based on TextMate's XML
# bundle's regexp
s, count = re.subn(r'&(?![a-zA-Z0-9_-]+|#[0-9]+|#x[0-9a-fA-F]+;)', '', s)
if count:
logging.warning("%d invalid XML entities replacement made", count)
plist = plistlib.readPlistFromString(s) plist = plistlib.readPlistFromString(s)
result = [] result = []
for photo_data in plist['Master Image List'].values(): for photo_data in plist['Master Image List'].values():
@@ -124,8 +111,7 @@ class Directories(directories.Directories):
def add_path(self, path): def add_path(self, path):
if path == Path('iPhoto Library'): if path == Path('iPhoto Library'):
if path in self: if path not in self:
raise AlreadyThereError()
self._dirs.append(path) self._dirs.append(path)
else: else:
directories.Directories.add_path(self, path) directories.Directories.add_path(self, path)
@@ -136,8 +122,7 @@ class DupeGuruPE(app_cocoa.DupeGuru):
app_cocoa.DupeGuru.__init__(self, data, 'dupeGuru Picture Edition', appid=5) app_cocoa.DupeGuru.__init__(self, data, 'dupeGuru Picture Edition', appid=5)
self.scanner = ScannerPE() self.scanner = ScannerPE()
self.directories = Directories() self.directories = Directories()
p = op.join(self.appdata, 'cached_pictures.db') self.scanner.cache_path = op.join(self.appdata, 'cached_pictures.db')
self.scanner.cached_blocks = Cache(p)
def _do_delete(self, j): def _do_delete(self, j):
def op(dupe): def op(dupe):
@@ -161,7 +146,7 @@ class DupeGuruPE(app_cocoa.DupeGuru):
except (CommandError, RuntimeError): except (CommandError, RuntimeError):
pass pass
j.start_job(self.results.mark_count, "Sending dupes to the Trash") j.start_job(self.results.mark_count, "Sending dupes to the Trash")
self.last_op_error_count = self.results.perform_on_marked(op, True) self.results.perform_on_marked(op, True)
del self.path2iphoto del self.path2iphoto
def _do_delete_dupe(self, dupe): def _do_delete_dupe(self, dupe):
@@ -171,14 +156,13 @@ class DupeGuruPE(app_cocoa.DupeGuru):
try: try:
a = app('iPhoto') a = app('iPhoto')
a.remove(photo, timeout=0) a.remove(photo, timeout=0)
return True except (CommandError, RuntimeError) as e:
except (CommandError, RuntimeError): raise EnvironmentError(unicode(e))
return False
else: else:
logging.warning(u"Could not find photo %s in iPhoto Library", unicode(dupe.path)) msg = u"Could not find photo %s in iPhoto Library" % unicode(dupe.path)
return False raise EnvironmentError(msg)
else: else:
return app_cocoa.DupeGuru._do_delete_dupe(self, dupe) app_cocoa.DupeGuru._do_delete_dupe(self, dupe)
def _do_load(self, j): def _do_load(self, j):
self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml')) self.directories.load_from_file(op.join(self.appdata, 'last_directories.xml'))

View File

@@ -8,7 +8,7 @@
from _block import NoBlocksError, DifferentBlockCountError, avgdiff, getblocks2 from _block import NoBlocksError, DifferentBlockCountError, avgdiff, getblocks2
# Converted to Cython # Converted to C
# def getblock(image): # def getblock(image):
# """Returns a 3 sized tuple containing the mean color of 'image'. # """Returns a 3 sized tuple containing the mean color of 'image'.
# #
@@ -43,7 +43,7 @@ from _block import NoBlocksError, DifferentBlockCountError, avgdiff, getblocks2
# result.append(getblock(crop)) # result.append(getblock(crop))
# return result # return result
# Converted to Cython # Converted to C
# def getblocks2(image,block_count_per_side): # def getblocks2(image,block_count_per_side):
# """Returns a list of blocks (3 sized tuples). # """Returns a list of blocks (3 sized tuples).
# #
@@ -70,7 +70,7 @@ from _block import NoBlocksError, DifferentBlockCountError, avgdiff, getblocks2
# result.append(getblock(crop)) # result.append(getblock(crop))
# return result # return result
# Converted to Cython # Converted to C
# def diff(first, second): # def diff(first, second):
# """Returns the difference between the first block and the second. # """Returns the difference between the first block and the second.
# #
@@ -80,7 +80,7 @@ from _block import NoBlocksError, DifferentBlockCountError, avgdiff, getblocks2
# r2, g2, b2 = second # r2, g2, b2 = second
# return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2) # return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2)
# Converted to Cython # Converted to C
# def avgdiff(first, second, limit=768, min_iterations=1): # def avgdiff(first, second, limit=768, min_iterations=1):
# """Returns the average diff between first blocks and seconds. # """Returns the average diff between first blocks and seconds.
# #

View File

@@ -10,8 +10,6 @@ import os
import logging import logging
import sqlite3 as sqlite import sqlite3 as sqlite
import hsutil.sqlite
from _cache import string_to_colors from _cache import string_to_colors
def colors_to_string(colors): def colors_to_string(colors):
@@ -22,7 +20,7 @@ def colors_to_string(colors):
""" """
return ''.join(['%02x%02x%02x' % (r,g,b) for r,g,b in colors]) return ''.join(['%02x%02x%02x' % (r,g,b) for r,g,b in colors])
# This function is an important bottleneck of dupeGuru PE. It has been converted to Cython. # This function is an important bottleneck of dupeGuru PE. It has been converted to C.
# def string_to_colors(s): # def string_to_colors(s):
# """Transform the string 's' in a list of 3 sized tuples. # """Transform the string 's' in a list of 3 sized tuples.
# """ # """
@@ -35,31 +33,10 @@ def colors_to_string(colors):
class Cache(object): class Cache(object):
"""A class to cache picture blocks. """A class to cache picture blocks.
""" """
def __init__(self, db=':memory:', threaded=True): def __init__(self, db=':memory:'):
def create_tables():
sql = "create table pictures(path TEXT, blocks TEXT)"
self.con.execute(sql);
sql = "create index idx_path on pictures (path)"
self.con.execute(sql)
self.dbname = db self.dbname = db
if threaded: self.con = None
self.con = hsutil.sqlite.ThreadedConn(db, True) self._create_con()
else:
self.con = sqlite.connect(db, isolation_level=None)
try:
self.con.execute("select * from pictures where 1=2")
except sqlite.OperationalError: # new db
create_tables()
except sqlite.DatabaseError, e: # corrupted db
logging.warning('Could not create picture cache because of an error: %s', str(e))
self.con.close()
os.remove(db)
if threaded:
self.con = hsutil.sqlite.ThreadedConn(db, True)
else:
self.con = sqlite.connect(db, isolation_level=None)
create_tables()
def __contains__(self, key): def __contains__(self, key):
sql = "select count(*) from pictures where path = ?" sql = "select count(*) from pictures where path = ?"
@@ -108,10 +85,37 @@ class Cache(object):
except sqlite.DatabaseError, e: except sqlite.DatabaseError, e:
logging.warning('DatabaseError while setting %r for key %r: %s', value, key, str(e)) logging.warning('DatabaseError while setting %r for key %r: %s', value, key, str(e))
def clear(self): def _create_con(self, second_try=False):
sql = "delete from pictures" def create_tables():
sql = "create table pictures(path TEXT, blocks TEXT)"
self.con.execute(sql);
sql = "create index idx_path on pictures (path)"
self.con.execute(sql) self.con.execute(sql)
self.con = sqlite.connect(self.dbname, isolation_level=None)
try:
self.con.execute("select * from pictures where 1=2")
except sqlite.OperationalError: # new db
create_tables()
except sqlite.DatabaseError, e: # corrupted db
if second_try:
raise # Something really strange is happening
logging.warning('Could not create picture cache because of an error: %s', str(e))
self.con.close()
os.remove(self.dbname)
self._create_con(second_try=True)
def clear(self):
self.close()
if self.dbname != ':memory:':
os.remove(self.dbname)
self._create_con()
def close(self):
if self.con is not None:
self.con.close()
self.con = None
def filter(self, func): def filter(self, func):
to_delete = [key for key in self if not func(key)] to_delete = [key for key in self if not func(key)]
for key in to_delete: for key in to_delete:

View File

@@ -63,9 +63,9 @@ def GetDupeSortKey(dupe, get_group, key, delta):
return m.percentage return m.percentage
if key == 8: if key == 8:
return 0 return 0
r = cmp_value(getattr(dupe, COLUMNS[key]['attr'])) r = cmp_value(getattr(dupe, COLUMNS[key]['attr'], ''))
if delta and (key in (2, 5, 6)): if delta and (key in (2, 5, 6)):
r -= cmp_value(getattr(get_group().ref, COLUMNS[key]['attr'])) r -= cmp_value(getattr(get_group().ref, COLUMNS[key]['attr'], ''))
return r return r
def GetGroupSortKey(group, key): def GetGroupSortKey(group, key):
@@ -73,5 +73,5 @@ def GetGroupSortKey(group, key):
return group.percentage return group.percentage
if key == 8: if key == 8:
return len(group) return len(group)
return cmp_value(getattr(group.ref, COLUMNS[key]['attr'])) return cmp_value(getattr(group.ref, COLUMNS[key]['attr'], ''))

View File

@@ -18,14 +18,11 @@ def move(src, dst):
print 'Moving %s --> %s' % (src, dst) print 'Moving %s --> %s' % (src, dst)
os.rename(src, dst) os.rename(src, dst)
# The CC=gcc-4.0 thing is because, in Snow Leopard, gcc-4.2 can't compile these units. os.chdir('modules')
os.environ['CC'] = 'gcc-4.0'
os.chdir(op.join('modules', 'block'))
os.system('python setup.py build_ext --inplace') os.system('python setup.py build_ext --inplace')
os.chdir(op.join('..', 'cache')) os.chdir('..')
os.system('python setup.py build_ext --inplace') move(op.join('modules', '_block.so'), '_block.so')
os.chdir(op.join('..', '..')) move(op.join('modules', '_block.pyd'), '_block.pyd')
move(op.join('modules', 'block', '_block.so'), '_block.so') move(op.join('modules', '_block_osx.so'), '_block_osx.so')
move(op.join('modules', 'block', '_block.pyd'), '_block.pyd') move(op.join('modules', '_cache.so'), '_cache.so')
move(op.join('modules', 'cache', '_cache.so'), '_cache.so') move(op.join('modules', '_cache.pyd'), '_cache.pyd')
move(op.join('modules', 'cache', '_cache.pyd'), '_cache.pyd')

View File

@@ -26,20 +26,21 @@ BLOCK_COUNT_PER_SIDE = 15
# collection made by the main process. # collection made by the main process.
RESULTS_QUEUE_LIMIT = multiprocessing.cpu_count() * 2 RESULTS_QUEUE_LIMIT = multiprocessing.cpu_count() * 2
def prepare_pictures(pictures, cached_blocks, j=job.nulljob): def prepare_pictures(pictures, cache_path, j=job.nulljob):
# The MemoryError handlers in there use logging without first caring about whether or not # The MemoryError handlers in there use logging without first caring about whether or not
# there is enough memory left to carry on the operation because it is assumed that the # there is enough memory left to carry on the operation because it is assumed that the
# MemoryError happens when trying to read an image file, which is freed from memory by the # MemoryError happens when trying to read an image file, which is freed from memory by the
# time that MemoryError is raised. # time that MemoryError is raised.
cache = Cache(cache_path)
prepared = [] # only pictures for which there was no error getting blocks prepared = [] # only pictures for which there was no error getting blocks
try: try:
for picture in j.iter_with_progress(pictures, 'Analyzed %d/%d pictures'): for picture in j.iter_with_progress(pictures, 'Analyzed %d/%d pictures'):
picture.dimensions picture.dimensions
picture.unicode_path = unicode(picture.path) picture.unicode_path = unicode(picture.path)
try: try:
if picture.unicode_path not in cached_blocks: if picture.unicode_path not in cache:
blocks = picture.get_blocks(BLOCK_COUNT_PER_SIDE) blocks = picture.get_blocks(BLOCK_COUNT_PER_SIDE)
cached_blocks[picture.unicode_path] = blocks cache[picture.unicode_path] = blocks
prepared.append(picture) prepared.append(picture)
except IOError as e: except IOError as e:
logging.warning(unicode(e)) logging.warning(unicode(e))
@@ -49,6 +50,7 @@ def prepare_pictures(pictures, cached_blocks, j=job.nulljob):
raise raise
except MemoryError: except MemoryError:
logging.warning('Ran out of memory while preparing pictures') logging.warning('Ran out of memory while preparing pictures')
cache.close()
return prepared return prepared
def get_match(first, second, percentage): def get_match(first, second, percentage):
@@ -57,7 +59,7 @@ def get_match(first, second, percentage):
return Match(first, second, percentage) return Match(first, second, percentage)
def async_compare(ref_id, other_ids, dbname, threshold): def async_compare(ref_id, other_ids, dbname, threshold):
cache = Cache(dbname, threaded=False) cache = Cache(dbname)
limit = 100 - threshold limit = 100 - threshold
ref_blocks = cache[ref_id] ref_blocks = cache[ref_id]
pairs = cache.get_multiple(other_ids) pairs = cache.get_multiple(other_ids)
@@ -70,10 +72,10 @@ def async_compare(ref_id, other_ids, dbname, threshold):
percentage = 0 percentage = 0
if percentage >= threshold: if percentage >= threshold:
results.append((ref_id, other_id, percentage)) results.append((ref_id, other_id, percentage))
cache.con.close() cache.close()
return results return results
def getmatches(pictures, cached_blocks, threshold=75, match_scaled=False, j=job.nulljob): def getmatches(pictures, cache_path, threshold=75, match_scaled=False, j=job.nulljob):
def empty_out_queue(queue, into): def empty_out_queue(queue, into):
try: try:
while True: while True:
@@ -82,9 +84,9 @@ def getmatches(pictures, cached_blocks, threshold=75, match_scaled=False, j=job.
pass pass
j = j.start_subjob([3, 7]) j = j.start_subjob([3, 7])
pictures = prepare_pictures(pictures, cached_blocks, j) pictures = prepare_pictures(pictures, cache_path, j)
j = j.start_subjob([9, 1], 'Preparing for matching') j = j.start_subjob([9, 1], 'Preparing for matching')
cache = cached_blocks cache = Cache(cache_path)
id2picture = {} id2picture = {}
dimensions2pictures = defaultdict(set) dimensions2pictures = defaultdict(set)
for picture in pictures: for picture in pictures:
@@ -95,6 +97,7 @@ def getmatches(pictures, cached_blocks, threshold=75, match_scaled=False, j=job.
dimensions2pictures[picture.dimensions].add(picture) dimensions2pictures[picture.dimensions].add(picture)
except ValueError: except ValueError:
pass pass
cache.close()
pictures = [p for p in pictures if hasattr(p, 'cache_id')] pictures = [p for p in pictures if hasattr(p, 'cache_id')]
pool = multiprocessing.Pool() pool = multiprocessing.Pool()
async_results = [] async_results = []
@@ -108,8 +111,13 @@ def getmatches(pictures, cached_blocks, threshold=75, match_scaled=False, j=job.
others = [pic for pic in others if not pic.is_ref] others = [pic for pic in others if not pic.is_ref]
if others: if others:
cache_ids = [f.cache_id for f in others] cache_ids = [f.cache_id for f in others]
args = (ref.cache_id, cache_ids, cached_blocks.dbname, threshold) # We limit the number of cache_ids we send for multi-processing because otherwise, we
# might get an error saying "String or BLOB exceeded size limit"
ARG_LIMIT = 1000
while cache_ids:
args = (ref.cache_id, cache_ids[:ARG_LIMIT], cache_path, threshold)
async_results.append(pool.apply_async(async_compare, args)) async_results.append(pool.apply_async(async_compare, args))
cache_ids = cache_ids[ARG_LIMIT:]
if len(async_results) > RESULTS_QUEUE_LIMIT: if len(async_results) > RESULTS_QUEUE_LIMIT:
result = async_results.pop(0) result = async_results.pop(0)
matches.extend(result.get()) matches.extend(result.get())

239
core_pe/modules/block.c Normal file
View File

@@ -0,0 +1,239 @@
/* Created By: Virgil Dupras
* Created On: 2010-01-30
* Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
*
* This software is licensed under the "HS" License as described in the "LICENSE" file,
* which should be included with this package. The terms are also available at
* http://www.hardcoded.net/licenses/hs_license
*/
#include "common.h"
/* avgdiff/maxdiff has been called with empty lists */
static PyObject *NoBlocksError;
/* avgdiff/maxdiff has been called with 2 block lists of different size. */
static PyObject *DifferentBlockCountError;
/* Returns a 3 sized tuple containing the mean color of 'image'.
* image: a PIL image or crop.
*/
static PyObject* getblock(PyObject *image)
{
int i, totr, totg, totb;
Py_ssize_t pixel_count;
PyObject *ppixels;
totr = totg = totb = 0;
ppixels = PyObject_CallMethod(image, "getdata", NULL);
if (ppixels == NULL) {
return NULL;
}
pixel_count = PySequence_Length(ppixels);
for (i=0; i<pixel_count; i++) {
PyObject *ppixel, *pr, *pg, *pb;
int r, g, b;
ppixel = PySequence_ITEM(ppixels, i);
pr = PySequence_ITEM(ppixel, 0);
pg = PySequence_ITEM(ppixel, 1);
pb = PySequence_ITEM(ppixel, 2);
Py_DECREF(ppixel);
r = PyInt_AsSsize_t(pr);
g = PyInt_AsSsize_t(pg);
b = PyInt_AsSsize_t(pb);
Py_DECREF(pr);
Py_DECREF(pg);
Py_DECREF(pb);
totr += r;
totg += g;
totb += b;
}
Py_DECREF(ppixels);
if (pixel_count) {
totr /= pixel_count;
totg /= pixel_count;
totb /= pixel_count;
}
return inttuple(3, totr, totg, totb);
}
/* Returns the difference between the first block and the second.
* It returns an absolute sum of the 3 differences (RGB).
*/
static int diff(PyObject *first, PyObject *second)
{
Py_ssize_t r1, g1, b1, r2, b2, g2;
PyObject *pr, *pg, *pb;
pr = PySequence_ITEM(first, 0);
pg = PySequence_ITEM(first, 1);
pb = PySequence_ITEM(first, 2);
r1 = PyInt_AsSsize_t(pr);
g1 = PyInt_AsSsize_t(pg);
b1 = PyInt_AsSsize_t(pb);
Py_DECREF(pr);
Py_DECREF(pg);
Py_DECREF(pb);
pr = PySequence_ITEM(second, 0);
pg = PySequence_ITEM(second, 1);
pb = PySequence_ITEM(second, 2);
r2 = PyInt_AsSsize_t(pr);
g2 = PyInt_AsSsize_t(pg);
b2 = PyInt_AsSsize_t(pb);
Py_DECREF(pr);
Py_DECREF(pg);
Py_DECREF(pb);
return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2);
}
PyDoc_STRVAR(block_getblocks2_doc,
"Returns a list of blocks (3 sized tuples).\n\
\n\
image: A PIL image to base the blocks on.\n\
block_count_per_side: This integer determine the number of blocks the function will return.\n\
If it is 10, for example, 100 blocks will be returns (10 width, 10 height). The blocks will not\n\
necessarely cover square areas. The area covered by each block will be proportional to the image\n\
itself.\n");
static PyObject* block_getblocks2(PyObject *self, PyObject *args)
{
int block_count_per_side, width, height, block_width, block_height, ih;
PyObject *image;
PyObject *pimage_size, *pwidth, *pheight;
PyObject *result;
if (!PyArg_ParseTuple(args, "Oi", &image, &block_count_per_side)) {
return NULL;
}
pimage_size = PyObject_GetAttrString(image, "size");
pwidth = PySequence_ITEM(pimage_size, 0);
pheight = PySequence_ITEM(pimage_size, 1);
width = PyInt_AsSsize_t(pwidth);
height = PyInt_AsSsize_t(pheight);
Py_DECREF(pimage_size);
Py_DECREF(pwidth);
Py_DECREF(pheight);
if (!(width && height)) {
return PyList_New(0);
}
block_width = max(width / block_count_per_side, 1);
block_height = max(height / block_count_per_side, 1);
result = PyList_New(block_count_per_side * block_count_per_side);
if (result == NULL) {
return NULL;
}
for (ih=0; ih<block_count_per_side; ih++) {
int top, bottom, iw;
top = min(ih*block_height, height-block_height);
bottom = top + block_height;
for (iw=0; iw<block_count_per_side; iw++) {
int left, right;
PyObject *pbox;
PyObject *pmethodname;
PyObject *pcrop;
PyObject *pblock;
left = min(iw*block_width, width-block_width);
right = left + block_width;
pbox = inttuple(4, left, top, right, bottom);
pmethodname = PyString_FromString("crop");
pcrop = PyObject_CallMethodObjArgs(image, pmethodname, pbox);
Py_DECREF(pmethodname);
Py_DECREF(pbox);
if (pcrop == NULL) {
Py_DECREF(result);
return NULL;
}
pblock = getblock(pcrop);
Py_DECREF(pcrop);
if (pblock == NULL) {
Py_DECREF(result);
return NULL;
}
PyList_SET_ITEM(result, ih*block_count_per_side+iw, pblock);
}
}
return result;
}
PyDoc_STRVAR(block_avgdiff_doc,
"Returns the average diff between first blocks and seconds.\n\
\n\
If the result surpasses limit, limit + 1 is returned, except if less than min_iterations\n\
iterations have been made in the blocks.\n");
static PyObject* block_avgdiff(PyObject *self, PyObject *args)
{
PyObject *first, *second;
int limit, min_iterations;
Py_ssize_t count;
int sum, i, result;
if (!PyArg_ParseTuple(args, "OOii", &first, &second, &limit, &min_iterations)) {
return NULL;
}
count = PySequence_Length(first);
if (count != PySequence_Length(second)) {
PyErr_SetString(DifferentBlockCountError, "");
return NULL;
}
if (!count) {
PyErr_SetString(NoBlocksError, "");
return NULL;
}
sum = 0;
for (i=0; i<count; i++) {
int iteration_count;
PyObject *item1, *item2;
iteration_count = i + 1;
item1 = PySequence_ITEM(first, i);
item2 = PySequence_ITEM(second, i);
sum += diff(item1, item2);
Py_DECREF(item1);
Py_DECREF(item2);
if ((sum > limit*iteration_count) && (iteration_count >= min_iterations)) {
return PyInt_FromSsize_t(limit + 1);
}
}
result = sum / count;
if (!result && sum) {
result = 1;
}
return PyInt_FromSsize_t(result);
}
static PyMethodDef BlockMethods[] = {
{"getblocks2", block_getblocks2, METH_VARARGS, block_getblocks2_doc},
{"avgdiff", block_avgdiff, METH_VARARGS, block_avgdiff_doc},
{NULL, NULL, 0, NULL} /* Sentinel */
};
PyMODINIT_FUNC
init_block(void)
{
PyObject *m = Py_InitModule("_block", BlockMethods);
if (m == NULL) {
return;
}
NoBlocksError = PyErr_NewException("_block.NoBlocksError", NULL, NULL);
PyModule_AddObject(m, "NoBlocksError", NoBlocksError);
DifferentBlockCountError = PyErr_NewException("_block.DifferentBlockCountError", NULL, NULL);
PyModule_AddObject(m, "DifferentBlockCountError", DifferentBlockCountError);
}

View File

@@ -1,96 +0,0 @@
# Created By: Virgil Dupras
# Created On: 2009-04-23
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
#
# This software is licensed under the "HS" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/hs_license
cdef extern from "stdlib.h":
int abs(int n) # required so that abs() is applied on ints, not python objects
class NoBlocksError(Exception):
"""avgdiff/maxdiff has been called with empty lists"""
class DifferentBlockCountError(Exception):
"""avgdiff/maxdiff has been called with 2 block lists of different size."""
cdef object getblock(object image):
"""Returns a 3 sized tuple containing the mean color of 'image'.
image: a PIL image or crop.
"""
cdef int pixel_count, red, green, blue, r, g, b
if image.size[0]:
pixel_count = image.size[0] * image.size[1]
red = green = blue = 0
for r, g, b in image.getdata():
red += r
green += g
blue += b
return (red // pixel_count, green // pixel_count, blue // pixel_count)
else:
return (0, 0, 0)
def getblocks2(image, int block_count_per_side):
"""Returns a list of blocks (3 sized tuples).
image: A PIL image to base the blocks on.
block_count_per_side: This integer determine the number of blocks the function will return.
If it is 10, for example, 100 blocks will be returns (10 width, 10 height). The blocks will not
necessarely cover square areas. The area covered by each block will be proportional to the image
itself.
"""
if not image.size[0]:
return []
cdef int width, height, block_width, block_height, ih, iw, top, bottom, left, right
width, height = image.size
block_width = max(width // block_count_per_side, 1)
block_height = max(height // block_count_per_side, 1)
result = []
for ih in range(block_count_per_side):
top = min(ih * block_height, height - block_height)
bottom = top + block_height
for iw in range(block_count_per_side):
left = min(iw * block_width, width - block_width)
right = left + block_width
box = (left, top, right, bottom)
crop = image.crop(box)
result.append(getblock(crop))
return result
cdef int diff(first, second):
"""Returns the difference between the first block and the second.
It returns an absolute sum of the 3 differences (RGB).
"""
cdef int r1, g1, b1, r2, g2, b2
r1, g1, b1 = first
r2, g2, b2 = second
return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2)
def avgdiff(first, second, int limit, int min_iterations):
"""Returns the average diff between first blocks and seconds.
If the result surpasses limit, limit + 1 is returned, except if less than min_iterations
iterations have been made in the blocks.
"""
cdef int count, sum, i, iteration_count
count = len(first)
if count != len(second):
raise DifferentBlockCountError()
if not count:
raise NoBlocksError()
sum = 0
for i in range(count):
iteration_count = i + 1
item1 = first[i]
item2 = second[i]
sum += diff(item1, item2)
if sum > limit * iteration_count and iteration_count >= min_iterations:
return limit + 1
result = sum // count
if (not result) and sum:
result = 1
return result

230
core_pe/modules/block_osx.m Normal file
View File

@@ -0,0 +1,230 @@
/* Created By: Virgil Dupras
* Created On: 2010-02-04
* Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
*
* This software is licensed under the "HS" License as described in the "LICENSE" file,
* which should be included with this package. The terms are also available at
* http://www.hardcoded.net/licenses/hs_license
**/
#include "common.h"
#import <Foundation/Foundation.h>
static CFStringRef
pystring2cfstring(PyObject *pystring)
{
PyObject *encoded;
UInt8 *s;
CFIndex size;
CFStringRef result;
if (PyUnicode_Check(pystring)) {
encoded = PyUnicode_AsUTF8String(pystring);
if (encoded == NULL) {
return NULL;
}
} else {
encoded = pystring;
Py_INCREF(encoded);
}
s = (UInt8*)PyString_AS_STRING(encoded);
size = PyString_GET_SIZE(encoded);
result = CFStringCreateWithBytes(NULL, s, size, kCFStringEncodingUTF8, FALSE);
Py_DECREF(encoded);
return result;
}
static PyObject* block_osx_get_image_size(PyObject *self, PyObject *args)
{
PyObject *path;
CFStringRef image_path;
CFURLRef image_url;
CGImageSourceRef source;
CGImageRef image;
size_t width, height;
PyObject *pwidth, *pheight;
PyObject *result;
width = 0;
height = 0;
if (!PyArg_ParseTuple(args, "O", &path)) {
return NULL;
}
image_path = pystring2cfstring(path);
if (image_path == NULL) {
return PyErr_NoMemory();
}
image_url = CFURLCreateWithFileSystemPath(NULL, image_path, kCFURLPOSIXPathStyle, FALSE);
CFRelease(image_path);
source = CGImageSourceCreateWithURL(image_url, NULL);
CFRelease(image_url);
if (source != NULL) {
image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
if (image != NULL) {
width = CGImageGetWidth(image);
height = CGImageGetHeight(image);
CGImageRelease(image);
}
CFRelease(source);
}
pwidth = PyInt_FromSsize_t(width);
if (pwidth == NULL) {
return NULL;
}
pheight = PyInt_FromSsize_t(height);
if (pheight == NULL) {
return NULL;
}
result = PyTuple_Pack(2, pwidth, pheight);
Py_DECREF(pwidth);
Py_DECREF(pheight);
return result;
}
static CGContextRef
MyCreateBitmapContext(int width, int height)
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void *bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
bitmapBytesPerRow = (width * 4);
bitmapByteCount = (bitmapBytesPerRow * height);
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
// calloc() must be used to allocate bitmapData here because the buffer has to be zeroed.
// If it's not zeroes, when images with transparency are drawn in the context, this buffer
// will stay with undefined pixels, which means that two pictures with the same pixels will
// most likely have different blocks (which is not supposed to happen).
bitmapData = calloc(bitmapByteCount, 1);
if (bitmapData == NULL) {
fprintf(stderr, "Memory not allocated!");
return NULL;
}
context = CGBitmapContextCreate(bitmapData, width, height, 8, bitmapBytesPerRow, colorSpace,
kCGImageAlphaNoneSkipLast);
if (context== NULL) {
free(bitmapData);
fprintf(stderr, "Context not created!");
return NULL;
}
CGColorSpaceRelease(colorSpace);
return context;
}
static PyObject* getblock(unsigned char *imageData, int imageWidth, int imageHeight, int boxX, int boxY, int boxW, int boxH)
{
int i,j, totalR, totalG, totalB;
totalR = totalG = totalB = 0;
for(i=boxY; i<boxY+boxH; i++) {
for(j=boxX; j<boxX+boxW; j++) {
int offset = (i * imageWidth * 4) + (j * 4);
totalR += *(imageData + offset);
totalG += *(imageData + offset + 1);
totalB += *(imageData + offset + 2);
}
}
int pixelCount = boxH * boxW;
totalR /= pixelCount;
totalG /= pixelCount;
totalB /= pixelCount;
return inttuple(3, totalR, totalG, totalB);
}
static PyObject* block_osx_getblocks(PyObject *self, PyObject *args)
{
PyObject *path, *result;
CFStringRef image_path;
CFURLRef image_url;
CGImageSourceRef source;
CGImageRef image;
size_t width, height;
int block_count, block_width, block_height, i;
width = 0;
height = 0;
if (!PyArg_ParseTuple(args, "Oi", &path, &block_count)) {
return NULL;
}
image_path = pystring2cfstring(path);
if (image_path == NULL) {
return PyErr_NoMemory();
}
image_url = CFURLCreateWithFileSystemPath(NULL, image_path, kCFURLPOSIXPathStyle, FALSE);
CFRelease(image_path);
source = CGImageSourceCreateWithURL(image_url, NULL);
CFRelease(image_url);
if (source == NULL) {
return PyErr_NoMemory();
}
image = CGImageSourceCreateImageAtIndex(source, 0, NULL);
if (image == NULL) {
CFRelease(source);
return PyErr_NoMemory();
}
width = CGImageGetWidth(image);
height = CGImageGetHeight(image);
CGContextRef myContext = MyCreateBitmapContext(width, height);
CGRect myBoundingBox = CGRectMake(0, 0, width, height);
CGContextDrawImage(myContext, myBoundingBox, image);
unsigned char *bitmapData = CGBitmapContextGetData(myContext);
CGContextRelease(myContext);
CGImageRelease(image);
CFRelease(source);
if (bitmapData == NULL) {
return PyErr_NoMemory();
}
block_width = max(width/block_count, 1);
block_height = max(height/block_count, 1);
result = PyList_New(block_count * block_count);
if (result == NULL) {
return NULL;
}
for(i=0; i<block_count; i++) {
int j, top;
top = min(i*block_height, height-block_height);
for(j=0; j<block_count; j++) {
int left;
left = min(j*block_width, width-block_width);
PyObject *block = getblock(bitmapData, width, height, left, top, block_width, block_height);
if (block == NULL) {
Py_DECREF(result);
return NULL;
}
PyList_SET_ITEM(result, i*block_count+j, block);
}
}
free(bitmapData);
return result;
}
static PyMethodDef BlockOsxMethods[] = {
{"get_image_size", block_osx_get_image_size, METH_VARARGS, ""},
{"getblocks", block_osx_getblocks, METH_VARARGS, ""},
{NULL, NULL, 0, NULL} /* Sentinel */
};
PyMODINIT_FUNC
init_block_osx(void)
{
Py_InitModule("_block_osx", BlockOsxMethods);
}

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