mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-25 08:01:39 +00:00
Compare commits
704 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4de40af1b0 | ||
|
|
54988650d7 | ||
|
|
a47c208f45 | ||
|
|
c321427a8f | ||
|
|
26c77a18fd | ||
|
|
9f19451ac7 | ||
|
|
bcd9d7e7d0 | ||
|
|
b42b0be512 | ||
|
|
d90764a9ea | ||
|
|
c5c4e02bf4 | ||
|
|
fae3a6ac3a | ||
|
|
09b91aab66 | ||
|
|
b5a219cc00 | ||
|
|
9cb62e0544 | ||
|
|
653668dd96 | ||
|
|
5a5a74d0e1 | ||
|
|
5247ac8abd | ||
|
|
f992599beb | ||
|
|
51f8c51ef3 | ||
|
|
fcdc692b61 | ||
|
|
deb5260c6a | ||
|
|
81df280ea6 | ||
|
|
4f097a3a89 | ||
|
|
8cd1e13814 | ||
|
|
7e81e6c93f | ||
|
|
b19d6c9a27 | ||
|
|
977fb606eb | ||
|
|
9e7d27dcda | ||
|
|
caf04f0d3f | ||
|
|
a2553da578 | ||
|
|
3cd44705f8 | ||
|
|
0cf6987083 | ||
|
|
a67f7e2c9e | ||
|
|
5a3b6883fa | ||
|
|
b0f9a94375 | ||
|
|
ad5c4a954c | ||
|
|
292d993dce | ||
|
|
1fe42f673f | ||
|
|
244af5b652 | ||
|
|
abe9041a67 | ||
|
|
a2d73b216c | ||
|
|
f08b593acb | ||
|
|
cb35dc7897 | ||
|
|
903ecd9eae | ||
|
|
79e9251511 | ||
|
|
81daef6145 | ||
|
|
6a7af81685 | ||
|
|
b3db7c6842 | ||
|
|
85e5b4cfa7 | ||
|
|
79e6020982 | ||
|
|
b7e7e67c99 | ||
|
|
1017e3c730 | ||
|
|
b74e33f4b0 | ||
|
|
2d0facdb14 | ||
|
|
c34004ed94 | ||
|
|
4db5fae38b | ||
|
|
5d5670d4be | ||
|
|
e21a7e18b4 | ||
|
|
a29ed235f6 | ||
|
|
fd706e752f | ||
|
|
729db49183 | ||
|
|
a68e4310ee | ||
|
|
500314859d | ||
|
|
b6d457f908 | ||
|
|
6850c7e2f8 | ||
|
|
9e5630fe99 | ||
|
|
f1a21e62cd | ||
|
|
7cc2defa35 | ||
|
|
24a11ee4bd | ||
|
|
e2b23ca961 | ||
|
|
8fabb14b8b | ||
|
|
07de7d6f0e | ||
|
|
e0b844f617 | ||
|
|
a7bc76bf7c | ||
|
|
89fb531f3d | ||
|
|
f22baa8d5a | ||
|
|
7c2e601a30 | ||
|
|
16e4a5fddd | ||
|
|
4200f2a090 | ||
|
|
45c8291645 | ||
|
|
89f8214bce | ||
|
|
16e1ee93d0 | ||
|
|
222ae73590 | ||
|
|
21c0292154 | ||
|
|
a7eb90894a | ||
|
|
64baf2a10c | ||
|
|
392a802ef1 | ||
|
|
8efeab7b40 | ||
|
|
8a86ecee38 | ||
|
|
3e79b57409 | ||
|
|
362e020585 | ||
|
|
df5c8ddf22 | ||
|
|
70cc48d51f | ||
|
|
dccffd9516 | ||
|
|
04056c1597 | ||
|
|
69b2e37368 | ||
|
|
05478591a4 | ||
|
|
ead3b1e651 | ||
|
|
27f4c290c4 | ||
|
|
4f248ee981 | ||
|
|
d7397c0125 | ||
|
|
e4430168f7 | ||
|
|
78c2ae150d | ||
|
|
806e3917e3 | ||
|
|
6c62c2d563 | ||
|
|
7a768bd0e6 | ||
|
|
a6c01f6868 | ||
|
|
721591d3e3 | ||
|
|
1171705921 | ||
|
|
65d2581f74 | ||
|
|
2128d1787a | ||
|
|
cbf2ab82ce | ||
|
|
adef5a9dfa | ||
|
|
0c9e5c90a3 | ||
|
|
787f546c17 | ||
|
|
2d4ecf1122 | ||
|
|
1836003506 | ||
|
|
44d34f56f7 | ||
|
|
24c3d7ed00 | ||
|
|
8f7657573d | ||
|
|
c03c7f2be1 | ||
|
|
91a157cb2b | ||
|
|
7ced1e7b9d | ||
|
|
f4b8efff50 | ||
|
|
4c0545189e | ||
|
|
21af7b2fcd | ||
|
|
df9af9a796 | ||
|
|
933474400c | ||
|
|
b2c8e779bd | ||
|
|
e570a308bb | ||
|
|
69c01a658b | ||
|
|
0741cbe17b | ||
|
|
ad4ccfe347 | ||
|
|
57375f83f6 | ||
|
|
2d75ffefa7 | ||
|
|
87aa181e4b | ||
|
|
1f359e0391 | ||
|
|
c8af8ccc61 | ||
|
|
2daaaee893 | ||
|
|
03712860b1 | ||
|
|
9e1f79c152 | ||
|
|
edc385cfbc | ||
|
|
0f62917b55 | ||
|
|
515c195780 | ||
|
|
c63df97dd6 | ||
|
|
1f312d8532 | ||
|
|
fa547bb95e | ||
|
|
b500b34ef1 | ||
|
|
23cb71b522 | ||
|
|
720d2fce83 | ||
|
|
97f447fc27 | ||
|
|
6ee56dc55c | ||
|
|
771f22f208 | ||
|
|
80ff5f64f7 | ||
|
|
d0bfa2a6ca | ||
|
|
04a547656e | ||
|
|
657f6743c2 | ||
|
|
42b57431e0 | ||
|
|
3fc83d6245 | ||
|
|
ae16845477 | ||
|
|
49a7043b4d | ||
|
|
878c744c21 | ||
|
|
b4b9393e14 | ||
|
|
bf17eb715a | ||
|
|
cd9f54163b | ||
|
|
ef0a66f794 | ||
|
|
5fb7742cf4 | ||
|
|
22de2d803a | ||
|
|
188aa4bf2e | ||
|
|
05552c160c | ||
|
|
dd70bd6d41 | ||
|
|
70f88ba39c | ||
|
|
496f29b5c3 | ||
|
|
b0818f2bdf | ||
|
|
e0efd660f6 | ||
|
|
4592000464 | ||
|
|
93781a0f35 | ||
|
|
7dfb42fb41 | ||
|
|
a1fc64cd36 | ||
|
|
b12b70b0a1 | ||
|
|
9457a43993 | ||
|
|
ab201bfae0 | ||
|
|
66204ff0b5 | ||
|
|
95f77aef3f | ||
|
|
1e96b6f9c4 | ||
|
|
f813673d6c | ||
|
|
1da325a56b | ||
|
|
d885bf0b18 | ||
|
|
5f7e7fbb13 | ||
|
|
757f8569e6 | ||
|
|
bf86f56371 | ||
|
|
7d0948cd23 | ||
|
|
c4824b0f16 | ||
|
|
ea32af04fb | ||
|
|
e519e41e42 | ||
|
|
c937f2b20f | ||
|
|
fc477ab3b5 | ||
|
|
c53ddcdee1 | ||
|
|
9e668b11b8 | ||
|
|
f53a4c261c | ||
|
|
9a2554d24e | ||
|
|
b9ac135b89 | ||
|
|
235d3ae521 | ||
|
|
a487e83f3c | ||
|
|
4aaff8ffb1 | ||
|
|
302050b2d6 | ||
|
|
1b571f6fd2 | ||
|
|
baa2177439 | ||
|
|
29796e87b7 | ||
|
|
017e483b5a | ||
|
|
7a167208d0 | ||
|
|
475f2c7238 | ||
|
|
9392f818cc | ||
|
|
58347bc36f | ||
|
|
55db21f3e0 | ||
|
|
950cd0c341 | ||
|
|
937ea73c87 | ||
|
|
7bce70c128 | ||
|
|
f9ced08e6d | ||
|
|
4c8ce4b52d | ||
|
|
0d78201548 | ||
|
|
45d4915d88 | ||
|
|
a699a2ef45 | ||
|
|
f6dd1a6a42 | ||
|
|
e6819781f6 | ||
|
|
12467c9493 | ||
|
|
0c7d73854d | ||
|
|
a7eeb7db89 | ||
|
|
989026051c | ||
|
|
56fd94e205 | ||
|
|
c8e00cf0f7 | ||
|
|
0cca745d0d | ||
|
|
80043ccbea | ||
|
|
d62bfac95e | ||
|
|
f636333938 | ||
|
|
01f1e5e46e | ||
|
|
7ce72b1998 | ||
|
|
c4f95a4901 | ||
|
|
5b0d9f311c | ||
|
|
11d8f824e9 | ||
|
|
ceaf2ee4ba | ||
|
|
3b80de869a | ||
|
|
08813ce39c | ||
|
|
478f462ecc | ||
|
|
be53b6de76 | ||
|
|
ca602480d9 | ||
|
|
185cdbb6fa | ||
|
|
2d4903da26 | ||
|
|
98954bd582 | ||
|
|
f862f32fb4 | ||
|
|
804a5a1bbf | ||
|
|
f004535820 | ||
|
|
2abd932709 | ||
|
|
2a78b8ce41 | ||
|
|
2301082307 | ||
|
|
168546608d | ||
|
|
27c1a03496 | ||
|
|
d382cec0fe | ||
|
|
c5b7f6b3d5 | ||
|
|
725f9d51db | ||
|
|
b4815d91c7 | ||
|
|
28e5924633 | ||
|
|
66303a2076 | ||
|
|
d3918724c0 | ||
|
|
fa294ea142 | ||
|
|
565c58b3a9 | ||
|
|
d8970ca6b4 | ||
|
|
1b7068bfe9 | ||
|
|
756190cb8e | ||
|
|
3342b32882 | ||
|
|
561b469e41 | ||
|
|
69fbda5d2c | ||
|
|
63180eaa5b | ||
|
|
25faa458b9 | ||
|
|
760e4085fa | ||
|
|
fbe66d27c9 | ||
|
|
08fd17f208 | ||
|
|
f8af6dbd18 | ||
|
|
54d6fb080c | ||
|
|
8409a01bcc | ||
|
|
1e136d2703 | ||
|
|
25afe54be3 | ||
|
|
d9ae967439 | ||
|
|
9226a4fb7c | ||
|
|
fc5a0d914b | ||
|
|
fca66d5108 | ||
|
|
0571151c5f | ||
|
|
7e95404903 | ||
|
|
eb83b830df | ||
|
|
0b1bf79796 | ||
|
|
6ab074decb | ||
|
|
b7462f1d17 | ||
|
|
a2a8397e78 | ||
|
|
5c0d9411e5 | ||
|
|
cd2afeb32b | ||
|
|
b2fd022d07 | ||
|
|
878046b579 | ||
|
|
428a400848 | ||
|
|
8aa5826080 | ||
|
|
6b5d1e9894 | ||
|
|
07a6a37502 | ||
|
|
dd0af2fe15 | ||
|
|
df6d7141f1 | ||
|
|
81d4cdde33 | ||
|
|
88a613268d | ||
|
|
b7aa4a1ad8 | ||
|
|
6c75d3afdf | ||
|
|
771f26ba0f | ||
|
|
30676fd20b | ||
|
|
607433d918 | ||
|
|
29db39f144 | ||
|
|
49e49d5e1a | ||
|
|
b9b84c9b7d | ||
|
|
d80a56db78 | ||
|
|
036026d64a | ||
|
|
13ef2fae90 | ||
|
|
54731e4ba0 | ||
|
|
aa341bc5ed | ||
|
|
4426f924e2 | ||
|
|
a6deb04049 | ||
|
|
806d6be36c | ||
|
|
152a8772da | ||
|
|
5885ead5ab | ||
|
|
6fc5ce4bad | ||
|
|
a4ae503bd4 | ||
|
|
020746be10 | ||
|
|
436a8e686d | ||
|
|
b6d66f6d0b | ||
|
|
5284decd67 | ||
|
|
73d22de752 | ||
|
|
26e496a051 | ||
|
|
75f0ed14aa | ||
|
|
27cecb0dbc | ||
|
|
094d6702ba | ||
|
|
f8750dd392 | ||
|
|
76b873a504 | ||
|
|
dd031ffa1d | ||
|
|
a0991745e2 | ||
|
|
3553d1a458 | ||
|
|
1b855ad64b | ||
|
|
9a7a20472d | ||
|
|
9fac97c147 | ||
|
|
11aa2c147c | ||
|
|
2c260742f6 | ||
|
|
b8ac192d2a | ||
|
|
ae21ff988a | ||
|
|
8102c89802 | ||
|
|
48e2acf0a2 | ||
|
|
01731a8277 | ||
|
|
abe25d6967 | ||
|
|
669e4b390b | ||
|
|
1fafe04f19 | ||
|
|
43c4dcb267 | ||
|
|
b44e52689f | ||
|
|
f0441db88a | ||
|
|
0da1947902 | ||
|
|
3b4ea50119 | ||
|
|
e21627bbde | ||
|
|
70689ce057 | ||
|
|
60462698ac | ||
|
|
f2164924f7 | ||
|
|
f730f4f55a | ||
|
|
841b249b67 | ||
|
|
0f12103616 | ||
|
|
edac54c5e6 | ||
|
|
818bc908a0 | ||
|
|
26e81a8cbf | ||
|
|
664803c2ca | ||
|
|
5a26f1c2ae | ||
|
|
880f0787ce | ||
|
|
549e3e1f3b | ||
|
|
cf606a494c | ||
|
|
90f9493ccc | ||
|
|
3ec2a3ef81 | ||
|
|
b65c9b8c9a | ||
|
|
2dc588e0fd | ||
|
|
9c30486f14 | ||
|
|
518228a368 | ||
|
|
ff228035a3 | ||
|
|
eeb7f84601 | ||
|
|
ee24234156 | ||
|
|
d462fd44c4 | ||
|
|
22fedc4ee4 | ||
|
|
548bd84a4b | ||
|
|
d66afca753 | ||
|
|
7222ec3f02 | ||
|
|
f2d77bb60b | ||
|
|
4b9cba4d7f | ||
|
|
47c9d39150 | ||
|
|
14aba2b507 | ||
|
|
bb2faa27f9 | ||
|
|
e10e9a6976 | ||
|
|
73ba4954c1 | ||
|
|
2c8c077b82 | ||
|
|
f7a3e78870 | ||
|
|
38638a90f1 | ||
|
|
11cff312f5 | ||
|
|
17656d8e7c | ||
|
|
42cb788d35 | ||
|
|
327fe0b660 | ||
|
|
2cad94941b | ||
|
|
d93b5d65b9 | ||
|
|
9bf9dd330a | ||
|
|
4449831ace | ||
|
|
303cf52d6a | ||
|
|
21b1a63687 | ||
|
|
b1dce31542 | ||
|
|
a64ddcb804 | ||
|
|
905c194cdd | ||
|
|
e61c698b03 | ||
|
|
77f70d120a | ||
|
|
3df35be0cf | ||
|
|
04938bb573 | ||
|
|
df57d993f6 | ||
|
|
b74984f3b6 | ||
|
|
0bbdeb0846 | ||
|
|
5441da4630 | ||
|
|
366a55b27d | ||
|
|
34a1b5d9b5 | ||
|
|
2ba3584b7e | ||
|
|
99e3c34060 | ||
|
|
577cee1a38 | ||
|
|
734b790581 | ||
|
|
526bcf2566 | ||
|
|
56207f4dbb | ||
|
|
cd9fd3a10b | ||
|
|
4399fe9d17 | ||
|
|
2a6f524a5b | ||
|
|
caee5e37f0 | ||
|
|
bbd9d68dfd | ||
|
|
16b1b00906 | ||
|
|
7183408535 | ||
|
|
591b4c7c6a | ||
|
|
8b1170a82b | ||
|
|
1f26fbeacc | ||
|
|
cc7ccff48e | ||
|
|
a0809333c1 | ||
|
|
8975f78a5f | ||
|
|
56bc1c1373 | ||
|
|
417233a47f | ||
|
|
59eaf5305a | ||
|
|
275c6be108 | ||
|
|
a0e2b11663 | ||
|
|
de23ce90d8 | ||
|
|
9d5f3029d0 | ||
|
|
33ee220933 | ||
|
|
23d36b58c8 | ||
|
|
39b895f01b | ||
|
|
5f4252cddc | ||
|
|
fc54a1ea39 | ||
|
|
285f338dce | ||
|
|
379e420577 | ||
|
|
0b20b35ffb | ||
|
|
d887cd118c | ||
|
|
54ffcfab79 | ||
|
|
f28ffc680a | ||
|
|
f33f30eabf | ||
|
|
279d44b7f3 | ||
|
|
0fea59007c | ||
|
|
54720b15ca | ||
|
|
39fc7a91d7 | ||
|
|
7f9c322d48 | ||
|
|
b2ff02c773 | ||
|
|
2c242aedfd | ||
|
|
70e4e6f5af | ||
|
|
ebeb068042 | ||
|
|
731e68f164 | ||
|
|
fa0c3aeb78 | ||
|
|
5a36f15667 | ||
|
|
b96fae74b6 | ||
|
|
404743a27f | ||
|
|
58c8fd0f09 | ||
|
|
fb3d3a135d | ||
|
|
96bddd1995 | ||
|
|
6e60ea6984 | ||
|
|
e410f88926 | ||
|
|
1b52feb8b8 | ||
|
|
ec8e915830 | ||
|
|
0bdbbbdf16 | ||
|
|
92e0647f19 | ||
|
|
69498147a2 | ||
|
|
4249c528e9 | ||
|
|
084068852e | ||
|
|
c524a85897 | ||
|
|
d39d46be5a | ||
|
|
b8980b4667 | ||
|
|
e0adec7b2b | ||
|
|
eb8b9d663f | ||
|
|
fa4b0cf9ec | ||
|
|
f72db8dd1d | ||
|
|
c5bf0f228a | ||
|
|
e150b26cab | ||
|
|
da41d07dae | ||
|
|
c885cb35d8 | ||
|
|
7c38217308 | ||
|
|
a88519b814 | ||
|
|
0aa91b170c | ||
|
|
e9bb1c01f7 | ||
|
|
883875e88e | ||
|
|
4cab6b0ad2 | ||
|
|
91a2664830 | ||
|
|
6abbeaf987 | ||
|
|
21efef42f7 | ||
|
|
9d0e8d94ca | ||
|
|
0f57ca698c | ||
|
|
69c572e875 | ||
|
|
4083b60ff3 | ||
|
|
41fbeb7ec9 | ||
|
|
1162357b9b | ||
|
|
a2a526866f | ||
|
|
d3338b699e | ||
|
|
6c60e76b55 | ||
|
|
8a0d31f612 | ||
|
|
6fc7e5ace1 | ||
|
|
8175762e74 | ||
|
|
f48e14af8a | ||
|
|
cd2a61d926 | ||
|
|
f45997afe4 | ||
|
|
b6f56721cb | ||
|
|
f9e7e82772 | ||
|
|
dbcd7b63d8 | ||
|
|
bf807684dd | ||
|
|
f02fcb5e4b | ||
|
|
2c127adf59 | ||
|
|
7f8a357019 | ||
|
|
99daf5b7b7 | ||
|
|
42cff20710 | ||
|
|
04d7880a0c | ||
|
|
e7d26e3f82 | ||
|
|
19308bf686 | ||
|
|
92970489c5 | ||
|
|
d51f5184d7 | ||
|
|
30eb26af7d | ||
|
|
3ea43f8213 | ||
|
|
9833067ba7 | ||
|
|
ad3114c56b | ||
|
|
9da9c269c1 | ||
|
|
0a22bb8469 | ||
|
|
c9fd1b1a17 | ||
|
|
19b40d45c0 | ||
|
|
90e2a1cda0 | ||
|
|
5e47b9f4a7 | ||
|
|
50b6948250 | ||
|
|
3ef118c9fa | ||
|
|
064707db43 | ||
|
|
8f71a1318d | ||
|
|
1b8ab35fdd | ||
|
|
4a1fe2f8ab | ||
|
|
e6e4e14781 | ||
|
|
d139157234 | ||
|
|
94104f4e03 | ||
|
|
8bea978715 | ||
|
|
eefe464fba | ||
|
|
33c0ba808c | ||
|
|
e0cc8ecda2 | ||
|
|
2d423b2358 | ||
|
|
b5b27b141c | ||
|
|
800a879927 | ||
|
|
f6806e42db | ||
|
|
3aae21b810 | ||
|
|
75239d6a64 | ||
|
|
09082955a3 | ||
|
|
6a6f2d51aa | ||
|
|
7b0d3ea8ac | ||
|
|
1c88b6bb26 | ||
|
|
e5e8e5d908 | ||
|
|
92fadd26b7 | ||
|
|
45d783ac43 | ||
|
|
ea9e76e7ae | ||
|
|
28426c0e91 | ||
|
|
3a9f51b600 | ||
|
|
f1b4db368e | ||
|
|
95efac187b | ||
|
|
6770d22438 | ||
|
|
4ce97613c4 | ||
|
|
030eb8eb6e | ||
|
|
c9da8e26e6 | ||
|
|
7ddf9772df | ||
|
|
0382ad1534 | ||
|
|
1b6e1369a0 | ||
|
|
835050c337 | ||
|
|
ca6a42e6eb | ||
|
|
a2e4d893ac | ||
|
|
657520b0b3 | ||
|
|
ea4b87895c | ||
|
|
19db500a19 | ||
|
|
1366cfd478 | ||
|
|
56a6df1f68 | ||
|
|
a1b35a8abf | ||
|
|
8a8a181186 | ||
|
|
463a551f7d | ||
|
|
fc613fb325 | ||
|
|
4517bea664 | ||
|
|
81dcfbe6ae | ||
|
|
fa8e64d04a | ||
|
|
562123b219 | ||
|
|
b217309618 | ||
|
|
357a02c74b | ||
|
|
508eeffa6e | ||
|
|
31555aa473 | ||
|
|
d2f968def7 | ||
|
|
d574bc611b | ||
|
|
a50a3b0123 | ||
|
|
5b6891dd45 | ||
|
|
4886982d43 | ||
|
|
7360f57beb | ||
|
|
491279b7a8 | ||
|
|
05b79f81af | ||
|
|
96ef2f2dd3 | ||
|
|
2542af17b6 | ||
|
|
c86bc649ff | ||
|
|
4b8e48ed88 | ||
|
|
a1addfd416 | ||
|
|
a1a57d8933 | ||
|
|
864970b860 | ||
|
|
a056be0842 | ||
|
|
c672e75739 | ||
|
|
7b5dd3f964 | ||
|
|
a6072f608b | ||
|
|
06462c65a5 | ||
|
|
359f9c0680 | ||
|
|
01db7c4948 | ||
|
|
f67f14a78d | ||
|
|
0a64d653e1 | ||
|
|
456a835285 | ||
|
|
0d8ed92a68 | ||
|
|
9bd093a03c | ||
|
|
361d4698a9 | ||
|
|
b342b15011 | ||
|
|
95638a3a80 | ||
|
|
2204fe3355 | ||
|
|
abcd774c9d | ||
|
|
ee209f8f88 | ||
|
|
b1f2e1c191 | ||
|
|
33f372f6c6 | ||
|
|
8e5c2a8875 | ||
|
|
36f3638ae4 | ||
|
|
d10210011f | ||
|
|
e867840d81 | ||
|
|
fb7e3189a8 | ||
|
|
5733c0143b | ||
|
|
ac4881f231 | ||
|
|
939efd7dab | ||
|
|
a93d96d742 | ||
|
|
f21804c769 | ||
|
|
4bc05a8d46 | ||
|
|
eebe2b0e80 | ||
|
|
250a496a78 | ||
|
|
29163ed053 | ||
|
|
cc05661f9e | ||
|
|
89409c22d1 | ||
|
|
e2f240ebc9 | ||
|
|
8d56f4c33b | ||
|
|
36eccb7122 | ||
|
|
c8827769b4 | ||
|
|
12e6c400b9 | ||
|
|
4c273a7910 | ||
|
|
58da335b17 | ||
|
|
5b2d506462 | ||
|
|
531430d44a | ||
|
|
7450eec7eb | ||
|
|
3a5802435f | ||
|
|
1b6b058097 | ||
|
|
a5797a2350 | ||
|
|
e81a5147c5 | ||
|
|
565c990687 | ||
|
|
0ccdfe0e26 | ||
|
|
f8a558e3a7 | ||
|
|
c5fa195cc6 | ||
|
|
3a821edd45 | ||
|
|
854d194f88 | ||
|
|
fb79daad6a | ||
|
|
b2ae0e8759 | ||
|
|
09f73988b3 | ||
|
|
9e6f289319 | ||
|
|
d2a55ffd31 | ||
|
|
793c2aa423 | ||
|
|
5daa332b6c | ||
|
|
d5511a857c | ||
|
|
7fecd21331 | ||
|
|
88b79e512f | ||
|
|
853bf63777 | ||
|
|
ff16fea54a | ||
|
|
a03e2a69d4 | ||
|
|
56a39df635 | ||
|
|
ac1593ff75 | ||
|
|
4d66b4667c | ||
|
|
fdde538b66 | ||
|
|
de1147219c | ||
|
|
371426a08e | ||
|
|
75eb005ba0 | ||
|
|
601b67145c | ||
|
|
c65afbc057 | ||
|
|
378589a473 | ||
|
|
fa264972a4 | ||
|
|
6b10e01c03 | ||
|
|
5a6d74ab37 | ||
|
|
73f1bb6968 | ||
|
|
d1a7f51859 | ||
|
|
2ae16396a6 | ||
|
|
ef090a5dc5 | ||
|
|
5c0799e82b | ||
|
|
fa2ee01d3f | ||
|
|
d6ba80bd3f |
38
.hgignore
38
.hgignore
@@ -1,25 +1,33 @@
|
|||||||
syntax: glob
|
syntax: glob
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
run.py
|
||||||
*.pyc
|
*.pyc
|
||||||
*.so
|
*.so
|
||||||
*.mode1v3
|
*.mo
|
||||||
*.pbxuser
|
|
||||||
*.tm_build_errors
|
|
||||||
*.pyd
|
*.pyd
|
||||||
conf.yaml
|
*.waf*
|
||||||
|
.lock-waf*
|
||||||
|
*.xcodeproj/xcuserdata
|
||||||
|
*.xcodeproj/project.xcworkspace/xcuserdata
|
||||||
|
conf.json
|
||||||
build
|
build
|
||||||
|
dist
|
||||||
|
install
|
||||||
|
installer_tmp-cache
|
||||||
|
cocoa/autogen
|
||||||
cocoa/*/Info.plist
|
cocoa/*/Info.plist
|
||||||
cocoa/*/build
|
cocoa/*/build
|
||||||
cocoa/*/dg_cocoa.plugin
|
cocoa/*/*.app
|
||||||
|
cs.lproj
|
||||||
|
de.lproj
|
||||||
|
fr.lproj
|
||||||
|
it.lproj
|
||||||
|
hy.lproj
|
||||||
|
ru.lproj
|
||||||
|
uk.lproj
|
||||||
|
zh_CN.lproj
|
||||||
|
pt_BR.lproj
|
||||||
qt/base/*_rc.py
|
qt/base/*_rc.py
|
||||||
qt/base/*_ui.py
|
help/*/conf.py
|
||||||
qt/*/*_ui.py
|
help/*/changelog.rst
|
||||||
qt/*/build
|
|
||||||
qt/*/dist
|
|
||||||
qt/*/install
|
|
||||||
qt/*/logdict*.log
|
|
||||||
qt/*/warn*.txt
|
|
||||||
help_se/dupeguru_help
|
|
||||||
help_me/dupeguru_me_help
|
|
||||||
help_pe/dupeguru_pe_help
|
|
||||||
63
.hgtags
63
.hgtags
@@ -15,3 +15,66 @@ cbcf9c80fee4c908ef2efbf1c143c9e47676c9b2 pe1.8.0
|
|||||||
4c3cb1e671a333eabde1151c7c6ffb3609cab025 pe1.8.4
|
4c3cb1e671a333eabde1151c7c6ffb3609cab025 pe1.8.4
|
||||||
0a71306434bca51bea9a5d5ae54fe1bf0e4900d8 pe1.8.5
|
0a71306434bca51bea9a5d5ae54fe1bf0e4900d8 pe1.8.5
|
||||||
556baf4a410779e9bbf43129de133e4c4b26d679 pe1.8.6
|
556baf4a410779e9bbf43129de133e4c4b26d679 pe1.8.6
|
||||||
|
9149024283959a50fe9a47a5f175b905d1672c19 se2.10.0
|
||||||
|
388a7e5aef6385e515189f4a15b4c4fed3ae2fcf me5.8.0
|
||||||
|
27501167e3b9262ecb60c967941294f36d77eb25 pe1.9.0
|
||||||
|
cb0a860430bacd712820bce426bcf47a4135efe1 se2.10.1
|
||||||
|
cb0a860430bacd712820bce426bcf47a4135efe1 se2.10.1
|
||||||
|
f71d405e62badcfdc1b037facaac043cece40ee5 se2.10.1
|
||||||
|
3742e83edd9eadf44e1a501859f5e2462b1ef6fd me5.8.1
|
||||||
|
724ff565dd785fb739774588c6ee652cfc0612d5 pe1.9.1
|
||||||
|
634b66415c6529f46ae4f837318027cc9d70c3b5 before-py3k
|
||||||
|
2b67955db2b0580a8b0854dc918b6ab0d1fa3b88 se2.11.0
|
||||||
|
b56fe4dd8c95bca270b078a09e86848df77e2b2d me5.9.0
|
||||||
|
618a7365457d56fdc6920c70843a244762e2ea00 pe1.10.0
|
||||||
|
95b3a4b564c6222b414f2b40182dde2bd6d0e8a4 me5.9.1
|
||||||
|
9735a5218d2b5b3b1e1dfe17f2f874177cf8f61c se2.11.1
|
||||||
|
dbfee3ee2fa5cbb9e7ab36570659c17cd5b8561f se2.12.0
|
||||||
|
d3fe0d0dcda1e0bf1100d02f117503d3bf6baacf me5.10.0
|
||||||
|
b07ac1398703dd358912c1f3d20bd995633db9fe pe1.11.0
|
||||||
|
96b6aee668398d663b04eafc8d5dae05e18500ee before-fairware
|
||||||
|
22239f94589baf2a9fad2123045b8a718dbd68f5 se2.12.2
|
||||||
|
f9cae82a0752191276b24ffb2cc4e4a8afb5d754 me5.10.2
|
||||||
|
154c8cb6f018d446d88fa099490c900906e86386 pe1.11.2
|
||||||
|
ca93352ce35184853ad9fcb881935a43a8b1e249 me5.10.3
|
||||||
|
44f6ff67066c083f79daa18a9d2f1ab909e0a62e me5.10.4
|
||||||
|
3f71a8f5bf8f6d0729748a27af9163e013723294 pe1.11.3
|
||||||
|
0056293b0dade8b8230f68c1fe6f0c2d1e0b74d8 se2.12.3
|
||||||
|
8d12cab3b12b723e3a86d02cf8002731a0f73f95 se3.0.0
|
||||||
|
778876a8a9787658aa6adf6944b53aebcb7faeea se3.0.1
|
||||||
|
f1d40b556c01f32c58f9ef9f9acac5b78e01ba7a pe2.0.0
|
||||||
|
2fd901a516f8cb6b4438491f63f2ebfd52a57c13 me6.0.0
|
||||||
|
ff43c6d9feb388f103b7857eaa6f7809185f78ec before-pluginbuilder
|
||||||
|
d274bcb98f2d02b86470a04cd62e718eff33b74f pe2.1.0
|
||||||
|
77e169f757195c11e9c1c5febeb2db8eb3589510 se3.0.2
|
||||||
|
97893f37d7d0767b5aedf1b4b40de57ee36d426b se3.1.0
|
||||||
|
e44d5127ed605daa7a17a01eee65d0a157de20c0 pe2.2.0
|
||||||
|
ecf9aaa568340e3d03e8854b7556edd5a3285107 pe2.2.1
|
||||||
|
db1f325c907ffa9808a49cb7bc2886b9fca7aee2 se3.1.1
|
||||||
|
e62183e907d6177cf0bac354e31afa9546c199e6 se3.1.2
|
||||||
|
28ba95706dc54ba32b1c0cf4e1e6350515d19ba3 me6.0.2
|
||||||
|
925847384dcef62a5c3518fc9e5ce42feab2b093 pe2.2.2
|
||||||
|
383b14d6e8555ed2c8d5552259bc7ce600dfb1d0 before-leopard-drop
|
||||||
|
a2f7b7302e178f08725a6404ddc28464409510b1 se3.2.0
|
||||||
|
5a5134a4fa9bb7ca80ae3e32010030f5bd7ba434 me6.1.0
|
||||||
|
0fd77be57ff716d5c93232e829dc02acec036d7c se3.2.1
|
||||||
|
3dd08060135b0b9cef70b6f5a81f191ea339c8d5 me6.1.1
|
||||||
|
4e6cbef6bcdfcc0e56ff9690fbfe1cac1f4b1b09 pe2.3.0
|
||||||
|
9ea9af1b886cd1adc4f42fd2276cc2b38376eab0 se3.3.0
|
||||||
|
6e3379be6821bb36d7f0877a17dd6c07aa037b0a se3.3.1
|
||||||
|
015ba7e2c10d09afb944f387c2a9c97f7eff7571 me6.2.0
|
||||||
|
8178bda48324461a17118c98634241952c074f29 pe2.4.0
|
||||||
|
2a96f2fb3ddb6f1e0ae87951586733fc3e4a28a0 se3.3.2
|
||||||
|
6a08c1205dfe5e537e5c2dc99d05e05d1d3928f6 me6.2.1
|
||||||
|
a619f313712e2923160b8f90d8250ee0e184c7b9 pe2.4.1
|
||||||
|
fad463ae749b7189dce92f1e42a57ac4ee03987d se3.3.3
|
||||||
|
236cf9b690a144392e7e86e7c9749fc834a8b271 me6.3.0
|
||||||
|
90318f1303858d9d01065d92d78d98b888b38ea0 se3.4.0
|
||||||
|
93ed33410df2d2f21229a77ae49c83ece2c50a55 pe2.5.0
|
||||||
|
c153aef25e5c9911f2197d13899591c50cf38ffc se3.4.1
|
||||||
|
71b7e18613f3790cea18cb0dd8c9c986ce237267 me6.3.1
|
||||||
|
c3d9f91dc9c9d60f370c72bc211f09be3e4fc18d se3.5.0
|
||||||
|
254bce83ad6e56c102d69fd603f6845e2324b470 me6.4.0
|
||||||
|
e772f1de86744999ffbbe5845554417965b1dfba me6.4.1
|
||||||
|
c8a9a4d355927e509f514308c82306192bc71f92 pe2.6.0
|
||||||
|
a618e954f01e4bbdbe9a03e5667a67d62be995a7 me6.4.2
|
||||||
|
|||||||
3
LICENSE
3
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright 2010 Hardcoded Software Inc. (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software Inc. (http://www.hardcoded.net)
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
@@ -6,6 +6,5 @@ Redistribution and use in source and binary forms, with or without modification,
|
|||||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
* Neither the name of Hardcoded Software Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
* Neither the name of Hardcoded Software Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
* If the source code has been published less than two years ago, any redistribution, in whole or in part, must retain full licensing functionality, without any attempt to change, obscure or in other ways circumvent its intent.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
93
README
93
README
@@ -1,57 +1,97 @@
|
|||||||
Contents
|
Contents
|
||||||
=====
|
========
|
||||||
|
|
||||||
This package contains the source for dupeGuru. To learns how to build it, refer to the "Build dupeGuru" section. Below is the description of the various subfolders:
|
This package contains the source for dupeGuru. To learn how to build it, refer to the "Build dupeGuru" section. Below is the description of the various subfolders:
|
||||||
|
|
||||||
- core: Contains the core logic code for dupeGuru. It's Python code written in TDD style.
|
- core: Contains the core logic code for dupeGuru. It's Python code written in TDD style.
|
||||||
- core_*: Edition-specific-cross-toolkit code written in Python.
|
- core_*: Edition-specific-cross-toolkit code written in Python.
|
||||||
- cocoa: UI code for the Cocoa toolkit. It's Objective-C code.
|
- cocoa: UI code for the Cocoa toolkit. It's Objective-C code.
|
||||||
- qt: UI code for the Qt toolkit. It's written in Python and uses PyQt. Before redistributing changes in this part of the code, read the "qt/WARNING" file.
|
- qt: UI code for the Qt toolkit. It's written in Python and uses PyQt.
|
||||||
- images: Images used by the different UI codebases.
|
- images: Images used by the different UI codebases.
|
||||||
|
- debian_*: Skeleton files required to create a .deb package
|
||||||
|
- help: Help document, written for Sphinx.
|
||||||
|
|
||||||
There are also other sub-folder that comes from external repositories (automatically checked out
|
There are also other sub-folder that comes from external repositories (automatically checked out
|
||||||
with svn:externals):
|
with as mercurial subrepos):
|
||||||
|
|
||||||
- hsutil: A collection of helpers used across HS applications.
|
- hscommon: A collection of helpers used across HS applications.
|
||||||
- hsdocgen: An ad-hoc document generation used across HS project (used for help files)
|
|
||||||
- hsmedia: A library to read audio file metadata, used in dupeGuru ME.
|
|
||||||
- cocoalib: A collection of helpers used across Cocoa UI codebases of HS applications.
|
- cocoalib: A collection of helpers used across Cocoa UI codebases of HS applications.
|
||||||
- qtlib: A collection of helpers used across Qt UI codebases of HS applications.
|
- qtlib: A collection of helpers used across Qt UI codebases of HS applications.
|
||||||
|
|
||||||
dupeGuru Dependencies
|
dupeGuru Dependencies
|
||||||
=====
|
=====================
|
||||||
|
|
||||||
Before being able to build dupeGuru, a few dependencies have to be installed:
|
Before being able to build dupeGuru, a few dependencies have to be installed:
|
||||||
|
|
||||||
General dependencies
|
General dependencies
|
||||||
-----
|
--------------------
|
||||||
|
|
||||||
- Python 2.6 (http://www.python.org)
|
- Python 3.2 (http://www.python.org)
|
||||||
- Send2Trash (http://hg.hardcoded.net/send2trash)
|
- Send2Trash3k (http://hg.hardcoded.net/send2trash)
|
||||||
- lxml, to read and write XML files. (http://codespeak.net/lxml/)
|
- hsaudiotag3k 1.1.0 (for ME) (http://hg.hardcoded.net/hsaudiotag)
|
||||||
- Mako, to generate help files. (http://www.makotemplates.org/)
|
- jobprogress 1.0.3 (http://hg.hardcoded.net/jobprogress)
|
||||||
- PyYaml, for help files and the build system. (http://pyyaml.org/)
|
- Sphinx 1.1 (http://sphinx.pocoo.org/)
|
||||||
- Nose, to run unit tests. (http://somethingaboutorange.com/mrl/projects/nose/)
|
- polib 0.7.0 (http://bitbucket.org/izi/polib)
|
||||||
|
- pytest 2.0.0, to run unit tests. (http://pytest.org/)
|
||||||
|
|
||||||
OS X prerequisites
|
OS X prerequisites
|
||||||
-----
|
------------------
|
||||||
|
|
||||||
- XCode 3.1 (http://developer.apple.com/TOOLS/xcode/)
|
- XCode's command line tools
|
||||||
- Sparkle (http://sparkle.andymatuschak.org/)
|
- objp 1.1.0 (http://bitbucket.org/hsoft/objp)
|
||||||
- PyObjC 2.2. (http://pyobjc.sourceforge.net/)
|
- pluginbuilder 1.1.0 (http://bitbucket.org/hsoft/pluginbuilder)
|
||||||
- py2app (http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html)
|
- appscript 1.0.0 for ME and PE (http://appscript.sourceforge.net/)
|
||||||
|
- xibless 0.4.0 (https://bitbucket.org/hsoft/xibless)
|
||||||
|
|
||||||
Windows prerequisites
|
Windows prerequisites
|
||||||
---
|
---------------------
|
||||||
|
|
||||||
- Visual Studio 2008 (Express is enough) is needed to build C extensions. (http://www.microsoft.com/Express/)
|
- Visual Studio 2008 (Express is enough) is needed to build C extensions. (http://www.microsoft.com/Express/)
|
||||||
- PyQt 4.6 (http://www.riverbankcomputing.co.uk/news)
|
- PyQt 4.7.5 (http://www.riverbankcomputing.co.uk/news)
|
||||||
- Python Imaging Library for dupeGuru PE. (http://www.pythonware.com/products/pil/)
|
|
||||||
- cx_Freeze, if you want to build a exe. You don't need it if you just want to run dupeGuru. (http://cx-freeze.sourceforge.net/)
|
- cx_Freeze, if you want to build a exe. You don't need it if you just want to run dupeGuru. (http://cx-freeze.sourceforge.net/)
|
||||||
- Advanced Installer, if you want to build the installer file. (http://www.advancedinstaller.com/)
|
- Advanced Installer, if you want to build the installer file. (http://www.advancedinstaller.com/)
|
||||||
|
|
||||||
|
Linux prerequisites
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- PyQt 4.7.5 (http://www.riverbankcomputing.co.uk/news)
|
||||||
|
|
||||||
|
The easy way!
|
||||||
|
-------------
|
||||||
|
|
||||||
|
There's an easy way to install the majority of the prerequisites above, and it's `pip <http://www.pip-installer.org/>`_ which has recently started to support Python 3. So install it and then run::
|
||||||
|
|
||||||
|
pip install -r requirements-[osx|win].txt
|
||||||
|
|
||||||
|
([osx|win] depends, of course, on your platform. On other platforms, just use requirements.txt).
|
||||||
|
You might have to compile PyObjC manually (see gotchas below). Sparkle and
|
||||||
|
Advanced Installer, having nothing to do with Python, are also manual installs.
|
||||||
|
|
||||||
|
PyQt isn't in the requirements file either (there's no package uploaded on PyPI) and you also have
|
||||||
|
to install it manually.
|
||||||
|
|
||||||
|
Prerequisite gotchas
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Correctly installing the prerequisites is tricky. Make sure you have at least the version number
|
||||||
|
required for each prerequisite.
|
||||||
|
|
||||||
|
If you didn't use mercurial to download this source, you probably have an incomplete source folder!
|
||||||
|
External projects (hscommon, qtlib, cocoalib) need to be at the root of the dupeGuru project folder.
|
||||||
|
You'll have to download those separately. Or use mercurial, it's much easier.
|
||||||
|
|
||||||
|
Another one on OS X: I wouldn't use macports/fink/whatever. Whenever I tried using those, I always
|
||||||
|
ended up with problems.
|
||||||
|
|
||||||
|
Whenever you have a problem, always double-check that you're running the correct python version.
|
||||||
|
You'll probably have to tweak your $PATH.
|
||||||
|
|
||||||
|
To setup a build machine under Ubuntu 12.04 and up, install those packages: python3-dev, python3-pyqt4,
|
||||||
|
pyqt4-dev-tools, mercurial and then python3-setuptools. Once you've done that, install pip with
|
||||||
|
`easy_install`. Once you've done that, you can then perform "The easy way!" installation.
|
||||||
|
|
||||||
Building dupeGuru
|
Building dupeGuru
|
||||||
=====
|
=================
|
||||||
|
|
||||||
First, make sure you meet the dependencies listed in the section above. Then you need to configure your build with:
|
First, make sure you meet the dependencies listed in the section above. Then you need to configure your build with:
|
||||||
|
|
||||||
@@ -67,8 +107,3 @@ Then, just build the thing and then run it with:
|
|||||||
If you want to create ready-to-upload package, run:
|
If you want to create ready-to-upload package, run:
|
||||||
|
|
||||||
python package.py
|
python package.py
|
||||||
|
|
||||||
64-bit on OS X
|
|
||||||
---
|
|
||||||
|
|
||||||
The "release" configuration of dupeGuru's XCode project build with archs "i386 x86_64 ppc". However there are currently problems with py2app and 64 bit. If you want to correctly build 64-bit apps, refer to http://www.hardcoded.net/articles/building-64-bit-pyobjc-applications-with-py2app.htm .
|
|
||||||
409
build.py
409
build.py
@@ -1,106 +1,361 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Created By: Virgil Dupras
|
# Created By: Virgil Dupras
|
||||||
# Created On: 2009-12-30
|
# Created On: 2009-12-30
|
||||||
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
# Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
#
|
#
|
||||||
# This software is licensed under the "HS" License as described in the "LICENSE" file,
|
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import os.path as op
|
import os.path as op
|
||||||
|
from optparse import OptionParser
|
||||||
import shutil
|
import shutil
|
||||||
|
import json
|
||||||
|
import importlib
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup, Extension
|
||||||
import yaml
|
|
||||||
|
|
||||||
from hsdocgen import generate_help, filters
|
from hscommon import sphinxgen
|
||||||
from hsutil.build import add_to_pythonpath, print_and_do, build_all_qt_ui, copy_packages
|
from hscommon.build import (add_to_pythonpath, print_and_do, copy_packages, filereplace,
|
||||||
|
get_module_version, move_all, copy_sysconfig_files_for_embed, copy_all, move, copy,
|
||||||
|
OSXAppStructure, build_cocoalib_xibless, fix_qt_resource_file)
|
||||||
|
from hscommon import loc
|
||||||
|
from hscommon.plat import ISOSX
|
||||||
|
from hscommon.util import ensure_folder
|
||||||
|
|
||||||
def build_cocoa(edition, dev, help_destpath):
|
def parse_args():
|
||||||
if not dev:
|
usage = "usage: %prog [options]"
|
||||||
print "Building help index"
|
parser = OptionParser(usage=usage)
|
||||||
os.system('open -a /Developer/Applications/Utilities/Help\\ Indexer.app {0}'.format(help_destpath))
|
parser.add_option('--clean', action='store_true', dest='clean',
|
||||||
|
help="Clean build folder before building")
|
||||||
|
parser.add_option('--doc', action='store_true', dest='doc',
|
||||||
|
help="Build only the help file")
|
||||||
|
parser.add_option('--loc', action='store_true', dest='loc',
|
||||||
|
help="Build only localization")
|
||||||
|
parser.add_option('--cocoa-ext', action='store_true', dest='cocoa_ext',
|
||||||
|
help="Build only Cocoa extensions")
|
||||||
|
parser.add_option('--cocoa-compile', action='store_true', dest='cocoa_compile',
|
||||||
|
help="Build only Cocoa executable")
|
||||||
|
parser.add_option('--xibless', action='store_true', dest='xibless',
|
||||||
|
help="Build only xibless UIs")
|
||||||
|
parser.add_option('--updatepot', action='store_true', dest='updatepot',
|
||||||
|
help="Generate .pot files from source code.")
|
||||||
|
parser.add_option('--mergepot', action='store_true', dest='mergepot',
|
||||||
|
help="Update all .po files based on .pot files.")
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
return options
|
||||||
|
|
||||||
print "Building dg_cocoa.plugin"
|
def cocoa_compile_command(edition):
|
||||||
if op.exists('build'):
|
return '{0} waf configure --edition {1} && {0} waf'.format(sys.executable, edition)
|
||||||
shutil.rmtree('build')
|
|
||||||
os.mkdir('build')
|
def cocoa_app(edition):
|
||||||
if not dev:
|
app_path = {
|
||||||
|
'se': 'build/dupeGuru.app',
|
||||||
|
'me': 'build/dupeGuru ME.app',
|
||||||
|
'pe': 'build/dupeGuru PE.app',
|
||||||
|
}[edition]
|
||||||
|
return OSXAppStructure(app_path)
|
||||||
|
|
||||||
|
def build_xibless(edition, dest='cocoa/autogen'):
|
||||||
|
import xibless
|
||||||
|
ensure_folder(dest)
|
||||||
|
FNPAIRS = [
|
||||||
|
('ignore_list_dialog.py', 'IgnoreListDialog_UI'),
|
||||||
|
('deletion_options.py', 'DeletionOptions_UI'),
|
||||||
|
('problem_dialog.py', 'ProblemDialog_UI'),
|
||||||
|
('directory_panel.py', 'DirectoryPanel_UI'),
|
||||||
|
('prioritize_dialog.py', 'PrioritizeDialog_UI'),
|
||||||
|
('result_window.py', 'ResultWindow_UI'),
|
||||||
|
('main_menu.py', 'MainMenu_UI'),
|
||||||
|
('preferences_panel.py', 'PreferencesPanel_UI'),
|
||||||
|
]
|
||||||
|
for srcname, dstname in FNPAIRS:
|
||||||
|
xibless.generate(op.join('cocoa', 'base', 'ui', srcname), op.join(dest, dstname),
|
||||||
|
localizationTable='Localizable', args={'edition': edition})
|
||||||
|
if edition == 'pe':
|
||||||
|
xibless.generate('cocoa/pe/ui/details_panel.py', op.join(dest, 'DetailsPanel_UI'), localizationTable='Localizable')
|
||||||
|
else:
|
||||||
|
xibless.generate('cocoa/base/ui/details_panel.py', op.join(dest, 'DetailsPanel_UI'), localizationTable='Localizable')
|
||||||
|
|
||||||
|
def build_cocoa(edition, dev):
|
||||||
|
print("Creating OS X app structure")
|
||||||
|
ed = lambda s: s.format(edition)
|
||||||
|
app = cocoa_app(edition)
|
||||||
|
app_version = get_module_version(ed('core_{}'))
|
||||||
|
cocoa_project_path = ed('cocoa/{}')
|
||||||
|
filereplace(op.join(cocoa_project_path, 'InfoTemplate.plist'), op.join('build', 'Info.plist'), version=app_version)
|
||||||
|
app.create(op.join('build', 'Info.plist'))
|
||||||
|
print("Building localizations")
|
||||||
|
build_localizations('cocoa', edition)
|
||||||
|
print("Building xibless UIs")
|
||||||
|
build_cocoalib_xibless()
|
||||||
|
build_xibless(edition)
|
||||||
|
print("Building Python extensions")
|
||||||
|
build_cocoa_proxy_module()
|
||||||
|
build_cocoa_bridging_interfaces(edition)
|
||||||
|
print("Building the cocoa layer")
|
||||||
|
from pluginbuilder import copy_embeddable_python_dylib, collect_dependencies
|
||||||
|
copy_embeddable_python_dylib('build')
|
||||||
|
pydep_folder = op.join(app.resources, 'py')
|
||||||
|
if not op.exists(pydep_folder):
|
||||||
|
os.mkdir(pydep_folder)
|
||||||
|
shutil.copy(op.join(cocoa_project_path, 'dg_cocoa.py'), 'build')
|
||||||
specific_packages = {
|
specific_packages = {
|
||||||
'se': ['core_se'],
|
'se': ['core_se'],
|
||||||
'me': ['core_me', 'hsmedia'],
|
'me': ['core_me'],
|
||||||
'pe': ['core_pe'],
|
'pe': ['core_pe'],
|
||||||
}[edition]
|
}[edition]
|
||||||
copy_packages(['core', 'hsutil'] + specific_packages, 'build')
|
tocopy = ['core', 'hscommon', 'cocoa/inter', 'cocoalib/cocoa'] + specific_packages
|
||||||
cocoa_project_path = 'cocoa/{0}'.format(edition)
|
copy_packages(tocopy, 'build')
|
||||||
shutil.copy(op.join(cocoa_project_path, 'dg_cocoa.py'), 'build')
|
sys.path.insert(0, 'build')
|
||||||
os.chdir('build')
|
collect_dependencies('build/dg_cocoa.py', pydep_folder, excludes=['PyQt4'])
|
||||||
script_args = ['py2app', '-A'] if dev else ['py2app']
|
del sys.path[0]
|
||||||
setup(
|
|
||||||
script_args = script_args,
|
|
||||||
plugin = ['dg_cocoa.py'],
|
|
||||||
setup_requires = ['py2app'],
|
|
||||||
)
|
|
||||||
os.chdir('..')
|
|
||||||
pluginpath = op.join(cocoa_project_path, 'dg_cocoa.plugin')
|
|
||||||
if op.exists(pluginpath):
|
|
||||||
shutil.rmtree(pluginpath)
|
|
||||||
shutil.move('build/dist/dg_cocoa.plugin', pluginpath)
|
|
||||||
if dev:
|
if dev:
|
||||||
# In alias mode, the tweakings we do to the pythonpath aren't counted in. We have to
|
copy_packages(tocopy, pydep_folder, create_links=True)
|
||||||
# manually put a .pth in the plugin
|
# Views are not referenced by python code, so they're not found by the collector.
|
||||||
pthpath = op.join(pluginpath, 'Contents/Resources/dev.pth')
|
copy_all('build/inter/*.so', op.join(pydep_folder, 'inter'))
|
||||||
open(pthpath, 'w').write(op.abspath('.'))
|
copy_sysconfig_files_for_embed(pydep_folder)
|
||||||
os.chdir(cocoa_project_path)
|
print("Compiling with WAF")
|
||||||
print "Building the XCode project"
|
os.chdir('cocoa')
|
||||||
args = []
|
print_and_do(cocoa_compile_command(edition))
|
||||||
if dev:
|
|
||||||
args.append('-configuration dev')
|
|
||||||
else:
|
|
||||||
args.append('-configuration release')
|
|
||||||
args = ' '.join(args)
|
|
||||||
os.system('xcodebuild {0}'.format(args))
|
|
||||||
os.chdir('..')
|
os.chdir('..')
|
||||||
|
app.copy_executable('cocoa/build/dupeGuru')
|
||||||
|
print("Copying resources and frameworks")
|
||||||
|
image_path = ed('cocoa/{}/dupeguru.icns')
|
||||||
|
resources = [image_path, 'cocoa/base/dsa_pub.pem', 'build/dg_cocoa.py', 'build/help']
|
||||||
|
app.copy_resources(*resources, use_symlinks=dev)
|
||||||
|
app.copy_frameworks('build/Python', 'cocoalib/Sparkle.framework')
|
||||||
|
print("Creating the run.py file")
|
||||||
|
tmpl = open('run_template_cocoa.py', 'rt').read()
|
||||||
|
run_contents = tmpl.replace('{{app_path}}', app.dest)
|
||||||
|
open('run.py', 'wt').write(run_contents)
|
||||||
|
|
||||||
def build_qt(edition, dev):
|
def build_qt(edition, dev):
|
||||||
build_all_qt_ui(op.join('qtlib', 'ui'))
|
print("Building localizations")
|
||||||
build_all_qt_ui(op.join('qt', 'base'))
|
build_localizations('qt', edition)
|
||||||
build_all_qt_ui(op.join('qt', edition))
|
print("Building Qt stuff")
|
||||||
print_and_do("pyrcc4 {0} > {1}".format(op.join('qt', 'base', 'dg.qrc'), op.join('qt', 'base', 'dg_rc.py')))
|
print_and_do("pyrcc4 -py3 {0} > {1}".format(op.join('qt', 'base', 'dg.qrc'), op.join('qt', 'base', 'dg_rc.py')))
|
||||||
if edition == 'pe':
|
fix_qt_resource_file(op.join('qt', 'base', 'dg_rc.py'))
|
||||||
os.chdir(op.join('qt', edition))
|
print("Creating the run.py file")
|
||||||
os.system('python gen.py')
|
tmpl = open('run_template_qt.py', 'rt').read()
|
||||||
os.chdir(op.join('..', '..'))
|
run_contents = tmpl.replace('{{edition}}', edition)
|
||||||
|
open('run.py', 'wt').write(run_contents)
|
||||||
|
|
||||||
def main():
|
def build_help(edition):
|
||||||
conf = yaml.load(open('conf.yaml'))
|
print("Generating Help")
|
||||||
edition = conf['edition']
|
current_path = op.abspath('.')
|
||||||
ui = conf['ui']
|
help_basepath = op.join(current_path, 'help', 'en')
|
||||||
dev = conf['dev']
|
help_destpath = op.join(current_path, 'build', 'help'.format(edition))
|
||||||
print "Building dupeGuru {0} with UI {1}".format(edition.upper(), ui)
|
changelog_path = op.join(current_path, 'help', 'changelog_{}'.format(edition))
|
||||||
if dev:
|
tixurl = "https://hardcoded.lighthouseapp.com/projects/31699-dupeguru/tickets/{0}"
|
||||||
print "Building in Dev mode"
|
appname = {'se': 'dupeGuru', 'me': 'dupeGuru Music Edition', 'pe': 'dupeGuru Picture Edition'}[edition]
|
||||||
add_to_pythonpath('.')
|
homepage = 'http://www.hardcoded.net/dupeguru{}/'.format('_' + edition if edition != 'se' else '')
|
||||||
print "Generating Help"
|
confrepl = {'edition': edition, 'appname': appname, 'homepage': homepage, 'language': 'en'}
|
||||||
windows = sys.platform == 'win32'
|
changelogtmpl = op.join(current_path, 'help', 'changelog.tmpl')
|
||||||
tix = filters.tixgen("https://hardcoded.lighthouseapp.com/projects/31699-dupeguru/tickets/{0}")
|
conftmpl = op.join(current_path, 'help', 'conf.tmpl')
|
||||||
help_dir = 'help_{0}'.format(edition)
|
sphinxgen.gen(help_basepath, help_destpath, changelog_path, tixurl, confrepl, conftmpl, changelogtmpl)
|
||||||
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"
|
def build_base_localizations():
|
||||||
if edition == 'pe':
|
loc.compile_all_po('locale')
|
||||||
os.chdir('core_pe')
|
loc.compile_all_po(op.join('hscommon', 'locale'))
|
||||||
os.system('python gen.py')
|
loc.merge_locale_dir(op.join('hscommon', 'locale'), 'locale')
|
||||||
os.chdir('..')
|
|
||||||
|
def build_cocoa_localizations(edition):
|
||||||
|
print("Creating lproj folders based on .po files")
|
||||||
|
app = cocoa_app(edition)
|
||||||
|
en_stringsfile = op.join('cocoa', 'base', 'en.lproj', 'Localizable.strings')
|
||||||
|
en_cocoastringsfile = op.join('cocoalib', 'en.lproj', 'cocoalib.strings')
|
||||||
|
for lang in loc.get_langs('locale'):
|
||||||
|
pofile = op.join('locale', lang, 'LC_MESSAGES', 'ui.po')
|
||||||
|
dest_lproj = op.join(app.resources, lang + '.lproj')
|
||||||
|
ensure_folder(dest_lproj)
|
||||||
|
loc.po2strings(pofile, en_stringsfile, op.join(dest_lproj, 'Localizable.strings'))
|
||||||
|
pofile = op.join('cocoalib', 'locale', lang, 'LC_MESSAGES', 'cocoalib.po')
|
||||||
|
loc.po2strings(pofile, en_cocoastringsfile, op.join(dest_lproj, 'cocoalib.strings'))
|
||||||
|
# We also have to copy the "en.lproj" strings
|
||||||
|
en_lproj = op.join(app.resources, 'en.lproj')
|
||||||
|
ensure_folder(en_lproj)
|
||||||
|
copy(en_stringsfile, en_lproj)
|
||||||
|
copy(en_cocoastringsfile, en_lproj)
|
||||||
|
|
||||||
|
def build_qt_localizations():
|
||||||
|
loc.compile_all_po(op.join('qtlib', 'locale'))
|
||||||
|
loc.merge_locale_dir(op.join('qtlib', 'locale'), 'locale')
|
||||||
|
|
||||||
|
def build_localizations(ui, edition):
|
||||||
|
build_base_localizations()
|
||||||
if ui == 'cocoa':
|
if ui == 'cocoa':
|
||||||
build_cocoa(edition, dev, help_destpath)
|
build_cocoa_localizations(edition)
|
||||||
|
locale_dest = op.join(cocoa_app(edition).resources, 'locale')
|
||||||
|
elif ui == 'qt':
|
||||||
|
build_qt_localizations()
|
||||||
|
locale_dest = op.join('build', 'locale')
|
||||||
|
if op.exists(locale_dest):
|
||||||
|
shutil.rmtree(locale_dest)
|
||||||
|
shutil.copytree('locale', locale_dest, ignore=shutil.ignore_patterns('*.po', '*.pot'))
|
||||||
|
|
||||||
|
def build_updatepot():
|
||||||
|
if ISOSX:
|
||||||
|
print("Updating Cocoa strings file.")
|
||||||
|
# We need to have strings from *all* editions in here, so we'll call xibless for all editions
|
||||||
|
# in dummy subfolders.
|
||||||
|
build_cocoalib_xibless('cocoalib/autogen')
|
||||||
|
loc.generate_cocoa_strings_from_code('cocoalib', 'cocoalib/en.lproj')
|
||||||
|
for edition in ('se', 'me', 'pe'):
|
||||||
|
build_xibless(edition, op.join('cocoa', 'autogen', edition))
|
||||||
|
loc.generate_cocoa_strings_from_code('cocoa', 'cocoa/base/en.lproj')
|
||||||
|
print("Building .pot files from source files")
|
||||||
|
print("Building core.pot")
|
||||||
|
all_cores = ['core', 'core_se', 'core_me', 'core_pe']
|
||||||
|
loc.generate_pot(all_cores, op.join('locale', 'core.pot'), ['tr'])
|
||||||
|
print("Building columns.pot")
|
||||||
|
loc.generate_pot(all_cores, op.join('locale', 'columns.pot'), ['coltr'])
|
||||||
|
print("Building ui.pot")
|
||||||
|
ui_packages = ['qt', op.join('cocoa', 'inter')]
|
||||||
|
loc.generate_pot(ui_packages, op.join('locale', 'ui.pot'), ['tr'])
|
||||||
|
print("Building hscommon.pot")
|
||||||
|
loc.generate_pot(['hscommon'], op.join('hscommon', 'locale', 'hscommon.pot'), ['tr'])
|
||||||
|
print("Building qtlib.pot")
|
||||||
|
loc.generate_pot(['qtlib'], op.join('qtlib', 'locale', 'qtlib.pot'), ['tr'])
|
||||||
|
if ISOSX:
|
||||||
|
print("Building cocoalib.pot")
|
||||||
|
cocoalib_pot = op.join('cocoalib', 'locale', 'cocoalib.pot')
|
||||||
|
os.remove(cocoalib_pot)
|
||||||
|
loc.strings2pot(op.join('cocoalib', 'en.lproj', 'cocoalib.strings'), cocoalib_pot)
|
||||||
|
print("Enhancing ui.pot with Cocoa's strings files")
|
||||||
|
loc.strings2pot(op.join('cocoa', 'base', 'en.lproj', 'Localizable.strings'),
|
||||||
|
op.join('locale', 'ui.pot'))
|
||||||
|
|
||||||
|
def build_mergepot():
|
||||||
|
print("Updating .po files using .pot files")
|
||||||
|
loc.merge_pots_into_pos('locale')
|
||||||
|
loc.merge_pots_into_pos(op.join('hscommon', 'locale'))
|
||||||
|
loc.merge_pots_into_pos(op.join('qtlib', 'locale'))
|
||||||
|
loc.merge_pots_into_pos(op.join('cocoalib', 'locale'))
|
||||||
|
|
||||||
|
def build_cocoa_ext(extname, dest, source_files, extra_frameworks=(), extra_includes=()):
|
||||||
|
extra_link_args = ["-framework", "CoreFoundation", "-framework", "Foundation"]
|
||||||
|
for extra in extra_frameworks:
|
||||||
|
extra_link_args += ['-framework', extra]
|
||||||
|
ext = Extension(extname, source_files, extra_link_args=extra_link_args, include_dirs=extra_includes)
|
||||||
|
setup(script_args=['build_ext', '--inplace'], ext_modules=[ext])
|
||||||
|
fn = extname + '.so'
|
||||||
|
assert op.exists(fn)
|
||||||
|
move(fn, op.join(dest, fn))
|
||||||
|
|
||||||
|
def build_cocoa_proxy_module():
|
||||||
|
print("Building Cocoa Proxy")
|
||||||
|
import objp.p2o
|
||||||
|
objp.p2o.generate_python_proxy_code('cocoalib/cocoa/CocoaProxy.h', 'build/CocoaProxy.m')
|
||||||
|
build_cocoa_ext("CocoaProxy", 'cocoalib/cocoa',
|
||||||
|
['cocoalib/cocoa/CocoaProxy.m', 'build/CocoaProxy.m', 'build/ObjP.m',
|
||||||
|
'cocoalib/HSErrorReportWindow.m', 'cocoa/autogen/HSErrorReportWindow_UI.m'],
|
||||||
|
['AppKit', 'CoreServices'],
|
||||||
|
['cocoalib', 'cocoa/autogen'])
|
||||||
|
|
||||||
|
def build_cocoa_bridging_interfaces(edition):
|
||||||
|
print("Building Cocoa Bridging Interfaces")
|
||||||
|
import objp.o2p
|
||||||
|
import objp.p2o
|
||||||
|
add_to_pythonpath('cocoa')
|
||||||
|
add_to_pythonpath('cocoalib')
|
||||||
|
from cocoa.inter import (PyGUIObject, GUIObjectView, PyColumns, ColumnsView, PyOutline,
|
||||||
|
OutlineView, PySelectableList, SelectableListView, PyTable, TableView, PyFairware)
|
||||||
|
from inter.deletion_options import PyDeletionOptions, DeletionOptionsView
|
||||||
|
from inter.details_panel import PyDetailsPanel, DetailsPanelView
|
||||||
|
from inter.directory_outline import PyDirectoryOutline, DirectoryOutlineView
|
||||||
|
from inter.prioritize_dialog import PyPrioritizeDialog, PrioritizeDialogView
|
||||||
|
from inter.prioritize_list import PyPrioritizeList, PrioritizeListView
|
||||||
|
from inter.problem_dialog import PyProblemDialog
|
||||||
|
from inter.ignore_list_dialog import PyIgnoreListDialog, IgnoreListDialogView
|
||||||
|
from inter.result_table import PyResultTable, ResultTableView
|
||||||
|
from inter.stats_label import PyStatsLabel, StatsLabelView
|
||||||
|
from inter.app import PyDupeGuruBase, DupeGuruView
|
||||||
|
appmod = importlib.import_module('inter.app_{}'.format(edition))
|
||||||
|
allclasses = [PyGUIObject, PyColumns, PyOutline, PySelectableList, PyTable, PyFairware,
|
||||||
|
PyDetailsPanel, PyDirectoryOutline, PyPrioritizeDialog, PyPrioritizeList, PyProblemDialog,
|
||||||
|
PyIgnoreListDialog, PyDeletionOptions, PyResultTable, PyStatsLabel, PyDupeGuruBase,
|
||||||
|
appmod.PyDupeGuru]
|
||||||
|
for class_ in allclasses:
|
||||||
|
objp.o2p.generate_objc_code(class_, 'cocoa/autogen', inherit=True)
|
||||||
|
allclasses = [GUIObjectView, ColumnsView, OutlineView, SelectableListView, TableView,
|
||||||
|
DetailsPanelView, DirectoryOutlineView, PrioritizeDialogView, PrioritizeListView,
|
||||||
|
IgnoreListDialogView, DeletionOptionsView, ResultTableView, StatsLabelView, DupeGuruView]
|
||||||
|
clsspecs = [objp.o2p.spec_from_python_class(class_) for class_ in allclasses]
|
||||||
|
objp.p2o.generate_python_proxy_code_from_clsspec(clsspecs, 'build/CocoaViews.m')
|
||||||
|
build_cocoa_ext('CocoaViews', 'cocoa/inter', ['build/CocoaViews.m', 'build/ObjP.m'])
|
||||||
|
|
||||||
|
def build_pe_modules(ui):
|
||||||
|
print("Building PE Modules")
|
||||||
|
exts = [
|
||||||
|
Extension("_block", [op.join('core_pe', 'modules', 'block.c'), op.join('core_pe', 'modules', 'common.c')]),
|
||||||
|
Extension("_cache", [op.join('core_pe', 'modules', 'cache.c'), op.join('core_pe', 'modules', 'common.c')]),
|
||||||
|
]
|
||||||
|
if ui == 'qt':
|
||||||
|
exts.append(Extension("_block_qt", [op.join('qt', 'pe', 'modules', 'block.c')]))
|
||||||
|
elif ui == 'cocoa':
|
||||||
|
exts.append(Extension(
|
||||||
|
"_block_osx", [op.join('core_pe', 'modules', 'block_osx.m'), op.join('core_pe', 'modules', 'common.c')],
|
||||||
|
extra_link_args=[
|
||||||
|
"-framework", "CoreFoundation",
|
||||||
|
"-framework", "Foundation",
|
||||||
|
"-framework", "ApplicationServices",]
|
||||||
|
))
|
||||||
|
setup(
|
||||||
|
script_args = ['build_ext', '--inplace'],
|
||||||
|
ext_modules = exts,
|
||||||
|
)
|
||||||
|
move_all('_block_qt*', op.join('qt', 'pe'))
|
||||||
|
move_all('_block*', 'core_pe')
|
||||||
|
move_all('_cache*', 'core_pe')
|
||||||
|
|
||||||
|
def build_normal(edition, ui, dev):
|
||||||
|
print("Building dupeGuru {0} with UI {1}".format(edition.upper(), ui))
|
||||||
|
add_to_pythonpath('.')
|
||||||
|
build_help(edition)
|
||||||
|
print("Building dupeGuru")
|
||||||
|
if edition == 'pe':
|
||||||
|
build_pe_modules(ui)
|
||||||
|
if ui == 'cocoa':
|
||||||
|
build_cocoa(edition, dev)
|
||||||
elif ui == 'qt':
|
elif ui == 'qt':
|
||||||
build_qt(edition, dev)
|
build_qt(edition, dev)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
options = parse_args()
|
||||||
|
conf = json.load(open('conf.json'))
|
||||||
|
edition = conf['edition']
|
||||||
|
ui = conf['ui']
|
||||||
|
dev = conf['dev']
|
||||||
|
if dev:
|
||||||
|
print("Building in Dev mode")
|
||||||
|
if options.clean:
|
||||||
|
if op.exists('build'):
|
||||||
|
shutil.rmtree('build')
|
||||||
|
if not op.exists('build'):
|
||||||
|
os.mkdir('build')
|
||||||
|
if options.doc:
|
||||||
|
build_help(edition)
|
||||||
|
elif options.loc:
|
||||||
|
build_localizations(ui, edition)
|
||||||
|
elif options.updatepot:
|
||||||
|
build_updatepot()
|
||||||
|
elif options.mergepot:
|
||||||
|
build_mergepot()
|
||||||
|
elif options.cocoa_ext:
|
||||||
|
build_cocoa_proxy_module()
|
||||||
|
build_cocoa_bridging_interfaces(edition)
|
||||||
|
elif options.cocoa_compile:
|
||||||
|
os.chdir('cocoa')
|
||||||
|
print_and_do(cocoa_compile_command(edition))
|
||||||
|
os.chdir('..')
|
||||||
|
cocoa_app(edition).copy_executable('cocoa/build/dupeGuru')
|
||||||
|
elif options.xibless:
|
||||||
|
build_cocoalib_xibless()
|
||||||
|
build_xibless(edition)
|
||||||
|
else:
|
||||||
|
build_normal(edition, ui, dev)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "RecentDirectories.h"
|
|
||||||
#import "PyDupeGuru.h"
|
|
||||||
#import "ResultWindow.h"
|
|
||||||
#import "DetailsPanel.h"
|
|
||||||
#import "DirectoryPanel.h"
|
|
||||||
|
|
||||||
@interface AppDelegateBase : NSObject
|
|
||||||
{
|
|
||||||
IBOutlet PyDupeGuruBase *py;
|
|
||||||
IBOutlet RecentDirectories *recentDirectories;
|
|
||||||
IBOutlet NSMenuItem *unlockMenuItem;
|
|
||||||
IBOutlet ResultWindowBase *result;
|
|
||||||
|
|
||||||
DirectoryPanel *_directoryPanel;
|
|
||||||
DetailsPanel *_detailsPanel;
|
|
||||||
BOOL _savedResults;
|
|
||||||
}
|
|
||||||
- (IBAction)unlockApp:(id)sender;
|
|
||||||
|
|
||||||
- (PyDupeGuruBase *)py;
|
|
||||||
- (RecentDirectories *)recentDirectories;
|
|
||||||
- (DirectoryPanel *)directoryPanel;
|
|
||||||
- (DetailsPanel *)detailsPanel;
|
|
||||||
- (void)saveResults;
|
|
||||||
@end
|
|
||||||
@@ -1,117 +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 "AppDelegate.h"
|
|
||||||
#import "ProgressController.h"
|
|
||||||
#import "RegistrationInterface.h"
|
|
||||||
#import "Utils.h"
|
|
||||||
#import "Consts.h"
|
|
||||||
#import <Sparkle/SUUpdater.h>
|
|
||||||
|
|
||||||
@implementation AppDelegateBase
|
|
||||||
- (IBAction)unlockApp:(id)sender
|
|
||||||
{
|
|
||||||
if ([[self py] isRegistered])
|
|
||||||
return;
|
|
||||||
RegistrationInterface *ri = [[RegistrationInterface alloc] initWithApp:[self py]];
|
|
||||||
if ([ri enterCode] == NSOKButton)
|
|
||||||
{
|
|
||||||
NSString *menuTitle = [NSString stringWithFormat:@"Thanks for buying %@!",[py appName]];
|
|
||||||
[unlockMenuItem setTitle:menuTitle];
|
|
||||||
}
|
|
||||||
[ri release];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (PyDupeGuruBase *)py { return py; }
|
|
||||||
- (RecentDirectories *)recentDirectories { return recentDirectories; }
|
|
||||||
- (DirectoryPanel *)directoryPanel
|
|
||||||
{
|
|
||||||
if (!_directoryPanel)
|
|
||||||
_directoryPanel = [[DirectoryPanel alloc] initWithParentApp:self];
|
|
||||||
return _directoryPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (DetailsPanel *)detailsPanel
|
|
||||||
{
|
|
||||||
if (!_detailsPanel)
|
|
||||||
_detailsPanel = [[DetailsPanel alloc] initWithPy:py];
|
|
||||||
return _detailsPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)saveResults
|
|
||||||
{
|
|
||||||
if (_savedResults) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[py saveIgnoreList];
|
|
||||||
[py saveResults];
|
|
||||||
_savedResults = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Delegate */
|
|
||||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
|
||||||
{
|
|
||||||
[[ProgressController mainProgressController] setWorker:py];
|
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
|
||||||
//Restore Columns
|
|
||||||
NSArray *columnsOrder = [ud arrayForKey:@"columnsOrder"];
|
|
||||||
NSDictionary *columnsWidth = [ud dictionaryForKey:@"columnsWidth"];
|
|
||||||
if ([columnsOrder count])
|
|
||||||
[result restoreColumnsPosition:columnsOrder widths:columnsWidth];
|
|
||||||
else
|
|
||||||
[result resetColumnsToDefault:nil];
|
|
||||||
//Reg stuff
|
|
||||||
if ([RegistrationInterface showNagWithApp:[self py]])
|
|
||||||
[unlockMenuItem setTitle:[NSString stringWithFormat:@"Thanks for buying %@!",[py appName]]];
|
|
||||||
//Restore results
|
|
||||||
[py loadIgnoreList];
|
|
||||||
[py loadResults];
|
|
||||||
_savedResults = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationWillBecomeActive:(NSNotification *)aNotification
|
|
||||||
{
|
|
||||||
if (![[result window] isVisible])
|
|
||||||
[result showWindow:NSApp];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
|
||||||
{
|
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
|
||||||
[ud setObject: [result getColumnsOrder] forKey:@"columnsOrder"];
|
|
||||||
[ud setObject: [result getColumnsWidth] forKey:@"columnsWidth"];
|
|
||||||
[self saveResults];
|
|
||||||
NSInteger sc = [ud integerForKey:@"sessionCountSinceLastIgnorePurge"];
|
|
||||||
if (sc >= 10)
|
|
||||||
{
|
|
||||||
sc = -1;
|
|
||||||
[py purgeIgnoreList];
|
|
||||||
}
|
|
||||||
sc++;
|
|
||||||
[ud setInteger:sc forKey:@"sessionCountSinceLastIgnorePurge"];
|
|
||||||
// NSApplication does not release nib instances objects, we must do it manually
|
|
||||||
// Well, it isn't needed because the memory is freed anyway (we are quitting the application
|
|
||||||
// But I need to release RecentDirectories so it saves the user defaults
|
|
||||||
[recentDirectories release];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)recentDirecoryClicked:(NSString *)directory
|
|
||||||
{
|
|
||||||
[[self directoryPanel] addDirectory:directory];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SUUpdater delegate */
|
|
||||||
|
|
||||||
- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update untilInvoking:(NSInvocation *)invocation;
|
|
||||||
{
|
|
||||||
/* If results aren't saved now, we might get a weird utf-8 lookup error when saving later.
|
|
||||||
**/
|
|
||||||
[self saveResults];
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
77
cocoa/base/AppDelegateBase.h
Normal file
77
cocoa/base/AppDelegateBase.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <Sparkle/SUUpdater.h>
|
||||||
|
#import "PyDupeGuru.h"
|
||||||
|
#import "ResultWindow.h"
|
||||||
|
#import "DetailsPanel.h"
|
||||||
|
#import "DirectoryPanel.h"
|
||||||
|
#import "IgnoreListDialog.h"
|
||||||
|
#import "HSAboutBox.h"
|
||||||
|
#import "HSRecentFiles.h"
|
||||||
|
|
||||||
|
@interface AppDelegateBase : NSObject
|
||||||
|
{
|
||||||
|
NSMenu *recentResultsMenu;
|
||||||
|
NSMenu *columnsMenu;
|
||||||
|
SUUpdater *updater;
|
||||||
|
|
||||||
|
PyDupeGuru *model;
|
||||||
|
ResultWindowBase *_resultWindow;
|
||||||
|
DirectoryPanel *_directoryPanel;
|
||||||
|
DetailsPanel *_detailsPanel;
|
||||||
|
IgnoreListDialog *_ignoreListDialog;
|
||||||
|
NSWindowController *_preferencesPanel;
|
||||||
|
HSAboutBox *_aboutBox;
|
||||||
|
HSRecentFiles *_recentResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property (readwrite, retain) NSMenu *recentResultsMenu;
|
||||||
|
@property (readwrite, retain) NSMenu *columnsMenu;
|
||||||
|
@property (readwrite, retain) SUUpdater *updater;
|
||||||
|
|
||||||
|
/* Virtual */
|
||||||
|
+ (NSDictionary *)defaultPreferences;
|
||||||
|
- (PyDupeGuru *)model;
|
||||||
|
- (ResultWindowBase *)createResultWindow;
|
||||||
|
- (DirectoryPanel *)createDirectoryPanel;
|
||||||
|
- (DetailsPanel *)createDetailsPanel;
|
||||||
|
- (NSString *)homepageURL;
|
||||||
|
|
||||||
|
/* Public */
|
||||||
|
- (void)finalizeInit;
|
||||||
|
- (ResultWindowBase *)resultWindow;
|
||||||
|
- (DirectoryPanel *)directoryPanel;
|
||||||
|
- (DetailsPanel *)detailsPanel;
|
||||||
|
- (HSRecentFiles *)recentResults;
|
||||||
|
|
||||||
|
/* Delegate */
|
||||||
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
|
||||||
|
- (void)applicationWillBecomeActive:(NSNotification *)aNotification;
|
||||||
|
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
|
||||||
|
- (void)applicationWillTerminate:(NSNotification *)aNotification;
|
||||||
|
- (void)recentFileClicked:(NSString *)path;
|
||||||
|
|
||||||
|
/* Actions */
|
||||||
|
- (void)loadResults;
|
||||||
|
- (void)openWebsite;
|
||||||
|
- (void)openHelp;
|
||||||
|
- (void)showAboutBox;
|
||||||
|
- (void)showDirectoryWindow;
|
||||||
|
- (void)showPreferencesPanel;
|
||||||
|
- (void)showResultWindow;
|
||||||
|
- (void)showIgnoreList;
|
||||||
|
- (void)startScanning;
|
||||||
|
|
||||||
|
/* model --> view */
|
||||||
|
- (void)showMessage:(NSString *)msg;
|
||||||
|
- (void)setupAsRegistered;
|
||||||
|
- (void)showFairwareNagWithPrompt:(NSString *)prompt;
|
||||||
|
- (void)showDemoNagWithPrompt:(NSString *)prompt;
|
||||||
|
@end
|
||||||
308
cocoa/base/AppDelegateBase.m
Normal file
308
cocoa/base/AppDelegateBase.m
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "AppDelegateBase.h"
|
||||||
|
#import "ProgressController.h"
|
||||||
|
#import "HSFairwareReminder.h"
|
||||||
|
#import "HSPyUtil.h"
|
||||||
|
#import "Consts.h"
|
||||||
|
#import "Dialogs.h"
|
||||||
|
#import "Utils.h"
|
||||||
|
#import "ValueTransformers.h"
|
||||||
|
#import "PreferencesPanel_UI.h"
|
||||||
|
|
||||||
|
@implementation AppDelegateBase
|
||||||
|
|
||||||
|
@synthesize recentResultsMenu;
|
||||||
|
@synthesize columnsMenu;
|
||||||
|
@synthesize updater;
|
||||||
|
|
||||||
|
+ (NSDictionary *)defaultPreferences
|
||||||
|
{
|
||||||
|
NSMutableDictionary *d = [NSMutableDictionary dictionary];
|
||||||
|
[d setObject:i2n(1) forKey:@"recreatePathType"];
|
||||||
|
[d setObject:i2n(11) forKey:TableFontSize];
|
||||||
|
[d setObject:b2n(YES) forKey:@"mixFileKind"];
|
||||||
|
[d setObject:b2n(NO) forKey:@"useRegexpFilter"];
|
||||||
|
[d setObject:b2n(NO) forKey:@"ignoreHardlinkMatches"];
|
||||||
|
[d setObject:b2n(NO) forKey:@"removeEmptyFolders"];
|
||||||
|
[d setObject:b2n(NO) forKey:@"DebugMode"];
|
||||||
|
[d setObject:@"" forKey:@"CustomCommand"];
|
||||||
|
[d setObject:[NSArray array] forKey:@"recentDirectories"];
|
||||||
|
[d setObject:[NSArray array] forKey:@"columnsOrder"];
|
||||||
|
[d setObject:[NSDictionary dictionary] forKey:@"columnsWidth"];
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)initialize
|
||||||
|
{
|
||||||
|
HSVTAdd *vt = [[[HSVTAdd alloc] initWithValue:4] autorelease];
|
||||||
|
[NSValueTransformer setValueTransformer:vt forName:@"vtRowHeightOffset"];
|
||||||
|
NSDictionary *d = [self defaultPreferences];
|
||||||
|
[[NSUserDefaultsController sharedUserDefaultsController] setInitialValues:d];
|
||||||
|
[[NSUserDefaults standardUserDefaults] registerDefaults:d];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)init
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
model = [[PyDupeGuru alloc] init];
|
||||||
|
[model bindCallback:createCallback(@"DupeGuruView", self)];
|
||||||
|
[self setUpdater:[SUUpdater sharedUpdater]];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)finalizeInit
|
||||||
|
{
|
||||||
|
// We can only finalize initialization once the main menu has been created, which cannot happen
|
||||||
|
// before AppDelegate is created.
|
||||||
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
|
/* Because the pref pane is lazily loaded, we have to manually do the update check if the
|
||||||
|
preference is set.
|
||||||
|
*/
|
||||||
|
if ([ud boolForKey:@"SUEnableAutomaticChecks"]) {
|
||||||
|
[[SUUpdater sharedUpdater] checkForUpdatesInBackground];
|
||||||
|
}
|
||||||
|
_recentResults = [[HSRecentFiles alloc] initWithName:@"recentResults" menu:recentResultsMenu];
|
||||||
|
[_recentResults setDelegate:self];
|
||||||
|
_resultWindow = [self createResultWindow];
|
||||||
|
_directoryPanel = [self createDirectoryPanel];
|
||||||
|
_detailsPanel = [self createDetailsPanel];
|
||||||
|
_ignoreListDialog = [[IgnoreListDialog alloc] initWithPyRef:[model ignoreListDialog]];
|
||||||
|
_aboutBox = nil; // Lazily loaded
|
||||||
|
_preferencesPanel = nil; // Lazily loaded
|
||||||
|
[[[self directoryPanel] window] makeKeyAndOrderFront:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Virtual */
|
||||||
|
|
||||||
|
- (PyDupeGuru *)model
|
||||||
|
{
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ResultWindowBase *)createResultWindow
|
||||||
|
{
|
||||||
|
return nil; // must be overriden by all editions
|
||||||
|
}
|
||||||
|
|
||||||
|
- (DirectoryPanel *)createDirectoryPanel
|
||||||
|
{
|
||||||
|
return [[DirectoryPanel alloc] initWithParentApp:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (DetailsPanel *)createDetailsPanel
|
||||||
|
{
|
||||||
|
return [[DetailsPanel alloc] initWithPyRef:[model detailsPanel]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)homepageURL
|
||||||
|
{
|
||||||
|
return @""; // must be overriden by all editions
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Public */
|
||||||
|
- (ResultWindowBase *)resultWindow
|
||||||
|
{
|
||||||
|
return _resultWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (DirectoryPanel *)directoryPanel
|
||||||
|
{
|
||||||
|
return _directoryPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (DetailsPanel *)detailsPanel
|
||||||
|
{
|
||||||
|
return _detailsPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (HSRecentFiles *)recentResults
|
||||||
|
{
|
||||||
|
return _recentResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actions */
|
||||||
|
- (void)loadResults
|
||||||
|
{
|
||||||
|
NSOpenPanel *op = [NSOpenPanel openPanel];
|
||||||
|
[op setCanChooseFiles:YES];
|
||||||
|
[op setCanChooseDirectories:NO];
|
||||||
|
[op setCanCreateDirectories:NO];
|
||||||
|
[op setAllowsMultipleSelection:NO];
|
||||||
|
[op setAllowedFileTypes:[NSArray arrayWithObject:@"dupeguru"]];
|
||||||
|
[op setTitle:NSLocalizedString(@"Select a results file to load", @"")];
|
||||||
|
if ([op runModal] == NSOKButton) {
|
||||||
|
NSString *filename = [[op filenames] objectAtIndex:0];
|
||||||
|
[model loadResultsFrom:filename];
|
||||||
|
[[self recentResults] addFile:filename];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)openWebsite
|
||||||
|
{
|
||||||
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[self homepageURL]]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)openHelp
|
||||||
|
{
|
||||||
|
NSBundle *b = [NSBundle mainBundle];
|
||||||
|
NSString *p = [b pathForResource:@"index" ofType:@"html" inDirectory:@"help"];
|
||||||
|
NSURL *u = [NSURL fileURLWithPath:p];
|
||||||
|
[[NSWorkspace sharedWorkspace] openURL:u];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showAboutBox
|
||||||
|
{
|
||||||
|
if (_aboutBox == nil) {
|
||||||
|
_aboutBox = [[HSAboutBox alloc] initWithApp:model];
|
||||||
|
}
|
||||||
|
[[_aboutBox window] makeKeyAndOrderFront:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showDirectoryWindow
|
||||||
|
{
|
||||||
|
[[[self directoryPanel] window] makeKeyAndOrderFront:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showPreferencesPanel
|
||||||
|
{
|
||||||
|
if (_preferencesPanel == nil) {
|
||||||
|
_preferencesPanel = [[NSWindowController alloc] initWithWindow:createPreferencesPanel_UI(nil)];
|
||||||
|
}
|
||||||
|
[_preferencesPanel showWindow:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showResultWindow
|
||||||
|
{
|
||||||
|
[[[self resultWindow] window] makeKeyAndOrderFront:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showIgnoreList
|
||||||
|
{
|
||||||
|
[model showIgnoreList];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)startScanning
|
||||||
|
{
|
||||||
|
[[self resultWindow] startDuplicateScan];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Delegate */
|
||||||
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||||
|
{
|
||||||
|
[[ProgressController mainProgressController] setWorker:model];
|
||||||
|
[model initialRegistrationSetup];
|
||||||
|
[model loadSession];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applicationWillBecomeActive:(NSNotification *)aNotification
|
||||||
|
{
|
||||||
|
if (![[[self directoryPanel] window] isVisible]) {
|
||||||
|
[[self directoryPanel] showWindow:NSApp];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
||||||
|
{
|
||||||
|
if ([model resultsAreModified]) {
|
||||||
|
NSString *msg = NSLocalizedString(@"You have unsaved results, do you really want to quit?", @"");
|
||||||
|
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) { // NO
|
||||||
|
return NSTerminateCancel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NSTerminateNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||||
|
{
|
||||||
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
|
NSInteger sc = [ud integerForKey:@"sessionCountSinceLastIgnorePurge"];
|
||||||
|
if (sc >= 10) {
|
||||||
|
sc = -1;
|
||||||
|
[model purgeIgnoreList];
|
||||||
|
}
|
||||||
|
sc++;
|
||||||
|
[model saveSession];
|
||||||
|
[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 HSRecentFiles so it saves the user defaults
|
||||||
|
[_directoryPanel release];
|
||||||
|
[_recentResults release];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)recentFileClicked:(NSString *)path
|
||||||
|
{
|
||||||
|
[model loadResultsFrom:path];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* model --> view */
|
||||||
|
- (void)showMessage:(NSString *)msg
|
||||||
|
{
|
||||||
|
[Dialogs showMessage:msg];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)askYesNoWithPrompt:(NSString *)prompt
|
||||||
|
{
|
||||||
|
return [Dialogs askYesNo:prompt] == NSAlertFirstButtonReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showProblemDialog
|
||||||
|
{
|
||||||
|
[[self resultWindow] showProblemDialog];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setupAsRegistered
|
||||||
|
{
|
||||||
|
// Nothing to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showFairwareNagWithPrompt:(NSString *)prompt
|
||||||
|
{
|
||||||
|
[HSFairwareReminder showFairwareNagWithApp:[self model] prompt:prompt];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showDemoNagWithPrompt:(NSString *)prompt
|
||||||
|
{
|
||||||
|
[HSFairwareReminder showDemoNagWithApp:[self model] prompt:prompt];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)selectDestFolderWithPrompt:(NSString *)prompt
|
||||||
|
{
|
||||||
|
NSOpenPanel *op = [NSOpenPanel openPanel];
|
||||||
|
[op setCanChooseFiles:NO];
|
||||||
|
[op setCanChooseDirectories:YES];
|
||||||
|
[op setCanCreateDirectories:YES];
|
||||||
|
[op setAllowsMultipleSelection:NO];
|
||||||
|
[op setTitle:prompt];
|
||||||
|
if ([op runModal] == NSOKButton) {
|
||||||
|
return [[op filenames] objectAtIndex:0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)selectDestFileWithPrompt:(NSString *)prompt extension:(NSString *)extension
|
||||||
|
{
|
||||||
|
NSSavePanel *sp = [NSSavePanel savePanel];
|
||||||
|
[sp setCanCreateDirectories:YES];
|
||||||
|
[sp setAllowedFileTypes:[NSArray arrayWithObject:extension]];
|
||||||
|
[sp setTitle:prompt];
|
||||||
|
if ([sp runModal] == NSOKButton) {
|
||||||
|
return [sp filename];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
#define RegistrationRequired @"RegistrationRequired"
|
|
||||||
#define JobStarted @"JobStarted"
|
#define JobStarted @"JobStarted"
|
||||||
#define JobInProgress @"JobInProgress"
|
#define JobInProgress @"JobInProgress"
|
||||||
|
#define TableFontSize @"TableFontSize"
|
||||||
|
|
||||||
#define jobLoad @"job_load"
|
#define jobLoad @"job_load"
|
||||||
#define jobScan @"job_scan"
|
#define jobScan @"job_scan"
|
||||||
#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 DGPrioritizeIndexPasteboardType @"DGPrioritizeIndexPasteboardType"
|
||||||
|
|||||||
33
cocoa/base/DeletionOptions.h
Normal file
33
cocoa/base/DeletionOptions.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "PyDeletionOptions.h"
|
||||||
|
|
||||||
|
@interface DeletionOptions : NSWindowController
|
||||||
|
{
|
||||||
|
|
||||||
|
PyDeletionOptions *model;
|
||||||
|
|
||||||
|
NSTextField *messageTextField;
|
||||||
|
NSButton *linkButton;
|
||||||
|
NSMatrix *linkTypeRadio;
|
||||||
|
NSButton *directButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property (readwrite, retain) NSTextField *messageTextField;
|
||||||
|
@property (readwrite, retain) NSButton *linkButton;
|
||||||
|
@property (readwrite, retain) NSMatrix *linkTypeRadio;
|
||||||
|
@property (readwrite, retain) NSButton *directButton;
|
||||||
|
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef;
|
||||||
|
|
||||||
|
- (void)updateOptions;
|
||||||
|
- (void)proceed;
|
||||||
|
- (void)cancel;
|
||||||
|
@end
|
||||||
67
cocoa/base/DeletionOptions.m
Normal file
67
cocoa/base/DeletionOptions.m
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "DeletionOptions.h"
|
||||||
|
#import "DeletionOptions_UI.h"
|
||||||
|
#import "HSPyUtil.h"
|
||||||
|
|
||||||
|
@implementation DeletionOptions
|
||||||
|
|
||||||
|
@synthesize messageTextField;
|
||||||
|
@synthesize linkButton;
|
||||||
|
@synthesize linkTypeRadio;
|
||||||
|
@synthesize directButton;
|
||||||
|
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef
|
||||||
|
{
|
||||||
|
self = [super initWithWindow:nil];
|
||||||
|
model = [[PyDeletionOptions alloc] initWithModel:aPyRef];
|
||||||
|
[self setWindow:createDeletionOptions_UI(self)];
|
||||||
|
[model bindCallback:createCallback(@"DeletionOptionsView", self)];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[model release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateOptions
|
||||||
|
{
|
||||||
|
[model setLinkDeleted:[linkButton state] == NSOnState];
|
||||||
|
[model setUseHardlinks:[linkTypeRadio selectedColumn] == 1];
|
||||||
|
[model setDirect:[directButton state] == NSOnState];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)proceed
|
||||||
|
{
|
||||||
|
[NSApp stopModalWithCode:NSOKButton];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cancel
|
||||||
|
{
|
||||||
|
[NSApp stopModalWithCode:NSCancelButton];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* model --> view */
|
||||||
|
- (void)updateMsg:(NSString *)msg
|
||||||
|
{
|
||||||
|
[messageTextField setStringValue:msg];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)show
|
||||||
|
{
|
||||||
|
[linkButton setState:NSOffState];
|
||||||
|
[directButton setState:NSOffState];
|
||||||
|
[linkTypeRadio selectCellAtRow:0 column:0];
|
||||||
|
NSInteger r = [NSApp runModalForWindow:[self window]];
|
||||||
|
[[self window] close];
|
||||||
|
return r == NSOKButton;
|
||||||
|
}
|
||||||
|
@end
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "HSWindowController.h"
|
|
||||||
#import "PyApp.h"
|
|
||||||
#import "PyDetailsPanel.h"
|
|
||||||
|
|
||||||
@interface DetailsPanel : HSWindowController
|
|
||||||
{
|
|
||||||
IBOutlet NSTableView *detailsTable;
|
|
||||||
}
|
|
||||||
- (id)initWithPy:(PyApp *)aPy;
|
|
||||||
- (PyDetailsPanel *)py;
|
|
||||||
|
|
||||||
- (void)toggleVisibility;
|
|
||||||
|
|
||||||
/* Python --> Cocoa */
|
|
||||||
- (void)refresh;
|
|
||||||
@end
|
|
||||||
@@ -1,66 +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"
|
|
||||||
#import "Utils.h"
|
|
||||||
|
|
||||||
@implementation DetailsPanel
|
|
||||||
- (id)initWithPy:(PyApp *)aPy
|
|
||||||
{
|
|
||||||
self = [super initWithNibName:@"DetailsPanel" pyClassName:@"PyDetailsPanel" pyParent:aPy];
|
|
||||||
[self window]; //So the detailsTable is initialized.
|
|
||||||
[self connect];
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
[self disconnect];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (PyDetailsPanel *)py
|
|
||||||
{
|
|
||||||
return (PyDetailsPanel *)py;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)refreshDetails
|
|
||||||
{
|
|
||||||
[detailsTable reloadData];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)toggleVisibility
|
|
||||||
{
|
|
||||||
if ([[self window] isVisible]) {
|
|
||||||
[[self window] close];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[self refreshDetails]; // selection might have changed since last time
|
|
||||||
[[self window] orderFront:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NSTableView Delegate */
|
|
||||||
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
|
||||||
{
|
|
||||||
return [[self py] numberOfRows];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)row
|
|
||||||
{
|
|
||||||
return [[self py] valueForColumn:[column identifier] row:row];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Python --> Cocoa */
|
|
||||||
- (void)refresh
|
|
||||||
{
|
|
||||||
if ([[self window] isVisible]) {
|
|
||||||
[self refreshDetails];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
31
cocoa/base/DetailsPanelBase.h
Normal file
31
cocoa/base/DetailsPanelBase.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <Python.h>
|
||||||
|
#import "PyDetailsPanel.h"
|
||||||
|
|
||||||
|
@interface DetailsPanelBase : NSWindowController <NSTableViewDataSource>
|
||||||
|
{
|
||||||
|
NSTableView *detailsTable;
|
||||||
|
|
||||||
|
PyDetailsPanel *model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property (readwrite, retain) NSTableView *detailsTable;
|
||||||
|
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef;
|
||||||
|
- (PyDetailsPanel *)model;
|
||||||
|
|
||||||
|
- (NSWindow *)createWindow;
|
||||||
|
- (BOOL)isVisible;
|
||||||
|
- (void)toggleVisibility;
|
||||||
|
|
||||||
|
/* Python --> Cocoa */
|
||||||
|
- (void)refresh;
|
||||||
|
@end
|
||||||
80
cocoa/base/DetailsPanelBase.m
Normal file
80
cocoa/base/DetailsPanelBase.m
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "DetailsPanelBase.h"
|
||||||
|
#import "HSPyUtil.h"
|
||||||
|
|
||||||
|
@implementation DetailsPanelBase
|
||||||
|
|
||||||
|
@synthesize detailsTable;
|
||||||
|
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef
|
||||||
|
{
|
||||||
|
self = [super initWithWindow:nil];
|
||||||
|
[self setWindow:[self createWindow]];
|
||||||
|
model = [[PyDetailsPanel alloc] initWithModel:aPyRef];
|
||||||
|
[model bindCallback:createCallback(@"DetailsPanelView", self)];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[model release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (PyDetailsPanel *)model
|
||||||
|
{
|
||||||
|
return (PyDetailsPanel *)model;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSWindow *)createWindow
|
||||||
|
{
|
||||||
|
return nil; // Virtual
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)refreshDetails
|
||||||
|
{
|
||||||
|
[detailsTable reloadData];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isVisible
|
||||||
|
{
|
||||||
|
return [[self window] isVisible];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)toggleVisibility
|
||||||
|
{
|
||||||
|
if ([self isVisible]) {
|
||||||
|
[[self window] close];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[self refreshDetails]; // selection might have changed since last time
|
||||||
|
[[self window] orderFront:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NSTableView Delegate */
|
||||||
|
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
|
||||||
|
{
|
||||||
|
return [[self model] numberOfRows];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)row
|
||||||
|
{
|
||||||
|
return [[self model] valueForColumn:[column identifier] row:row];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Python --> Cocoa */
|
||||||
|
- (void)refresh
|
||||||
|
{
|
||||||
|
if ([[self window] isVisible]) {
|
||||||
|
[self refreshDetails];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <Python.h>
|
||||||
#import "HSOutline.h"
|
#import "HSOutline.h"
|
||||||
#import "PyDirectoryOutline.h"
|
#import "PyDirectoryOutline.h"
|
||||||
|
|
||||||
|
#define DGAddedFoldersNotification @"DGAddedFoldersNotification"
|
||||||
|
|
||||||
@interface DirectoryOutline : HSOutline {}
|
@interface DirectoryOutline : HSOutline {}
|
||||||
- (id)initWithPyParent:(id)aPyParent view:(HSOutlineView *)aOutlineView;
|
- (id)initWithPyRef:(PyObject *)aPyRef outlineView:(HSOutlineView *)aOutlineView;
|
||||||
- (PyDirectoryOutline *)py;
|
- (PyDirectoryOutline *)model;
|
||||||
@end;
|
@end;
|
||||||
@@ -1,31 +1,25 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "DirectoryOutline.h"
|
#import "DirectoryOutline.h"
|
||||||
|
|
||||||
@implementation DirectoryOutline
|
@implementation DirectoryOutline
|
||||||
- (id)initWithPyParent:(id)aPyParent view:(HSOutlineView *)aOutlineView
|
- (id)initWithPyRef:(PyObject *)aPyRef outlineView:(HSOutlineView *)aOutlineView
|
||||||
{
|
{
|
||||||
self = [super initWithPyClassName:@"PyDirectoryOutline" pyParent:aPyParent view:aOutlineView];
|
self = [super initWithPyRef:aPyRef wrapperClass:[PyDirectoryOutline class]
|
||||||
[outlineView registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
|
callbackClassName:@"DirectoryOutlineView" view:aOutlineView];
|
||||||
[self connect];
|
[[self view] registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (PyDirectoryOutline *)model
|
||||||
{
|
{
|
||||||
[self disconnect];
|
return (PyDirectoryOutline *)model;
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (PyDirectoryOutline *)py
|
|
||||||
{
|
|
||||||
return (PyDirectoryOutline *)py;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delegate */
|
/* Delegate */
|
||||||
@@ -49,12 +43,15 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
sourceDragMask = [info draggingSourceOperationMask];
|
sourceDragMask = [info draggingSourceOperationMask];
|
||||||
pboard = [info draggingPasteboard];
|
pboard = [info draggingPasteboard];
|
||||||
if ([[pboard types] containsObject:NSFilenamesPboardType]) {
|
if ([[pboard types] containsObject:NSFilenamesPboardType]) {
|
||||||
NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
|
NSArray *foldernames = [pboard propertyListForType:NSFilenamesPboardType];
|
||||||
if (!(sourceDragMask & NSDragOperationLink))
|
if (!(sourceDragMask & NSDragOperationLink))
|
||||||
return NO;
|
return NO;
|
||||||
for (NSString *filename in filenames) {
|
for (NSString *foldername in foldernames) {
|
||||||
[[self py] addDirectory:filename];
|
[[self model] addDirectory:foldername];
|
||||||
}
|
}
|
||||||
|
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:foldernames forKey:@"foldernames"];
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:DGAddedFoldersNotification
|
||||||
|
object:self userInfo:userInfo];
|
||||||
}
|
}
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
@@ -64,7 +61,7 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
|
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
|
||||||
NSTextFieldCell *textCell = cell;
|
NSTextFieldCell *textCell = cell;
|
||||||
NSIndexPath *path = item;
|
NSIndexPath *path = item;
|
||||||
BOOL selected = [path isEqualTo:[outlineView selectedPath]];
|
BOOL selected = [path isEqualTo:[[self view] selectedPath]];
|
||||||
if (selected) {
|
if (selected) {
|
||||||
[textCell setTextColor:[NSColor blackColor]];
|
[textCell setTextColor:[NSColor blackColor]];
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,33 +1,48 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "RecentDirectories.h"
|
|
||||||
#import "HSOutlineView.h"
|
#import "HSOutlineView.h"
|
||||||
|
#import "HSRecentFiles.h"
|
||||||
#import "DirectoryOutline.h"
|
#import "DirectoryOutline.h"
|
||||||
#import "PyDupeGuru.h"
|
#import "PyDupeGuru.h"
|
||||||
|
|
||||||
@interface DirectoryPanel : NSWindowController
|
@class AppDelegateBase;
|
||||||
|
|
||||||
|
@interface DirectoryPanel : NSWindowController <NSOpenSavePanelDelegate>
|
||||||
{
|
{
|
||||||
IBOutlet NSPopUpButton *addButtonPopUp;
|
AppDelegateBase *_app;
|
||||||
IBOutlet HSOutlineView *outlineView;
|
PyDupeGuru *model;
|
||||||
IBOutlet NSButton *removeButton;
|
HSRecentFiles *_recentDirectories;
|
||||||
|
|
||||||
PyDupeGuruBase *_py;
|
|
||||||
RecentDirectories *_recentDirectories;
|
|
||||||
DirectoryOutline *outline;
|
DirectoryOutline *outline;
|
||||||
|
BOOL _alwaysShowPopUp;
|
||||||
|
NSPopUpButton *addButtonPopUp;
|
||||||
|
NSPopUpButton *loadRecentButtonPopUp;
|
||||||
|
HSOutlineView *outlineView;
|
||||||
|
NSButton *removeButton;
|
||||||
|
NSButton *loadResultsButton;
|
||||||
}
|
}
|
||||||
- (id)initWithParentApp:(id)aParentApp;
|
|
||||||
|
|
||||||
- (IBAction)askForDirectory:(id)sender;
|
@property (readwrite, retain) NSPopUpButton *addButtonPopUp;
|
||||||
- (IBAction)popupAddDirectoryMenu:(id)sender;
|
@property (readwrite, retain) NSPopUpButton *loadRecentButtonPopUp;
|
||||||
- (IBAction)removeSelectedDirectory:(id)sender;
|
@property (readwrite, retain) HSOutlineView *outlineView;
|
||||||
- (IBAction)toggleVisible:(id)sender;
|
@property (readwrite, retain) NSButton *removeButton;
|
||||||
|
@property (readwrite, retain) NSButton *loadResultsButton;
|
||||||
|
|
||||||
|
- (id)initWithParentApp:(AppDelegateBase *)aParentApp;
|
||||||
|
|
||||||
|
- (void)fillPopUpMenu; // Virtual
|
||||||
|
- (void)adjustUIToLocalization;
|
||||||
|
|
||||||
|
- (void)askForDirectory;
|
||||||
|
- (void)popupAddDirectoryMenu:(id)sender;
|
||||||
|
- (void)popupLoadRecentMenu:(id)sender;
|
||||||
|
- (void)removeSelectedDirectory;
|
||||||
|
|
||||||
- (void)addDirectory:(NSString *)directory;
|
- (void)addDirectory:(NSString *)directory;
|
||||||
- (void)refreshRemoveButtonText;
|
- (void)refreshRemoveButtonText;
|
||||||
|
|||||||
@@ -1,114 +1,144 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "DirectoryPanel.h"
|
#import "DirectoryPanel.h"
|
||||||
|
#import "DirectoryPanel_UI.h"
|
||||||
#import "Dialogs.h"
|
#import "Dialogs.h"
|
||||||
#import "Utils.h"
|
#import "Utils.h"
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
@implementation DirectoryPanel
|
@implementation DirectoryPanel
|
||||||
- (id)initWithParentApp:(id)aParentApp
|
|
||||||
|
@synthesize addButtonPopUp;
|
||||||
|
@synthesize loadRecentButtonPopUp;
|
||||||
|
@synthesize outlineView;
|
||||||
|
@synthesize removeButton;
|
||||||
|
@synthesize loadResultsButton;
|
||||||
|
|
||||||
|
- (id)initWithParentApp:(AppDelegateBase *)aParentApp
|
||||||
{
|
{
|
||||||
self = [super initWithWindowNibName:@"DirectoryPanel"];
|
self = [super initWithWindow:nil];
|
||||||
[self window];
|
[self setWindow:createDirectoryPanel_UI(self)];
|
||||||
AppDelegateBase *app = aParentApp;
|
_app = aParentApp;
|
||||||
_py = [app py];
|
model = [_app model];
|
||||||
_recentDirectories = [app recentDirectories];
|
[[self window] setTitle:[model appName]];
|
||||||
outline = [[DirectoryOutline alloc] initWithPyParent:_py view:outlineView];
|
_alwaysShowPopUp = NO;
|
||||||
|
[self fillPopUpMenu];
|
||||||
|
_recentDirectories = [[HSRecentFiles alloc] initWithName:@"recentDirectories" menu:[addButtonPopUp menu]];
|
||||||
|
[_recentDirectories setDelegate:self];
|
||||||
|
outline = [[DirectoryOutline alloc] initWithPyRef:[model directoryTree] outlineView:outlineView];
|
||||||
[self refreshRemoveButtonText];
|
[self refreshRemoveButtonText];
|
||||||
|
[self adjustUIToLocalization];
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(directorySelectionChanged:)
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(directorySelectionChanged:)
|
||||||
name:NSOutlineViewSelectionDidChangeNotification object:outlineView];
|
name:NSOutlineViewSelectionDidChangeNotification object:outlineView];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(outlineAddedFolders:)
|
||||||
|
name:DGAddedFoldersNotification object:outline];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
[outline release];
|
[outline release];
|
||||||
|
[_recentDirectories release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Private */
|
||||||
|
|
||||||
|
- (void)fillPopUpMenu
|
||||||
|
{
|
||||||
|
NSMenu *m = [addButtonPopUp menu];
|
||||||
|
NSMenuItem *mi = [m addItemWithTitle:NSLocalizedString(@"Add New Folder...", @"") action:@selector(askForDirectory) keyEquivalent:@""];
|
||||||
|
[mi setTarget:self];
|
||||||
|
[m addItem:[NSMenuItem separatorItem]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)adjustUIToLocalization
|
||||||
|
{
|
||||||
|
NSString *lang = [[NSBundle preferredLocalizationsFromArray:[[NSBundle mainBundle] localizations]] objectAtIndex:0];
|
||||||
|
NSInteger loadResultsWidthDelta = 0;
|
||||||
|
if ([lang isEqual:@"ru"]) {
|
||||||
|
loadResultsWidthDelta = 50;
|
||||||
|
}
|
||||||
|
else if ([lang isEqual:@"uk"]) {
|
||||||
|
loadResultsWidthDelta = 70;
|
||||||
|
}
|
||||||
|
else if ([lang isEqual:@"hy"]) {
|
||||||
|
loadResultsWidthDelta = 30;
|
||||||
|
}
|
||||||
|
if (loadResultsWidthDelta) {
|
||||||
|
NSRect r = [loadResultsButton frame];
|
||||||
|
r.size.width += loadResultsWidthDelta;
|
||||||
|
r.origin.x -= loadResultsWidthDelta;
|
||||||
|
[loadResultsButton setFrame:r];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
|
|
||||||
- (IBAction)askForDirectory:(id)sender
|
- (void)askForDirectory
|
||||||
{
|
{
|
||||||
NSOpenPanel *op = [NSOpenPanel openPanel];
|
NSOpenPanel *op = [NSOpenPanel openPanel];
|
||||||
[op setCanChooseFiles:YES];
|
[op setCanChooseFiles:YES];
|
||||||
[op setCanChooseDirectories:YES];
|
[op setCanChooseDirectories:YES];
|
||||||
[op setAllowsMultipleSelection:NO];
|
[op setAllowsMultipleSelection:YES];
|
||||||
[op setTitle:@"Select a directory to add to the scanning list"];
|
[op setTitle:NSLocalizedString(@"Select a folder to add to the scanning list", @"")];
|
||||||
[op setDelegate:self];
|
[op setDelegate:self];
|
||||||
if ([op runModalForTypes:nil] == NSOKButton)
|
if ([op runModal] == NSOKButton) {
|
||||||
{
|
for (NSString *directory in [op filenames]) {
|
||||||
NSString *directory = [[op filenames] objectAtIndex:0];
|
|
||||||
[self addDirectory:directory];
|
[self addDirectory:directory];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)popupAddDirectoryMenu:(id)sender
|
|
||||||
{
|
|
||||||
if ([[_recentDirectories directories] count] == 0)
|
|
||||||
{
|
|
||||||
[self askForDirectory:sender];
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
NSMenu *m = [addButtonPopUp menu];
|
|
||||||
while ([m numberOfItems] > 0)
|
|
||||||
[m removeItemAtIndex:0];
|
|
||||||
NSMenuItem *mi = [m addItemWithTitle:@"Add New Directory..." action:@selector(askForDirectory:) keyEquivalent:@""];
|
|
||||||
[mi setTarget:self];
|
|
||||||
[m addItem:[NSMenuItem separatorItem]];
|
|
||||||
[_recentDirectories fillMenu:m];
|
|
||||||
[addButtonPopUp selectItem:nil];
|
|
||||||
[[addButtonPopUp cell] performClickWithFrame:[sender frame] inView:[sender superview]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)removeSelectedDirectory:(id)sender
|
- (void)popupAddDirectoryMenu:(id)sender
|
||||||
{
|
{
|
||||||
[[self window] makeKeyAndOrderFront:nil];
|
if ((!_alwaysShowPopUp) && ([[_recentDirectories filepaths] count] == 0)) {
|
||||||
if ([outlineView selectedRow] < 0)
|
[self askForDirectory];
|
||||||
return;
|
|
||||||
NSIndexPath *path = [outline selectedIndexPath];
|
|
||||||
NSInteger state = [outline intProperty:@"state" valueAtPath:path];
|
|
||||||
if (([path length] == 1) && (state != 2)) {
|
|
||||||
[_py removeDirectory:i2n([path indexAtPosition:0])];
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
NSInteger newState = state == 2 ? 0 : 2; // If excluded, put it back
|
[addButtonPopUp selectItem:nil];
|
||||||
[outline setIntProperty:@"state" value:newState atPath:path];
|
[[addButtonPopUp cell] performClickWithFrame:[sender frame] inView:[sender superview]];
|
||||||
[outlineView display];
|
|
||||||
}
|
}
|
||||||
[self refreshRemoveButtonText];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleVisible:(id)sender
|
- (void)popupLoadRecentMenu:(id)sender
|
||||||
|
{
|
||||||
|
if ([[[_app recentResults] filepaths] count] > 0) {
|
||||||
|
NSMenu *m = [loadRecentButtonPopUp menu];
|
||||||
|
while ([m numberOfItems] > 0) {
|
||||||
|
[m removeItemAtIndex:0];
|
||||||
|
}
|
||||||
|
NSMenuItem *mi = [m addItemWithTitle:NSLocalizedString(@"Load from file...", @"") action:@selector(loadResults) keyEquivalent:@""];
|
||||||
|
[mi setTarget:_app];
|
||||||
|
[m addItem:[NSMenuItem separatorItem]];
|
||||||
|
[[_app recentResults] fillMenu:m];
|
||||||
|
[loadRecentButtonPopUp selectItem:nil];
|
||||||
|
[[loadRecentButtonPopUp cell] performClickWithFrame:[sender frame] inView:[sender superview]];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[_app loadResults];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeSelectedDirectory
|
||||||
{
|
{
|
||||||
[[self window] makeKeyAndOrderFront:nil];
|
[[self window] makeKeyAndOrderFront:nil];
|
||||||
|
[[outline model] removeSelectedDirectory];
|
||||||
|
[self refreshRemoveButtonText];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Public */
|
/* Public */
|
||||||
- (void)addDirectory:(NSString *)directory
|
- (void)addDirectory:(NSString *)directory
|
||||||
{
|
{
|
||||||
NSInteger r = [[_py addDirectory:directory] intValue];
|
[model addDirectory:directory];
|
||||||
if (r) {
|
[_recentDirectories addFile:directory];
|
||||||
NSString *m;
|
|
||||||
switch (r) {
|
|
||||||
case 1: {
|
|
||||||
m = @"This directory already is in the list.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
m = @"This directory does not exist.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[Dialogs showMessage:m];
|
|
||||||
}
|
|
||||||
[_recentDirectories addDirectory:directory];
|
|
||||||
[[self window] makeKeyAndOrderFront:nil];
|
[[self window] makeKeyAndOrderFront:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,9 +149,13 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
[removeButton setEnabled:YES];
|
[removeButton setEnabled:YES];
|
||||||
NSInteger state = [outline intProperty:@"state" valueAtPath:[outline selectedIndexPath]];
|
NSIndexPath *path = [outline selectedIndexPath];
|
||||||
NSString *buttonText = state == 2 ? @"Put Back" : @"Remove";
|
if (path != nil) {
|
||||||
[removeButton setTitle:buttonText];
|
NSInteger state = [outline intProperty:@"state" valueAtPath:path];
|
||||||
|
BOOL shouldDisplayArrow = ([path length] > 1) && (state == 2);
|
||||||
|
NSString *imgName = shouldDisplayArrow ? @"NSGoLeftTemplate" : @"NSRemoveTemplate";
|
||||||
|
[removeButton setImage:[NSImage imageNamed:imgName]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delegate */
|
/* Delegate */
|
||||||
@@ -132,6 +166,11 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
return isdir;
|
return isdir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)recentFileClicked:(NSString *)path
|
||||||
|
{
|
||||||
|
[self addDirectory:path];
|
||||||
|
}
|
||||||
|
|
||||||
/* Notifications */
|
/* Notifications */
|
||||||
|
|
||||||
- (void)directorySelectionChanged:(NSNotification *)aNotification
|
- (void)directorySelectionChanged:(NSNotification *)aNotification
|
||||||
@@ -139,4 +178,12 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[self refreshRemoveButtonText];
|
[self refreshRemoveButtonText];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)outlineAddedFolders:(NSNotification *)aNotification
|
||||||
|
{
|
||||||
|
NSArray *foldernames = [[aNotification userInfo] objectForKey:@"foldernames"];
|
||||||
|
for (NSString *foldername in foldernames) {
|
||||||
|
[_recentDirectories addFile:foldername];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
25
cocoa/base/IgnoreListDialog.h
Normal file
25
cocoa/base/IgnoreListDialog.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "PyIgnoreListDialog.h"
|
||||||
|
#import "HSTable.h"
|
||||||
|
|
||||||
|
@interface IgnoreListDialog : NSWindowController
|
||||||
|
{
|
||||||
|
PyIgnoreListDialog *model;
|
||||||
|
HSTable *ignoreListTable;
|
||||||
|
NSTableView *ignoreListTableView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property (readwrite, retain) PyIgnoreListDialog *model;
|
||||||
|
@property (readwrite, retain) NSTableView *ignoreListTableView;
|
||||||
|
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef;
|
||||||
|
- (void)initializeColumns;
|
||||||
|
@end
|
||||||
51
cocoa/base/IgnoreListDialog.m
Normal file
51
cocoa/base/IgnoreListDialog.m
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "IgnoreListDialog.h"
|
||||||
|
#import "IgnoreListDialog_UI.h"
|
||||||
|
#import "HSPyUtil.h"
|
||||||
|
|
||||||
|
@implementation IgnoreListDialog
|
||||||
|
|
||||||
|
@synthesize model;
|
||||||
|
@synthesize ignoreListTableView;
|
||||||
|
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef
|
||||||
|
{
|
||||||
|
self = [super initWithWindow:nil];
|
||||||
|
self.model = [[[PyIgnoreListDialog alloc] initWithModel:aPyRef] autorelease];
|
||||||
|
[self.model bindCallback:createCallback(@"IgnoreListDialogView", self)];
|
||||||
|
[self setWindow:createIgnoreListDialog_UI(self)];
|
||||||
|
ignoreListTable = [[HSTable alloc] initWithPyRef:[model ignoreListTable] tableView:ignoreListTableView];
|
||||||
|
[self initializeColumns];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[ignoreListTable release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)initializeColumns
|
||||||
|
{
|
||||||
|
HSColumnDef defs[] = {
|
||||||
|
{@"path1", 240, 40, 0, NO, nil},
|
||||||
|
{@"path2", 240, 40, 0, NO, nil},
|
||||||
|
nil
|
||||||
|
};
|
||||||
|
[[ignoreListTable columns] initializeColumns:defs];
|
||||||
|
[[ignoreListTable columns] setColumnsAsReadOnly];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* model --> view */
|
||||||
|
- (void)show
|
||||||
|
{
|
||||||
|
[self showWindow:self];
|
||||||
|
}
|
||||||
|
@end
|
||||||
37
cocoa/base/PrioritizeDialog.h
Normal file
37
cocoa/base/PrioritizeDialog.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "PyPrioritizeDialog.h"
|
||||||
|
#import "HSPopUpList.h"
|
||||||
|
#import "HSSelectableList.h"
|
||||||
|
#import "PrioritizeList.h"
|
||||||
|
#import "PyDupeGuru.h"
|
||||||
|
|
||||||
|
@interface PrioritizeDialog : NSWindowController
|
||||||
|
{
|
||||||
|
NSPopUpButton *categoryPopUpView;
|
||||||
|
NSTableView *criteriaTableView;
|
||||||
|
NSTableView *prioritizationTableView;
|
||||||
|
|
||||||
|
PyPrioritizeDialog *model;
|
||||||
|
HSPopUpList *categoryPopUp;
|
||||||
|
HSSelectableList *criteriaList;
|
||||||
|
PrioritizeList *prioritizationList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property (readwrite, retain) NSPopUpButton *categoryPopUpView;
|
||||||
|
@property (readwrite, retain) NSTableView *criteriaTableView;
|
||||||
|
@property (readwrite, retain) NSTableView *prioritizationTableView;
|
||||||
|
|
||||||
|
- (id)initWithApp:(PyDupeGuru *)aApp;
|
||||||
|
- (PyPrioritizeDialog *)model;
|
||||||
|
|
||||||
|
- (void)ok;
|
||||||
|
- (void)cancel;
|
||||||
|
@end;
|
||||||
56
cocoa/base/PrioritizeDialog.m
Normal file
56
cocoa/base/PrioritizeDialog.m
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "PrioritizeDialog.h"
|
||||||
|
#import "PrioritizeDialog_UI.h"
|
||||||
|
#import "HSPyUtil.h"
|
||||||
|
|
||||||
|
@implementation PrioritizeDialog
|
||||||
|
|
||||||
|
@synthesize categoryPopUpView;
|
||||||
|
@synthesize criteriaTableView;
|
||||||
|
@synthesize prioritizationTableView;
|
||||||
|
|
||||||
|
- (id)initWithApp:(PyDupeGuru *)aApp
|
||||||
|
{
|
||||||
|
self = [super initWithWindowNibName:@"PrioritizeDialog"];
|
||||||
|
model = [[PyPrioritizeDialog alloc] initWithApp:[aApp pyRef]];
|
||||||
|
[self setWindow:createPrioritizeDialog_UI(self)];
|
||||||
|
categoryPopUp = [[HSPopUpList alloc] initWithPyRef:[[self model] categoryList] popupView:categoryPopUpView];
|
||||||
|
criteriaList = [[HSSelectableList alloc] initWithPyRef:[[self model] criteriaList] tableView:criteriaTableView];
|
||||||
|
prioritizationList = [[PrioritizeList alloc] initWithPyRef:[[self model] prioritizationList] tableView:prioritizationTableView];
|
||||||
|
[model bindCallback:createCallback(@"PrioritizeDialogView", self)];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[categoryPopUp release];
|
||||||
|
[criteriaList release];
|
||||||
|
[prioritizationList release];
|
||||||
|
[model release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (PyPrioritizeDialog *)model
|
||||||
|
{
|
||||||
|
return (PyPrioritizeDialog *)model;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)ok
|
||||||
|
{
|
||||||
|
[NSApp stopModal];
|
||||||
|
[self close];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cancel
|
||||||
|
{
|
||||||
|
[NSApp abortModal];
|
||||||
|
[self close];
|
||||||
|
}
|
||||||
|
@end
|
||||||
16
cocoa/base/PrioritizeList.h
Normal file
16
cocoa/base/PrioritizeList.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "HSSelectableList.h"
|
||||||
|
#import "PyPrioritizeList.h"
|
||||||
|
|
||||||
|
@interface PrioritizeList : HSSelectableList {}
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef tableView:(NSTableView *)aTableView;
|
||||||
|
- (PyPrioritizeList *)model;
|
||||||
|
@end
|
||||||
58
cocoa/base/PrioritizeList.m
Normal file
58
cocoa/base/PrioritizeList.m
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "PrioritizeList.h"
|
||||||
|
#import "Utils.h"
|
||||||
|
#import "Consts.h"
|
||||||
|
|
||||||
|
@implementation PrioritizeList
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef tableView:(NSTableView *)aTableView
|
||||||
|
{
|
||||||
|
self = [super initWithPyRef:aPyRef wrapperClass:[PyPrioritizeList class]
|
||||||
|
callbackClassName:@"PrioritizeListView" view:aTableView];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (PyPrioritizeList *)model
|
||||||
|
{
|
||||||
|
return (PyPrioritizeList *)model;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setView:(NSTableView *)aTableView
|
||||||
|
{
|
||||||
|
[super setView:aTableView];
|
||||||
|
[[self view] registerForDraggedTypes:[NSArray arrayWithObject:DGPrioritizeIndexPasteboardType]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
|
||||||
|
{
|
||||||
|
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
|
||||||
|
[pboard declareTypes:[NSArray arrayWithObject:DGPrioritizeIndexPasteboardType] owner:self];
|
||||||
|
[pboard setData:data forType:DGPrioritizeIndexPasteboardType];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id <NSDraggingInfo>)info proposedRow:(NSInteger)row
|
||||||
|
proposedDropOperation:(NSTableViewDropOperation)op
|
||||||
|
{
|
||||||
|
if (op == NSTableViewDropAbove) {
|
||||||
|
return NSDragOperationMove;
|
||||||
|
}
|
||||||
|
return NSDragOperationNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info
|
||||||
|
row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation
|
||||||
|
{
|
||||||
|
NSPasteboard* pboard = [info draggingPasteboard];
|
||||||
|
NSData* rowData = [pboard dataForType:DGPrioritizeIndexPasteboardType];
|
||||||
|
NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData];
|
||||||
|
[[self model] moveIndexes:[Utils indexSet2Array:rowIndexes] toIndex:row];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
@end
|
||||||
@@ -1,25 +1,26 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "HSWindowController.h"
|
|
||||||
#import "PyApp.h"
|
|
||||||
#import "PyProblemDialog.h"
|
#import "PyProblemDialog.h"
|
||||||
#import "HSTable.h"
|
#import "HSTable.h"
|
||||||
|
|
||||||
@interface ProblemDialog : HSWindowController
|
@interface ProblemDialog : NSWindowController
|
||||||
{
|
{
|
||||||
IBOutlet NSTableView *problemTableView;
|
PyProblemDialog *model;
|
||||||
|
|
||||||
HSTable *problemTable;
|
HSTable *problemTable;
|
||||||
|
NSTableView *problemTableView;
|
||||||
}
|
}
|
||||||
- (id)initWithPy:(PyApp *)aPy;
|
|
||||||
- (PyProblemDialog *)py;
|
|
||||||
|
|
||||||
- (IBAction)revealSelected:(id)sender;
|
@property (readwrite, retain) PyProblemDialog *model;
|
||||||
|
@property (readwrite, retain) NSTableView *problemTableView;
|
||||||
|
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef;
|
||||||
|
|
||||||
|
- (void)initializeColumns;
|
||||||
@end
|
@end
|
||||||
@@ -1,40 +1,44 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "ProblemDialog.h"
|
#import "ProblemDialog.h"
|
||||||
|
#import "ProblemDialog_UI.h"
|
||||||
#import "Utils.h"
|
#import "Utils.h"
|
||||||
|
|
||||||
@implementation ProblemDialog
|
@implementation ProblemDialog
|
||||||
- (id)initWithPy:(PyApp *)aPy
|
|
||||||
|
@synthesize model;
|
||||||
|
@synthesize problemTableView;
|
||||||
|
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef
|
||||||
{
|
{
|
||||||
self = [super initWithNibName:@"ProblemDialog" pyClassName:@"PyProblemDialog" pyParent:aPy];
|
self = [super initWithWindow:nil];
|
||||||
[self window]; //So the detailsTable is initialized.
|
self.model = [[PyProblemDialog alloc] initWithModel:aPyRef];
|
||||||
problemTable = [[HSTable alloc] initWithPyClassName:@"PyProblemTable" pyParent:[self py] view:problemTableView];
|
[self setWindow:createProblemDialog_UI(self)];
|
||||||
[self connect];
|
problemTable = [[HSTable alloc] initWithPyRef:[self.model problemTable] tableView:problemTableView];
|
||||||
[problemTable connect];
|
[self initializeColumns];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
[problemTable disconnect];
|
|
||||||
[self disconnect];
|
|
||||||
[problemTable release];
|
[problemTable release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (PyProblemDialog *)py
|
- (void)initializeColumns
|
||||||
{
|
{
|
||||||
return (PyProblemDialog *)py;
|
HSColumnDef defs[] = {
|
||||||
}
|
{@"path", 202, 40, 0, NO, nil},
|
||||||
|
{@"msg", 228, 40, 0, NO, nil},
|
||||||
- (IBAction)revealSelected:(id)sender
|
nil
|
||||||
{
|
};
|
||||||
[[self py] revealSelected];
|
[[problemTable columns] initializeColumns:defs];
|
||||||
|
[[problemTable columns] setColumnsAsReadOnly];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "PyGUI.h"
|
|
||||||
|
|
||||||
@interface PyDetailsPanel : PyGUI
|
|
||||||
- (NSInteger)numberOfRows;
|
|
||||||
- (id)valueForColumn:(NSString *)column row:(NSInteger)row;
|
|
||||||
@end
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "PyOutline.h"
|
|
||||||
|
|
||||||
@interface PyDirectoryOutline : PyOutline
|
|
||||||
- (void)addDirectory:(NSString *)directoryPath;
|
|
||||||
@end
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "PyApp.h"
|
|
||||||
|
|
||||||
@interface PyDupeGuruBase : PyApp
|
|
||||||
//Actions
|
|
||||||
- (NSNumber *)addDirectory:(NSString *)name;
|
|
||||||
- (void)removeDirectory:(NSNumber *)index;
|
|
||||||
- (void)loadResults;
|
|
||||||
- (void)saveResults;
|
|
||||||
- (void)loadIgnoreList;
|
|
||||||
- (void)saveIgnoreList;
|
|
||||||
- (void)clearIgnoreList;
|
|
||||||
- (void)purgeIgnoreList;
|
|
||||||
- (NSString *)exportToXHTMLwithColumns:(NSArray *)aColIds;
|
|
||||||
- (void)invokeCommand:(NSString *)cmd;
|
|
||||||
|
|
||||||
- (NSNumber *)doScan;
|
|
||||||
|
|
||||||
- (void)toggleSelectedMark;
|
|
||||||
- (void)markAll;
|
|
||||||
- (void)markInvert;
|
|
||||||
- (void)markNone;
|
|
||||||
|
|
||||||
- (void)addSelectedToIgnoreList;
|
|
||||||
- (void)openSelected;
|
|
||||||
- (void)revealSelected;
|
|
||||||
- (void)makeSelectedReference;
|
|
||||||
- (void)applyFilter:(NSString *)filter;
|
|
||||||
|
|
||||||
- (void)copyOrMove:(NSNumber *)aCopy markedTo:(NSString *)destination recreatePath:(NSNumber *)aRecreateType;
|
|
||||||
- (void)deleteMarked;
|
|
||||||
- (void)removeMarked;
|
|
||||||
|
|
||||||
//Data
|
|
||||||
- (NSNumber *)getIgnoreListCount;
|
|
||||||
- (NSNumber *)getMarkCount;
|
|
||||||
- (BOOL)scanWasProblematic;
|
|
||||||
|
|
||||||
//Scanning options
|
|
||||||
- (void)setMinMatchPercentage:(NSNumber *)percentage;
|
|
||||||
- (void)setMixFileKind:(NSNumber *)mix_file_kind;
|
|
||||||
- (void)setEscapeFilterRegexp:(NSNumber *)escape_filter_regexp;
|
|
||||||
- (void)setRemoveEmptyFolders:(NSNumber *)remove_empty_folders;
|
|
||||||
- (void)setSizeThreshold:(NSInteger)size_threshold;
|
|
||||||
@end
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "PyGUI.h"
|
|
||||||
|
|
||||||
@interface PyProblemDialog : PyGUI
|
|
||||||
- (void)revealSelected;
|
|
||||||
@end
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#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
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "PyGUI.h"
|
|
||||||
|
|
||||||
@interface PyStatsLabel : PyGUI
|
|
||||||
- (NSString *)display;
|
|
||||||
@end
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#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;
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import "ResultOutline.h"
|
|
||||||
#import "Dialogs.h"
|
|
||||||
#import "Utils.h"
|
|
||||||
#import "Consts.h"
|
|
||||||
|
|
||||||
@implementation ResultOutline
|
|
||||||
- (id)initWithPyParent:(id)aPyParent view:(HSOutlineView *)aOutlineView
|
|
||||||
{
|
|
||||||
self = [super initWithPyClassName:@"PyResultOutline" pyParent:aPyParent view:aOutlineView];
|
|
||||||
_rootChildrenCounts = nil;
|
|
||||||
[self connect];
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
[self disconnect];
|
|
||||||
[_deltaColumns release];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (PyResultTree *)py
|
|
||||||
{
|
|
||||||
return (PyResultTree *)py;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Public */
|
|
||||||
- (BOOL)powerMarkerMode
|
|
||||||
{
|
|
||||||
return [[self py] powerMarkerMode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode
|
|
||||||
{
|
|
||||||
[[self py] setPowerMarkerMode:aPowerMarkerMode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)deltaValuesMode
|
|
||||||
{
|
|
||||||
return [[self py] deltaValuesMode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode
|
|
||||||
{
|
|
||||||
[[self py] setDeltaValuesMode:aDeltaValuesMode];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setDeltaColumns:(NSIndexSet *)aDeltaColumns
|
|
||||||
{
|
|
||||||
[_deltaColumns release];
|
|
||||||
_deltaColumns = [aDeltaColumns retain];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)selectedDupeCount
|
|
||||||
{
|
|
||||||
NSArray *selected = [self selectedIndexPaths];
|
|
||||||
if ([self powerMarkerMode]) {
|
|
||||||
return [selected count];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
NSInteger r = 0;
|
|
||||||
for (NSIndexPath *path in selected) {
|
|
||||||
if ([path length] == 2) {
|
|
||||||
r++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeSelected
|
|
||||||
{
|
|
||||||
NSInteger selectedDupeCount = [self selectedDupeCount];
|
|
||||||
if (!selectedDupeCount)
|
|
||||||
return;
|
|
||||||
NSString *msg = [NSString stringWithFormat:@"You are about to remove %d files from results. Continue?",selectedDupeCount];
|
|
||||||
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) // NO
|
|
||||||
return;
|
|
||||||
[[self py] removeSelected];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Datasource */
|
|
||||||
- (NSInteger)outlineView:(NSOutlineView *)aOutlineView numberOfChildrenOfItem:(id)item
|
|
||||||
{
|
|
||||||
NSIndexPath *path = item;
|
|
||||||
if ((path != nil) && ([path length] == 1)) {
|
|
||||||
if (_rootChildrenCounts == nil) {
|
|
||||||
_rootChildrenCounts = [[[self py] rootChildrenCounts] retain];
|
|
||||||
}
|
|
||||||
NSInteger index = [path indexAtPosition:0];
|
|
||||||
return n2i([_rootChildrenCounts objectAtIndex:index]);
|
|
||||||
}
|
|
||||||
return [super outlineView:aOutlineView numberOfChildrenOfItem:item];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)column byItem:(id)item
|
|
||||||
{
|
|
||||||
NSIndexPath *path = item;
|
|
||||||
NSString *identifier = [column identifier];
|
|
||||||
if ([identifier isEqual:@"marked"]) {
|
|
||||||
return b2n([self boolProperty:@"marked" valueAtPath:path]);
|
|
||||||
}
|
|
||||||
NSInteger columnId = [identifier integerValue];
|
|
||||||
return [[self py] valueForPath:p2a(path) column:columnId];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)outlineView:(NSOutlineView *)aOutlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
|
|
||||||
{
|
|
||||||
if ([[tableColumn identifier] isEqual:@"0"]) {
|
|
||||||
NSIndexPath *path = item;
|
|
||||||
NSString *oldName = [[self py] valueForPath:p2a(path) column:0];
|
|
||||||
NSString *newName = object;
|
|
||||||
if (![newName isEqual:oldName]) {
|
|
||||||
BOOL renamed = [[self py] renameSelected:newName];
|
|
||||||
if (!renamed) {
|
|
||||||
[Dialogs showMessage:[NSString stringWithFormat:@"The name '%@' already exists.", newName]];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[self refreshItemAtPath:path];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[super outlineView:aOutlineView setObjectValue:object forTableColumn:tableColumn byItem:item];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Delegate */
|
|
||||||
- (void)outlineView:(NSOutlineView *)aOutlineView didClickTableColumn:(NSTableColumn *)tableColumn
|
|
||||||
{
|
|
||||||
if ([[outlineView sortDescriptors] count] < 1)
|
|
||||||
return;
|
|
||||||
NSSortDescriptor *sd = [[outlineView sortDescriptors] objectAtIndex:0];
|
|
||||||
[[self py] sortBy:[[sd key] integerValue] ascending:[sd ascending]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
|
|
||||||
{
|
|
||||||
NSIndexPath *path = item;
|
|
||||||
BOOL isMarkable = [self boolProperty:@"markable" valueAtPath:path];
|
|
||||||
if ([[tableColumn identifier] isEqual:@"marked"]) {
|
|
||||||
[cell setEnabled:isMarkable];
|
|
||||||
}
|
|
||||||
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
|
|
||||||
// Determine if the text color will be blue due to directory being reference.
|
|
||||||
NSTextFieldCell *textCell = cell;
|
|
||||||
if (isMarkable) {
|
|
||||||
[textCell setTextColor:[NSColor blackColor]];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[textCell setTextColor:[NSColor blueColor]];
|
|
||||||
}
|
|
||||||
if (([self deltaValuesMode]) && ([self powerMarkerMode] || ([path length] > 1))) {
|
|
||||||
NSInteger i = [[tableColumn identifier] integerValue];
|
|
||||||
if ([_deltaColumns containsIndex:i]) {
|
|
||||||
[textCell setTextColor:[NSColor orangeColor]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tableViewHadDeletePressed:(NSTableView *)tableView
|
|
||||||
{
|
|
||||||
[self removeSelected];
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)tableViewHadSpacePressed:(NSTableView *)tableView
|
|
||||||
{
|
|
||||||
[[self py] markSelected];
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* don't calls saveEdits and cancelEdits */
|
|
||||||
- (void)outlineViewDidEndEditing:(HSOutlineView *)outlineView
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)outlineViewCancelsEdition:(HSOutlineView *)outlineView
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Python --> Cocoa */
|
|
||||||
- (void)refresh /* Override */
|
|
||||||
{
|
|
||||||
[_rootChildrenCounts release];
|
|
||||||
_rootChildrenCounts = nil;
|
|
||||||
[super refresh];
|
|
||||||
[outlineView expandItem:nil expandChildren:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateMarkings
|
|
||||||
{
|
|
||||||
for (NSMutableDictionary *props in [itemData objectEnumerator]) {
|
|
||||||
[props removeObjectForKey:@"marked"];
|
|
||||||
}
|
|
||||||
[outlineView setNeedsDisplay:YES];
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
24
cocoa/base/ResultTable.h
Normal file
24
cocoa/base/ResultTable.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <Quartz/Quartz.h>
|
||||||
|
#import "HSTable.h"
|
||||||
|
#import "PyResultTable.h"
|
||||||
|
|
||||||
|
@interface ResultTable : HSTable <QLPreviewPanelDataSource, QLPreviewPanelDelegate>
|
||||||
|
{
|
||||||
|
NSSet *_deltaColumns;
|
||||||
|
}
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef view:(NSTableView *)aTableView;
|
||||||
|
- (PyResultTable *)model;
|
||||||
|
- (BOOL)powerMarkerMode;
|
||||||
|
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode;
|
||||||
|
- (BOOL)deltaValuesMode;
|
||||||
|
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode;
|
||||||
|
@end;
|
||||||
189
cocoa/base/ResultTable.m
Normal file
189
cocoa/base/ResultTable.m
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "ResultTable.h"
|
||||||
|
#import "Dialogs.h"
|
||||||
|
#import "Utils.h"
|
||||||
|
#import "HSQuicklook.h"
|
||||||
|
|
||||||
|
@interface HSTable (private)
|
||||||
|
- (void)setPySelection;
|
||||||
|
- (void)setViewSelection;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ResultTable
|
||||||
|
- (id)initWithPyRef:(PyObject *)aPyRef view:(NSTableView *)aTableView
|
||||||
|
{
|
||||||
|
self = [super initWithPyRef:aPyRef wrapperClass:[PyResultTable class] callbackClassName:@"ResultTableView" view:aTableView];
|
||||||
|
_deltaColumns = [[NSSet setWithArray:[[self model] deltaColumns]] retain];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[_deltaColumns release];
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (PyResultTable *)model
|
||||||
|
{
|
||||||
|
return (PyResultTable *)model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Private */
|
||||||
|
- (void)updateQuicklookIfNeeded
|
||||||
|
{
|
||||||
|
if ([[QLPreviewPanel sharedPreviewPanel] dataSource] == self) {
|
||||||
|
[[QLPreviewPanel sharedPreviewPanel] reloadData];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setPySelection
|
||||||
|
{
|
||||||
|
[super setPySelection];
|
||||||
|
[self updateQuicklookIfNeeded];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setViewSelection
|
||||||
|
{
|
||||||
|
[super setViewSelection];
|
||||||
|
[self updateQuicklookIfNeeded];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Public */
|
||||||
|
- (BOOL)powerMarkerMode
|
||||||
|
{
|
||||||
|
return [[self model] powerMarkerMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setPowerMarkerMode:(BOOL)aPowerMarkerMode
|
||||||
|
{
|
||||||
|
[[self model] setPowerMarkerMode:aPowerMarkerMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)deltaValuesMode
|
||||||
|
{
|
||||||
|
return [[self model] deltaValuesMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDeltaValuesMode:(BOOL)aDeltaValuesMode
|
||||||
|
{
|
||||||
|
[[self model] setDeltaValuesMode:aDeltaValuesMode];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Datasource */
|
||||||
|
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)row
|
||||||
|
{
|
||||||
|
NSString *identifier = [column identifier];
|
||||||
|
if ([identifier isEqual:@"marked"]) {
|
||||||
|
return [[self model] valueForColumn:@"marked" row:row];
|
||||||
|
}
|
||||||
|
return [[self model] valueForRow:row column:identifier];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)column row:(NSInteger)row
|
||||||
|
{
|
||||||
|
NSString *identifier = [column identifier];
|
||||||
|
if ([identifier isEqual:@"marked"]) {
|
||||||
|
[[self model] setValue:object forColumn:identifier row:row];
|
||||||
|
}
|
||||||
|
else if ([identifier isEqual:@"name"]) {
|
||||||
|
NSString *oldName = [[self model] valueForRow:row column:identifier];
|
||||||
|
NSString *newName = object;
|
||||||
|
if (![newName isEqual:oldName]) {
|
||||||
|
BOOL renamed = [[self model] renameSelected:newName];
|
||||||
|
if (!renamed) {
|
||||||
|
[Dialogs showMessage:[NSString stringWithFormat:NSLocalizedString(@"The name '%@' already exists.", @""), newName]];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
[[self view] setNeedsDisplay:YES];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delegate */
|
||||||
|
- (void)tableView:(NSTableView *)aTableView didClickTableColumn:(NSTableColumn *)tableColumn
|
||||||
|
{
|
||||||
|
if ([[[self view] sortDescriptors] count] < 1)
|
||||||
|
return;
|
||||||
|
NSSortDescriptor *sd = [[[self view] sortDescriptors] objectAtIndex:0];
|
||||||
|
[[self model] sortBy:[sd key] ascending:[sd ascending]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)column row:(NSInteger)row
|
||||||
|
{
|
||||||
|
BOOL isSelected = [[self view] isRowSelected:row];
|
||||||
|
BOOL isMarkable = n2b([[self model] valueForColumn:@"markable" row:row]);
|
||||||
|
if ([[column identifier] isEqual:@"marked"]) {
|
||||||
|
[cell setEnabled:isMarkable];
|
||||||
|
// Low-tech solution, for indentation, but it works...
|
||||||
|
NSCellImagePosition pos = isMarkable ? NSImageRight : NSImageLeft;
|
||||||
|
[cell setImagePosition:pos];
|
||||||
|
}
|
||||||
|
if ([cell isKindOfClass:[NSTextFieldCell class]]) {
|
||||||
|
NSColor *color = [NSColor textColor];
|
||||||
|
if (isSelected) {
|
||||||
|
color = [NSColor selectedTextColor];
|
||||||
|
}
|
||||||
|
else if (isMarkable) {
|
||||||
|
if ([self deltaValuesMode]) {
|
||||||
|
if ([_deltaColumns containsObject:[column identifier]]) {
|
||||||
|
color = [NSColor orangeColor];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
color = [NSColor blueColor];
|
||||||
|
}
|
||||||
|
[(NSTextFieldCell *)cell setTextColor:color];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)tableViewHadDeletePressed:(NSTableView *)tableView
|
||||||
|
{
|
||||||
|
[[self model] removeSelected];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)tableViewHadSpacePressed:(NSTableView *)tableView
|
||||||
|
{
|
||||||
|
[[self model] markSelected];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quicklook */
|
||||||
|
- (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel *)panel
|
||||||
|
{
|
||||||
|
return [[[self model] selectedRows] count];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id <QLPreviewItem>)previewPanel:(QLPreviewPanel *)panel previewItemAtIndex:(NSInteger)index
|
||||||
|
{
|
||||||
|
NSArray *selectedRows = [[self model] selectedRows];
|
||||||
|
NSInteger absIndex = n2i([selectedRows objectAtIndex:index]);
|
||||||
|
NSString *path = [[self model] pathAtIndex:absIndex];
|
||||||
|
return [[HSQLPreviewItem alloc] initWithUrl:[NSURL fileURLWithPath:path] title:path];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)previewPanel:(QLPreviewPanel *)panel handleEvent:(NSEvent *)event
|
||||||
|
{
|
||||||
|
// redirect all key down events to the table view
|
||||||
|
if ([event type] == NSKeyDown) {
|
||||||
|
[[self view] keyDown:event];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Python --> Cocoa */
|
||||||
|
- (void)invalidateMarkings
|
||||||
|
{
|
||||||
|
[[self view] setNeedsDisplay:YES];
|
||||||
|
}
|
||||||
|
@end
|
||||||
@@ -1,74 +1,85 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "HSOutlineView.h"
|
#import <Quartz/Quartz.h>
|
||||||
#import "StatsLabel.h"
|
#import "StatsLabel.h"
|
||||||
#import "ResultOutline.h"
|
#import "ResultTable.h"
|
||||||
#import "ProblemDialog.h"
|
#import "ProblemDialog.h"
|
||||||
|
#import "DeletionOptions.h"
|
||||||
|
#import "HSTableView.h"
|
||||||
#import "PyDupeGuru.h"
|
#import "PyDupeGuru.h"
|
||||||
|
|
||||||
|
@class AppDelegateBase;
|
||||||
|
|
||||||
@interface ResultWindowBase : NSWindowController
|
@interface ResultWindowBase : NSWindowController
|
||||||
{
|
{
|
||||||
@protected
|
@protected
|
||||||
IBOutlet PyDupeGuruBase *py;
|
NSSegmentedControl *optionsSwitch;
|
||||||
IBOutlet id app;
|
NSToolbarItem *optionsToolbarItem;
|
||||||
IBOutlet NSSegmentedControl *deltaSwitch;
|
HSTableView *matches;
|
||||||
IBOutlet HSOutlineView *matches;
|
NSTextField *stats;
|
||||||
IBOutlet NSSegmentedControl *pmSwitch;
|
NSSearchField *filterField;
|
||||||
IBOutlet NSTextField *stats;
|
|
||||||
IBOutlet NSMenu *columnsMenu;
|
|
||||||
IBOutlet NSSearchField *filterField;
|
|
||||||
|
|
||||||
NSMutableArray *_resultColumns;
|
AppDelegateBase *app;
|
||||||
NSWindowController *preferencesPanel;
|
PyDupeGuru *model;
|
||||||
ResultOutline *outline;
|
ResultTable *table;
|
||||||
StatsLabel *statsLabel;
|
StatsLabel *statsLabel;
|
||||||
ProblemDialog *problemDialog;
|
ProblemDialog *problemDialog;
|
||||||
|
DeletionOptions *deletionOptions;
|
||||||
|
QLPreviewPanel* previewPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property (readwrite, retain) NSSegmentedControl *optionsSwitch;
|
||||||
|
@property (readwrite, retain) NSToolbarItem *optionsToolbarItem;
|
||||||
|
@property (readwrite, retain) HSTableView *matches;
|
||||||
|
@property (readwrite, retain) NSTextField *stats;
|
||||||
|
@property (readwrite, retain) NSSearchField *filterField;
|
||||||
|
|
||||||
|
- (id)initWithParentApp:(AppDelegateBase *)app;
|
||||||
|
|
||||||
|
/* Virtual */
|
||||||
|
- (void)initResultColumns;
|
||||||
|
- (void)setScanOptions;
|
||||||
|
|
||||||
/* Helpers */
|
/* Helpers */
|
||||||
- (void)fillColumnsMenu;
|
- (void)fillColumnsMenu;
|
||||||
- (NSTableColumn *)getColumnForIdentifier:(NSInteger)aIdentifier title:(NSString *)aTitle width:(NSInteger)aWidth refCol:(NSTableColumn *)aColumn;
|
- (void)updateOptionSegments;
|
||||||
- (NSArray *)getColumnsOrder;
|
- (void)showProblemDialog;
|
||||||
- (NSDictionary *)getColumnsWidth;
|
- (void)adjustUIToLocalization;
|
||||||
- (void)initResultColumns;
|
|
||||||
- (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth;
|
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
- (IBAction)clearIgnoreList:(id)sender;
|
- (void)changeOptions;
|
||||||
- (IBAction)changeDelta:(id)sender;
|
- (void)copyMarked;
|
||||||
- (IBAction)changePowerMarker:(id)sender;
|
- (void)trashMarked;
|
||||||
- (IBAction)copyMarked:(id)sender;
|
- (void)filter;
|
||||||
- (IBAction)deleteMarked:(id)sender;
|
- (void)focusOnFilterField;
|
||||||
- (IBAction)exportToXHTML:(id)sender;
|
- (void)ignoreSelected;
|
||||||
- (IBAction)filter:(id)sender;
|
- (void)invokeCustomCommand;
|
||||||
- (IBAction)ignoreSelected:(id)sender;
|
- (void)markAll;
|
||||||
- (IBAction)invokeCustomCommand:(id)sender;
|
- (void)markInvert;
|
||||||
- (IBAction)markAll:(id)sender;
|
- (void)markNone;
|
||||||
- (IBAction)markInvert:(id)sender;
|
- (void)markSelected;
|
||||||
- (IBAction)markNone:(id)sender;
|
- (void)moveMarked;
|
||||||
- (IBAction)markSelected:(id)sender;
|
- (void)openClicked;
|
||||||
- (IBAction)moveMarked:(id)sender;
|
- (void)openSelected;
|
||||||
- (IBAction)openClicked:(id)sender;
|
- (void)removeMarked;
|
||||||
- (IBAction)openSelected:(id)sender;
|
- (void)removeSelected;
|
||||||
- (IBAction)removeMarked:(id)sender;
|
- (void)renameSelected;
|
||||||
- (IBAction)removeSelected:(id)sender;
|
- (void)reprioritizeResults;
|
||||||
- (IBAction)renameSelected:(id)sender;
|
- (void)resetColumnsToDefault;
|
||||||
- (IBAction)resetColumnsToDefault:(id)sender;
|
- (void)revealSelected;
|
||||||
- (IBAction)revealSelected:(id)sender;
|
- (void)saveResults;
|
||||||
- (IBAction)showPreferencesPanel:(id)sender;
|
- (void)startDuplicateScan;
|
||||||
- (IBAction)startDuplicateScan:(id)sender;
|
- (void)switchSelected;
|
||||||
- (IBAction)switchSelected:(id)sender;
|
- (void)toggleColumn:(id)sender;
|
||||||
- (IBAction)toggleColumn:(id)sender;
|
- (void)toggleDelta;
|
||||||
- (IBAction)toggleDelta:(id)sender;
|
- (void)toggleDetailsPanel;
|
||||||
- (IBAction)toggleDetailsPanel:(id)sender;
|
- (void)togglePowerMarker;
|
||||||
- (IBAction)togglePowerMarker:(id)sender;
|
- (void)toggleQuicklookPanel;
|
||||||
|
|
||||||
/* Notifications */
|
|
||||||
- (void)jobCompleted:(NSNotification *)aNotification;
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,412 +1,353 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "ResultWindow.h"
|
#import "ResultWindow.h"
|
||||||
|
#import "ResultWindow_UI.h"
|
||||||
#import "Dialogs.h"
|
#import "Dialogs.h"
|
||||||
#import "ProgressController.h"
|
#import "ProgressController.h"
|
||||||
#import "Utils.h"
|
#import "Utils.h"
|
||||||
#import "RegistrationInterface.h"
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
#import "Consts.h"
|
#import "Consts.h"
|
||||||
|
#import "PrioritizeDialog.h"
|
||||||
|
|
||||||
@implementation ResultWindowBase
|
@implementation ResultWindowBase
|
||||||
- (void)awakeFromNib
|
|
||||||
{
|
|
||||||
[self window];
|
|
||||||
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 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];
|
@synthesize optionsSwitch;
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobCompleted:) name:JobCompletedNotification object:nil];
|
@synthesize optionsToolbarItem;
|
||||||
|
@synthesize matches;
|
||||||
|
@synthesize stats;
|
||||||
|
@synthesize filterField;
|
||||||
|
|
||||||
|
- (id)initWithParentApp:(AppDelegateBase *)aApp;
|
||||||
|
{
|
||||||
|
self = [super initWithWindow:nil];
|
||||||
|
app = aApp;
|
||||||
|
model = [app model];
|
||||||
|
[self setWindow:createResultWindow_UI(self)];
|
||||||
|
[[self window] setTitle:fmt(@"%@ Results", [model appName])];
|
||||||
|
/* Put a cute iTunes-like bottom bar */
|
||||||
|
[[self window] setContentBorderThickness:28 forEdge:NSMinYEdge];
|
||||||
|
table = [[ResultTable alloc] initWithPyRef:[model resultTable] view:matches];
|
||||||
|
statsLabel = [[StatsLabel alloc] initWithPyRef:[model statsLabel] view:stats];
|
||||||
|
problemDialog = [[ProblemDialog alloc] initWithPyRef:[model problemDialog]];
|
||||||
|
deletionOptions = [[DeletionOptions alloc] initWithPyRef:[model deletionOptions]];
|
||||||
|
[self initResultColumns];
|
||||||
|
[[table columns] setColumnsAsReadOnly];
|
||||||
|
[self fillColumnsMenu];
|
||||||
|
[matches setTarget:self];
|
||||||
|
[matches setDoubleAction:@selector(openClicked)];
|
||||||
|
[self adjustUIToLocalization];
|
||||||
|
|
||||||
[[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];
|
||||||
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
[outline release];
|
[table release];
|
||||||
[preferencesPanel release];
|
|
||||||
[statsLabel release];
|
[statsLabel release];
|
||||||
[problemDialog release];
|
[problemDialog release];
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Virtual */
|
||||||
|
- (void)initResultColumns
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setScanOptions
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/* Helpers */
|
/* Helpers */
|
||||||
- (void)fillColumnsMenu
|
- (void)fillColumnsMenu
|
||||||
{
|
{
|
||||||
// The columns menu is supposed to be empty and initResultColumns must have been called
|
[[app columnsMenu] removeAllItems];
|
||||||
for (NSTableColumn *col in _resultColumns)
|
NSArray *menuItems = [[[table columns] model] menuItems];
|
||||||
{
|
for (NSInteger i=0; i < [menuItems count]; i++) {
|
||||||
NSMenuItem *mi = [columnsMenu addItemWithTitle:[[col headerCell] stringValue] action:@selector(toggleColumn:) keyEquivalent:@""];
|
NSArray *pair = [menuItems objectAtIndex:i];
|
||||||
[mi setTag:[[col identifier] integerValue]];
|
NSString *display = [pair objectAtIndex:0];
|
||||||
|
BOOL marked = n2b([pair objectAtIndex:1]);
|
||||||
|
NSMenuItem *mi = [[app columnsMenu] addItemWithTitle:display action:@selector(toggleColumn:) keyEquivalent:@""];
|
||||||
[mi setTarget:self];
|
[mi setTarget:self];
|
||||||
if ([[matches tableColumns] containsObject:col])
|
[mi setState:marked ? NSOnState : NSOffState];
|
||||||
[mi setState:NSOnState];
|
[mi setTag:i];
|
||||||
}
|
}
|
||||||
[columnsMenu addItem:[NSMenuItem separatorItem]];
|
[[app columnsMenu] addItem:[NSMenuItem separatorItem]];
|
||||||
NSMenuItem *mi = [columnsMenu addItemWithTitle:@"Reset to Default" action:@selector(resetColumnsToDefault:) keyEquivalent:@""];
|
NSMenuItem *mi = [[app columnsMenu] addItemWithTitle:NSLocalizedString(@"Reset to Default", @"")
|
||||||
|
action:@selector(resetColumnsToDefault) keyEquivalent:@""];
|
||||||
[mi setTarget:self];
|
[mi setTarget:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSTableColumn *)getColumnForIdentifier:(NSInteger)aIdentifier title:(NSString *)aTitle width:(NSInteger)aWidth refCol:(NSTableColumn *)aColumn
|
- (void)updateOptionSegments
|
||||||
{
|
{
|
||||||
NSNumber *n = [NSNumber numberWithInteger:aIdentifier];
|
[optionsSwitch setSelected:[[app detailsPanel] isVisible] forSegment:0];
|
||||||
NSTableColumn *col = [[NSTableColumn alloc] initWithIdentifier:[n stringValue]];
|
[optionsSwitch setSelected:[table powerMarkerMode] forSegment:1];
|
||||||
[col setWidth:aWidth];
|
[optionsSwitch setSelected:[table deltaValuesMode] forSegment:2];
|
||||||
[col setEditable:NO];
|
|
||||||
[[col dataCell] setFont:[[aColumn dataCell] font]];
|
|
||||||
[[col headerCell] setStringValue:aTitle];
|
|
||||||
[col setResizingMask:NSTableColumnUserResizingMask];
|
|
||||||
[col setSortDescriptorPrototype:[[NSSortDescriptor alloc] initWithKey:[n stringValue] ascending:YES]];
|
|
||||||
return col;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Returns an array of identifiers, in order.
|
- (void)showProblemDialog
|
||||||
- (NSArray *)getColumnsOrder
|
|
||||||
{
|
{
|
||||||
NSMutableArray *result = [NSMutableArray array];
|
[problemDialog showWindow:self];
|
||||||
for (NSTableColumn *col in [matches tableColumns]) {
|
|
||||||
NSString *colId = [col identifier];
|
|
||||||
[result addObject:colId];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)getColumnsWidth
|
- (void)adjustUIToLocalization
|
||||||
{
|
{
|
||||||
NSMutableDictionary *result = [NSMutableDictionary dictionary];
|
NSString *lang = [[NSBundle preferredLocalizationsFromArray:[[NSBundle mainBundle] localizations]] objectAtIndex:0];
|
||||||
for (NSTableColumn *col in [matches tableColumns]) {
|
NSInteger seg1delta = 0;
|
||||||
NSString *colId = [col identifier];
|
NSInteger seg2delta = 0;
|
||||||
NSNumber *width = [NSNumber numberWithDouble:[col width]];
|
if ([lang isEqual:@"ru"]) {
|
||||||
[result setObject:width forKey:colId];
|
seg2delta = 20;
|
||||||
}
|
}
|
||||||
return result;
|
else if ([lang isEqual:@"uk"]) {
|
||||||
}
|
seg2delta = 20;
|
||||||
|
|
||||||
- (void)initResultColumns
|
|
||||||
{
|
|
||||||
// Virtual
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)restoreColumnsPosition:(NSArray *)aColumnsOrder widths:(NSDictionary *)aColumnsWidth
|
|
||||||
{
|
|
||||||
for (NSMenuItem *mi in [columnsMenu itemArray]) {
|
|
||||||
if ([mi state] == NSOnState) {
|
|
||||||
[self toggleColumn:mi];
|
|
||||||
}
|
}
|
||||||
|
else if ([lang isEqual:@"hy"]) {
|
||||||
|
seg1delta = 20;
|
||||||
}
|
}
|
||||||
//Add columns and set widths
|
if (seg1delta || seg2delta) {
|
||||||
for (NSString *colId in aColumnsOrder) {
|
[optionsSwitch setWidth:[optionsSwitch widthForSegment:0]+seg1delta forSegment:0];
|
||||||
NSInteger colIndex = [colId integerValue];
|
[optionsSwitch setWidth:[optionsSwitch widthForSegment:1]+seg2delta forSegment:1];
|
||||||
if ((colIndex == 0) && (![colId isEqual:@"0"])) {
|
NSSize s = [optionsToolbarItem maxSize];
|
||||||
continue;
|
s.width += seg1delta + seg2delta;
|
||||||
}
|
[optionsToolbarItem setMaxSize:s];
|
||||||
NSTableColumn *col = [_resultColumns objectAtIndex:colIndex];
|
[optionsToolbarItem setMinSize:s];
|
||||||
NSNumber *width = [aColumnsWidth objectForKey:[col identifier]];
|
|
||||||
NSMenuItem *mi = [columnsMenu itemWithTag:colIndex];
|
|
||||||
if (width) {
|
|
||||||
[col setWidth:[width floatValue]];
|
|
||||||
}
|
|
||||||
[self toggleColumn:mi];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
- (IBAction)clearIgnoreList:(id)sender
|
- (void)changeOptions
|
||||||
{
|
{
|
||||||
NSInteger i = n2i([py getIgnoreListCount]);
|
NSInteger seg = [optionsSwitch selectedSegment];
|
||||||
if (!i)
|
if (seg == 0) {
|
||||||
return;
|
[self toggleDetailsPanel];
|
||||||
if ([Dialogs askYesNo:[NSString stringWithFormat:@"Do you really want to remove all %d items from the ignore list?",i]] == NSAlertSecondButtonReturn) // NO
|
}
|
||||||
return;
|
else if (seg == 1) {
|
||||||
[py clearIgnoreList];
|
[self togglePowerMarker];
|
||||||
}
|
}
|
||||||
|
else if (seg == 2) {
|
||||||
- (IBAction)changeDelta:(id)sender
|
[self toggleDelta];
|
||||||
{
|
|
||||||
[outline setDeltaValuesMode:[deltaSwitch selectedSegment] == 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)changePowerMarker:(id)sender
|
|
||||||
{
|
|
||||||
[outline setPowerMarkerMode:[pmSwitch selectedSegment] == 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)copyMarked:(id)sender
|
|
||||||
{
|
|
||||||
NSInteger mark_count = [[py getMarkCount] intValue];
|
|
||||||
if (!mark_count)
|
|
||||||
return;
|
|
||||||
NSOpenPanel *op = [NSOpenPanel openPanel];
|
|
||||||
[op setCanChooseFiles:NO];
|
|
||||||
[op setCanChooseDirectories:YES];
|
|
||||||
[op setCanCreateDirectories:YES];
|
|
||||||
[op setAllowsMultipleSelection:NO];
|
|
||||||
[op setTitle:@"Select a directory to copy marked files to"];
|
|
||||||
if ([op runModalForTypes:nil] == NSOKButton)
|
|
||||||
{
|
|
||||||
NSString *directory = [[op filenames] objectAtIndex:0];
|
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
|
||||||
[py copyOrMove:b2n(YES) markedTo:directory recreatePath:[ud objectForKey:@"recreatePathType"]];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)deleteMarked:(id)sender
|
- (void)copyMarked
|
||||||
{
|
|
||||||
NSInteger mark_count = [[py getMarkCount] intValue];
|
|
||||||
if (!mark_count)
|
|
||||||
return;
|
|
||||||
if ([Dialogs askYesNo:[NSString stringWithFormat:@"You are about to send %d files to Trash. Continue?",mark_count]] == NSAlertSecondButtonReturn) // NO
|
|
||||||
return;
|
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
|
||||||
[py setRemoveEmptyFolders:[ud objectForKey:@"removeEmptyFolders"]];
|
|
||||||
[py deleteMarked];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)exportToXHTML:(id)sender
|
|
||||||
{
|
|
||||||
NSString *exported = [py exportToXHTMLwithColumns:[self getColumnsOrder]];
|
|
||||||
[[NSWorkspace sharedWorkspace] openFile:exported];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)filter:(id)sender
|
|
||||||
{
|
{
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
[py setEscapeFilterRegexp:b2n(!n2b([ud objectForKey:@"useRegexpFilter"]))];
|
[model setRemoveEmptyFolders:n2b([ud objectForKey:@"removeEmptyFolders"])];
|
||||||
[py applyFilter:[filterField stringValue]];
|
[model setCopyMoveDestType:n2i([ud objectForKey:@"recreatePathType"])];
|
||||||
|
[model copyMarked];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)ignoreSelected:(id)sender
|
- (void)trashMarked
|
||||||
{
|
|
||||||
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];
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
NSString *cmd = [ud stringForKey:@"CustomCommand"];
|
[model setRemoveEmptyFolders:n2b([ud objectForKey:@"removeEmptyFolders"])];
|
||||||
if ((cmd != nil) && ([cmd length] > 0)) {
|
[model deleteMarked];
|
||||||
[py invokeCommand:cmd];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[Dialogs showMessage:@"You have no custom command set up. Set it up in your preferences."];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)markAll:(id)sender
|
- (void)filter
|
||||||
{
|
{
|
||||||
[py markAll];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)markInvert:(id)sender
|
|
||||||
{
|
|
||||||
[py markInvert];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)markNone:(id)sender
|
|
||||||
{
|
|
||||||
[py markNone];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)markSelected:(id)sender
|
|
||||||
{
|
|
||||||
[py toggleSelectedMark];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)moveMarked:(id)sender
|
|
||||||
{
|
|
||||||
NSInteger mark_count = [[py getMarkCount] intValue];
|
|
||||||
if (!mark_count)
|
|
||||||
return;
|
|
||||||
NSOpenPanel *op = [NSOpenPanel openPanel];
|
|
||||||
[op setCanChooseFiles:NO];
|
|
||||||
[op setCanChooseDirectories:YES];
|
|
||||||
[op setCanCreateDirectories:YES];
|
|
||||||
[op setAllowsMultipleSelection:NO];
|
|
||||||
[op setTitle:@"Select a directory to move marked files to"];
|
|
||||||
if ([op runModalForTypes:nil] == NSOKButton)
|
|
||||||
{
|
|
||||||
NSString *directory = [[op filenames] objectAtIndex:0];
|
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
[py setRemoveEmptyFolders:[ud objectForKey:@"removeEmptyFolders"]];
|
[model setEscapeFilterRegexp:!n2b([ud objectForKey:@"useRegexpFilter"])];
|
||||||
[py copyOrMove:b2n(NO) markedTo:directory recreatePath:[ud objectForKey:@"recreatePathType"]];
|
[model applyFilter:[filterField stringValue]];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)openClicked:(id)sender
|
- (void)focusOnFilterField
|
||||||
|
{
|
||||||
|
[[self window] makeFirstResponder:filterField];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)ignoreSelected
|
||||||
|
{
|
||||||
|
[model addSelectedToIgnoreList];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)invokeCustomCommand
|
||||||
|
{
|
||||||
|
[model invokeCustomCommand];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)markAll
|
||||||
|
{
|
||||||
|
[model markAll];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)markInvert
|
||||||
|
{
|
||||||
|
[model markInvert];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)markNone
|
||||||
|
{
|
||||||
|
[model markNone];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)markSelected
|
||||||
|
{
|
||||||
|
[model toggleSelectedMark];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)moveMarked
|
||||||
|
{
|
||||||
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
|
[model setRemoveEmptyFolders:n2b([ud objectForKey:@"removeEmptyFolders"])];
|
||||||
|
[model setCopyMoveDestType:n2i([ud objectForKey:@"recreatePathType"])];
|
||||||
|
[model moveMarked];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)openClicked
|
||||||
{
|
{
|
||||||
if ([matches clickedRow] < 0) {
|
if ([matches clickedRow] < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
[matches selectRowIndexes:[NSIndexSet indexSetWithIndex:[matches clickedRow]] byExtendingSelection:NO];
|
[matches selectRowIndexes:[NSIndexSet indexSetWithIndex:[matches clickedRow]] byExtendingSelection:NO];
|
||||||
[py openSelected];
|
[model openSelected];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)openSelected:(id)sender
|
- (void)openSelected
|
||||||
{
|
{
|
||||||
[py openSelected];
|
[model openSelected];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)removeMarked:(id)sender
|
- (void)removeMarked
|
||||||
{
|
{
|
||||||
int mark_count = [[py getMarkCount] intValue];
|
[model removeMarked];
|
||||||
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
|
- (void)removeSelected
|
||||||
{
|
{
|
||||||
[outline removeSelected];
|
[model removeSelected];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)renameSelected:(id)sender
|
- (void)renameSelected
|
||||||
{
|
{
|
||||||
NSInteger col = [matches columnWithIdentifier:@"0"];
|
NSInteger col = [matches columnWithIdentifier:@"name"];
|
||||||
NSInteger row = [matches selectedRow];
|
NSInteger row = [matches selectedRow];
|
||||||
[matches editColumn:col row:row withEvent:[NSApp currentEvent] select:YES];
|
[matches editColumn:col row:row withEvent:[NSApp currentEvent] select:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)resetColumnsToDefault:(id)sender
|
- (void)reprioritizeResults
|
||||||
{
|
{
|
||||||
// Virtual
|
PrioritizeDialog *dlg = [[PrioritizeDialog alloc] initWithApp:model];
|
||||||
|
NSInteger result = [NSApp runModalForWindow:[dlg window]];
|
||||||
|
if (result == NSRunStoppedResponse) {
|
||||||
|
[[dlg model] performReprioritization];
|
||||||
|
}
|
||||||
|
[dlg release];
|
||||||
|
[[self window] makeKeyAndOrderFront:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)revealSelected:(id)sender
|
- (void)resetColumnsToDefault
|
||||||
{
|
{
|
||||||
[py revealSelected];
|
[[[table columns] model] resetToDefaults];
|
||||||
|
[self fillColumnsMenu];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)showPreferencesPanel:(id)sender
|
- (void)revealSelected
|
||||||
{
|
{
|
||||||
[preferencesPanel showWindow:sender];
|
[model revealSelected];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)startDuplicateScan:(id)sender
|
- (void)saveResults
|
||||||
{
|
{
|
||||||
// Virtual
|
NSSavePanel *sp = [NSSavePanel savePanel];
|
||||||
|
[sp setCanCreateDirectories:YES];
|
||||||
|
[sp setAllowedFileTypes:[NSArray arrayWithObject:@"dupeguru"]];
|
||||||
|
[sp setTitle:NSLocalizedString(@"Select a file to save your results to", @"")];
|
||||||
|
if ([sp runModal] == NSOKButton) {
|
||||||
|
[model saveResultsAs:[sp filename]];
|
||||||
|
[[app recentResults] addFile:[sp filename]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)switchSelected:(id)sender
|
- (void)startDuplicateScan
|
||||||
{
|
{
|
||||||
[py makeSelectedReference];
|
if ([model resultsAreModified]) {
|
||||||
|
if ([Dialogs askYesNo:NSLocalizedString(@"You have unsaved results, do you really want to continue?", @"")] == NSAlertSecondButtonReturn) // NO
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[self setScanOptions];
|
||||||
|
[model doScan];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleColumn:(id)sender
|
- (void)switchSelected
|
||||||
|
{
|
||||||
|
[model makeSelectedReference];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)toggleColumn:(id)sender
|
||||||
{
|
{
|
||||||
NSMenuItem *mi = sender;
|
NSMenuItem *mi = sender;
|
||||||
NSString *colId = [NSString stringWithFormat:@"%d",[mi tag]];
|
BOOL checked = [[[table columns] model] toggleMenuItem:[mi tag]];
|
||||||
NSTableColumn *col = [matches tableColumnWithIdentifier:colId];
|
[mi setState:checked ? NSOnState : NSOffState];
|
||||||
if (col == nil)
|
|
||||||
{
|
|
||||||
//Add Column
|
|
||||||
col = [_resultColumns objectAtIndex:[mi tag]];
|
|
||||||
[matches addTableColumn:col];
|
|
||||||
[mi setState:NSOnState];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Remove column
|
|
||||||
[matches removeTableColumn:col];
|
|
||||||
[mi setState:NSOffState];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleDelta:(id)sender
|
- (void)toggleDetailsPanel
|
||||||
{
|
{
|
||||||
if ([deltaSwitch selectedSegment] == 1)
|
[[app detailsPanel] toggleVisibility];
|
||||||
[deltaSwitch setSelectedSegment:0];
|
[self updateOptionSegments];
|
||||||
else
|
|
||||||
[deltaSwitch setSelectedSegment:1];
|
|
||||||
[self changeDelta:sender];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleDetailsPanel:(id)sender
|
- (void)toggleDelta
|
||||||
{
|
{
|
||||||
[[(AppDelegateBase *)app detailsPanel] toggleVisibility];
|
[table setDeltaValuesMode:![table deltaValuesMode]];
|
||||||
|
[self updateOptionSegments];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)togglePowerMarker:(id)sender
|
- (void)togglePowerMarker
|
||||||
{
|
{
|
||||||
if ([pmSwitch selectedSegment] == 1)
|
[table setPowerMarkerMode:![table powerMarkerMode]];
|
||||||
[pmSwitch setSelectedSegment:0];
|
[self updateOptionSegments];
|
||||||
else
|
|
||||||
[pmSwitch setSelectedSegment:1];
|
|
||||||
[self changePowerMarker:sender];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Notifications */
|
- (void)toggleQuicklookPanel
|
||||||
- (void)windowWillClose:(NSNotification *)aNotification
|
|
||||||
{
|
{
|
||||||
[NSApp hide:NSApp];
|
if ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]) {
|
||||||
}
|
[[QLPreviewPanel sharedPreviewPanel] orderOut:nil];
|
||||||
|
|
||||||
- (void)jobCompleted:(NSNotification *)aNotification
|
|
||||||
{
|
|
||||||
id lastAction = [[ProgressController mainProgressController] jobId];
|
|
||||||
if ([lastAction isEqualTo:jobCopy]) {
|
|
||||||
if ([py scanWasProblematic]) {
|
|
||||||
[problemDialog showWindow:self];
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
[Dialogs showMessage:@"All marked files were copied sucessfully."];
|
[[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil];
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ([lastAction isEqualTo:jobMove]) {
|
|
||||||
if ([py scanWasProblematic]) {
|
|
||||||
[problemDialog showWindow:self];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[Dialogs showMessage:@"All marked files were moved sucessfully."];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ([lastAction isEqualTo:jobDelete]) {
|
|
||||||
if ([py scanWasProblematic]) {
|
|
||||||
[problemDialog showWindow:self];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[Dialogs showMessage:@"All marked files were sucessfully sent to Trash."];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ([lastAction isEqualTo:jobScan]) {
|
|
||||||
NSInteger groupCount = [outline intProperty:@"children_count" valueAtPath:nil];
|
|
||||||
if (groupCount == 0)
|
|
||||||
[Dialogs showMessage:@"No duplicates found."];
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Re-activate toolbar items right after the progress bar stops showing instead of waiting until
|
/* Quicklook */
|
||||||
// a mouse-over is performed
|
- (BOOL)acceptsPreviewPanelControl:(QLPreviewPanel *)panel;
|
||||||
[[[self window] toolbar] validateVisibleItems];
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)beginPreviewPanelControl:(QLPreviewPanel *)panel
|
||||||
|
{
|
||||||
|
// This document is now responsible of the preview panel
|
||||||
|
// It is allowed to set the delegate, data source and refresh panel.
|
||||||
|
previewPanel = [panel retain];
|
||||||
|
panel.delegate = table;
|
||||||
|
panel.dataSource = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)endPreviewPanelControl:(QLPreviewPanel *)panel
|
||||||
|
{
|
||||||
|
// This document loses its responsisibility on the preview panel
|
||||||
|
// Until the next call to -beginPreviewPanelControl: it must not
|
||||||
|
// change the panel's delegate, data source or refresh it.
|
||||||
|
[previewPanel release];
|
||||||
|
previewPanel = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)jobInProgress:(NSNotification *)aNotification
|
- (void)jobInProgress:(NSNotification *)aNotification
|
||||||
{
|
{
|
||||||
[Dialogs showMessage:@"A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again."];
|
[Dialogs showMessage:NSLocalizedString(@"A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again.", @"")];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)jobStarted:(NSNotification *)aNotification
|
- (void)jobStarted:(NSNotification *)aNotification
|
||||||
{
|
{
|
||||||
|
[[self window] makeKeyAndOrderFront:nil];
|
||||||
NSDictionary *ui = [aNotification userInfo];
|
NSDictionary *ui = [aNotification userInfo];
|
||||||
NSString *desc = [ui valueForKey:@"desc"];
|
NSString *desc = [ui valueForKey:@"desc"];
|
||||||
[[ProgressController mainProgressController] setJobDesc:desc];
|
[[ProgressController mainProgressController] setJobDesc:desc];
|
||||||
@@ -415,12 +356,6 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
[[ProgressController mainProgressController] showSheetForParent:[self window]];
|
[[ProgressController mainProgressController] showSheetForParent:[self window]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)registrationRequired:(NSNotification *)aNotification
|
|
||||||
{
|
|
||||||
NSString *msg = @"This is a demo version, which only allows you 10 delete/copy/move actions per session. You cannot continue.";
|
|
||||||
[Dialogs showMessage:msg];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
|
- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
|
||||||
{
|
{
|
||||||
return ![[ProgressController mainProgressController] isShown];
|
return ![[ProgressController mainProgressController] isShown];
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "HSGUIController.h"
|
#import "HSGUIController.h"
|
||||||
#import "PyStatsLabel.h"
|
#import "PyStatsLabel.h"
|
||||||
|
|
||||||
@interface StatsLabel : HSGUIController
|
@interface StatsLabel : HSGUIController {}
|
||||||
{
|
- (id)initWithPyRef:(PyObject *)aPyRef view:(NSTextField *)aLabelView;
|
||||||
NSTextField *labelView;
|
- (PyStatsLabel *)model;
|
||||||
}
|
- (NSTextField *)labelView;
|
||||||
- (id)initWithPyParent:(id)aPyParent labelView:(NSTextField *)aLabelView;
|
|
||||||
- (PyStatsLabel *)py;
|
|
||||||
@end
|
@end
|
||||||
@@ -1,38 +1,34 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "StatsLabel.h"
|
#import "StatsLabel.h"
|
||||||
#import "Utils.h"
|
#import "Utils.h"
|
||||||
|
|
||||||
@implementation StatsLabel
|
@implementation StatsLabel
|
||||||
- (id)initWithPyParent:(id)aPyParent labelView:(NSTextField *)aLabelView
|
- (id)initWithPyRef:(PyObject *)aPyRef view:(NSTextField *)aLabelView
|
||||||
{
|
{
|
||||||
self = [super initWithPyClassName:@"PyStatsLabel" pyParent:aPyParent];
|
return [super initWithPyRef:aPyRef wrapperClass:[PyStatsLabel class]
|
||||||
labelView = [aLabelView retain];
|
callbackClassName:@"StatsLabelView" view:aLabelView];
|
||||||
[self connect];
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (PyStatsLabel *)model
|
||||||
{
|
{
|
||||||
[self disconnect];
|
return (PyStatsLabel *)model;
|
||||||
[labelView release];
|
|
||||||
[super dealloc];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (PyStatsLabel *)py
|
- (NSTextField *)labelView
|
||||||
{
|
{
|
||||||
return (PyStatsLabel *)py;
|
return (NSTextField *)view;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Python --> Cocoa */
|
/* Python --> Cocoa */
|
||||||
- (void)refresh
|
- (void)refresh
|
||||||
{
|
{
|
||||||
[labelView setStringValue:[[self py] display]];
|
[[self labelView] setStringValue:[[self model] display]];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|||||||
150
cocoa/base/en.lproj/Localizable.strings
Normal file
150
cocoa/base/en.lproj/Localizable.strings
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
|
||||||
|
"A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again." = "A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again.";
|
||||||
|
"About dupeGuru" = "About dupeGuru";
|
||||||
|
"Action" = "Action";
|
||||||
|
"Actions" = "Actions";
|
||||||
|
"Add Aperture Library" = "Add Aperture Library";
|
||||||
|
"Add criteria to the right box and click OK to send the dupes that correspond the best to these criteria to their respective group's reference position. Read the help file for more information." = "Add criteria to the right box and click OK to send the dupes that correspond the best to these criteria to their respective group's reference position. Read the help file for more information.";
|
||||||
|
"Add iPhoto Library" = "Add iPhoto Library";
|
||||||
|
"Add iTunes Library" = "Add iTunes Library";
|
||||||
|
"Add New Folder..." = "Add New Folder...";
|
||||||
|
"Add Selected to Ignore List" = "Add Selected to Ignore List";
|
||||||
|
"Advanced" = "Advanced";
|
||||||
|
"After having deleted a duplicate, place a link targeting the reference file to replace the deleted file." = "After having deleted a duplicate, place a link targeting the reference file to replace the deleted file.";
|
||||||
|
"Album" = "Album";
|
||||||
|
"Artist" = "Artist";
|
||||||
|
"Attribute" = "Attribute";
|
||||||
|
"Audio Content" = "Audio Content";
|
||||||
|
"Automatically check for updates" = "Automatically check for updates";
|
||||||
|
"Basic" = "Basic";
|
||||||
|
"Bring All to Front" = "Bring All to Front";
|
||||||
|
"Can mix file kind" = "Can mix file kind";
|
||||||
|
"Cancel" = "Cancel";
|
||||||
|
"Check for update..." = "Check for update...";
|
||||||
|
"Clear" = "Clear";
|
||||||
|
"Clear Picture Cache" = "Clear Picture Cache";
|
||||||
|
"Close" = "Close";
|
||||||
|
"Close Window" = "Close Window";
|
||||||
|
"Columns" = "Columns";
|
||||||
|
"Content" = "Content";
|
||||||
|
"Contents" = "Contents";
|
||||||
|
"Copy" = "Copy";
|
||||||
|
"Copy and Move:" = "Copy and Move:";
|
||||||
|
"Copy Marked to..." = "Copy Marked to...";
|
||||||
|
"Custom command (arguments: %d for dupe, %r for ref):" = "Custom command (arguments: %d for dupe, %r for ref):";
|
||||||
|
"Cut" = "Cut";
|
||||||
|
"Debug mode (restart required)" = "Debug mode (restart required)";
|
||||||
|
"Deletion Options" = "Deletion Options";
|
||||||
|
"Delta" = "Delta";
|
||||||
|
"Details" = "Details";
|
||||||
|
"Details of Selected File" = "Details of Selected File";
|
||||||
|
"Details Panel" = "Details Panel";
|
||||||
|
"Directly delete files" = "Directly delete files";
|
||||||
|
"Directories" = "Directories";
|
||||||
|
"Do you really want to remove all your cached picture analysis?" = "Do you really want to remove all your cached picture analysis?";
|
||||||
|
"dupeGuru" = "dupeGuru";
|
||||||
|
"dupeGuru Help" = "dupeGuru Help";
|
||||||
|
"dupeGuru ME Preferences" = "dupeGuru ME Preferences";
|
||||||
|
"dupeGuru PE Preferences" = "dupeGuru PE Preferences";
|
||||||
|
"dupeGuru Preferences" = "dupeGuru Preferences";
|
||||||
|
"dupeGuru Results" = "dupeGuru Results";
|
||||||
|
"dupeGuru Website" = "dupeGuru Website";
|
||||||
|
"Dupes Only" = "Dupes Only";
|
||||||
|
"Edit" = "Edit";
|
||||||
|
"Excluded" = "Excluded";
|
||||||
|
"EXIF Timestamp" = "EXIF Timestamp";
|
||||||
|
"Export Results to CSV" = "Export Results to CSV";
|
||||||
|
"Export Results to XHTML" = "Export Results to XHTML";
|
||||||
|
"Fewer results" = "Fewer results";
|
||||||
|
"File" = "File";
|
||||||
|
"Filename" = "Filename";
|
||||||
|
"Filename - Fields" = "Filename - Fields";
|
||||||
|
"Filename - Fields (No Order)" = "Filename - Fields (No Order)";
|
||||||
|
"Filter" = "Filter";
|
||||||
|
"Filter hardness:" = "Filter hardness:";
|
||||||
|
"Filter Results..." = "Filter Results...";
|
||||||
|
"Folder Selection Window" = "Folder Selection Window";
|
||||||
|
"Folders" = "Folders";
|
||||||
|
"Font Size:" = "Font Size:";
|
||||||
|
"Genre" = "Genre";
|
||||||
|
"Help" = "Help";
|
||||||
|
"Hide dupeGuru" = "Hide dupeGuru";
|
||||||
|
"Hide Others" = "Hide Others";
|
||||||
|
"Ignore duplicates hardlinking to the same file" = "Ignore duplicates hardlinking to the same file";
|
||||||
|
"Ignore files smaller than:" = "Ignore files smaller than:";
|
||||||
|
"Ignore List" = "Ignore List";
|
||||||
|
"Instead of sending files to trash, delete them directly. This option is usually used as a workaround when the normal deletion method doesn't work." = "Instead of sending files to trash, delete them directly. This option is usually used as a workaround when the normal deletion method doesn't work.";
|
||||||
|
"Invert Marking" = "Invert Marking";
|
||||||
|
"Invoke Custom Command" = "Invoke Custom Command";
|
||||||
|
"KB" = "KB";
|
||||||
|
"Link deleted files" = "Link deleted files";
|
||||||
|
"Load from file..." = "Load from file...";
|
||||||
|
"Load Recent Results" = "Load Recent Results";
|
||||||
|
"Load Results" = "Load Results";
|
||||||
|
"Load Results..." = "Load Results...";
|
||||||
|
"Make Selected Reference" = "Make Selected Reference";
|
||||||
|
"Mark All" = "Mark All";
|
||||||
|
"Mark None" = "Mark None";
|
||||||
|
"Mark Selected" = "Mark Selected";
|
||||||
|
"Match pictures of different dimensions" = "Match pictures of different dimensions";
|
||||||
|
"Match similar words" = "Match similar words";
|
||||||
|
"Minimize" = "Minimize";
|
||||||
|
"Mode" = "Mode";
|
||||||
|
"More results" = "More results";
|
||||||
|
"Move Marked to..." = "Move Marked to...";
|
||||||
|
"Name" = "Name";
|
||||||
|
"Normal" = "Normal";
|
||||||
|
"Ok" = "Ok";
|
||||||
|
"Open Selected with Default Application" = "Open Selected with Default Application";
|
||||||
|
"Options" = "Options";
|
||||||
|
"Paste" = "Paste";
|
||||||
|
"Preferences..." = "Preferences...";
|
||||||
|
"Problems!" = "Problems!";
|
||||||
|
"Proceed" = "Proceed";
|
||||||
|
"Quick Look" = "Quick Look";
|
||||||
|
"Quit dupeGuru" = "Quit dupeGuru";
|
||||||
|
"Re-Prioritize duplicates" = "Re-Prioritize duplicates";
|
||||||
|
"Re-Prioritize Results" = "Re-Prioritize Results";
|
||||||
|
"Recreate absolute path" = "Recreate absolute path";
|
||||||
|
"Recreate relative path" = "Recreate relative path";
|
||||||
|
"Reference" = "Reference";
|
||||||
|
"Remove Dead Tracks in iTunes" = "Remove Dead Tracks in iTunes";
|
||||||
|
"Remove empty folders on delete or move" = "Remove empty folders on delete or move";
|
||||||
|
"Remove Marked from Results" = "Remove Marked from Results";
|
||||||
|
"Remove Selected" = "Remove Selected";
|
||||||
|
"Remove Selected from Results" = "Remove Selected from Results";
|
||||||
|
"Rename Selected" = "Rename Selected";
|
||||||
|
"Reset to Default" = "Reset to Default";
|
||||||
|
"Reset To Defaults" = "Reset To Defaults";
|
||||||
|
"Results Window" = "Results Window";
|
||||||
|
"Reveal" = "Reveal";
|
||||||
|
"Reveal Selected in Finder" = "Reveal Selected in Finder";
|
||||||
|
"Right in destination" = "Right in destination";
|
||||||
|
"Save Results..." = "Save Results...";
|
||||||
|
"Scan" = "Scan";
|
||||||
|
"Scan Type:" = "Scan Type:";
|
||||||
|
"Select a file to save your results to" = "Select a file to save your results to";
|
||||||
|
"Select a folder to add to the scanning list" = "Select a folder to add to the scanning list";
|
||||||
|
"Select a results file to load" = "Select a results file to load";
|
||||||
|
"Select folders to scan and press \"Scan\"." = "Select folders to scan and press \"Scan\".";
|
||||||
|
"Selected" = "Selected";
|
||||||
|
"Send Marked to Trash..." = "Send Marked to Trash...";
|
||||||
|
"Services" = "Services";
|
||||||
|
"Show All" = "Show All";
|
||||||
|
"Show Delta Values" = "Show Delta Values";
|
||||||
|
"Show Dupes Only" = "Show Dupes Only";
|
||||||
|
"Start Duplicate Scan" = "Start Duplicate Scan";
|
||||||
|
"State" = "State";
|
||||||
|
"Tags" = "Tags";
|
||||||
|
"Tags to scan:" = "Tags to scan:";
|
||||||
|
"The name '%@' already exists." = "The name '%@' already exists.";
|
||||||
|
"There were problems processing some (or all) of the files. The cause of these problems are described in the table below. Those files were not removed from your results." = "There were problems processing some (or all) of the files. The cause of these problems are described in the table below. Those files were not removed from your results.";
|
||||||
|
"Title" = "Title";
|
||||||
|
"Track" = "Track";
|
||||||
|
"Use regular expressions when filtering" = "Use regular expressions when filtering";
|
||||||
|
"Window" = "Window";
|
||||||
|
"Word weighting" = "Word weighting";
|
||||||
|
"Year" = "Year";
|
||||||
|
"You have unsaved results, do you really want to continue?" = "You have unsaved results, do you really want to continue?";
|
||||||
|
"You have unsaved results, do you really want to quit?" = "You have unsaved results, do you really want to quit?";
|
||||||
|
"Zoom" = "Zoom";
|
||||||
49
cocoa/base/main.m
Normal file
49
cocoa/base/main.m
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <Python.h>
|
||||||
|
#import <wchar.h>
|
||||||
|
#import <locale.h>
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
#import "MainMenu_UI.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
/* We have to set the locate to UTF8 for mbstowcs() to correctly convert non-ascii chars in paths */
|
||||||
|
setlocale(LC_ALL, "en_US.UTF-8");
|
||||||
|
NSString *respath = [[NSBundle mainBundle] resourcePath];
|
||||||
|
NSString *mainpy = [respath stringByAppendingPathComponent:@"dg_cocoa.py"];
|
||||||
|
wchar_t wPythonPath[PATH_MAX+1];
|
||||||
|
NSString *pypath = [respath stringByAppendingPathComponent:@"py"];
|
||||||
|
mbstowcs(wPythonPath, [pypath fileSystemRepresentation], PATH_MAX+1);
|
||||||
|
Py_SetPath(wPythonPath);
|
||||||
|
Py_SetPythonHome(wPythonPath);
|
||||||
|
Py_Initialize();
|
||||||
|
PyEval_InitThreads();
|
||||||
|
PyGILState_STATE gilState = PyGILState_Ensure();
|
||||||
|
FILE* fp = fopen([mainpy UTF8String], "r");
|
||||||
|
PyRun_SimpleFile(fp, [mainpy UTF8String]);
|
||||||
|
fclose(fp);
|
||||||
|
PyGILState_Release(gilState);
|
||||||
|
if (gilState == PyGILState_LOCKED) {
|
||||||
|
PyThreadState_Swap(NULL);
|
||||||
|
PyEval_ReleaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
[NSApplication sharedApplication];
|
||||||
|
AppDelegate *appDelegate = [[AppDelegate alloc] init];
|
||||||
|
[NSApp setDelegate:appDelegate];
|
||||||
|
[NSApp setMainMenu:createMainMenu_UI(appDelegate)];
|
||||||
|
[appDelegate finalizeInit];
|
||||||
|
[pool release];
|
||||||
|
[NSApp run];
|
||||||
|
Py_Finalize();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
49
cocoa/base/ui/deletion_options.py
Normal file
49
cocoa/base/ui/deletion_options.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
ownerclass = 'DeletionOptions'
|
||||||
|
ownerimport = 'DeletionOptions.h'
|
||||||
|
|
||||||
|
result = Window(450, 240, "Deletion Options")
|
||||||
|
messageLabel = Label(result, "")
|
||||||
|
linkCheckbox = Checkbox(result, "Link deleted files")
|
||||||
|
linkLabel = Label(result, "After having deleted a duplicate, place a link targeting the "
|
||||||
|
"reference file to replace the deleted file.")
|
||||||
|
linkTypeChoice = RadioButtons(result, ["Symlink", "Hardlink"], columns=2)
|
||||||
|
directCheckbox = Checkbox(result, "Directly delete files")
|
||||||
|
directLabel = Label(result, "Instead of sending files to trash, delete them directly. This option "
|
||||||
|
"is usually used as a workaround when the normal deletion method doesn't work.")
|
||||||
|
proceedButton = Button(result, "Proceed")
|
||||||
|
cancelButton = Button(result, "Cancel")
|
||||||
|
|
||||||
|
owner.linkButton = linkCheckbox
|
||||||
|
owner.linkTypeRadio = linkTypeChoice
|
||||||
|
owner.directButton = directCheckbox
|
||||||
|
owner.messageTextField = messageLabel
|
||||||
|
|
||||||
|
result.canMinimize = False
|
||||||
|
result.canResize = False
|
||||||
|
linkLabel.controlSize = ControlSize.Small
|
||||||
|
directLabel.controlSize = ControlSize.Small
|
||||||
|
linkTypeChoice.controlSize = ControlSize.Small
|
||||||
|
proceedButton.keyEquivalent = '\\r'
|
||||||
|
cancelButton.keyEquivalent = '\\e'
|
||||||
|
linkCheckbox.action = directCheckbox.action = linkTypeChoice.action = Action(owner, 'updateOptions')
|
||||||
|
proceedButton.action = Action(owner, 'proceed')
|
||||||
|
cancelButton.action = Action(owner, 'cancel')
|
||||||
|
|
||||||
|
linkLabel.height *= 2 # 2 lines
|
||||||
|
directLabel.height *= 3 # 3 lines
|
||||||
|
proceedButton.width = 92
|
||||||
|
cancelButton.width = 92
|
||||||
|
|
||||||
|
mainLayout = VLayout([messageLabel, linkCheckbox, linkLabel, linkTypeChoice, directCheckbox,
|
||||||
|
directLabel])
|
||||||
|
mainLayout.packToCorner(Pack.UpperLeft)
|
||||||
|
mainLayout.fill(Pack.Right)
|
||||||
|
buttonLayout = HLayout([cancelButton, proceedButton])
|
||||||
|
buttonLayout.packToCorner(Pack.LowerRight)
|
||||||
|
|
||||||
|
# indent the labels under checkboxes a little bit to the right
|
||||||
|
for indentedView in (linkLabel, directLabel, linkTypeChoice):
|
||||||
|
indentedView.x += 20
|
||||||
|
indentedView.width -= 20
|
||||||
|
# We actually don't want the link choice radio buttons to take all the width, it looks weird.
|
||||||
|
linkTypeChoice.width = 170
|
||||||
32
cocoa/base/ui/details_panel.py
Normal file
32
cocoa/base/ui/details_panel.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
ownerclass = 'DetailsPanel'
|
||||||
|
ownerimport = 'DetailsPanel.h'
|
||||||
|
|
||||||
|
result = Panel(451, 146, "Details of Selected File")
|
||||||
|
table = TableView(result)
|
||||||
|
|
||||||
|
owner.detailsTable = table
|
||||||
|
|
||||||
|
result.style = PanelStyle.Utility
|
||||||
|
result.xProportion = 0.2
|
||||||
|
result.yProportion = 0.4
|
||||||
|
result.canMinimize = False
|
||||||
|
result.autosaveName = 'DetailsPanel'
|
||||||
|
result.minSize = Size(result.width, result.height)
|
||||||
|
|
||||||
|
table.dataSource = owner
|
||||||
|
table.allowsColumnReordering = False
|
||||||
|
table.allowsColumnSelection = False
|
||||||
|
table.allowsMultipleSelection = False
|
||||||
|
table.font = Font(FontFamily.System, FontSize.SmallSystem)
|
||||||
|
table.rowHeight = 14
|
||||||
|
table.editable = False
|
||||||
|
col = table.addColumn('0', "Attribute", 70)
|
||||||
|
col.autoResizable = True
|
||||||
|
col = table.addColumn('1', "Selected", 198)
|
||||||
|
col.autoResizable = True
|
||||||
|
col = table.addColumn('2', "Reference", 172)
|
||||||
|
col.autoResizable = True
|
||||||
|
|
||||||
|
table.packToCorner(Pack.UpperLeft, margin=0)
|
||||||
|
table.fill(Pack.LowerRight, margin=0)
|
||||||
|
table.setAnchor(Pack.UpperLeft, growX=True, growY=True)
|
||||||
64
cocoa/base/ui/directory_panel.py
Normal file
64
cocoa/base/ui/directory_panel.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
ownerclass = 'DirectoryPanel'
|
||||||
|
ownerimport = 'DirectoryPanel.h'
|
||||||
|
|
||||||
|
result = Window(425, 300, "dupeGuru")
|
||||||
|
promptLabel = Label(result, "Select folders to scan and press \"Scan\".")
|
||||||
|
directoryOutline = OutlineView(result)
|
||||||
|
directoryOutline.OBJC_CLASS = 'HSOutlineView'
|
||||||
|
addButton = Button(result, "")
|
||||||
|
removeButton = Button(result, "")
|
||||||
|
loadResultsButton = Button(result, "Load Results")
|
||||||
|
scanButton = Button(result, "Scan")
|
||||||
|
addPopup = Popup(None)
|
||||||
|
loadRecentPopup = Popup(None)
|
||||||
|
|
||||||
|
owner.outlineView = directoryOutline
|
||||||
|
owner.removeButton = removeButton
|
||||||
|
owner.loadResultsButton = loadResultsButton
|
||||||
|
owner.addButtonPopUp = addPopup
|
||||||
|
owner.loadRecentButtonPopUp = loadRecentPopup
|
||||||
|
|
||||||
|
result.autosaveName = 'DirectoryPanel'
|
||||||
|
result.canMinimize = False
|
||||||
|
result.minSize = Size(370, 270)
|
||||||
|
addButton.bezelStyle = removeButton.bezelStyle = const.NSTexturedRoundedBezelStyle
|
||||||
|
addButton.image = 'NSAddTemplate'
|
||||||
|
removeButton.image = 'NSRemoveTemplate'
|
||||||
|
for button in (addButton, removeButton):
|
||||||
|
button.style = const.NSTexturedRoundedBezelStyle
|
||||||
|
button.imagePosition = const.NSImageOnly
|
||||||
|
scanButton.keyEquivalent = '\\r'
|
||||||
|
addButton.action = Action(owner, 'popupAddDirectoryMenu:')
|
||||||
|
removeButton.action = Action(owner, 'removeSelectedDirectory')
|
||||||
|
loadResultsButton.action = Action(owner, 'popupLoadRecentMenu:')
|
||||||
|
scanButton.action = Action(None, 'startScanning')
|
||||||
|
|
||||||
|
directoryOutline.font = Font(FontFamily.System, FontSize.SmallSystem)
|
||||||
|
col = directoryOutline.addColumn('name', "Name", 100)
|
||||||
|
col.editable = False
|
||||||
|
col.autoResizable = True
|
||||||
|
col = directoryOutline.addColumn('state', "State", 85)
|
||||||
|
col.editable = True
|
||||||
|
col.autoResizable = False
|
||||||
|
col.dataCell = Popup(None, ["Normal", "Reference", "Excluded"])
|
||||||
|
col.dataCell.controlSize = const.NSSmallControlSize
|
||||||
|
directoryOutline.allowsColumnReordering = False
|
||||||
|
directoryOutline.allowsColumnSelection = False
|
||||||
|
directoryOutline.allowsMultipleSelection = True
|
||||||
|
|
||||||
|
for button in (addButton, removeButton):
|
||||||
|
button.width = 28
|
||||||
|
for button in (loadResultsButton, scanButton):
|
||||||
|
button.width = 118
|
||||||
|
|
||||||
|
buttonLayout = HLayout([addButton, removeButton, None, loadResultsButton, scanButton])
|
||||||
|
promptLabel.packToCorner(Pack.UpperLeft)
|
||||||
|
promptLabel.fill(Pack.Right)
|
||||||
|
directoryOutline.packRelativeTo(promptLabel, Pack.Below)
|
||||||
|
buttonLayout.packRelativeTo(directoryOutline, Pack.Below, margin=8)
|
||||||
|
directoryOutline.fill(Pack.LowerRight)
|
||||||
|
buttonLayout.fill(Pack.Right)
|
||||||
|
|
||||||
|
promptLabel.setAnchor(Pack.UpperLeft, growX=True)
|
||||||
|
directoryOutline.setAnchor(Pack.UpperLeft, growX=True, growY=True)
|
||||||
|
buttonLayout.setAnchor(Pack.Below)
|
||||||
30
cocoa/base/ui/ignore_list_dialog.py
Normal file
30
cocoa/base/ui/ignore_list_dialog.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
ownerclass = 'IgnoreListDialog'
|
||||||
|
ownerimport = 'IgnoreListDialog.h'
|
||||||
|
|
||||||
|
result = Window(550, 350, "Ignore List")
|
||||||
|
table = TableView(result)
|
||||||
|
removeSelectedButton = Button(result, "Remove Selected")
|
||||||
|
clearButton = Button(result, "Clear")
|
||||||
|
closeButton = Button(result, "Close")
|
||||||
|
|
||||||
|
owner.ignoreListTableView = table
|
||||||
|
|
||||||
|
result.canMinimize = False
|
||||||
|
removeSelectedButton.action = Action(owner.model, 'removeSelected')
|
||||||
|
clearButton.action = Action(owner.model, 'clear')
|
||||||
|
closeButton.action = Action(result, 'performClose:')
|
||||||
|
closeButton.keyEquivalent = '\\r'
|
||||||
|
table.allowsColumnReordering = False
|
||||||
|
table.allowsColumnSelection = False
|
||||||
|
table.allowsMultipleSelection = True
|
||||||
|
|
||||||
|
removeSelectedButton.width = 142
|
||||||
|
clearButton.width = 142
|
||||||
|
closeButton.width = 84
|
||||||
|
buttonLayout = HLayout([removeSelectedButton, clearButton, None, closeButton])
|
||||||
|
buttonLayout.packToCorner(Pack.LowerLeft)
|
||||||
|
buttonLayout.fill(Pack.Right)
|
||||||
|
buttonLayout.setAnchor(Pack.Below)
|
||||||
|
table.packRelativeTo(buttonLayout, Pack.Above)
|
||||||
|
table.fill(Pack.UpperRight)
|
||||||
|
table.setAnchor(Pack.UpperLeft, growX=True, growY=True)
|
||||||
82
cocoa/base/ui/main_menu.py
Normal file
82
cocoa/base/ui/main_menu.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
ownerclass = 'AppDelegateBase'
|
||||||
|
ownerimport = 'AppDelegateBase.h'
|
||||||
|
edition = args.get('edition', 'se')
|
||||||
|
|
||||||
|
result = Menu("")
|
||||||
|
appMenu = result.addMenu("dupeGuru")
|
||||||
|
fileMenu = result.addMenu("File")
|
||||||
|
editMenu = result.addMenu("Edit")
|
||||||
|
actionMenu = result.addMenu("Actions")
|
||||||
|
owner.columnsMenu = result.addMenu("Columns")
|
||||||
|
modeMenu = result.addMenu("Mode")
|
||||||
|
windowMenu = result.addMenu("Window")
|
||||||
|
helpMenu = result.addMenu("Help")
|
||||||
|
|
||||||
|
appMenu.addItem("About dupeGuru", Action(owner, 'showAboutBox'))
|
||||||
|
appMenu.addItem("Check for update...", Action(owner.updater, 'checkForUpdates:'))
|
||||||
|
appMenu.addSeparator()
|
||||||
|
appMenu.addItem("Preferences...", Action(owner, 'showPreferencesPanel'), 'cmd+,')
|
||||||
|
appMenu.addSeparator()
|
||||||
|
NSApp.servicesMenu = appMenu.addMenu("Services")
|
||||||
|
appMenu.addSeparator()
|
||||||
|
appMenu.addItem("Hide dupeGuru", Action(NSApp, 'hide:'), 'cmd+h')
|
||||||
|
appMenu.addItem("Hide Others", Action(NSApp, 'hideOtherApplications:'), 'cmd+alt+h')
|
||||||
|
appMenu.addItem("Show All", Action(NSApp, 'unhideAllApplications:'))
|
||||||
|
appMenu.addSeparator()
|
||||||
|
appMenu.addItem("Quit dupeGuru", Action(NSApp, 'terminate:'), 'cmd+q')
|
||||||
|
|
||||||
|
fileMenu.addItem("Load Results...", Action(None, 'loadResults'), 'cmd+o')
|
||||||
|
owner.recentResultsMenu = fileMenu.addMenu("Load Recent Results")
|
||||||
|
fileMenu.addItem("Save Results...", Action(None, 'saveResults'), 'cmd+s')
|
||||||
|
fileMenu.addItem("Export Results to XHTML", Action(owner.model, 'exportToXHTML'), 'cmd+shift+e')
|
||||||
|
fileMenu.addItem("Export Results to CSV", Action(owner.model, 'exportToCSV'))
|
||||||
|
if edition == 'pe':
|
||||||
|
fileMenu.addItem("Clear Picture Cache", Action(owner, 'clearPictureCache'), 'cmd+shift+p')
|
||||||
|
elif edition == 'me':
|
||||||
|
fileMenu.addItem("Remove Dead Tracks in iTunes", Action(owner, 'removeDeadTracks'))
|
||||||
|
|
||||||
|
editMenu.addItem("Mark All", Action(None, 'markAll'), 'cmd+a')
|
||||||
|
editMenu.addItem("Mark None", Action(None, 'markNone'), 'cmd+shift+a')
|
||||||
|
editMenu.addItem("Invert Marking", Action(None, 'markInvert'), 'cmd+alt+a')
|
||||||
|
editMenu.addItem("Mark Selected", Action(None, 'markSelected'), 'ctrl+cmd+a')
|
||||||
|
editMenu.addSeparator()
|
||||||
|
editMenu.addItem("Cut", Action(None, 'cut:'), 'cmd+x')
|
||||||
|
editMenu.addItem("Copy", Action(None, 'copy:'), 'cmd+c')
|
||||||
|
editMenu.addItem("Paste", Action(None, 'paste:'), 'cmd+v')
|
||||||
|
editMenu.addSeparator()
|
||||||
|
editMenu.addItem("Filter Results...", Action(None, 'focusOnFilterField'), 'cmd+alt+f')
|
||||||
|
|
||||||
|
actionMenu.addItem("Start Duplicate Scan", Action(owner, 'startScanning'), 'cmd+d')
|
||||||
|
actionMenu.addSeparator()
|
||||||
|
actionMenu.addItem("Send Marked to Trash...", Action(None, 'trashMarked'), 'cmd+t')
|
||||||
|
actionMenu.addItem("Move Marked to...", Action(None, 'moveMarked'), 'cmd+m')
|
||||||
|
actionMenu.addItem("Copy Marked to...", Action(None, 'copyMarked'), 'cmd+alt+m')
|
||||||
|
actionMenu.addItem("Remove Marked from Results", Action(None, 'removeMarked'), 'cmd+r')
|
||||||
|
actionMenu.addItem("Re-Prioritize Results", Action(None, 'reprioritizeResults'))
|
||||||
|
actionMenu.addSeparator()
|
||||||
|
actionMenu.addItem("Remove Selected from Results", Action(None, 'removeSelected'), 'cmd+backspace')
|
||||||
|
actionMenu.addItem("Add Selected to Ignore List", Action(None, 'ignoreSelected'), 'cmd+g')
|
||||||
|
actionMenu.addItem("Make Selected Reference", Action(None, 'switchSelected'), 'cmd+arrowup')
|
||||||
|
actionMenu.addSeparator()
|
||||||
|
actionMenu.addItem("Open Selected with Default Application", Action(None, 'openSelected'), 'cmd+return')
|
||||||
|
actionMenu.addItem("Reveal Selected in Finder", Action(None, 'revealSelected'), 'cmd+alt+return')
|
||||||
|
actionMenu.addItem("Invoke Custom Command", Action(None, 'invokeCustomCommand'), 'cmd+shift+c')
|
||||||
|
actionMenu.addItem("Rename Selected", Action(None, 'renameSelected'), 'enter')
|
||||||
|
|
||||||
|
modeMenu.addItem("Show Dupes Only", Action(None, 'togglePowerMarker'), 'cmd+1')
|
||||||
|
modeMenu.addItem("Show Delta Values", Action(None, 'toggleDelta'), 'cmd+2')
|
||||||
|
|
||||||
|
windowMenu.addItem("Results Window", Action(owner, 'showResultWindow'))
|
||||||
|
windowMenu.addItem("Folder Selection Window", Action(owner, 'showDirectoryWindow'))
|
||||||
|
windowMenu.addItem("Ignore List", Action(owner, 'showIgnoreList'))
|
||||||
|
windowMenu.addItem("Details Panel", Action(None, 'toggleDetailsPanel'), 'cmd+i')
|
||||||
|
windowMenu.addItem("Quick Look", Action(None, 'toggleQuicklookPanel'), 'cmd+l')
|
||||||
|
windowMenu.addSeparator()
|
||||||
|
windowMenu.addItem("Minimize", Action(None, 'performMinimize:'))
|
||||||
|
windowMenu.addItem("Zoom", Action(None, 'performZoom:'))
|
||||||
|
windowMenu.addItem("Close Window", Action(None, 'performClose:'), 'cmd+w')
|
||||||
|
windowMenu.addSeparator()
|
||||||
|
windowMenu.addItem("Bring All to Front", Action(None, 'arrangeInFront:'))
|
||||||
|
|
||||||
|
helpMenu.addItem("dupeGuru Help", Action(owner, 'openHelp'), 'cmd+?')
|
||||||
|
helpMenu.addItem("dupeGuru Website", Action(owner, 'openWebsite'))
|
||||||
189
cocoa/base/ui/preferences_panel.py
Normal file
189
cocoa/base/ui/preferences_panel.py
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
edition = args.get('edition', 'se')
|
||||||
|
dialogTitles = {
|
||||||
|
'se': "dupeGuru Preferences",
|
||||||
|
'me': "dupeGuru ME Preferences",
|
||||||
|
'pe': "dupeGuru PE Preferences",
|
||||||
|
}
|
||||||
|
dialogHeights = {
|
||||||
|
'se': 345,
|
||||||
|
'me': 365,
|
||||||
|
'pe': 275,
|
||||||
|
}
|
||||||
|
scanTypeNames = {
|
||||||
|
'se': ["Filename", "Content", "Folders"],
|
||||||
|
'me': ["Filename", "Filename - Fields", "Filename - Fields (No Order)", "Tags", "Content", "Audio Content"],
|
||||||
|
'pe': ["Contents", "EXIF Timestamp"],
|
||||||
|
}
|
||||||
|
|
||||||
|
result = Window(410, dialogHeights[edition], dialogTitles[edition])
|
||||||
|
tabView = TabView(result)
|
||||||
|
basicTab = tabView.addTab("Basic")
|
||||||
|
advancedTab = tabView.addTab("Advanced")
|
||||||
|
scanTypePopup = Popup(basicTab.view, scanTypeNames[edition])
|
||||||
|
scanTypeLabel = Label(basicTab.view, "Scan Type:")
|
||||||
|
thresholdSlider = Slider(basicTab.view, 1, 100, 80)
|
||||||
|
thresholdLabel = Label(basicTab.view, "Filter hardness:")
|
||||||
|
moreResultsLabel = Label(basicTab.view, "More results")
|
||||||
|
fewerResultsLabel = Label(basicTab.view, "Fewer results")
|
||||||
|
thresholdValueLabel = Label(basicTab.view, "")
|
||||||
|
fontSizeCombo = Combobox(basicTab.view, ["11", "12", "13", "14", "18", "24"])
|
||||||
|
fontSizeLabel = Label(basicTab.view, "Font Size:")
|
||||||
|
if edition in ('se', 'me'):
|
||||||
|
wordWeightingBox = Checkbox(basicTab.view, "Word weighting")
|
||||||
|
matchSimilarWordsBox = Checkbox(basicTab.view, "Match similar words")
|
||||||
|
elif edition == 'pe':
|
||||||
|
matchDifferentDimensionsBox = Checkbox(basicTab.view, "Match pictures of different dimensions")
|
||||||
|
mixKindBox = Checkbox(basicTab.view, "Can mix file kind")
|
||||||
|
removeEmptyFoldersBox = Checkbox(basicTab.view, "Remove empty folders on delete or move")
|
||||||
|
checkForUpdatesBox = Checkbox(basicTab.view, "Automatically check for updates")
|
||||||
|
if edition == 'se':
|
||||||
|
ignoreSmallFilesBox = Checkbox(basicTab.view, "Ignore files smaller than:")
|
||||||
|
smallFilesThresholdText = TextField(basicTab.view, "")
|
||||||
|
smallFilesThresholdSuffixLabel = Label(basicTab.view, "KB")
|
||||||
|
elif edition == 'me':
|
||||||
|
tagsToScanLabel = Label(basicTab.view, "Tags to scan:")
|
||||||
|
trackBox = Checkbox(basicTab.view, "Track")
|
||||||
|
artistBox = Checkbox(basicTab.view, "Artist")
|
||||||
|
albumBox = Checkbox(basicTab.view, "Album")
|
||||||
|
titleBox = Checkbox(basicTab.view, "Title")
|
||||||
|
genreBox = Checkbox(basicTab.view, "Genre")
|
||||||
|
yearBox = Checkbox(basicTab.view, "Year")
|
||||||
|
tagBoxes = [trackBox, artistBox, albumBox, titleBox, genreBox, yearBox]
|
||||||
|
|
||||||
|
regexpCheckbox = Checkbox(advancedTab.view, "Use regular expressions when filtering")
|
||||||
|
ignoreHardlinksBox = Checkbox(advancedTab.view, "Ignore duplicates hardlinking to the same file")
|
||||||
|
debugModeCheckbox = Checkbox(advancedTab.view, "Debug mode (restart required)")
|
||||||
|
customCommandLabel = Label(advancedTab.view, "Custom command (arguments: %d for dupe, %r for ref):")
|
||||||
|
customCommandText = TextField(advancedTab.view, "")
|
||||||
|
copyMoveLabel = Label(advancedTab.view, "Copy and Move:")
|
||||||
|
copyMovePopup = Popup(advancedTab.view, ["Right in destination", "Recreate relative path", "Recreate absolute path"])
|
||||||
|
|
||||||
|
resetToDefaultsButton = Button(result, "Reset To Defaults")
|
||||||
|
|
||||||
|
scanTypePopup.bind('selectedIndex', defaults, 'values.scanType')
|
||||||
|
thresholdSlider.bind('value', defaults, 'values.minMatchPercentage')
|
||||||
|
thresholdValueLabel.bind('value', defaults, 'values.minMatchPercentage')
|
||||||
|
fontSizeCombo.bind('value', defaults, 'values.TableFontSize')
|
||||||
|
mixKindBox.bind('value', defaults, 'values.mixFileKind')
|
||||||
|
removeEmptyFoldersBox.bind('value', defaults, 'values.removeEmptyFolders')
|
||||||
|
checkForUpdatesBox.bind('value', defaults, 'values.SUEnableAutomaticChecks')
|
||||||
|
regexpCheckbox.bind('value', defaults, 'values.useRegexpFilter')
|
||||||
|
ignoreHardlinksBox.bind('value', defaults, 'values.ignoreHardlinkMatches')
|
||||||
|
debugModeCheckbox.bind('value', defaults, 'values.DebugMode')
|
||||||
|
customCommandText.bind('value', defaults, 'values.CustomCommand')
|
||||||
|
copyMovePopup.bind('selectedIndex', defaults, 'values.recreatePathType')
|
||||||
|
if edition in ('se', 'me'):
|
||||||
|
wordWeightingBox.bind('value', defaults, 'values.wordWeighting')
|
||||||
|
matchSimilarWordsBox.bind('value', defaults, 'values.matchSimilarWords')
|
||||||
|
disableWhenContentScan = [thresholdSlider, wordWeightingBox, matchSimilarWordsBox]
|
||||||
|
for control in disableWhenContentScan:
|
||||||
|
control.bind('enabled', defaults, 'values.scanType', valueTransformer='vtScanTypeIsNotContent')
|
||||||
|
if edition == 'se':
|
||||||
|
ignoreSmallFilesBox.bind('value', defaults, 'values.ignoreSmallFiles')
|
||||||
|
smallFilesThresholdText.bind('value', defaults, 'values.smallFileThreshold')
|
||||||
|
elif edition == 'me':
|
||||||
|
for box in tagBoxes:
|
||||||
|
box.bind('enabled', defaults, 'values.scanType', valueTransformer='vtScanTypeIsTag')
|
||||||
|
trackBox.bind('value', defaults, 'values.scanTagTrack')
|
||||||
|
artistBox.bind('value', defaults, 'values.scanTagArtist')
|
||||||
|
albumBox.bind('value', defaults, 'values.scanTagAlbum')
|
||||||
|
titleBox.bind('value', defaults, 'values.scanTagTitle')
|
||||||
|
genreBox.bind('value', defaults, 'values.scanTagGenre')
|
||||||
|
yearBox.bind('value', defaults, 'values.scanTagYear')
|
||||||
|
elif edition == 'pe':
|
||||||
|
matchDifferentDimensionsBox.bind('value', defaults, 'values.matchScaled')
|
||||||
|
thresholdSlider.bind('enabled', defaults, 'values.scanType', valueTransformer='vtScanTypeIsFuzzy')
|
||||||
|
|
||||||
|
result.canResize = False
|
||||||
|
result.canMinimize = False
|
||||||
|
thresholdValueLabel.formatter = NumberFormatter(NumberStyle.Decimal)
|
||||||
|
thresholdValueLabel.formatter.maximumFractionDigits = 0
|
||||||
|
allLabels = [scanTypeLabel, thresholdValueLabel, moreResultsLabel, fewerResultsLabel,
|
||||||
|
thresholdLabel, fontSizeLabel, customCommandLabel, copyMoveLabel]
|
||||||
|
allCheckboxes = [mixKindBox, removeEmptyFoldersBox, checkForUpdatesBox, regexpCheckbox,
|
||||||
|
ignoreHardlinksBox, debugModeCheckbox]
|
||||||
|
if edition == 'se':
|
||||||
|
allLabels += [smallFilesThresholdSuffixLabel]
|
||||||
|
allCheckboxes += [ignoreSmallFilesBox, wordWeightingBox, matchSimilarWordsBox]
|
||||||
|
elif edition == 'me':
|
||||||
|
allLabels += [tagsToScanLabel]
|
||||||
|
allCheckboxes += tagBoxes + [wordWeightingBox, matchSimilarWordsBox]
|
||||||
|
elif edition == 'pe':
|
||||||
|
allCheckboxes += [matchDifferentDimensionsBox]
|
||||||
|
for label in allLabels:
|
||||||
|
label.controlSize = ControlSize.Small
|
||||||
|
fewerResultsLabel.alignment = TextAlignment.Right
|
||||||
|
for checkbox in allCheckboxes:
|
||||||
|
checkbox.font = scanTypeLabel.font
|
||||||
|
resetToDefaultsButton.action = Action(defaults, 'revertToInitialValues:')
|
||||||
|
|
||||||
|
scanTypeLabel.width = thresholdLabel.width = fontSizeLabel.width = 94
|
||||||
|
fontSizeCombo.width = 66
|
||||||
|
thresholdValueLabel.width = 25
|
||||||
|
resetToDefaultsButton.width = 136
|
||||||
|
if edition == 'se':
|
||||||
|
smallFilesThresholdText.width = 60
|
||||||
|
smallFilesThresholdSuffixLabel.width = 40
|
||||||
|
elif edition == 'me':
|
||||||
|
for box in tagBoxes:
|
||||||
|
box.width = 70
|
||||||
|
|
||||||
|
tabView.packToCorner(Pack.UpperLeft)
|
||||||
|
tabView.fill(Pack.Right)
|
||||||
|
resetToDefaultsButton.packRelativeTo(tabView, Pack.Below, align=Pack.Right)
|
||||||
|
tabView.fill(Pack.Below, margin=14)
|
||||||
|
tabView.setAnchor(Pack.UpperLeft, growX=True, growY=True)
|
||||||
|
scanTypePopup.packToCorner(Pack.UpperRight)
|
||||||
|
scanTypeLabel.packRelativeTo(scanTypePopup, Pack.Left)
|
||||||
|
scanTypePopup.fill(Pack.Left)
|
||||||
|
thresholdSlider.packRelativeTo(scanTypePopup, Pack.Below)
|
||||||
|
thresholdValueLabel.packRelativeTo(thresholdSlider, Pack.Right)
|
||||||
|
thresholdSlider.fill(Pack.Right)
|
||||||
|
# We want to give the labels as much space as possible, and we only "know" how much is available
|
||||||
|
# after the slider's fill operation.
|
||||||
|
moreResultsLabel.width = fewerResultsLabel.width = thresholdSlider.width // 2
|
||||||
|
moreResultsLabel.packRelativeTo(thresholdSlider, Pack.Below, align=Pack.Left, margin=6)
|
||||||
|
fewerResultsLabel.packRelativeTo(thresholdSlider, Pack.Below, align=Pack.Right, margin=6)
|
||||||
|
thresholdLabel.packRelativeTo(thresholdSlider, Pack.Left)
|
||||||
|
fontSizeCombo.packRelativeTo(moreResultsLabel, Pack.Below)
|
||||||
|
fontSizeLabel.packRelativeTo(fontSizeCombo, Pack.Left)
|
||||||
|
|
||||||
|
if edition == 'me':
|
||||||
|
tagsToScanLabel.packRelativeTo(fontSizeCombo, Pack.Below)
|
||||||
|
tagsToScanLabel.fill(Pack.Left)
|
||||||
|
tagsToScanLabel.fill(Pack.Right)
|
||||||
|
trackBox.packRelativeTo(tagsToScanLabel, Pack.Below)
|
||||||
|
trackBox.x += 10
|
||||||
|
artistBox.packRelativeTo(trackBox, Pack.Right)
|
||||||
|
albumBox.packRelativeTo(artistBox, Pack.Right)
|
||||||
|
titleBox.packRelativeTo(trackBox, Pack.Below)
|
||||||
|
genreBox.packRelativeTo(titleBox, Pack.Right)
|
||||||
|
yearBox.packRelativeTo(genreBox, Pack.Right)
|
||||||
|
viewToPackCheckboxesUnder = titleBox
|
||||||
|
else:
|
||||||
|
viewToPackCheckboxesUnder = fontSizeCombo
|
||||||
|
|
||||||
|
if edition == 'se':
|
||||||
|
checkboxesToLayout = [wordWeightingBox, matchSimilarWordsBox, mixKindBox, removeEmptyFoldersBox,
|
||||||
|
ignoreSmallFilesBox]
|
||||||
|
elif edition == 'me':
|
||||||
|
checkboxesToLayout = [wordWeightingBox, matchSimilarWordsBox, mixKindBox, removeEmptyFoldersBox,
|
||||||
|
checkForUpdatesBox]
|
||||||
|
elif edition == 'pe':
|
||||||
|
checkboxesToLayout = [matchDifferentDimensionsBox, mixKindBox, removeEmptyFoldersBox,
|
||||||
|
checkForUpdatesBox]
|
||||||
|
checkboxLayout = VLayout(checkboxesToLayout)
|
||||||
|
checkboxLayout.packRelativeTo(viewToPackCheckboxesUnder, Pack.Below)
|
||||||
|
checkboxLayout.fill(Pack.Left)
|
||||||
|
checkboxLayout.fill(Pack.Right)
|
||||||
|
|
||||||
|
if edition == 'se':
|
||||||
|
smallFilesThresholdText.packRelativeTo(ignoreSmallFilesBox, Pack.Below, margin=4)
|
||||||
|
checkForUpdatesBox.packRelativeTo(smallFilesThresholdText, Pack.Below, margin=4)
|
||||||
|
checkForUpdatesBox.fill(Pack.Right)
|
||||||
|
smallFilesThresholdText.x += 20
|
||||||
|
smallFilesThresholdSuffixLabel.packRelativeTo(smallFilesThresholdText, Pack.Right)
|
||||||
|
|
||||||
|
advancedLayout = VLayout(advancedTab.view.subviews[:])
|
||||||
|
advancedLayout.packToCorner(Pack.UpperLeft)
|
||||||
|
advancedLayout.fill(Pack.Right)
|
||||||
50
cocoa/base/ui/prioritize_dialog.py
Normal file
50
cocoa/base/ui/prioritize_dialog.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
ownerclass = 'PrioritizeDialog'
|
||||||
|
ownerimport = 'PrioritizeDialog.h'
|
||||||
|
|
||||||
|
result = Window(610, 400, "Re-Prioritize duplicates")
|
||||||
|
promptLabel = Label(result, "Add criteria to the right box and click OK to send the dupes that "
|
||||||
|
"correspond the best to these criteria to their respective group's reference position. Read "
|
||||||
|
"the help file for more information.")
|
||||||
|
categoryPopup = Popup(result)
|
||||||
|
criteriaTable = ListView(result)
|
||||||
|
prioritizationTable = ListView(result)
|
||||||
|
addButton = Button(result, NLSTR("-->"))
|
||||||
|
removeButton = Button(result, NLSTR("<--"))
|
||||||
|
okButton = Button(result, "Ok")
|
||||||
|
cancelButton = Button(result, "Cancel")
|
||||||
|
|
||||||
|
owner.categoryPopUpView = categoryPopup
|
||||||
|
owner.criteriaTableView = criteriaTable
|
||||||
|
owner.prioritizationTableView = prioritizationTable
|
||||||
|
|
||||||
|
result.canMinimize = False
|
||||||
|
result.canClose = False
|
||||||
|
result.minSize = Size(result.width, result.height)
|
||||||
|
addButton.action = Action(owner.model, 'addSelected')
|
||||||
|
removeButton.action = Action(owner.model, 'removeSelected')
|
||||||
|
okButton.action = Action(owner, 'ok')
|
||||||
|
cancelButton.action = Action(owner, 'cancel')
|
||||||
|
okButton.keyEquivalent = '\\r'
|
||||||
|
cancelButton.keyEquivalent = '\\e'
|
||||||
|
|
||||||
|
promptLabel.height *= 3 # 3 lines
|
||||||
|
|
||||||
|
leftLayout = VLayout([categoryPopup, criteriaTable], width=262, filler=criteriaTable)
|
||||||
|
middleLayout = VLayout([addButton, removeButton], width=41)
|
||||||
|
buttonLayout = HLayout([None, cancelButton, okButton])
|
||||||
|
|
||||||
|
promptLabel.packToCorner(Pack.UpperLeft)
|
||||||
|
promptLabel.fill(Pack.Right)
|
||||||
|
leftLayout.packRelativeTo(promptLabel, Pack.Below)
|
||||||
|
middleLayout.packRelativeTo(leftLayout, Pack.Right, align=Pack.Above)
|
||||||
|
prioritizationTable.packRelativeTo(middleLayout, Pack.Right, align=Pack.Above)
|
||||||
|
buttonLayout.packRelativeTo(leftLayout, Pack.Below)
|
||||||
|
buttonLayout.fill(Pack.Right)
|
||||||
|
leftLayout.fill(Pack.Below)
|
||||||
|
middleLayout.packRelativeTo(leftLayout, Pack.Right, align=Pack.Middle)
|
||||||
|
prioritizationTable.fill(Pack.Below, goal=leftLayout.y)
|
||||||
|
prioritizationTable.fill(Pack.Right)
|
||||||
|
|
||||||
|
promptLabel.setAnchor(Pack.UpperLeft, growX=True)
|
||||||
|
prioritizationTable.setAnchor(Pack.UpperLeft, growX=True, growY=True)
|
||||||
|
buttonLayout.setAnchor(Pack.Below)
|
||||||
35
cocoa/base/ui/problem_dialog.py
Normal file
35
cocoa/base/ui/problem_dialog.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
ownerclass = 'ProblemDialog'
|
||||||
|
ownerimport = 'ProblemDialog.h'
|
||||||
|
|
||||||
|
result = Window(480, 310, "Problems!")
|
||||||
|
messageLabel = Label(result, "There were problems processing some (or all) of the files. The cause "
|
||||||
|
"of these problems are described in the table below. Those files were not removed from your "
|
||||||
|
"results.")
|
||||||
|
problemTable = TableView(result)
|
||||||
|
revealButton = Button(result, "Reveal")
|
||||||
|
closeButton = Button(result, "Close")
|
||||||
|
|
||||||
|
owner.problemTableView = problemTable
|
||||||
|
|
||||||
|
result.canMinimize = False
|
||||||
|
result.minSize = Size(300, 300)
|
||||||
|
closeButton.keyEquivalent = '\\r'
|
||||||
|
revealButton.action = Action(owner.model, 'revealSelected')
|
||||||
|
closeButton.action = Action(result, 'performClose:')
|
||||||
|
|
||||||
|
messageLabel.height *= 3 # 3 lines
|
||||||
|
revealButton.width = 150
|
||||||
|
closeButton.width = 98
|
||||||
|
|
||||||
|
messageLabel.packToCorner(Pack.UpperLeft)
|
||||||
|
messageLabel.fill(Pack.Right)
|
||||||
|
problemTable.packRelativeTo(messageLabel, Pack.Below)
|
||||||
|
problemTable.fill(Pack.Right)
|
||||||
|
revealButton.packRelativeTo(problemTable, Pack.Below)
|
||||||
|
closeButton.packRelativeTo(problemTable, Pack.Below, align=Pack.Right)
|
||||||
|
problemTable.fill(Pack.Below)
|
||||||
|
|
||||||
|
messageLabel.setAnchor(Pack.UpperLeft, growX=True)
|
||||||
|
problemTable.setAnchor(Pack.UpperLeft, growX=True, growY=True)
|
||||||
|
revealButton.setAnchor(Pack.LowerLeft)
|
||||||
|
closeButton.setAnchor(Pack.LowerRight)
|
||||||
97
cocoa/base/ui/result_window.py
Normal file
97
cocoa/base/ui/result_window.py
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
wnerclass = 'ResultWindow'
|
||||||
|
ownerimport = 'ResultWindow.h'
|
||||||
|
|
||||||
|
result = Window(557, 400, "dupeGuru Results")
|
||||||
|
toolbar = result.createToolbar('ResultsToolbar')
|
||||||
|
table = TableView(result)
|
||||||
|
table.OBJC_CLASS = 'HSTableView'
|
||||||
|
statsLabel = Label(result, "")
|
||||||
|
contextMenu = Menu("")
|
||||||
|
|
||||||
|
#Setup toolbar items
|
||||||
|
toolbar.displayMode = const.NSToolbarDisplayModeIconOnly
|
||||||
|
directoriesToolItem = toolbar.addItem('Directories', "Directories", image='folder32')
|
||||||
|
actionToolItem = toolbar.addItem('Action', "Action")
|
||||||
|
filterToolItem = toolbar.addItem('Filter', "Filter")
|
||||||
|
optionsToolItem = toolbar.addItem('Options', "Options")
|
||||||
|
quicklookToolItem = toolbar.addItem('QuickLook', "Quick Look")
|
||||||
|
toolbar.defaultItems = [actionToolItem, optionsToolItem, quicklookToolItem, directoriesToolItem,
|
||||||
|
toolbar.flexibleSpace(), filterToolItem]
|
||||||
|
actionPopup = Popup(None)
|
||||||
|
actionPopup.pullsdown = True
|
||||||
|
actionPopup.bezelStyle = const.NSTexturedRoundedBezelStyle
|
||||||
|
actionPopup.arrowPosition = const.NSPopUpArrowAtBottom
|
||||||
|
item = actionPopup.menu.addItem("") # First item is invisible
|
||||||
|
item.hidden = True
|
||||||
|
item.image = 'NSActionTemplate'
|
||||||
|
actionPopup.width = 44
|
||||||
|
actionToolItem.view = actionPopup
|
||||||
|
filterField = SearchField(None, "Filter")
|
||||||
|
filterField.action = Action(owner, 'filter')
|
||||||
|
filterField.sendsWholeSearchString = True
|
||||||
|
filterToolItem.view = filterField
|
||||||
|
filterToolItem.minSize = Size(80, 22)
|
||||||
|
filterToolItem.maxSize = Size(300, 22)
|
||||||
|
quickLookButton = Button(None, "")
|
||||||
|
quickLookButton.bezelStyle = const.NSTexturedRoundedBezelStyle
|
||||||
|
quickLookButton.image = 'NSQuickLookTemplate'
|
||||||
|
quickLookButton.width = 44
|
||||||
|
quickLookButton.action = Action(owner, 'toggleQuicklookPanel')
|
||||||
|
quicklookToolItem.view = quickLookButton
|
||||||
|
optionsSegments = SegmentedControl(None)
|
||||||
|
optionsSegments.segmentStyle = const.NSSegmentStyleCapsule
|
||||||
|
optionsSegments.trackingMode = const.NSSegmentSwitchTrackingSelectAny
|
||||||
|
optionsSegments.font = Font(FontFamily.System, 11)
|
||||||
|
optionsSegments.addSegment("Details", 57)
|
||||||
|
optionsSegments.addSegment("Dupes Only", 82)
|
||||||
|
optionsSegments.addSegment("Delta", 48)
|
||||||
|
optionsSegments.action = Action(owner, 'changeOptions')
|
||||||
|
optionsToolItem.view = optionsSegments
|
||||||
|
|
||||||
|
# Popuplate menus
|
||||||
|
actionPopup.menu.addItem("Send Marked to Trash...", action=Action(owner, 'trashMarked'))
|
||||||
|
actionPopup.menu.addItem("Move Marked to...", action=Action(owner, 'moveMarked'))
|
||||||
|
actionPopup.menu.addItem("Copy Marked to...", action=Action(owner, 'copyMarked'))
|
||||||
|
actionPopup.menu.addItem("Remove Marked from Results", action=Action(owner, 'removeMarked'))
|
||||||
|
actionPopup.menu.addSeparator()
|
||||||
|
for menu in (actionPopup.menu, contextMenu):
|
||||||
|
menu.addItem("Remove Selected from Results", action=Action(owner, 'removeSelected'))
|
||||||
|
menu.addItem("Add Selected to Ignore List", action=Action(owner, 'ignoreSelected'))
|
||||||
|
menu.addItem("Make Selected Reference", action=Action(owner, 'switchSelected'))
|
||||||
|
menu.addSeparator()
|
||||||
|
menu.addItem("Open Selected with Default Application", action=Action(owner, 'openSelected'))
|
||||||
|
menu.addItem("Reveal Selected in Finder", action=Action(owner, 'revealSelected'))
|
||||||
|
menu.addItem("Rename Selected", action=Action(owner, 'renameSelected'))
|
||||||
|
|
||||||
|
# Doing connections
|
||||||
|
owner.filterField = filterField
|
||||||
|
owner.matches = table
|
||||||
|
owner.optionsSwitch = optionsSegments
|
||||||
|
owner.optionsToolbarItem = optionsToolItem
|
||||||
|
owner.stats = statsLabel
|
||||||
|
table.bind('rowHeight', defaults, 'values.TableFontSize', valueTransformer='vtRowHeightOffset')
|
||||||
|
|
||||||
|
# Rest of the setup
|
||||||
|
result.minSize = Size(340, 340)
|
||||||
|
result.autosaveName = 'MainWindow'
|
||||||
|
statsLabel.alignment = TextAlignment.Center
|
||||||
|
table.alternatingRows = True
|
||||||
|
table.menu = contextMenu
|
||||||
|
table.allowsColumnReordering = True
|
||||||
|
table.allowsColumnResizing = True
|
||||||
|
table.allowsColumnSelection = False
|
||||||
|
table.allowsEmptySelection = False
|
||||||
|
table.allowsMultipleSelection = True
|
||||||
|
table.allowsTypeSelect = True
|
||||||
|
table.gridStyleMask = const.NSTableViewSolidHorizontalGridLineMask
|
||||||
|
table.setAnchor(Pack.UpperLeft, growX=True, growY=True)
|
||||||
|
statsLabel.setAnchor(Pack.LowerLeft, growX=True)
|
||||||
|
|
||||||
|
# Layout
|
||||||
|
# It's a little weird to pack with a margin of -1, but if I don't do that, I get too thick of a
|
||||||
|
# border on the upper side of the table.
|
||||||
|
table.packToCorner(Pack.UpperLeft, margin=-1)
|
||||||
|
table.fill(Pack.Right, margin=0)
|
||||||
|
statsLabel.packRelativeTo(table, Pack.Below, margin=6)
|
||||||
|
statsLabel.fill(Pack.Right, margin=0)
|
||||||
|
table.fill(Pack.Below, margin=5)
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
9
cocoa/inter/all.py
Normal file
9
cocoa/inter/all.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from .deletion_options import PyDeletionOptions
|
||||||
|
from .details_panel import PyDetailsPanel
|
||||||
|
from .directory_outline import PyDirectoryOutline
|
||||||
|
from .prioritize_dialog import PyPrioritizeDialog
|
||||||
|
from .prioritize_list import PyPrioritizeList
|
||||||
|
from .problem_dialog import PyProblemDialog
|
||||||
|
from .ignore_list_dialog import PyIgnoreListDialog
|
||||||
|
from .result_table import PyResultTable
|
||||||
|
from .stats_label import PyStatsLabel
|
||||||
228
cocoa/inter/app.py
Normal file
228
cocoa/inter/app.py
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from objp.util import pyref, dontwrap
|
||||||
|
from jobprogress import job
|
||||||
|
import cocoa
|
||||||
|
from cocoa import install_exception_hook, install_cocoa_logger, proxy
|
||||||
|
from cocoa.inter import PyFairware, FairwareView
|
||||||
|
from hscommon.trans import trget
|
||||||
|
|
||||||
|
from core.app import JobType
|
||||||
|
|
||||||
|
tr = trget('ui')
|
||||||
|
|
||||||
|
JOBID2TITLE = {
|
||||||
|
JobType.Scan: tr("Scanning for duplicates"),
|
||||||
|
JobType.Load: tr("Loading"),
|
||||||
|
JobType.Move: tr("Moving"),
|
||||||
|
JobType.Copy: tr("Copying"),
|
||||||
|
JobType.Delete: tr("Sending to Trash"),
|
||||||
|
}
|
||||||
|
|
||||||
|
class DupeGuruView(FairwareView):
|
||||||
|
def askYesNoWithPrompt_(self, prompt: str) -> bool: pass
|
||||||
|
def showProblemDialog(self): pass
|
||||||
|
def selectDestFolderWithPrompt_(self, prompt: str) -> str: pass
|
||||||
|
def selectDestFileWithPrompt_extension_(self, prompt: str, extension: str) -> str: pass
|
||||||
|
|
||||||
|
class PyDupeGuruBase(PyFairware):
|
||||||
|
FOLLOW_PROTOCOLS = ['Worker']
|
||||||
|
|
||||||
|
@dontwrap
|
||||||
|
def _init(self, modelclass):
|
||||||
|
logging.basicConfig(level=logging.WARNING, format='%(levelname)s %(message)s')
|
||||||
|
install_exception_hook()
|
||||||
|
install_cocoa_logger()
|
||||||
|
appdata = proxy.getAppdataPath()
|
||||||
|
self.model = modelclass(self, appdata)
|
||||||
|
self.progress = cocoa.ThreadedJobPerformer()
|
||||||
|
|
||||||
|
#---Sub-proxies
|
||||||
|
def detailsPanel(self) -> pyref:
|
||||||
|
return self.model.details_panel
|
||||||
|
|
||||||
|
def directoryTree(self) -> pyref:
|
||||||
|
return self.model.directory_tree
|
||||||
|
|
||||||
|
def problemDialog(self) -> pyref:
|
||||||
|
return self.model.problem_dialog
|
||||||
|
|
||||||
|
def statsLabel(self) -> pyref:
|
||||||
|
return self.model.stats_label
|
||||||
|
|
||||||
|
def resultTable(self) -> pyref:
|
||||||
|
return self.model.result_table
|
||||||
|
|
||||||
|
def ignoreListDialog(self) -> pyref:
|
||||||
|
return self.model.ignore_list_dialog
|
||||||
|
|
||||||
|
def deletionOptions(self) -> pyref:
|
||||||
|
return self.model.deletion_options
|
||||||
|
|
||||||
|
#---Directories
|
||||||
|
def addDirectory_(self, directory: str):
|
||||||
|
self.model.add_directory(directory)
|
||||||
|
|
||||||
|
#---Results
|
||||||
|
def doScan(self):
|
||||||
|
self.model.start_scanning()
|
||||||
|
|
||||||
|
def exportToXHTML(self):
|
||||||
|
self.model.export_to_xhtml()
|
||||||
|
|
||||||
|
def exportToCSV(self):
|
||||||
|
self.model.export_to_csv()
|
||||||
|
|
||||||
|
def loadSession(self):
|
||||||
|
self.model.load()
|
||||||
|
|
||||||
|
def loadResultsFrom_(self, filename: str):
|
||||||
|
self.model.load_from(filename)
|
||||||
|
|
||||||
|
def markAll(self):
|
||||||
|
self.model.mark_all()
|
||||||
|
|
||||||
|
def markNone(self):
|
||||||
|
self.model.mark_none()
|
||||||
|
|
||||||
|
def markInvert(self):
|
||||||
|
self.model.mark_invert()
|
||||||
|
|
||||||
|
def purgeIgnoreList(self):
|
||||||
|
self.model.purge_ignore_list()
|
||||||
|
|
||||||
|
def toggleSelectedMark(self):
|
||||||
|
self.model.toggle_selected_mark_state()
|
||||||
|
|
||||||
|
def saveSession(self):
|
||||||
|
self.model.save()
|
||||||
|
|
||||||
|
def saveResultsAs_(self, filename: str):
|
||||||
|
self.model.save_as(filename)
|
||||||
|
|
||||||
|
#---Actions
|
||||||
|
def addSelectedToIgnoreList(self):
|
||||||
|
self.model.add_selected_to_ignore_list()
|
||||||
|
|
||||||
|
def deleteMarked(self):
|
||||||
|
self.model.delete_marked()
|
||||||
|
|
||||||
|
def applyFilter_(self, filter: str):
|
||||||
|
self.model.apply_filter(filter)
|
||||||
|
|
||||||
|
def makeSelectedReference(self):
|
||||||
|
self.model.make_selected_reference()
|
||||||
|
|
||||||
|
def copyMarked(self):
|
||||||
|
self.model.copy_or_move_marked(copy=True)
|
||||||
|
|
||||||
|
def moveMarked(self):
|
||||||
|
self.model.copy_or_move_marked(copy=False)
|
||||||
|
|
||||||
|
def openSelected(self):
|
||||||
|
self.model.open_selected()
|
||||||
|
|
||||||
|
def removeMarked(self):
|
||||||
|
self.model.remove_marked()
|
||||||
|
|
||||||
|
def removeSelected(self):
|
||||||
|
self.model.remove_selected()
|
||||||
|
|
||||||
|
def revealSelected(self):
|
||||||
|
self.model.reveal_selected()
|
||||||
|
|
||||||
|
def invokeCustomCommand(self):
|
||||||
|
self.model.invoke_custom_command()
|
||||||
|
|
||||||
|
def showIgnoreList(self):
|
||||||
|
self.model.ignore_list_dialog.show()
|
||||||
|
|
||||||
|
#---Information
|
||||||
|
def resultsAreModified(self) -> bool:
|
||||||
|
return self.model.results.is_modified
|
||||||
|
|
||||||
|
#---Properties
|
||||||
|
def setMixFileKind_(self, mix_file_kind: bool):
|
||||||
|
self.model.scanner.mix_file_kind = mix_file_kind
|
||||||
|
|
||||||
|
def setEscapeFilterRegexp_(self, escape_filter_regexp: bool):
|
||||||
|
self.model.options['escape_filter_regexp'] = escape_filter_regexp
|
||||||
|
|
||||||
|
def setRemoveEmptyFolders_(self, remove_empty_folders: bool):
|
||||||
|
self.model.options['clean_empty_dirs'] = remove_empty_folders
|
||||||
|
|
||||||
|
def setIgnoreHardlinkMatches_(self, ignore_hardlink_matches: bool):
|
||||||
|
self.model.options['ignore_hardlink_matches'] = ignore_hardlink_matches
|
||||||
|
|
||||||
|
def setCopyMoveDestType_(self, copymove_dest_type: int):
|
||||||
|
self.model.options['copymove_dest_type'] = copymove_dest_type
|
||||||
|
|
||||||
|
#---Worker
|
||||||
|
def getJobProgress(self) -> object: # NSNumber
|
||||||
|
try:
|
||||||
|
return self.progress.last_progress
|
||||||
|
except AttributeError:
|
||||||
|
# I have *no idea* why this can possible happen (last_progress is always set by
|
||||||
|
# create_job() *before* any threaded job notification, which shows the progress panel,
|
||||||
|
# is sent), but it happens anyway, so there we go. ref: #106
|
||||||
|
return -1
|
||||||
|
|
||||||
|
def getJobDesc(self) -> str:
|
||||||
|
try:
|
||||||
|
return self.progress.last_desc
|
||||||
|
except AttributeError:
|
||||||
|
# see getJobProgress
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def cancelJob(self):
|
||||||
|
self.progress.job_cancelled = True
|
||||||
|
|
||||||
|
def jobCompleted_(self, jobid: str):
|
||||||
|
result = self.model._job_completed(jobid, self.progress.last_error)
|
||||||
|
if not result:
|
||||||
|
self.progress.reraise_if_error()
|
||||||
|
|
||||||
|
#--- model --> view
|
||||||
|
@dontwrap
|
||||||
|
def open_path(self, path):
|
||||||
|
proxy.openPath_(str(path))
|
||||||
|
|
||||||
|
@dontwrap
|
||||||
|
def reveal_path(self, path):
|
||||||
|
proxy.revealPath_(str(path))
|
||||||
|
|
||||||
|
@dontwrap
|
||||||
|
def start_job(self, jobid, func, args=()):
|
||||||
|
try:
|
||||||
|
j = self.progress.create_job()
|
||||||
|
args = tuple([j] + list(args))
|
||||||
|
self.progress.run_threaded(func, args=args)
|
||||||
|
except job.JobInProgressError:
|
||||||
|
proxy.postNotification_userInfo_('JobInProgress', None)
|
||||||
|
else:
|
||||||
|
ud = {'desc': JOBID2TITLE[jobid], 'jobid':jobid}
|
||||||
|
proxy.postNotification_userInfo_('JobStarted', ud)
|
||||||
|
|
||||||
|
@dontwrap
|
||||||
|
def ask_yes_no(self, prompt):
|
||||||
|
return self.callback.askYesNoWithPrompt_(prompt)
|
||||||
|
|
||||||
|
@dontwrap
|
||||||
|
def show_results_window(self):
|
||||||
|
# Not needed yet because our progress dialog is shown as a sheet of the results window,
|
||||||
|
# which causes it to be already visible when the scan/load ends.
|
||||||
|
# XXX Make progress sheet be a child of the folder selection window.
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dontwrap
|
||||||
|
def show_problem_dialog(self):
|
||||||
|
self.callback.showProblemDialog()
|
||||||
|
|
||||||
|
@dontwrap
|
||||||
|
def select_dest_folder(self, prompt):
|
||||||
|
return self.callback.selectDestFolderWithPrompt_(prompt)
|
||||||
|
|
||||||
|
@dontwrap
|
||||||
|
def select_dest_file(self, prompt, extension):
|
||||||
|
return self.callback.selectDestFileWithPrompt_extension_(prompt, extension)
|
||||||
|
|
||||||
288
cocoa/inter/app_me.py
Normal file
288
cocoa/inter/app_me.py
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
# Created By: Virgil Dupras
|
||||||
|
# Created On: 2006/11/16
|
||||||
|
# Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
#
|
||||||
|
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
# which should be included with this package. The terms are also available at
|
||||||
|
# http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import plistlib
|
||||||
|
import time
|
||||||
|
import os.path as op
|
||||||
|
from appscript import app, its, k, CommandError, ApplicationNotFoundError
|
||||||
|
from . import tunes
|
||||||
|
|
||||||
|
from cocoa import as_fetch, proxy
|
||||||
|
from hscommon import io
|
||||||
|
from hscommon.trans import trget
|
||||||
|
from hscommon.path import Path
|
||||||
|
from hscommon.util import remove_invalid_xml
|
||||||
|
|
||||||
|
from core import directories
|
||||||
|
from core.app import JobType
|
||||||
|
from core.scanner import ScanType
|
||||||
|
from core_me.app import DupeGuru as DupeGuruBase
|
||||||
|
from core_me import fs
|
||||||
|
from .app import JOBID2TITLE, PyDupeGuruBase
|
||||||
|
|
||||||
|
tr = trget('ui')
|
||||||
|
|
||||||
|
JobType.RemoveDeadTracks = 'jobRemoveDeadTracks'
|
||||||
|
JobType.ScanDeadTracks = 'jobScanDeadTracks'
|
||||||
|
|
||||||
|
JOBID2TITLE.update({
|
||||||
|
JobType.RemoveDeadTracks: tr("Removing dead tracks from your iTunes Library"),
|
||||||
|
JobType.ScanDeadTracks: tr("Scanning the iTunes Library"),
|
||||||
|
})
|
||||||
|
|
||||||
|
ITUNES = 'iTunes'
|
||||||
|
ITUNES_PATH = Path('iTunes Library')
|
||||||
|
|
||||||
|
def get_itunes_library(a):
|
||||||
|
try:
|
||||||
|
[source] = [s for s in a.sources(timeout=0) if s.kind(timeout=0) == k.library]
|
||||||
|
[library] = source.library_playlists(timeout=0)
|
||||||
|
return library
|
||||||
|
except ValueError:
|
||||||
|
logging.warning('Some unexpected iTunes configuration encountered')
|
||||||
|
return None
|
||||||
|
|
||||||
|
class ITunesSong(fs.MusicFile):
|
||||||
|
def __init__(self, song_data):
|
||||||
|
path = Path(proxy.url2path_(song_data['Location']))
|
||||||
|
fs.MusicFile.__init__(self, path)
|
||||||
|
self.id = song_data['Track ID']
|
||||||
|
|
||||||
|
def remove_from_library(self):
|
||||||
|
try:
|
||||||
|
a = app(ITUNES, terms=tunes)
|
||||||
|
library = get_itunes_library(a)
|
||||||
|
if library is None:
|
||||||
|
return
|
||||||
|
[song] = library.file_tracks[its.database_ID == self.id]()
|
||||||
|
a.delete(song, timeout=0)
|
||||||
|
except ValueError:
|
||||||
|
msg = "Could not find song '{}' (trackid: {}) in iTunes Library".format(str(self.path), self.id)
|
||||||
|
raise EnvironmentError(msg)
|
||||||
|
except (CommandError, RuntimeError) as e:
|
||||||
|
raise EnvironmentError(str(e))
|
||||||
|
|
||||||
|
display_folder_path = ITUNES_PATH
|
||||||
|
|
||||||
|
def get_itunes_database_path():
|
||||||
|
plisturls = proxy.prefValue_inDomain_('iTunesRecentDatabases', 'com.apple.iApps')
|
||||||
|
if not plisturls:
|
||||||
|
raise directories.InvalidPathError()
|
||||||
|
plistpath = proxy.url2path_(plisturls[0])
|
||||||
|
return Path(plistpath)
|
||||||
|
|
||||||
|
def get_itunes_songs(plistpath):
|
||||||
|
if not io.exists(plistpath):
|
||||||
|
return []
|
||||||
|
s = io.open(plistpath, 'rt', encoding='utf-8').read()
|
||||||
|
# iTunes sometimes produces XML files with invalid characters in it.
|
||||||
|
s = remove_invalid_xml(s, replace_with='')
|
||||||
|
plist = plistlib.readPlistFromBytes(s.encode('utf-8'))
|
||||||
|
result = []
|
||||||
|
for song_data in plist['Tracks'].values():
|
||||||
|
if song_data['Track Type'] != 'File':
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
song = ITunesSong(song_data)
|
||||||
|
except KeyError: # No "Location" or "Track ID" key in track
|
||||||
|
continue
|
||||||
|
if io.exists(song.path):
|
||||||
|
result.append(song)
|
||||||
|
return result
|
||||||
|
|
||||||
|
class Directories(directories.Directories):
|
||||||
|
def __init__(self, fileclasses):
|
||||||
|
directories.Directories.__init__(self, fileclasses)
|
||||||
|
try:
|
||||||
|
self.itunes_libpath = get_itunes_database_path()
|
||||||
|
except directories.InvalidPathError:
|
||||||
|
self.itunes_libpath = None
|
||||||
|
|
||||||
|
def _get_files(self, from_path, j):
|
||||||
|
if from_path == ITUNES_PATH:
|
||||||
|
if self.itunes_libpath is None:
|
||||||
|
return []
|
||||||
|
is_ref = self.get_state(from_path) == directories.DirectoryState.Reference
|
||||||
|
songs = get_itunes_songs(self.itunes_libpath)
|
||||||
|
for song in songs:
|
||||||
|
song.is_ref = is_ref
|
||||||
|
return songs
|
||||||
|
else:
|
||||||
|
return directories.Directories._get_files(self, from_path, j)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_subfolders(path):
|
||||||
|
if path == ITUNES_PATH:
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return directories.Directories.get_subfolders(path)
|
||||||
|
|
||||||
|
def add_path(self, path):
|
||||||
|
if path == ITUNES_PATH:
|
||||||
|
if path not in self:
|
||||||
|
self._dirs.append(path)
|
||||||
|
else:
|
||||||
|
directories.Directories.add_path(self, path)
|
||||||
|
|
||||||
|
def has_itunes_path(self):
|
||||||
|
return any(path == ITUNES_PATH for path in self._dirs)
|
||||||
|
|
||||||
|
def has_any_file(self):
|
||||||
|
# If we don't do that, it causes a hangup in the GUI when we click Start Scanning because
|
||||||
|
# checking if there's any file to scan involves reading the whole library. If we have the
|
||||||
|
# iTunes library, we assume we have at least one file.
|
||||||
|
if self.has_itunes_path():
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return directories.Directories.has_any_file(self)
|
||||||
|
|
||||||
|
|
||||||
|
class DupeGuruME(DupeGuruBase):
|
||||||
|
def __init__(self, view, appdata):
|
||||||
|
appdata = op.join(appdata, 'dupeGuru Music Edition')
|
||||||
|
DupeGuruBase.__init__(self, view, appdata)
|
||||||
|
# Use fileclasses set in DupeGuruBase.__init__()
|
||||||
|
self.directories = Directories(fileclasses=self.directories.fileclasses)
|
||||||
|
self.dead_tracks = []
|
||||||
|
|
||||||
|
def _do_delete(self, j, *args):
|
||||||
|
# XXX If I read correctly, Python 3.3 will allow us to go fetch inner function easily, so
|
||||||
|
# we'll be able to replace "op" below with DupeGuruBase._do_delete.op.
|
||||||
|
def op(dupe):
|
||||||
|
j.add_progress()
|
||||||
|
return self._do_delete_dupe(dupe, *args)
|
||||||
|
|
||||||
|
marked = [dupe for dupe in self.results.dupes if self.results.is_marked(dupe)]
|
||||||
|
j.start_job(self.results.mark_count, tr("Sending dupes to the Trash"))
|
||||||
|
if any(isinstance(dupe, ITunesSong) for dupe in marked):
|
||||||
|
j.add_progress(0, desc=tr("Talking to iTunes. Don't touch it!"))
|
||||||
|
try:
|
||||||
|
a = app(ITUNES, terms=tunes)
|
||||||
|
a.activate(timeout=0)
|
||||||
|
except (CommandError, RuntimeError, ApplicationNotFoundError):
|
||||||
|
pass
|
||||||
|
self.results.perform_on_marked(op, True)
|
||||||
|
|
||||||
|
def _do_delete_dupe(self, dupe, *args):
|
||||||
|
if isinstance(dupe, ITunesSong):
|
||||||
|
dupe.remove_from_library()
|
||||||
|
DupeGuruBase._do_delete_dupe(self, dupe, *args)
|
||||||
|
|
||||||
|
def _create_file(self, path):
|
||||||
|
if (self.directories.itunes_libpath is not None) and (path in self.directories.itunes_libpath[:-1]):
|
||||||
|
if not hasattr(self, 'itunes_songs'):
|
||||||
|
songs = get_itunes_songs(self.directories.itunes_libpath)
|
||||||
|
self.itunes_songs = {song.path: song for song in songs}
|
||||||
|
if path in self.itunes_songs:
|
||||||
|
return self.itunes_songs[path]
|
||||||
|
else:
|
||||||
|
pass # We'll return the default file type, as per the last line of this method
|
||||||
|
return DupeGuruBase._create_file(self, path)
|
||||||
|
|
||||||
|
def _job_completed(self, jobid, exc):
|
||||||
|
if (jobid in {JobType.RemoveDeadTracks, JobType.ScanDeadTracks}) and (exc is not None):
|
||||||
|
msg = tr("There were communication problems with iTunes. The operation couldn't be completed.")
|
||||||
|
self.view.show_message(msg)
|
||||||
|
return True
|
||||||
|
if jobid == JobType.ScanDeadTracks:
|
||||||
|
dead_tracks_count = len(self.dead_tracks)
|
||||||
|
if dead_tracks_count > 0:
|
||||||
|
msg = tr("Your iTunes Library contains %d dead tracks ready to be removed. Continue?")
|
||||||
|
if self.view.ask_yes_no(msg % dead_tracks_count):
|
||||||
|
self.remove_dead_tracks()
|
||||||
|
else:
|
||||||
|
msg = tr("You have no dead tracks in your iTunes Library")
|
||||||
|
self.view.show_message(msg)
|
||||||
|
if jobid == JobType.Load:
|
||||||
|
if hasattr(self, 'itunes_songs'):
|
||||||
|
# If we load another file, we want a refresh song list
|
||||||
|
del self.itunes_songs
|
||||||
|
DupeGuruBase._job_completed(self, jobid, exc)
|
||||||
|
|
||||||
|
def copy_or_move(self, dupe, copy, destination, dest_type):
|
||||||
|
if isinstance(dupe, ITunesSong):
|
||||||
|
copy = True
|
||||||
|
return DupeGuruBase.copy_or_move(self, dupe, copy, destination, dest_type)
|
||||||
|
|
||||||
|
def start_scanning(self):
|
||||||
|
if self.directories.has_itunes_path():
|
||||||
|
try:
|
||||||
|
app(ITUNES, terms=tunes)
|
||||||
|
except ApplicationNotFoundError:
|
||||||
|
self.view.show_message(tr("The iTunes application couldn't be found."))
|
||||||
|
return
|
||||||
|
DupeGuruBase.start_scanning(self)
|
||||||
|
|
||||||
|
def remove_dead_tracks(self):
|
||||||
|
def do(j):
|
||||||
|
a = app(ITUNES, terms=tunes)
|
||||||
|
a.activate(timeout=0)
|
||||||
|
for index, track in enumerate(j.iter_with_progress(self.dead_tracks)):
|
||||||
|
if index % 100 == 0:
|
||||||
|
time.sleep(.1)
|
||||||
|
try:
|
||||||
|
track.delete(timeout=0)
|
||||||
|
except CommandError as e:
|
||||||
|
logging.warning('Error while trying to remove a track from iTunes: %s' % str(e))
|
||||||
|
|
||||||
|
self.view.start_job(JobType.RemoveDeadTracks, do)
|
||||||
|
|
||||||
|
def scan_dead_tracks(self):
|
||||||
|
def do(j):
|
||||||
|
a = app(ITUNES, terms=tunes)
|
||||||
|
a.activate(timeout=0)
|
||||||
|
library = get_itunes_library(a)
|
||||||
|
if library is None:
|
||||||
|
return
|
||||||
|
self.dead_tracks = []
|
||||||
|
tracks = as_fetch(library.file_tracks, k.file_track)
|
||||||
|
for index, track in enumerate(j.iter_with_progress(tracks)):
|
||||||
|
if index % 100 == 0:
|
||||||
|
time.sleep(.1)
|
||||||
|
if track.location(timeout=0) == k.missing_value:
|
||||||
|
self.dead_tracks.append(track)
|
||||||
|
logging.info('Found %d dead tracks' % len(self.dead_tracks))
|
||||||
|
|
||||||
|
self.view.start_job(JobType.ScanDeadTracks, do)
|
||||||
|
|
||||||
|
class PyDupeGuru(PyDupeGuruBase):
|
||||||
|
def __init__(self):
|
||||||
|
self._init(DupeGuruME)
|
||||||
|
|
||||||
|
def scanDeadTracks(self):
|
||||||
|
self.model.scan_dead_tracks()
|
||||||
|
|
||||||
|
#---Properties
|
||||||
|
def setMinMatchPercentage_(self, percentage: int):
|
||||||
|
self.model.scanner.min_match_percentage = percentage
|
||||||
|
|
||||||
|
def setScanType_(self, scan_type: int):
|
||||||
|
try:
|
||||||
|
self.model.scanner.scan_type = [
|
||||||
|
ScanType.Filename,
|
||||||
|
ScanType.Fields,
|
||||||
|
ScanType.FieldsNoOrder,
|
||||||
|
ScanType.Tag,
|
||||||
|
ScanType.Contents,
|
||||||
|
ScanType.ContentsAudio,
|
||||||
|
][scan_type]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setWordWeighting_(self, words_are_weighted: bool):
|
||||||
|
self.model.scanner.word_weighting = words_are_weighted
|
||||||
|
|
||||||
|
def setMatchSimilarWords_(self, match_similar_words: bool):
|
||||||
|
self.model.scanner.match_similar_words = match_similar_words
|
||||||
|
|
||||||
|
def enable_scanForTag_(self, enable: bool, scan_tag: str):
|
||||||
|
if enable:
|
||||||
|
self.model.scanner.scanned_tags.add(scan_tag)
|
||||||
|
else:
|
||||||
|
self.model.scanner.scanned_tags.discard(scan_tag)
|
||||||
328
cocoa/inter/app_pe.py
Normal file
328
cocoa/inter/app_pe.py
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
# Created By: Virgil Dupras
|
||||||
|
# Created On: 2006/11/13
|
||||||
|
# Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
#
|
||||||
|
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
# which should be included with this package. The terms are also available at
|
||||||
|
# http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
|
||||||
|
import os.path as op
|
||||||
|
import plistlib
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from appscript import app, its, k, CommandError, ApplicationNotFoundError
|
||||||
|
|
||||||
|
from hscommon import io
|
||||||
|
from hscommon.util import remove_invalid_xml, first
|
||||||
|
from hscommon.path import Path
|
||||||
|
from hscommon.trans import trget
|
||||||
|
from cocoa import proxy
|
||||||
|
|
||||||
|
from core.scanner import ScanType
|
||||||
|
from core import directories
|
||||||
|
from core.app import JobType
|
||||||
|
from core_pe import _block_osx
|
||||||
|
from core_pe.photo import Photo as PhotoBase
|
||||||
|
from core_pe.app import DupeGuru as DupeGuruBase
|
||||||
|
from .app import PyDupeGuruBase
|
||||||
|
|
||||||
|
tr = trget('ui')
|
||||||
|
|
||||||
|
IPHOTO_PATH = Path('iPhoto Library')
|
||||||
|
APERTURE_PATH = Path('Aperture Library')
|
||||||
|
|
||||||
|
class Photo(PhotoBase):
|
||||||
|
HANDLED_EXTS = PhotoBase.HANDLED_EXTS.copy()
|
||||||
|
HANDLED_EXTS.update({'psd', 'nef', 'cr2', 'orf'})
|
||||||
|
|
||||||
|
def _plat_get_dimensions(self):
|
||||||
|
return _block_osx.get_image_size(str(self.path))
|
||||||
|
|
||||||
|
def _plat_get_blocks(self, block_count_per_side, orientation):
|
||||||
|
try:
|
||||||
|
blocks = _block_osx.getblocks(str(self.path), block_count_per_side, orientation)
|
||||||
|
except Exception as e:
|
||||||
|
raise IOError('The reading of "%s" failed with "%s"' % (str(self.path), str(e)))
|
||||||
|
if not blocks:
|
||||||
|
raise IOError('The picture %s could not be read' % str(self.path))
|
||||||
|
return blocks
|
||||||
|
|
||||||
|
|
||||||
|
class IPhoto(Photo):
|
||||||
|
def __init__(self, path, db_id):
|
||||||
|
# In IPhoto, we don't care about the db_id, we find photos by path.
|
||||||
|
Photo.__init__(self, path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_folder_path(self):
|
||||||
|
return IPHOTO_PATH
|
||||||
|
|
||||||
|
class AperturePhoto(Photo):
|
||||||
|
def __init__(self, path, db_id):
|
||||||
|
Photo.__init__(self, path)
|
||||||
|
self.db_id = db_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_folder_path(self):
|
||||||
|
return APERTURE_PATH
|
||||||
|
|
||||||
|
def get_iphoto_or_aperture_pictures(plistpath, photo_class):
|
||||||
|
# The structure of iPhoto and Aperture libraries for the base photo list are excactly the same.
|
||||||
|
if not io.exists(plistpath):
|
||||||
|
return []
|
||||||
|
s = io.open(plistpath, 'rt', encoding='utf-8').read()
|
||||||
|
# There was a case where a guy had 0x10 chars in his plist, causing expat errors on loading
|
||||||
|
s = remove_invalid_xml(s, replace_with='')
|
||||||
|
# It seems that iPhoto sometimes doesn't properly escape & chars. The regexp below is to find
|
||||||
|
# any & char that is not a &-based entity (&, ", etc.). based on TextMate's XML
|
||||||
|
# bundle's regexp
|
||||||
|
s, count = re.subn(r'&(?![a-zA-Z0-9_-]+|#[0-9]+|#x[0-9a-fA-F]+;)', '', s)
|
||||||
|
if count:
|
||||||
|
logging.warning("%d invalid XML entities replacement made", count)
|
||||||
|
plist = plistlib.readPlistFromBytes(s.encode('utf-8'))
|
||||||
|
result = []
|
||||||
|
for key, photo_data in plist['Master Image List'].items():
|
||||||
|
if photo_data['MediaType'] != 'Image':
|
||||||
|
continue
|
||||||
|
photo_path = Path(photo_data['ImagePath'])
|
||||||
|
photo = photo_class(photo_path, key)
|
||||||
|
result.append(photo)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_iphoto_pictures(plistpath):
|
||||||
|
return get_iphoto_or_aperture_pictures(plistpath, IPhoto)
|
||||||
|
|
||||||
|
def get_aperture_pictures(plistpath):
|
||||||
|
return get_iphoto_or_aperture_pictures(plistpath, AperturePhoto)
|
||||||
|
|
||||||
|
def get_iapps_database_path(prefname):
|
||||||
|
plisturls = proxy.prefValue_inDomain_(prefname, 'com.apple.iApps')
|
||||||
|
if not plisturls:
|
||||||
|
raise directories.InvalidPathError()
|
||||||
|
plistpath = proxy.url2path_(plisturls[0])
|
||||||
|
return Path(plistpath)
|
||||||
|
|
||||||
|
def get_iphoto_database_path():
|
||||||
|
return get_iapps_database_path('iPhotoRecentDatabases')
|
||||||
|
|
||||||
|
def get_aperture_database_path():
|
||||||
|
return get_iapps_database_path('ApertureLibraries')
|
||||||
|
|
||||||
|
class Directories(directories.Directories):
|
||||||
|
def __init__(self):
|
||||||
|
directories.Directories.__init__(self, fileclasses=[Photo])
|
||||||
|
try:
|
||||||
|
self.iphoto_libpath = get_iphoto_database_path()
|
||||||
|
self.set_state(self.iphoto_libpath[:-1], directories.DirectoryState.Excluded)
|
||||||
|
except directories.InvalidPathError:
|
||||||
|
self.iphoto_libpath = None
|
||||||
|
try:
|
||||||
|
self.aperture_libpath = get_aperture_database_path()
|
||||||
|
self.set_state(self.aperture_libpath[:-1], directories.DirectoryState.Excluded)
|
||||||
|
except directories.InvalidPathError:
|
||||||
|
self.aperture_libpath = None
|
||||||
|
|
||||||
|
def _get_files(self, from_path, j):
|
||||||
|
if from_path == IPHOTO_PATH:
|
||||||
|
if self.iphoto_libpath is None:
|
||||||
|
return []
|
||||||
|
is_ref = self.get_state(from_path) == directories.DirectoryState.Reference
|
||||||
|
photos = get_iphoto_pictures(self.iphoto_libpath)
|
||||||
|
for photo in photos:
|
||||||
|
photo.is_ref = is_ref
|
||||||
|
return photos
|
||||||
|
elif from_path == APERTURE_PATH:
|
||||||
|
if self.aperture_libpath is None:
|
||||||
|
return []
|
||||||
|
is_ref = self.get_state(from_path) == directories.DirectoryState.Reference
|
||||||
|
photos = get_aperture_pictures(self.aperture_libpath)
|
||||||
|
for photo in photos:
|
||||||
|
photo.is_ref = is_ref
|
||||||
|
return photos
|
||||||
|
else:
|
||||||
|
return directories.Directories._get_files(self, from_path, j)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_subfolders(path):
|
||||||
|
if path in {IPHOTO_PATH, APERTURE_PATH}:
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return directories.Directories.get_subfolders(path)
|
||||||
|
|
||||||
|
def add_path(self, path):
|
||||||
|
if path in {IPHOTO_PATH, APERTURE_PATH}:
|
||||||
|
if path not in self:
|
||||||
|
self._dirs.append(path)
|
||||||
|
else:
|
||||||
|
directories.Directories.add_path(self, path)
|
||||||
|
|
||||||
|
def has_iphoto_path(self):
|
||||||
|
return any(path in {IPHOTO_PATH, APERTURE_PATH} for path in self._dirs)
|
||||||
|
|
||||||
|
def has_any_file(self):
|
||||||
|
# If we don't do that, it causes a hangup in the GUI when we click Start Scanning because
|
||||||
|
# checking if there's any file to scan involves reading the whole library. If we have the
|
||||||
|
# iPhoto library, we assume we have at least one file.
|
||||||
|
if self.has_iphoto_path():
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return directories.Directories.has_any_file(self)
|
||||||
|
|
||||||
|
|
||||||
|
class DupeGuruPE(DupeGuruBase):
|
||||||
|
def __init__(self, view, appdata):
|
||||||
|
appdata = op.join(appdata, 'dupeGuru Picture Edition')
|
||||||
|
DupeGuruBase.__init__(self, view, appdata)
|
||||||
|
self.directories = Directories()
|
||||||
|
|
||||||
|
def _do_delete(self, j, *args):
|
||||||
|
def op(dupe):
|
||||||
|
j.add_progress()
|
||||||
|
return self._do_delete_dupe(dupe, *args)
|
||||||
|
|
||||||
|
self.deleted_aperture_photos = False
|
||||||
|
marked = [dupe for dupe in self.results.dupes if self.results.is_marked(dupe)]
|
||||||
|
j.start_job(self.results.mark_count, tr("Sending dupes to the Trash"))
|
||||||
|
if any(isinstance(dupe, IPhoto) for dupe in marked):
|
||||||
|
j.add_progress(0, desc=tr("Talking to iPhoto. Don't touch it!"))
|
||||||
|
try:
|
||||||
|
a = app('iPhoto')
|
||||||
|
a.activate(timeout=0)
|
||||||
|
a.select(a.photo_library_album(timeout=0), timeout=0)
|
||||||
|
except (CommandError, RuntimeError, ApplicationNotFoundError):
|
||||||
|
pass
|
||||||
|
if any(isinstance(dupe, AperturePhoto) for dupe in marked):
|
||||||
|
self.deleted_aperture_photos = True
|
||||||
|
j.add_progress(0, desc=tr("Talking to Aperture. Don't touch it!"))
|
||||||
|
try:
|
||||||
|
a = app('Aperture')
|
||||||
|
a.activate(timeout=0)
|
||||||
|
except (CommandError, RuntimeError, ApplicationNotFoundError):
|
||||||
|
pass
|
||||||
|
self.results.perform_on_marked(op, True)
|
||||||
|
|
||||||
|
def _do_delete_dupe(self, dupe, *args):
|
||||||
|
if isinstance(dupe, IPhoto):
|
||||||
|
try:
|
||||||
|
a = app('iPhoto')
|
||||||
|
album = a.photo_library_album()
|
||||||
|
if album is None:
|
||||||
|
msg = "There are communication problems with iPhoto. Try opening iPhoto first, it might solve it."
|
||||||
|
raise EnvironmentError(msg)
|
||||||
|
[photo] = album.photos[its.image_path == str(dupe.path)]()
|
||||||
|
a.remove(photo, timeout=0)
|
||||||
|
except ValueError:
|
||||||
|
msg = "Could not find photo '{}' in iPhoto Library".format(str(dupe.path))
|
||||||
|
raise EnvironmentError(msg)
|
||||||
|
except (CommandError, RuntimeError) as e:
|
||||||
|
raise EnvironmentError(str(e))
|
||||||
|
if isinstance(dupe, AperturePhoto):
|
||||||
|
try:
|
||||||
|
a = app('Aperture')
|
||||||
|
# I'm flying blind here. In my own test library, all photos are in an album with the
|
||||||
|
# id "LibraryFolder", so I'm going to guess that it's the case at least most of the
|
||||||
|
# time. As a safeguard, if we don't find any library with that id, we'll use the
|
||||||
|
# first album.
|
||||||
|
# Now, about deleting: All attempts I've made at sending photos to trash failed,
|
||||||
|
# even with normal applescript. So, what we're going to do here is to create a
|
||||||
|
# "dupeGuru Trash" project and tell the user to manually send those photos to trash.
|
||||||
|
libraries = a.libraries()
|
||||||
|
library = first(l for l in libraries if l.id == 'LibraryFolder')
|
||||||
|
if library is None:
|
||||||
|
library = libraries[0]
|
||||||
|
trash_project = a.projects["dupeGuru Trash"]
|
||||||
|
if trash_project.exists():
|
||||||
|
trash_project = trash_project()
|
||||||
|
else:
|
||||||
|
trash_project = library.make(new=k.project, with_properties={k.name: "dupeGuru Trash"})
|
||||||
|
[photo] = library.image_versions[its.id == dupe.db_id]()
|
||||||
|
photo.move(to=trash_project)
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
msg = "Could not find photo '{}' in Aperture Library".format(str(dupe.path))
|
||||||
|
raise EnvironmentError(msg)
|
||||||
|
except (CommandError, RuntimeError) as e:
|
||||||
|
raise EnvironmentError(str(e))
|
||||||
|
else:
|
||||||
|
DupeGuruBase._do_delete_dupe(self, dupe, *args)
|
||||||
|
|
||||||
|
def _create_file(self, path):
|
||||||
|
if (self.directories.iphoto_libpath is not None) and (path in self.directories.iphoto_libpath[:-1]):
|
||||||
|
if not hasattr(self, 'path2iphoto'):
|
||||||
|
photos = get_iphoto_pictures(self.directories.iphoto_libpath)
|
||||||
|
self.path2iphoto = {p.path: p for p in photos}
|
||||||
|
return self.path2iphoto.get(path)
|
||||||
|
if (self.directories.aperture_libpath is not None) and (path in self.directories.aperture_libpath[:-1]):
|
||||||
|
if not hasattr(self, 'path2aperture'):
|
||||||
|
photos = get_aperture_pictures(self.directories.aperture_libpath)
|
||||||
|
self.path2aperture = {p.path: p for p in photos}
|
||||||
|
return self.path2aperture.get(path)
|
||||||
|
return DupeGuruBase._create_file(self, path)
|
||||||
|
|
||||||
|
def _job_completed(self, jobid, exc):
|
||||||
|
DupeGuruBase._job_completed(self, jobid, exc)
|
||||||
|
if jobid == JobType.Load:
|
||||||
|
if hasattr(self, 'path2iphoto'):
|
||||||
|
del self.path2iphoto
|
||||||
|
if hasattr(self, 'path2aperture'):
|
||||||
|
del self.path2aperture
|
||||||
|
if jobid == JobType.Delete and self.deleted_aperture_photos:
|
||||||
|
msg = tr("Deleted Aperture photos were sent to a project called \"dupeGuru Trash\".")
|
||||||
|
self.view.show_message(msg)
|
||||||
|
|
||||||
|
def copy_or_move(self, dupe, copy, destination, dest_type):
|
||||||
|
if isinstance(dupe, (IPhoto, AperturePhoto)):
|
||||||
|
copy = True
|
||||||
|
return DupeGuruBase.copy_or_move(self, dupe, copy, destination, dest_type)
|
||||||
|
|
||||||
|
def selected_dupe_path(self):
|
||||||
|
if not self.selected_dupes:
|
||||||
|
return None
|
||||||
|
return self.selected_dupes[0].path
|
||||||
|
|
||||||
|
def selected_dupe_ref_path(self):
|
||||||
|
if not self.selected_dupes:
|
||||||
|
return None
|
||||||
|
ref = self.results.get_group_of_duplicate(self.selected_dupes[0]).ref
|
||||||
|
if ref is self.selected_dupes[0]: # we don't want the same pic to be displayed on both sides
|
||||||
|
return None
|
||||||
|
return ref.path
|
||||||
|
|
||||||
|
def start_scanning(self):
|
||||||
|
if self.directories.has_iphoto_path():
|
||||||
|
try:
|
||||||
|
app('iPhoto')
|
||||||
|
except ApplicationNotFoundError:
|
||||||
|
self.view.show_message(tr("The iPhoto application couldn't be found."))
|
||||||
|
return
|
||||||
|
DupeGuruBase.start_scanning(self)
|
||||||
|
|
||||||
|
class PyDupeGuru(PyDupeGuruBase):
|
||||||
|
def __init__(self):
|
||||||
|
self._init(DupeGuruPE)
|
||||||
|
|
||||||
|
def clearPictureCache(self):
|
||||||
|
self.model.scanner.clear_picture_cache()
|
||||||
|
|
||||||
|
#---Information
|
||||||
|
def getSelectedDupePath(self) -> str:
|
||||||
|
return str(self.model.selected_dupe_path())
|
||||||
|
|
||||||
|
def getSelectedDupeRefPath(self) -> str:
|
||||||
|
return str(self.model.selected_dupe_ref_path())
|
||||||
|
|
||||||
|
#---Properties
|
||||||
|
def setScanType_(self, scan_type: int):
|
||||||
|
try:
|
||||||
|
self.model.scanner.scan_type = [
|
||||||
|
ScanType.FuzzyBlock,
|
||||||
|
ScanType.ExifTimestamp,
|
||||||
|
][scan_type]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setMatchScaled_(self, match_scaled: bool):
|
||||||
|
self.model.scanner.match_scaled = match_scaled
|
||||||
|
|
||||||
|
def setMinMatchPercentage_(self, percentage: int):
|
||||||
|
self.model.scanner.threshold = percentage
|
||||||
102
cocoa/inter/app_se.py
Normal file
102
cocoa/inter/app_se.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# Created By: Virgil Dupras
|
||||||
|
# Created On: 2009-05-24
|
||||||
|
# Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
#
|
||||||
|
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
# which should be included with this package. The terms are also available at
|
||||||
|
# http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os.path as op
|
||||||
|
|
||||||
|
from hscommon import io
|
||||||
|
from hscommon.path import Path
|
||||||
|
from cocoa import proxy
|
||||||
|
|
||||||
|
from core.scanner import ScanType
|
||||||
|
from core import fs
|
||||||
|
from core.directories import Directories as DirectoriesBase, DirectoryState
|
||||||
|
from core_se.app import DupeGuru as DupeGuruBase
|
||||||
|
from .app import PyDupeGuruBase
|
||||||
|
|
||||||
|
def is_bundle(str_path):
|
||||||
|
uti = proxy.getUTI_(str_path)
|
||||||
|
if uti is None:
|
||||||
|
logging.warning('There was an error trying to detect the UTI of %s', str_path)
|
||||||
|
return proxy.type_conformsToType_(uti, 'com.apple.bundle') or proxy.type_conformsToType_(uti, 'com.apple.package')
|
||||||
|
|
||||||
|
class Bundle(fs.Folder):
|
||||||
|
@classmethod
|
||||||
|
def can_handle(cls, path):
|
||||||
|
return not io.islink(path) and io.isdir(path) and is_bundle(str(path))
|
||||||
|
|
||||||
|
|
||||||
|
class Directories(DirectoriesBase):
|
||||||
|
ROOT_PATH_TO_EXCLUDE = list(map(Path, ['/Library', '/Volumes', '/System', '/bin', '/sbin', '/opt', '/private', '/dev']))
|
||||||
|
HOME_PATH_TO_EXCLUDE = [Path('Library')]
|
||||||
|
def __init__(self):
|
||||||
|
DirectoriesBase.__init__(self, fileclasses=[Bundle, fs.File])
|
||||||
|
|
||||||
|
def _default_state_for_path(self, path):
|
||||||
|
result = DirectoriesBase._default_state_for_path(self, path)
|
||||||
|
if result is not None:
|
||||||
|
return result
|
||||||
|
if path in self.ROOT_PATH_TO_EXCLUDE:
|
||||||
|
return DirectoryState.Excluded
|
||||||
|
if path[:2] == Path('/Users') and path[3:] in self.HOME_PATH_TO_EXCLUDE:
|
||||||
|
return DirectoryState.Excluded
|
||||||
|
|
||||||
|
def _get_folders(self, from_folder, j):
|
||||||
|
# We don't want to scan bundle's subfolder even in Folders mode. Bundle's integrity has to
|
||||||
|
# stay intact.
|
||||||
|
if is_bundle(str(from_folder.path)):
|
||||||
|
# just yield the current folder and bail
|
||||||
|
state = self.get_state(from_folder.path)
|
||||||
|
if state != DirectoryState.Excluded:
|
||||||
|
from_folder.is_ref = state == DirectoryState.Reference
|
||||||
|
yield from_folder
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
for folder in DirectoriesBase._get_folders(self, from_folder, j):
|
||||||
|
yield folder
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_subfolders(path):
|
||||||
|
result = DirectoriesBase.get_subfolders(path)
|
||||||
|
return [p for p in result if not is_bundle(str(p))]
|
||||||
|
|
||||||
|
|
||||||
|
class DupeGuru(DupeGuruBase):
|
||||||
|
def __init__(self, view, appdata):
|
||||||
|
appdata = op.join(appdata, 'dupeGuru')
|
||||||
|
DupeGuruBase.__init__(self, view, appdata)
|
||||||
|
self.directories = Directories()
|
||||||
|
|
||||||
|
|
||||||
|
class PyDupeGuru(PyDupeGuruBase):
|
||||||
|
def __init__(self):
|
||||||
|
self._init(DupeGuru)
|
||||||
|
|
||||||
|
#---Properties
|
||||||
|
def setMinMatchPercentage_(self, percentage: int):
|
||||||
|
self.model.scanner.min_match_percentage = int(percentage)
|
||||||
|
|
||||||
|
def setScanType_(self, scan_type: int):
|
||||||
|
try:
|
||||||
|
self.model.scanner.scan_type = [
|
||||||
|
ScanType.Filename,
|
||||||
|
ScanType.Contents,
|
||||||
|
ScanType.Folders,
|
||||||
|
][scan_type]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setWordWeighting_(self, words_are_weighted: bool):
|
||||||
|
self.model.scanner.word_weighting = words_are_weighted
|
||||||
|
|
||||||
|
def setMatchSimilarWords_(self, match_similar_words: bool):
|
||||||
|
self.model.scanner.match_similar_words = match_similar_words
|
||||||
|
|
||||||
|
def setSizeThreshold_(self, size_threshold: int):
|
||||||
|
self.model.scanner.size_threshold = size_threshold
|
||||||
|
|
||||||
33
cocoa/inter/deletion_options.py
Normal file
33
cocoa/inter/deletion_options.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Created On: 2012-05-30
|
||||||
|
# Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
#
|
||||||
|
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
# which should be included with this package. The terms are also available at
|
||||||
|
# http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
|
||||||
|
from objp.util import dontwrap
|
||||||
|
from cocoa.inter import PyGUIObject, GUIObjectView
|
||||||
|
|
||||||
|
class DeletionOptionsView(GUIObjectView):
|
||||||
|
def updateMsg_(self, msg: str): pass
|
||||||
|
def show(self) -> bool: pass
|
||||||
|
|
||||||
|
class PyDeletionOptions(PyGUIObject):
|
||||||
|
def setLinkDeleted_(self, link_deleted: bool):
|
||||||
|
self.model.link_deleted = link_deleted
|
||||||
|
|
||||||
|
def setUseHardlinks_(self, use_hardlinks: bool):
|
||||||
|
self.model.use_hardlinks = use_hardlinks
|
||||||
|
|
||||||
|
def setDirect_(self, direct: bool):
|
||||||
|
self.model.direct = direct
|
||||||
|
|
||||||
|
#--- model --> view
|
||||||
|
@dontwrap
|
||||||
|
def update_msg(self, msg):
|
||||||
|
self.callback.updateMsg_(msg)
|
||||||
|
|
||||||
|
@dontwrap
|
||||||
|
def show(self):
|
||||||
|
return self.callback.show()
|
||||||
|
|
||||||
11
cocoa/inter/details_panel.py
Normal file
11
cocoa/inter/details_panel.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from cocoa.inter import PyGUIObject, GUIObjectView
|
||||||
|
|
||||||
|
class DetailsPanelView(GUIObjectView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PyDetailsPanel(PyGUIObject):
|
||||||
|
def numberOfRows(self) -> int:
|
||||||
|
return self.model.row_count()
|
||||||
|
|
||||||
|
def valueForColumn_row_(self, column: str, row: int) -> object:
|
||||||
|
return self.model.row(row)[int(column)]
|
||||||
18
cocoa/inter/directory_outline.py
Normal file
18
cocoa/inter/directory_outline.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from objp.util import dontwrap
|
||||||
|
from cocoa.inter import PyOutline, GUIObjectView
|
||||||
|
|
||||||
|
class DirectoryOutlineView(GUIObjectView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PyDirectoryOutline(PyOutline):
|
||||||
|
def addDirectory_(self, path: str):
|
||||||
|
self.model.add_directory(path)
|
||||||
|
|
||||||
|
def removeSelectedDirectory(self):
|
||||||
|
self.model.remove_selected()
|
||||||
|
|
||||||
|
# python --> cocoa
|
||||||
|
@dontwrap
|
||||||
|
def refresh_states(self):
|
||||||
|
# Under cocoa, both refresh() and refresh_states() do the same thing.
|
||||||
|
self.callback.refresh()
|
||||||
21
cocoa/inter/ignore_list_dialog.py
Normal file
21
cocoa/inter/ignore_list_dialog.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from objp.util import pyref, dontwrap
|
||||||
|
from cocoa.inter import PyGUIObject, GUIObjectView
|
||||||
|
|
||||||
|
class IgnoreListDialogView(GUIObjectView):
|
||||||
|
def show(self): pass
|
||||||
|
|
||||||
|
class PyIgnoreListDialog(PyGUIObject):
|
||||||
|
def ignoreListTable(self) -> pyref:
|
||||||
|
return self.model.ignore_list_table
|
||||||
|
|
||||||
|
def removeSelected(self):
|
||||||
|
self.model.remove_selected()
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.model.clear()
|
||||||
|
|
||||||
|
#--- model --> view
|
||||||
|
@dontwrap
|
||||||
|
def show(self):
|
||||||
|
self.callback.show()
|
||||||
|
|
||||||
29
cocoa/inter/prioritize_dialog.py
Normal file
29
cocoa/inter/prioritize_dialog.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from objp.util import pyref
|
||||||
|
from cocoa.inter import PyGUIObject, GUIObjectView
|
||||||
|
from core.gui.prioritize_dialog import PrioritizeDialog
|
||||||
|
|
||||||
|
class PrioritizeDialogView(GUIObjectView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PyPrioritizeDialog(PyGUIObject):
|
||||||
|
def __init__(self, app: pyref):
|
||||||
|
model = PrioritizeDialog(app.model)
|
||||||
|
PyGUIObject.__init__(self, model)
|
||||||
|
|
||||||
|
def categoryList(self) -> pyref:
|
||||||
|
return self.model.category_list
|
||||||
|
|
||||||
|
def criteriaList(self) -> pyref:
|
||||||
|
return self.model.criteria_list
|
||||||
|
|
||||||
|
def prioritizationList(self) -> pyref:
|
||||||
|
return self.model.prioritization_list
|
||||||
|
|
||||||
|
def addSelected(self):
|
||||||
|
self.model.add_selected()
|
||||||
|
|
||||||
|
def removeSelected(self):
|
||||||
|
self.model.remove_selected()
|
||||||
|
|
||||||
|
def performReprioritization(self):
|
||||||
|
self.model.perform_reprioritization()
|
||||||
8
cocoa/inter/prioritize_list.py
Normal file
8
cocoa/inter/prioritize_list.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from cocoa.inter import PySelectableList, SelectableListView
|
||||||
|
|
||||||
|
class PrioritizeListView(SelectableListView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PyPrioritizeList(PySelectableList):
|
||||||
|
def moveIndexes_toIndex_(self, indexes: list, dest_index: int):
|
||||||
|
self.model.move_indexes(indexes, dest_index)
|
||||||
9
cocoa/inter/problem_dialog.py
Normal file
9
cocoa/inter/problem_dialog.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from objp.util import pyref
|
||||||
|
from cocoa.inter import PyGUIObject
|
||||||
|
|
||||||
|
class PyProblemDialog(PyGUIObject):
|
||||||
|
def problemTable(self) -> pyref:
|
||||||
|
return self.model.problem_table
|
||||||
|
|
||||||
|
def revealSelected(self):
|
||||||
|
self.model.reveal_selected_dupe()
|
||||||
49
cocoa/inter/result_table.py
Normal file
49
cocoa/inter/result_table.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
from objp.util import dontwrap
|
||||||
|
from cocoa.inter import PyTable, TableView
|
||||||
|
|
||||||
|
class ResultTableView(TableView):
|
||||||
|
def invalidateMarkings(self): pass
|
||||||
|
|
||||||
|
class PyResultTable(PyTable):
|
||||||
|
def powerMarkerMode(self) -> bool:
|
||||||
|
return self.model.power_marker
|
||||||
|
|
||||||
|
def setPowerMarkerMode_(self, value: bool):
|
||||||
|
self.model.power_marker = value
|
||||||
|
|
||||||
|
def deltaValuesMode(self) -> bool:
|
||||||
|
return self.model.delta_values
|
||||||
|
|
||||||
|
def setDeltaValuesMode_(self, value: bool):
|
||||||
|
self.model.delta_values = value
|
||||||
|
|
||||||
|
def deltaColumns(self) -> list:
|
||||||
|
return list(self.model.DELTA_COLUMNS)
|
||||||
|
|
||||||
|
def valueForRow_column_(self, row_index: int, column: str) -> object:
|
||||||
|
return self.model.get_row_value(row_index, column)
|
||||||
|
|
||||||
|
def renameSelected_(self, newname: str) -> bool:
|
||||||
|
return self.model.rename_selected(newname)
|
||||||
|
|
||||||
|
def sortBy_ascending_(self, key: str, asc: bool):
|
||||||
|
self.model.sort(key, asc)
|
||||||
|
|
||||||
|
def markSelected(self):
|
||||||
|
self.model.app.toggle_selected_mark_state()
|
||||||
|
|
||||||
|
def removeSelected(self):
|
||||||
|
self.model.app.remove_selected()
|
||||||
|
|
||||||
|
def selectedDupeCount(self) -> int:
|
||||||
|
return self.model.selected_dupe_count
|
||||||
|
|
||||||
|
def pathAtIndex_(self, index: int) -> str:
|
||||||
|
row = self.model[index]
|
||||||
|
return str(row._dupe.path)
|
||||||
|
|
||||||
|
# python --> cocoa
|
||||||
|
@dontwrap
|
||||||
|
def invalidate_markings(self):
|
||||||
|
self.callback.invalidateMarkings()
|
||||||
|
|
||||||
9
cocoa/inter/stats_label.py
Normal file
9
cocoa/inter/stats_label.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from cocoa.inter import PyGUIObject, GUIObjectView
|
||||||
|
|
||||||
|
class StatsLabelView(GUIObjectView):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PyStatsLabel(PyGUIObject):
|
||||||
|
|
||||||
|
def display(self) -> str:
|
||||||
|
return self.model.display
|
||||||
282
cocoa/inter/tunes.py
Normal file
282
cocoa/inter/tunes.py
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
# Taken from https://github.com/abarnert/itunesterms
|
||||||
|
|
||||||
|
version = 1.1
|
||||||
|
path = '/Applications/iTunes.app'
|
||||||
|
|
||||||
|
classes = \
|
||||||
|
[('print_settings', b'pset'),
|
||||||
|
('application', b'capp'),
|
||||||
|
('artwork', b'cArt'),
|
||||||
|
('audio_CD_playlist', b'cCDP'),
|
||||||
|
('audio_CD_track', b'cCDT'),
|
||||||
|
('browser_window', b'cBrW'),
|
||||||
|
('device_playlist', b'cDvP'),
|
||||||
|
('device_track', b'cDvT'),
|
||||||
|
('encoder', b'cEnc'),
|
||||||
|
('EQ_preset', b'cEQP'),
|
||||||
|
('EQ_window', b'cEQW'),
|
||||||
|
('file_track', b'cFlT'),
|
||||||
|
('folder_playlist', b'cFoP'),
|
||||||
|
('item', b'cobj'),
|
||||||
|
('library_playlist', b'cLiP'),
|
||||||
|
('playlist', b'cPly'),
|
||||||
|
('playlist_window', b'cPlW'),
|
||||||
|
('radio_tuner_playlist', b'cRTP'),
|
||||||
|
('shared_track', b'cShT'),
|
||||||
|
('source', b'cSrc'),
|
||||||
|
('track', b'cTrk'),
|
||||||
|
('URL_track', b'cURT'),
|
||||||
|
('user_playlist', b'cUsP'),
|
||||||
|
('visual', b'cVis'),
|
||||||
|
('window', b'cwin')]
|
||||||
|
|
||||||
|
enums = \
|
||||||
|
[('track_listing', b'kTrk'),
|
||||||
|
('album_listing', b'kAlb'),
|
||||||
|
('cd_insert', b'kCDi'),
|
||||||
|
('standard', b'lwst'),
|
||||||
|
('detailed', b'lwdt'),
|
||||||
|
('stopped', b'kPSS'),
|
||||||
|
('playing', b'kPSP'),
|
||||||
|
('paused', b'kPSp'),
|
||||||
|
('fast_forwarding', b'kPSF'),
|
||||||
|
('rewinding', b'kPSR'),
|
||||||
|
('off', b'kRpO'),
|
||||||
|
('one', b'kRp1'),
|
||||||
|
('all', b'kAll'),
|
||||||
|
('small', b'kVSS'),
|
||||||
|
('medium', b'kVSM'),
|
||||||
|
('large', b'kVSL'),
|
||||||
|
('library', b'kLib'),
|
||||||
|
('iPod', b'kPod'),
|
||||||
|
('audio_CD', b'kACD'),
|
||||||
|
('MP3_CD', b'kMCD'),
|
||||||
|
('device', b'kDev'),
|
||||||
|
('radio_tuner', b'kTun'),
|
||||||
|
('shared_library', b'kShd'),
|
||||||
|
('unknown', b'kUnk'),
|
||||||
|
('albums', b'kSrL'),
|
||||||
|
('artists', b'kSrR'),
|
||||||
|
('composers', b'kSrC'),
|
||||||
|
('displayed', b'kSrV'),
|
||||||
|
('songs', b'kSrS'),
|
||||||
|
('none', b'kNon'),
|
||||||
|
('Books', b'kSpA'),
|
||||||
|
('folder', b'kSpF'),
|
||||||
|
('Genius', b'kSpG'),
|
||||||
|
('iTunes_U', b'kSpU'),
|
||||||
|
('Library', b'kSpL'),
|
||||||
|
('Movies', b'kSpI'),
|
||||||
|
('Music', b'kSpZ'),
|
||||||
|
('Party_Shuffle', b'kSpS'),
|
||||||
|
('Podcasts', b'kSpP'),
|
||||||
|
('Purchased_Music', b'kSpM'),
|
||||||
|
('TV_Shows', b'kSpT'),
|
||||||
|
('movie', b'kVdM'),
|
||||||
|
('music_video', b'kVdV'),
|
||||||
|
('TV_show', b'kVdT'),
|
||||||
|
('user', b'kRtU'),
|
||||||
|
('computed', b'kRtC')]
|
||||||
|
|
||||||
|
properties = \
|
||||||
|
[('copies', b'lwcp'),
|
||||||
|
('collating', b'lwcl'),
|
||||||
|
('starting_page', b'lwfp'),
|
||||||
|
('ending_page', b'lwlp'),
|
||||||
|
('pages_across', b'lwla'),
|
||||||
|
('pages_down', b'lwld'),
|
||||||
|
('error_handling', b'lweh'),
|
||||||
|
('requested_print_time', b'lwqt'),
|
||||||
|
('printer_features', b'lwpf'),
|
||||||
|
('fax_number', b'faxn'),
|
||||||
|
('target_printer', b'trpr'),
|
||||||
|
('current_encoder', b'pEnc'),
|
||||||
|
('current_EQ_preset', b'pEQP'),
|
||||||
|
('current_playlist', b'pPla'),
|
||||||
|
('current_stream_title', b'pStT'),
|
||||||
|
('current_stream_URL', b'pStU'),
|
||||||
|
('current_track', b'pTrk'),
|
||||||
|
('current_visual', b'pVis'),
|
||||||
|
('EQ_enabled', b'pEQ '),
|
||||||
|
('fixed_indexing', b'pFix'),
|
||||||
|
('frontmost', b'pisf'),
|
||||||
|
('full_screen', b'pFSc'),
|
||||||
|
('name', b'pnam'),
|
||||||
|
('mute', b'pMut'),
|
||||||
|
('player_position', b'pPos'),
|
||||||
|
('player_state', b'pPlS'),
|
||||||
|
('selection', b'sele'),
|
||||||
|
('sound_volume', b'pVol'),
|
||||||
|
('version', b'vers'),
|
||||||
|
('visuals_enabled', b'pVsE'),
|
||||||
|
('visual_size', b'pVSz'),
|
||||||
|
('data', b'pPCT'),
|
||||||
|
('description', b'pDes'),
|
||||||
|
('downloaded', b'pDlA'),
|
||||||
|
('format', b'pFmt'),
|
||||||
|
('kind', b'pKnd'),
|
||||||
|
('raw_data', b'pRaw'),
|
||||||
|
('artist', b'pArt'),
|
||||||
|
('compilation', b'pAnt'),
|
||||||
|
('composer', b'pCmp'),
|
||||||
|
('disc_count', b'pDsC'),
|
||||||
|
('disc_number', b'pDsN'),
|
||||||
|
('genre', b'pGen'),
|
||||||
|
('year', b'pYr '),
|
||||||
|
('location', b'pLoc'),
|
||||||
|
('minimized', b'pMin'),
|
||||||
|
('view', b'pPly'),
|
||||||
|
('band_1', b'pEQ1'),
|
||||||
|
('band_2', b'pEQ2'),
|
||||||
|
('band_3', b'pEQ3'),
|
||||||
|
('band_4', b'pEQ4'),
|
||||||
|
('band_5', b'pEQ5'),
|
||||||
|
('band_6', b'pEQ6'),
|
||||||
|
('band_7', b'pEQ7'),
|
||||||
|
('band_8', b'pEQ8'),
|
||||||
|
('band_9', b'pEQ9'),
|
||||||
|
('band_10', b'pEQ0'),
|
||||||
|
('modifiable', b'pMod'),
|
||||||
|
('preamp', b'pEQA'),
|
||||||
|
('update_tracks', b'pUTC'),
|
||||||
|
('container', b'ctnr'),
|
||||||
|
('id', b'ID '),
|
||||||
|
('index', b'pidx'),
|
||||||
|
('persistent_ID', b'pPIS'),
|
||||||
|
('duration', b'pDur'),
|
||||||
|
('parent', b'pPlP'),
|
||||||
|
('shuffle', b'pShf'),
|
||||||
|
('size', b'pSiz'),
|
||||||
|
('song_repeat', b'pRpt'),
|
||||||
|
('special_kind', b'pSpK'),
|
||||||
|
('time', b'pTim'),
|
||||||
|
('visible', b'pvis'),
|
||||||
|
('capacity', b'capa'),
|
||||||
|
('free_space', b'frsp'),
|
||||||
|
('album', b'pAlb'),
|
||||||
|
('album_artist', b'pAlA'),
|
||||||
|
('album_rating', b'pAlR'),
|
||||||
|
('album_rating_kind', b'pARk'),
|
||||||
|
('bit_rate', b'pBRt'),
|
||||||
|
('bookmark', b'pBkt'),
|
||||||
|
('bookmarkable', b'pBkm'),
|
||||||
|
('bpm', b'pBPM'),
|
||||||
|
('category', b'pCat'),
|
||||||
|
('comment', b'pCmt'),
|
||||||
|
('database_ID', b'pDID'),
|
||||||
|
('date_added', b'pAdd'),
|
||||||
|
('enabled', b'enbl'),
|
||||||
|
('episode_ID', b'pEpD'),
|
||||||
|
('episode_number', b'pEpN'),
|
||||||
|
('EQ', b'pEQp'),
|
||||||
|
('finish', b'pStp'),
|
||||||
|
('gapless', b'pGpl'),
|
||||||
|
('grouping', b'pGrp'),
|
||||||
|
('long_description', b'pLds'),
|
||||||
|
('lyrics', b'pLyr'),
|
||||||
|
('modification_date', b'asmo'),
|
||||||
|
('played_count', b'pPlC'),
|
||||||
|
('played_date', b'pPlD'),
|
||||||
|
('podcast', b'pTPc'),
|
||||||
|
('rating', b'pRte'),
|
||||||
|
('rating_kind', b'pRtk'),
|
||||||
|
('release_date', b'pRlD'),
|
||||||
|
('sample_rate', b'pSRt'),
|
||||||
|
('season_number', b'pSeN'),
|
||||||
|
('shufflable', b'pSfa'),
|
||||||
|
('skipped_count', b'pSkC'),
|
||||||
|
('skipped_date', b'pSkD'),
|
||||||
|
('show', b'pShw'),
|
||||||
|
('sort_album', b'pSAl'),
|
||||||
|
('sort_artist', b'pSAr'),
|
||||||
|
('sort_album_artist', b'pSAA'),
|
||||||
|
('sort_name', b'pSNm'),
|
||||||
|
('sort_composer', b'pSCm'),
|
||||||
|
('sort_show', b'pSSN'),
|
||||||
|
('start', b'pStr'),
|
||||||
|
('track_count', b'pTrC'),
|
||||||
|
('track_number', b'pTrN'),
|
||||||
|
('unplayed', b'pUnp'),
|
||||||
|
('video_kind', b'pVdK'),
|
||||||
|
('volume_adjustment', b'pAdj'),
|
||||||
|
('address', b'pURL'),
|
||||||
|
('shared', b'pShr'),
|
||||||
|
('smart', b'pSmt'),
|
||||||
|
('bounds', b'pbnd'),
|
||||||
|
('closeable', b'hclb'),
|
||||||
|
('collapseable', b'pWSh'),
|
||||||
|
('collapsed', b'wshd'),
|
||||||
|
('position', b'ppos'),
|
||||||
|
('resizable', b'prsz'),
|
||||||
|
('zoomable', b'iszm'),
|
||||||
|
('zoomed', b'pzum')]
|
||||||
|
|
||||||
|
elements = \
|
||||||
|
[('artworks', b'cArt'),
|
||||||
|
('audio_CD_playlists', b'cCDP'),
|
||||||
|
('audio_CD_tracks', b'cCDT'),
|
||||||
|
('browser_windows', b'cBrW'),
|
||||||
|
('device_playlists', b'cDvP'),
|
||||||
|
('device_tracks', b'cDvT'),
|
||||||
|
('encoders', b'cEnc'),
|
||||||
|
('EQ_presets', b'cEQP'),
|
||||||
|
('EQ_windows', b'cEQW'),
|
||||||
|
('file_tracks', b'cFlT'),
|
||||||
|
('folder_playlists', b'cFoP'),
|
||||||
|
('items', b'cobj'),
|
||||||
|
('library_playlists', b'cLiP'),
|
||||||
|
('playlists', b'cPly'),
|
||||||
|
('playlist_windows', b'cPlW'),
|
||||||
|
('radio_tuner_playlists', b'cRTP'),
|
||||||
|
('shared_tracks', b'cShT'),
|
||||||
|
('sources', b'cSrc'),
|
||||||
|
('tracks', b'cTrk'),
|
||||||
|
('URL_tracks', b'cURT'),
|
||||||
|
('user_playlists', b'cUsP'),
|
||||||
|
('visuals', b'cVis'),
|
||||||
|
('windows', b'cwin'),
|
||||||
|
('application', b'capp'),
|
||||||
|
('print_settings', b'pset')]
|
||||||
|
|
||||||
|
commands = \
|
||||||
|
[('set', b'coresetd', [('to', b'data')]),
|
||||||
|
('exists', b'coredoex', []),
|
||||||
|
('move', b'coremove', [('to', b'insh')]),
|
||||||
|
('subscribe', b'hookpSub', []),
|
||||||
|
('playpause', b'hookPlPs', []),
|
||||||
|
('download', b'hookDwnl', []),
|
||||||
|
('close', b'coreclos', []),
|
||||||
|
('open', b'aevtodoc', []),
|
||||||
|
('open_location', b'GURLGURL', []),
|
||||||
|
('quit', b'aevtquit', []),
|
||||||
|
('pause', b'hookPaus', []),
|
||||||
|
('make',
|
||||||
|
'corecrel',
|
||||||
|
[('new', b'kocl'), ('at', b'insh'), ('with_properties', b'prdt')]),
|
||||||
|
('duplicate', b'coreclon', [('to', b'insh')]),
|
||||||
|
('print_',
|
||||||
|
'aevtpdoc',
|
||||||
|
[('print_dialog', b'pdlg'),
|
||||||
|
('with_properties', b'prdt'),
|
||||||
|
('kind', b'pKnd'),
|
||||||
|
('theme', b'pThm')]),
|
||||||
|
('add', b'hookAdd ', [('to', b'insh')]),
|
||||||
|
('rewind', b'hookRwnd', []),
|
||||||
|
('play', b'hookPlay', [('once', b'POne')]),
|
||||||
|
('run', b'aevtoapp', []),
|
||||||
|
('resume', b'hookResu', []),
|
||||||
|
('updatePodcast', b'hookUpd1', []),
|
||||||
|
('next_track', b'hookNext', []),
|
||||||
|
('stop', b'hookStop', []),
|
||||||
|
('search', b'hookSrch', [('for_', b'pTrm'), ('only', b'pAre')]),
|
||||||
|
('updateAllPodcasts', b'hookUpdp', []),
|
||||||
|
('update', b'hookUpdt', []),
|
||||||
|
('previous_track', b'hookPrev', []),
|
||||||
|
('fast_forward', b'hookFast', []),
|
||||||
|
('count', b'corecnte', [('each', b'kocl')]),
|
||||||
|
('reveal', b'hookRevl', []),
|
||||||
|
('convert', b'hookConv', []),
|
||||||
|
('eject', b'hookEjct', []),
|
||||||
|
('back_track', b'hookBack', []),
|
||||||
|
('refresh', b'hookRfrs', []),
|
||||||
|
('delete', b'coredelo', [])]
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "../base/AppDelegate.h"
|
#import "AppDelegateBase.h"
|
||||||
#import "ResultWindow.h"
|
#import "ResultWindow.h"
|
||||||
#import "PyDupeGuru.h"
|
#import "PyDupeGuru.h"
|
||||||
|
|
||||||
@interface AppDelegate : AppDelegateBase {}
|
@interface AppDelegate : AppDelegateBase {}
|
||||||
- (IBAction)openWebsite:(id)sender;
|
- (void)removeDeadTracks;
|
||||||
- (IBAction)toggleDirectories:(id)sender;
|
|
||||||
|
|
||||||
- (PyDupeGuru *)py;
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,46 +1,36 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
#import "../../cocoalib/ProgressController.h"
|
#import "ProgressController.h"
|
||||||
#import "../../cocoalib/RegistrationInterface.h"
|
#import "Utils.h"
|
||||||
#import "../../cocoalib/Utils.h"
|
#import "ValueTransformers.h"
|
||||||
#import "../../cocoalib/ValueTransformers.h"
|
#import "Dialogs.h"
|
||||||
#import "../../cocoalib/Dialogs.h"
|
|
||||||
#import "DetailsPanel.h"
|
#import "DetailsPanel.h"
|
||||||
#import "DirectoryPanel.h"
|
#import "DirectoryPanel.h"
|
||||||
|
#import "ResultWindow.h"
|
||||||
#import "Consts.h"
|
#import "Consts.h"
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
+ (void)initialize
|
+ (NSDictionary *)defaultPreferences
|
||||||
{
|
{
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:[super defaultPreferences]];
|
||||||
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithCapacity:10];
|
|
||||||
[d setObject:i2n(3) forKey:@"scanType"];
|
[d setObject:i2n(3) forKey:@"scanType"];
|
||||||
[d setObject:i2n(80) forKey:@"minMatchPercentage"];
|
[d setObject:i2n(80) forKey:@"minMatchPercentage"];
|
||||||
[d setObject:i2n(1) forKey:@"recreatePathType"];
|
|
||||||
[d setObject:b2n(NO) forKey:@"wordWeighting"];
|
[d setObject:b2n(NO) forKey:@"wordWeighting"];
|
||||||
[d setObject:b2n(NO) forKey:@"matchSimilarWords"];
|
[d setObject:b2n(NO) forKey:@"matchSimilarWords"];
|
||||||
[d setObject:b2n(YES) forKey:@"mixFileKind"];
|
|
||||||
[d setObject:b2n(NO) forKey:@"useRegexpFilter"];
|
|
||||||
[d setObject:b2n(NO) forKey:@"removeEmptyFolders"];
|
|
||||||
[d setObject:b2n(NO) forKey:@"debug"];
|
|
||||||
[d setObject:b2n(NO) forKey:@"scanTagTrack"];
|
[d setObject:b2n(NO) forKey:@"scanTagTrack"];
|
||||||
[d setObject:b2n(YES) forKey:@"scanTagArtist"];
|
[d setObject:b2n(YES) forKey:@"scanTagArtist"];
|
||||||
[d setObject:b2n(YES) forKey:@"scanTagAlbum"];
|
[d setObject:b2n(YES) forKey:@"scanTagAlbum"];
|
||||||
[d setObject:b2n(YES) forKey:@"scanTagTitle"];
|
[d setObject:b2n(YES) forKey:@"scanTagTitle"];
|
||||||
[d setObject:b2n(NO) forKey:@"scanTagGenre"];
|
[d setObject:b2n(NO) forKey:@"scanTagGenre"];
|
||||||
[d setObject:b2n(NO) forKey:@"scanTagYear"];
|
[d setObject:b2n(NO) forKey:@"scanTagYear"];
|
||||||
[d setObject:[NSArray array] forKey:@"recentDirectories"];
|
return d;
|
||||||
[d setObject:[NSArray array] forKey:@"columnsOrder"];
|
|
||||||
[d setObject:[NSDictionary dictionary] forKey:@"columnsWidth"];
|
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] setInitialValues:d];
|
|
||||||
[ud registerDefaults:d];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
@@ -56,32 +46,23 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)openWebsite:(id)sender
|
- (NSString *)homepageURL
|
||||||
{
|
{
|
||||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.hardcoded.net/dupeguru_me"]];
|
return @"http://www.hardcoded.net/dupeguru_me/";
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleDirectories:(id)sender
|
- (ResultWindowBase *)createResultWindow
|
||||||
{
|
{
|
||||||
[[self directoryPanel] toggleVisible:sender];
|
return [[ResultWindow alloc] initWithParentApp:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (DirectoryPanel *)directoryPanel
|
- (DirectoryPanel *)createDirectoryPanel
|
||||||
{
|
{
|
||||||
if (!_directoryPanel)
|
return [[DirectoryPanelME alloc] initWithParentApp:self];
|
||||||
_directoryPanel = [[DirectoryPanelME alloc] initWithParentApp:self];
|
|
||||||
return _directoryPanel;
|
|
||||||
}
|
}
|
||||||
- (PyDupeGuru *)py { return (PyDupeGuru *)py; }
|
|
||||||
|
|
||||||
//Delegate
|
- (void)removeDeadTracks
|
||||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
|
||||||
{
|
{
|
||||||
NSMenu *actionsMenu = [[[NSApp mainMenu] itemWithTitle:@"Actions"] submenu];
|
[(ResultWindow *)[self resultWindow] removeDeadTracks];
|
||||||
// index 3 is just after "Export Results to XHTML"
|
|
||||||
NSMenuItem *mi = [actionsMenu insertItemWithTitle:@"Remove Dead Tracks in iTunes"
|
|
||||||
action:@selector(removeDeadTracks:) keyEquivalent:@"" atIndex:3];
|
|
||||||
[mi setTarget:result];
|
|
||||||
[super applicationDidFinishLaunching:aNotification];
|
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "../base/Consts.h"
|
#import "../base/Consts.h"
|
||||||
|
|||||||
13
cocoa/me/DetailsPanel.h
Normal file
13
cocoa/me/DetailsPanel.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import "DetailsPanelBase.h"
|
||||||
|
|
||||||
|
@interface DetailsPanel : DetailsPanelBase
|
||||||
|
@end
|
||||||
17
cocoa/me/DetailsPanel.m
Normal file
17
cocoa/me/DetailsPanel.m
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
|
which should be included with this package. The terms are also available at
|
||||||
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "DetailsPanel.h"
|
||||||
|
#import "DetailsPanel_UI.h"
|
||||||
|
|
||||||
|
@implementation DetailsPanel
|
||||||
|
- (NSWindow *)createWindow
|
||||||
|
{
|
||||||
|
return createDetailsPanel_UI(self);
|
||||||
|
}
|
||||||
|
@end
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "DirectoryPanel.h"
|
#import "DirectoryPanel.h"
|
||||||
|
|
||||||
@implementation DirectoryPanelME
|
@implementation DirectoryPanelME
|
||||||
- (IBAction)addiTunes:(id)sender
|
- (id)initWithParentApp:(id)aParentApp
|
||||||
{
|
{
|
||||||
[self addDirectory:[@"~/Music/iTunes/iTunes Music" stringByExpandingTildeInPath]];
|
self = [super initWithParentApp:aParentApp];
|
||||||
|
_alwaysShowPopUp = YES;
|
||||||
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)popupAddDirectoryMenu:(id)sender
|
- (void)fillPopUpMenu
|
||||||
{
|
{
|
||||||
|
[super fillPopUpMenu];
|
||||||
NSMenu *m = [addButtonPopUp menu];
|
NSMenu *m = [addButtonPopUp menu];
|
||||||
while ([m numberOfItems] > 0)
|
NSMenuItem *mi = [m insertItemWithTitle:NSLocalizedString(@"Add iTunes Library", @"") action:@selector(addiTunes:)
|
||||||
[m removeItemAtIndex:0];
|
keyEquivalent:@"" atIndex:1];
|
||||||
NSMenuItem *mi = [m addItemWithTitle:@"Add New Directory..." action:@selector(askForDirectory:) keyEquivalent:@""];
|
|
||||||
[mi setTarget:self];
|
[mi setTarget:self];
|
||||||
mi = [m addItemWithTitle:@"Add iTunes Directory" action:@selector(addiTunes:) keyEquivalent:@""];
|
}
|
||||||
[mi setTarget:self];
|
|
||||||
[m addItem:[NSMenuItem separatorItem]];
|
- (IBAction)addiTunes:(id)sender
|
||||||
[_recentDirectories fillMenu:m];
|
{
|
||||||
[addButtonPopUp selectItem: nil];
|
[self addDirectory:@"iTunes Library"];
|
||||||
[[addButtonPopUp cell] performClickWithFrame:[sender frame] inView:[sender superview]];
|
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>English</string>
|
<string>English</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
<string>dupeGuru</string>
|
||||||
<key>CFBundleHelpBookFolder</key>
|
<key>CFBundleHelpBookFolder</key>
|
||||||
<string>dupeguru_me_help</string>
|
<string>dupeguru_me_help</string>
|
||||||
<key>CFBundleHelpBookName</key>
|
<key>CFBundleHelpBookName</key>
|
||||||
@@ -13,23 +13,25 @@
|
|||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>dupeguru</string>
|
<string>dupeguru</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.hardcoded_software.dupeguru_me</string>
|
<string>com.hardcoded-software.dupeguru-me</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>${PRODUCT_NAME}</string>
|
<string>dupeGuru ME</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>hsft</string>
|
<string>hsft</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>{version}</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>5.7.2</string>
|
<string>{version}</string>
|
||||||
<key>NSMainNibFile</key>
|
<key>NSMainNibFile</key>
|
||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>© Hardcoded Software, 2010</string>
|
<string>© Hardcoded Software, 2012</string>
|
||||||
<key>SUFeedURL</key>
|
<key>SUFeedURL</key>
|
||||||
<string>http://www.hardcoded.net/updates/dupeguru_me.appcast</string>
|
<string>http://www.hardcoded.net/updates/dupeguru_me.appcast</string>
|
||||||
<key>SUPublicDSAKeyFile</key>
|
<key>SUPublicDSAKeyFile</key>
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "../base/PyDupeGuru.h"
|
|
||||||
|
|
||||||
@interface PyDupeGuru : PyDupeGuruBase
|
|
||||||
//Scanning options
|
|
||||||
- (void)setScanType:(NSNumber *)scan_type;
|
|
||||||
- (void)setMinWordCount:(NSNumber *)word_count;
|
|
||||||
- (void)setMinWordLength:(NSNumber *)word_length;
|
|
||||||
- (void)setWordWeighting:(NSNumber *)words_are_weighted;
|
|
||||||
- (void)setMatchSimilarWords:(NSNumber *)match_similar_words;
|
|
||||||
- (void)enable:(NSNumber *)enable scanForTag:(NSString *)tag;
|
|
||||||
- (void)scanDeadTracks;
|
|
||||||
- (void)removeDeadTracks;
|
|
||||||
- (NSInteger)deadTrackCount;
|
|
||||||
@end
|
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "../base/ResultWindow.h"
|
#import "../base/ResultWindow.h"
|
||||||
#import "DirectoryPanel.h"
|
|
||||||
|
|
||||||
@interface ResultWindow : ResultWindowBase {}
|
@interface ResultWindow : ResultWindowBase {}
|
||||||
- (IBAction)removeDeadTracks:(id)sender;
|
- (void)removeDeadTracks;
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,133 +1,77 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "ResultWindow.h"
|
#import "ResultWindow.h"
|
||||||
#import "../../cocoalib/Dialogs.h"
|
#import "Dialogs.h"
|
||||||
#import "../../cocoalib/ProgressController.h"
|
#import "Utils.h"
|
||||||
#import "../../cocoalib/RegistrationInterface.h"
|
#import "PyDupeGuru.h"
|
||||||
#import "../../cocoalib/Utils.h"
|
|
||||||
#import "AppDelegate.h"
|
|
||||||
#import "Consts.h"
|
#import "Consts.h"
|
||||||
|
#import "ProgressController.h"
|
||||||
|
|
||||||
@implementation ResultWindow
|
@implementation ResultWindow
|
||||||
/* Override */
|
/* Override */
|
||||||
- (void)awakeFromNib
|
- (void)setScanOptions
|
||||||
{
|
{
|
||||||
[super awakeFromNib];
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
[[self window] setTitle:@"dupeGuru Music Edition"];
|
[model setScanType:n2i([ud objectForKey:@"scanType"])];
|
||||||
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,7)];
|
[model enable:n2b([ud objectForKey:@"scanTagTrack"]) scanForTag:@"track"];
|
||||||
[deltaColumns removeIndex:6];
|
[model enable:n2b([ud objectForKey:@"scanTagArtist"]) scanForTag:@"artist"];
|
||||||
[outline setDeltaColumns:deltaColumns];
|
[model enable:n2b([ud objectForKey:@"scanTagAlbum"]) scanForTag:@"album"];
|
||||||
|
[model enable:n2b([ud objectForKey:@"scanTagTitle"]) scanForTag:@"title"];
|
||||||
|
[model enable:n2b([ud objectForKey:@"scanTagGenre"]) scanForTag:@"genre"];
|
||||||
|
[model enable:n2b([ud objectForKey:@"scanTagYear"]) scanForTag:@"year"];
|
||||||
|
[model setMinMatchPercentage:n2i([ud objectForKey:@"minMatchPercentage"])];
|
||||||
|
[model setWordWeighting:n2b([ud objectForKey:@"wordWeighting"])];
|
||||||
|
[model setMixFileKind:n2b([ud objectForKey:@"mixFileKind"])];
|
||||||
|
[model setIgnoreHardlinkMatches:n2b([ud objectForKey:@"ignoreHardlinkMatches"])];
|
||||||
|
[model setMatchSimilarWords:n2b([ud objectForKey:@"matchSimilarWords"])];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)initResultColumns
|
||||||
|
{
|
||||||
|
HSColumnDef defs[] = {
|
||||||
|
{@"marked", 26, 26, 26, YES, [NSButtonCell class]},
|
||||||
|
{@"name", 235, 16, 0, YES, nil},
|
||||||
|
{@"folder_path", 120, 16, 0, YES, nil},
|
||||||
|
{@"size", 63, 16, 0, YES, nil},
|
||||||
|
{@"duration", 50, 16, 0, YES, nil},
|
||||||
|
{@"bitrate", 50, 16, 0, YES, nil},
|
||||||
|
{@"samplerate", 60, 16, 0, YES, nil},
|
||||||
|
{@"extension", 40, 16, 0, YES, nil},
|
||||||
|
{@"mtime", 120, 16, 0, YES, nil},
|
||||||
|
{@"title", 120, 16, 0, YES, nil},
|
||||||
|
{@"artist", 120, 16, 0, YES, nil},
|
||||||
|
{@"album", 120, 16, 0, YES, nil},
|
||||||
|
{@"genre", 80, 16, 0, YES, nil},
|
||||||
|
{@"year", 40, 16, 0, YES, nil},
|
||||||
|
{@"track", 40, 16, 0, YES, nil},
|
||||||
|
{@"comment", 120, 16, 0, YES, nil},
|
||||||
|
{@"percentage", 57, 16, 0, YES, nil},
|
||||||
|
{@"words", 120, 16, 0, YES, nil},
|
||||||
|
{@"dupe_count", 80, 16, 0, YES, nil},
|
||||||
|
nil
|
||||||
|
};
|
||||||
|
[[table columns] initializeColumns:defs];
|
||||||
|
NSTableColumn *c = [matches tableColumnWithIdentifier:@"marked"];
|
||||||
|
[[c dataCell] setButtonType:NSSwitchButton];
|
||||||
|
[[c dataCell] setControlSize:NSSmallControlSize];
|
||||||
|
c = [matches tableColumnWithIdentifier:@"size"];
|
||||||
|
[[c dataCell] setAlignment:NSRightTextAlignment];
|
||||||
|
c = [matches tableColumnWithIdentifier:@"duration"];
|
||||||
|
[[c dataCell] setAlignment:NSRightTextAlignment];
|
||||||
|
c = [matches tableColumnWithIdentifier:@"bitrate"];
|
||||||
|
[[c dataCell] setAlignment:NSRightTextAlignment];
|
||||||
|
[[table columns] restoreColumns];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
- (IBAction)removeDeadTracks:(id)sender
|
- (void)removeDeadTracks
|
||||||
{
|
{
|
||||||
[(PyDupeGuru *)py scanDeadTracks];
|
[model scanDeadTracks];
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)resetColumnsToDefault:(id)sender
|
|
||||||
{
|
|
||||||
NSMutableArray *columnsOrder = [NSMutableArray array];
|
|
||||||
[columnsOrder addObject:@"0"];
|
|
||||||
[columnsOrder addObject:@"2"];
|
|
||||||
[columnsOrder addObject:@"3"];
|
|
||||||
[columnsOrder addObject:@"4"];
|
|
||||||
[columnsOrder addObject:@"16"];
|
|
||||||
NSMutableDictionary *columnsWidth = [NSMutableDictionary dictionary];
|
|
||||||
[columnsWidth setObject:i2n(214) forKey:@"0"];
|
|
||||||
[columnsWidth setObject:i2n(63) forKey:@"2"];
|
|
||||||
[columnsWidth setObject:i2n(50) forKey:@"3"];
|
|
||||||
[columnsWidth setObject:i2n(50) forKey:@"4"];
|
|
||||||
[columnsWidth setObject:i2n(57) forKey:@"16"];
|
|
||||||
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)startDuplicateScan:(id)sender
|
|
||||||
{
|
|
||||||
if ([matches numberOfRows] > 0)
|
|
||||||
{
|
|
||||||
if ([Dialogs askYesNo:@"Are you sure you want to start a new duplicate scan?"] == NSAlertSecondButtonReturn) // NO
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
|
||||||
PyDupeGuru *_py = (PyDupeGuru *)py;
|
|
||||||
[_py setScanType:[ud objectForKey:@"scanType"]];
|
|
||||||
[_py enable:[ud objectForKey:@"scanTagTrack"] scanForTag:@"track"];
|
|
||||||
[_py enable:[ud objectForKey:@"scanTagArtist"] scanForTag:@"artist"];
|
|
||||||
[_py enable:[ud objectForKey:@"scanTagAlbum"] scanForTag:@"album"];
|
|
||||||
[_py enable:[ud objectForKey:@"scanTagTitle"] scanForTag:@"title"];
|
|
||||||
[_py enable:[ud objectForKey:@"scanTagGenre"] scanForTag:@"genre"];
|
|
||||||
[_py enable:[ud objectForKey:@"scanTagYear"] scanForTag:@"year"];
|
|
||||||
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
|
|
||||||
[_py setWordWeighting:[ud objectForKey:@"wordWeighting"]];
|
|
||||||
[_py setMixFileKind:[ud objectForKey:@"mixFileKind"]];
|
|
||||||
[_py setMatchSimilarWords:[ud objectForKey:@"matchSimilarWords"]];
|
|
||||||
NSInteger r = n2i([py doScan]);
|
|
||||||
if (r == 1)
|
|
||||||
[Dialogs showMessage:@"You cannot make a duplicate scan with only reference directories."];
|
|
||||||
if (r == 3)
|
|
||||||
{
|
|
||||||
[Dialogs showMessage:@"The selected directories contain no scannable file."];
|
|
||||||
[app toggleDirectories:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Public */
|
|
||||||
- (void)initResultColumns
|
|
||||||
{
|
|
||||||
NSTableColumn *refCol = [matches tableColumnWithIdentifier:@"0"];
|
|
||||||
_resultColumns = [[NSMutableArray alloc] init];
|
|
||||||
[_resultColumns addObject:[matches tableColumnWithIdentifier:@"0"]]; // File Name
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:1 title:@"Directory" width:120 refCol:refCol]];
|
|
||||||
NSTableColumn *sizeCol = [self getColumnForIdentifier:2 title:@"Size (MB)" width:63 refCol:refCol];
|
|
||||||
[[sizeCol dataCell] setAlignment:NSRightTextAlignment];
|
|
||||||
[_resultColumns addObject:sizeCol];
|
|
||||||
NSTableColumn *timeCol = [self getColumnForIdentifier:3 title:@"Time" width:50 refCol:refCol];
|
|
||||||
[[timeCol dataCell] setAlignment:NSRightTextAlignment];
|
|
||||||
[_resultColumns addObject:timeCol];
|
|
||||||
NSTableColumn *brCol = [self getColumnForIdentifier:4 title:@"Bitrate" width:50 refCol:refCol];
|
|
||||||
[[brCol dataCell] setAlignment:NSRightTextAlignment];
|
|
||||||
[_resultColumns addObject:brCol];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Sample Rate" width:60 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Kind" width:40 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Creation" width:120 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Modification" width:120 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:9 title:@"Title" width:120 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:10 title:@"Artist" width:120 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:11 title:@"Album" width:120 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:12 title:@"Genre" width:80 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:13 title:@"Year" width:40 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:14 title:@"Track Number" width:40 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:15 title:@"Comment" width:120 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:16 title:@"Match %" width:57 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:17 title:@"Words Used" width:120 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:18 title:@"Dupe Count" width:80 refCol:refCol]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Notifications */
|
|
||||||
- (void)jobCompleted:(NSNotification *)aNotification
|
|
||||||
{
|
|
||||||
[super jobCompleted:aNotification];
|
|
||||||
id lastAction = [[ProgressController mainProgressController] jobId];
|
|
||||||
if ([lastAction isEqualTo:jobScanDeadTracks])
|
|
||||||
{
|
|
||||||
NSInteger deadTrackCount = [(PyDupeGuru *)py deadTrackCount];
|
|
||||||
if (deadTrackCount > 0)
|
|
||||||
{
|
|
||||||
NSString *msg = @"Your iTunes Library contains %d dead tracks ready to be removed. Continue?";
|
|
||||||
if ([Dialogs askYesNo:[NSString stringWithFormat:msg,deadTrackCount]] == NSAlertFirstButtonReturn)
|
|
||||||
[(PyDupeGuru *)py removeDeadTracks];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[Dialogs showMessage:@"You have no dead tracks in your iTunes Library"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,69 +1,17 @@
|
|||||||
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
# Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
#
|
#
|
||||||
# This software is licensed under the "HS" License as described in the "LICENSE" file,
|
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
|
||||||
from hsutil.cocoa import signature
|
from hscommon.trans import install_gettext_trans_under_cocoa
|
||||||
|
install_gettext_trans_under_cocoa()
|
||||||
|
|
||||||
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
|
from cocoa.inter import PySelectableList, PyColumns, PyTable
|
||||||
from core_me.app_cocoa import DupeGuruME
|
|
||||||
from core.scanner import (SCAN_TYPE_FILENAME, SCAN_TYPE_FIELDS, SCAN_TYPE_FIELDS_NO_ORDER,
|
|
||||||
SCAN_TYPE_TAG, SCAN_TYPE_CONTENT, SCAN_TYPE_CONTENT_AUDIO)
|
|
||||||
|
|
||||||
# Fix py2app imports which chokes on relative imports and other stuff
|
from inter.all import *
|
||||||
from core_me import app_cocoa, data, fs, scanner
|
from inter.app_me import PyDupeGuru
|
||||||
from hsmedia import aiff, flac, genres, id3v1, id3v2, mp4, mpeg, ogg, wma
|
|
||||||
from lxml import etree, _elementpath
|
|
||||||
import gzip
|
|
||||||
|
|
||||||
class PyDupeGuru(PyDupeGuruBase):
|
|
||||||
def init(self):
|
|
||||||
self = super(PyDupeGuru,self).init()
|
|
||||||
self.py = DupeGuruME()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def removeDeadTracks(self):
|
|
||||||
self.py.remove_dead_tracks()
|
|
||||||
|
|
||||||
def scanDeadTracks(self):
|
|
||||||
self.py.scan_dead_tracks()
|
|
||||||
|
|
||||||
#---Information
|
|
||||||
@signature('i@:')
|
|
||||||
def deadTrackCount(self):
|
|
||||||
return len(self.py.dead_tracks)
|
|
||||||
|
|
||||||
#---Properties
|
|
||||||
def setMinMatchPercentage_(self, percentage):
|
|
||||||
self.py.scanner.min_match_percentage = int(percentage)
|
|
||||||
|
|
||||||
def setScanType_(self, scan_type):
|
|
||||||
try:
|
|
||||||
self.py.scanner.scan_type = [
|
|
||||||
SCAN_TYPE_FILENAME,
|
|
||||||
SCAN_TYPE_FIELDS,
|
|
||||||
SCAN_TYPE_FIELDS_NO_ORDER,
|
|
||||||
SCAN_TYPE_TAG,
|
|
||||||
SCAN_TYPE_CONTENT,
|
|
||||||
SCAN_TYPE_CONTENT_AUDIO
|
|
||||||
][scan_type]
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def setWordWeighting_(self, words_are_weighted):
|
|
||||||
self.py.scanner.word_weighting = words_are_weighted
|
|
||||||
|
|
||||||
def setMatchSimilarWords_(self, match_similar_words):
|
|
||||||
self.py.scanner.match_similar_words = match_similar_words
|
|
||||||
|
|
||||||
def enable_scanForTag_(self, enable, scan_tag):
|
|
||||||
if enable:
|
|
||||||
self.py.scanner.scanned_tags.add(scan_tag)
|
|
||||||
else:
|
|
||||||
self.py.scanner.scanned_tags.discard(scan_tag)
|
|
||||||
|
|
||||||
#---Registration
|
|
||||||
def appName(self):
|
|
||||||
return "dupeGuru Music Edition"
|
|
||||||
|
|
||||||
|
# When built under virtualenv, the dependency collector misses this module, so we have to force it
|
||||||
|
# to see the module.
|
||||||
|
import distutils.sysconfig
|
||||||
@@ -1,584 +0,0 @@
|
|||||||
// !$*UTF8*$!
|
|
||||||
{
|
|
||||||
archiveVersion = 1;
|
|
||||||
classes = {
|
|
||||||
};
|
|
||||||
objectVersion = 44;
|
|
||||||
objects = {
|
|
||||||
|
|
||||||
/* Begin PBXAppleScriptBuildPhase section */
|
|
||||||
CE6B288A0AFB7FC900508D93 /* AppleScript */ = {
|
|
||||||
isa = PBXAppleScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
contextName = "";
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
isSharedContext = 0;
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXAppleScriptBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
|
||||||
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
|
|
||||||
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 */; };
|
|
||||||
CE0B3D6711243F83009A7A30 /* ResultOutline.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0B3D6611243F83009A7A30 /* ResultOutline.m */; };
|
|
||||||
CE1425890AFB718500BD5167 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
|
|
||||||
CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE1425880AFB718500BD5167 /* Sparkle.framework */; };
|
|
||||||
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9409914ACE003581CE /* AppDelegate.m */; };
|
|
||||||
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE381C9A09914ADF003581CE /* ResultWindow.m */; };
|
|
||||||
CE381D0509915304003581CE /* dg_cocoa.plugin in Resources */ = {isa = PBXBuildFile; fileRef = CE381CF509915304003581CE /* dg_cocoa.plugin */; };
|
|
||||||
CE3FBDD31094637800B72D77 /* DetailsPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3FBDD11094637800B72D77 /* DetailsPanel.xib */; };
|
|
||||||
CE3FBDD41094637800B72D77 /* DirectoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE3FBDD21094637800B72D77 /* DirectoryPanel.xib */; };
|
|
||||||
CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */; };
|
|
||||||
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */; };
|
|
||||||
CE4B59C91119919700C06C9E /* progress.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C61119919700C06C9E /* progress.xib */; };
|
|
||||||
CE4B59CA1119919700C06C9E /* registration.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE4B59C71119919700C06C9E /* registration.xib */; };
|
|
||||||
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE10FC6C12E00EC695D /* Dialogs.m */; };
|
|
||||||
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */; };
|
|
||||||
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DE70FC6C12E00EC695D /* ProgressController.m */; };
|
|
||||||
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */; };
|
|
||||||
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */; };
|
|
||||||
CE515DFA0FC6C12E00EC695D /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DF00FC6C12E00EC695D /* Utils.m */; };
|
|
||||||
CE515DFB0FC6C12E00EC695D /* ValueTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515DF20FC6C12E00EC695D /* ValueTransformers.m */; };
|
|
||||||
CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E160FC6C19300EC695D /* AppDelegate.m */; };
|
|
||||||
CE515E1E0FC6C19300EC695D /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E190FC6C19300EC695D /* DirectoryPanel.m */; };
|
|
||||||
CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CE515E1C0FC6C19300EC695D /* ResultWindow.m */; };
|
|
||||||
CE6032C00FE6784C007E33FF /* DetailsPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE6032BF0FE6784C007E33FF /* DetailsPanel.m */; };
|
|
||||||
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = CE68EE6609ABC48000971085 /* DirectoryPanel.m */; };
|
|
||||||
CE6E0E9F1054EB97008D9390 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = CE6E0E9E1054EB97008D9390 /* dsa_pub.pem */; };
|
|
||||||
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CE848A1809DD85810004CB44 /* Consts.h */; };
|
|
||||||
CE900AD2109B238600754048 /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD1109B238600754048 /* Preferences.xib */; };
|
|
||||||
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE900AD6109B2A9B00754048 /* MainMenu.xib */; };
|
|
||||||
CEDF07A3112493B200EE5BC0 /* StatsLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CEDF07A2112493B200EE5BC0 /* StatsLabel.m */; };
|
|
||||||
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */ = {isa = PBXBuildFile; fileRef = CEEB135109C837A2004D2330 /* dupeguru.icns */; };
|
|
||||||
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC294509C89E3D00D9F998 /* folder32.png */; };
|
|
||||||
CEFC295509C89FF200D9F998 /* details32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295309C89FF200D9F998 /* details32.png */; };
|
|
||||||
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */ = {isa = PBXBuildFile; fileRef = CEFC295409C89FF200D9F998 /* preferences32.png */; };
|
|
||||||
/* End PBXBuildFile section */
|
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
|
||||||
CECC02B709A36E8200CC0A94 /* CopyFiles */ = {
|
|
||||||
isa = PBXCopyFilesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
dstPath = "";
|
|
||||||
dstSubfolderSpec = 10;
|
|
||||||
files = (
|
|
||||||
CE14259F0AFB719300BD5167 /* Sparkle.framework in CopyFiles */,
|
|
||||||
CE848A1909DD85810004CB44 /* Consts.h in CopyFiles */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
|
||||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
|
|
||||||
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
|
|
||||||
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.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; };
|
|
||||||
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>"; };
|
|
||||||
CE0B3D6411243F83009A7A30 /* PyResultTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyResultTree.h; path = ../base/PyResultTree.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE0B3D6511243F83009A7A30 /* ResultOutline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultOutline.h; path = ../base/ResultOutline.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE0B3D6611243F83009A7A30 /* ResultOutline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultOutline.m; path = ../base/ResultOutline.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE1425880AFB718500BD5167 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = /Library/Frameworks/Sparkle.framework; sourceTree = "<absolute>"; };
|
|
||||||
CE381C9409914ACE003581CE /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE381C9509914ACE003581CE /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE381C9A09914ADF003581CE /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE381C9B09914ADF003581CE /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = ResultWindow.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE381CF509915304003581CE /* dg_cocoa.plugin */ = {isa = PBXFileReference; lastKnownFileType = folder; path = dg_cocoa.plugin; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE3FBDD11094637800B72D77 /* DetailsPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = DetailsPanel.xib; path = ../../base/xib/DetailsPanel.xib; sourceTree = "<group>"; };
|
|
||||||
CE3FBDD21094637800B72D77 /* DirectoryPanel.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = DirectoryPanel.xib; path = ../../base/xib/DirectoryPanel.xib; sourceTree = "<group>"; };
|
|
||||||
CE49DEF20FDFEB810098617B /* BRSingleLineFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BRSingleLineFormatter.h; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BRSingleLineFormatter.m; path = ../../cocoalib/brsinglelineformatter/BRSingleLineFormatter.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ErrorReportWindow.xib; sourceTree = "<group>"; };
|
|
||||||
CE4B59C61119919700C06C9E /* progress.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = progress.xib; sourceTree = "<group>"; };
|
|
||||||
CE4B59C71119919700C06C9E /* registration.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = registration.xib; sourceTree = "<group>"; };
|
|
||||||
CE515DE00FC6C12E00EC695D /* Dialogs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Dialogs.h; path = ../../cocoalib/Dialogs.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DE10FC6C12E00EC695D /* Dialogs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Dialogs.m; path = ../../cocoalib/Dialogs.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HSErrorReportWindow.h; path = ../../cocoalib/HSErrorReportWindow.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HSErrorReportWindow.m; path = ../../cocoalib/HSErrorReportWindow.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; };
|
|
||||||
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; };
|
|
||||||
CE515DE90FC6C12E00EC695D /* RecentDirectories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RecentDirectories.h; path = ../../cocoalib/RecentDirectories.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RecentDirectories.m; path = ../../cocoalib/RecentDirectories.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DEB0FC6C12E00EC695D /* RegistrationInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegistrationInterface.h; path = ../../cocoalib/RegistrationInterface.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RegistrationInterface.m; path = ../../cocoalib/RegistrationInterface.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DEF0FC6C12E00EC695D /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = ../../cocoalib/Utils.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DF00FC6C12E00EC695D /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Utils.m; path = ../../cocoalib/Utils.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DF10FC6C12E00EC695D /* ValueTransformers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ValueTransformers.h; path = ../../cocoalib/ValueTransformers.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515DF20FC6C12E00EC695D /* ValueTransformers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ValueTransformers.m; path = ../../cocoalib/ValueTransformers.m; 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; };
|
|
||||||
CE515E170FC6C19300EC695D /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = ../base/Consts.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515E180FC6C19300EC695D /* DirectoryPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DirectoryPanel.h; path = ../base/DirectoryPanel.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515E190FC6C19300EC695D /* DirectoryPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DirectoryPanel.m; path = ../base/DirectoryPanel.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDupeGuru.h; path = ../base/PyDupeGuru.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515E1B0FC6C19300EC695D /* ResultWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResultWindow.h; path = ../base/ResultWindow.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE515E1C0FC6C19300EC695D /* ResultWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ResultWindow.m; path = ../base/ResultWindow.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE6032BE0FE6784C007E33FF /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DetailsPanel.h; path = ../base/DetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE6032BF0FE6784C007E33FF /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DetailsPanel.m; path = ../base/DetailsPanel.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE68EE6509ABC48000971085 /* DirectoryPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DirectoryPanel.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE68EE6609ABC48000971085 /* DirectoryPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DirectoryPanel.m; sourceTree = SOURCE_ROOT; };
|
|
||||||
CE6E0E9E1054EB97008D9390 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dsa_pub.pem; path = ../base/dsa_pub.pem; sourceTree = "<group>"; };
|
|
||||||
CE848A1809DD85810004CB44 /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
|
|
||||||
CE900AD1109B238600754048 /* Preferences.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; };
|
|
||||||
CE900AD6109B2A9B00754048 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../../base/xib/MainMenu.xib; sourceTree = "<group>"; };
|
|
||||||
CECA899A09DB132E00A3D774 /* DetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = DetailsPanel.h; sourceTree = "<group>"; };
|
|
||||||
CECA899B09DB132E00A3D774 /* DetailsPanel.m */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.objc; path = DetailsPanel.m; sourceTree = "<group>"; };
|
|
||||||
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PyDetailsPanel.h; path = ../base/PyDetailsPanel.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
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>"; };
|
|
||||||
CEFC294509C89E3D00D9F998 /* folder32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = folder32.png; path = ../../images/folder32.png; sourceTree = SOURCE_ROOT; };
|
|
||||||
CEFC295309C89FF200D9F998 /* details32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = details32.png; path = ../../images/details32.png; sourceTree = SOURCE_ROOT; };
|
|
||||||
CEFC295409C89FF200D9F998 /* preferences32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = preferences32.png; path = ../../images/preferences32.png; sourceTree = SOURCE_ROOT; };
|
|
||||||
CEFF18A009A4D387005E6321 /* PyDupeGuru.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = PyDupeGuru.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
|
||||||
8D11072E0486CEB800E47090 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
|
|
||||||
CE1425890AFB718500BD5167 /* Sparkle.framework in Frameworks */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
080E96DDFE201D6D7F000001 /* Classes */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
CE381C9509914ACE003581CE /* AppDelegate.h */,
|
|
||||||
CE381C9409914ACE003581CE /* AppDelegate.m */,
|
|
||||||
CE848A1809DD85810004CB44 /* Consts.h */,
|
|
||||||
CECA899A09DB132E00A3D774 /* DetailsPanel.h */,
|
|
||||||
CECA899B09DB132E00A3D774 /* DetailsPanel.m */,
|
|
||||||
CE68EE6509ABC48000971085 /* DirectoryPanel.h */,
|
|
||||||
CE68EE6609ABC48000971085 /* DirectoryPanel.m */,
|
|
||||||
CEFF18A009A4D387005E6321 /* PyDupeGuru.h */,
|
|
||||||
CE381C9B09914ADF003581CE /* ResultWindow.h */,
|
|
||||||
CE381C9A09914ADF003581CE /* ResultWindow.m */,
|
|
||||||
);
|
|
||||||
name = Classes;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
CE1425880AFB718500BD5167 /* Sparkle.framework */,
|
|
||||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
|
|
||||||
);
|
|
||||||
name = "Linked Frameworks";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
29B97324FDCFA39411CA2CEA /* AppKit.framework */,
|
|
||||||
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
|
|
||||||
29B97325FDCFA39411CA2CEA /* Foundation.framework */,
|
|
||||||
);
|
|
||||||
name = "Other Frameworks";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
19C28FACFE9D520D11CA2CBB /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
8D1107320486CEB800E47090 /* dupeGuru ME.app */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
29B97314FDCFA39411CA2CEA /* dupeguru */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
080E96DDFE201D6D7F000001 /* Classes */,
|
|
||||||
CE515E140FC6C17900EC695D /* dgbase */,
|
|
||||||
CE515DDD0FC6C09400EC695D /* cocoalib */,
|
|
||||||
29B97315FDCFA39411CA2CEA /* Other Sources */,
|
|
||||||
29B97317FDCFA39411CA2CEA /* Resources */,
|
|
||||||
29B97323FDCFA39411CA2CEA /* Frameworks */,
|
|
||||||
19C28FACFE9D520D11CA2CBB /* Products */,
|
|
||||||
);
|
|
||||||
name = dupeguru;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
29B97316FDCFA39411CA2CEA /* main.m */,
|
|
||||||
);
|
|
||||||
name = "Other Sources";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
29B97317FDCFA39411CA2CEA /* Resources */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
CE3FBDD01094637800B72D77 /* xib */,
|
|
||||||
CE073F5409CAE1A3005C1D2F /* dupeguru_me_help */,
|
|
||||||
CE381CF509915304003581CE /* dg_cocoa.plugin */,
|
|
||||||
CEFC294309C89E0000D9F998 /* images */,
|
|
||||||
CEEB135109C837A2004D2330 /* dupeguru.icns */,
|
|
||||||
8D1107310486CEB800E47090 /* Info.plist */,
|
|
||||||
CE6E0E9E1054EB97008D9390 /* dsa_pub.pem */,
|
|
||||||
);
|
|
||||||
name = Resources;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
|
|
||||||
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
|
|
||||||
);
|
|
||||||
name = Frameworks;
|
|
||||||
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 */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
CE900AD6109B2A9B00754048 /* MainMenu.xib */,
|
|
||||||
CE3FBDD11094637800B72D77 /* DetailsPanel.xib */,
|
|
||||||
CE3FBDD21094637800B72D77 /* DirectoryPanel.xib */,
|
|
||||||
CE900AD1109B238600754048 /* Preferences.xib */,
|
|
||||||
);
|
|
||||||
path = xib;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
CE49DEF10FDFEB810098617B /* brsinglelineformatter */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
CE49DEF20FDFEB810098617B /* BRSingleLineFormatter.h */,
|
|
||||||
CE49DEF30FDFEB810098617B /* BRSingleLineFormatter.m */,
|
|
||||||
);
|
|
||||||
name = brsinglelineformatter;
|
|
||||||
path = ../../cocoalib/brsinglelineformatter;
|
|
||||||
sourceTree = SOURCE_ROOT;
|
|
||||||
};
|
|
||||||
CE4B59C41119919700C06C9E /* xib */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
CE4B59C51119919700C06C9E /* ErrorReportWindow.xib */,
|
|
||||||
CE4B59C61119919700C06C9E /* progress.xib */,
|
|
||||||
CE4B59C71119919700C06C9E /* registration.xib */,
|
|
||||||
);
|
|
||||||
name = xib;
|
|
||||||
path = ../../cocoalib/xib;
|
|
||||||
sourceTree = SOURCE_ROOT;
|
|
||||||
};
|
|
||||||
CE515DDD0FC6C09400EC695D /* cocoalib */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
CE003CB211242D00004B0AA7 /* controllers */,
|
|
||||||
CE003CBB11242D00004B0AA7 /* proxies */,
|
|
||||||
CE003CBF11242D00004B0AA7 /* views */,
|
|
||||||
CE4B59C41119919700C06C9E /* xib */,
|
|
||||||
CE49DEF10FDFEB810098617B /* brsinglelineformatter */,
|
|
||||||
CE515DE00FC6C12E00EC695D /* Dialogs.h */,
|
|
||||||
CE515DE10FC6C12E00EC695D /* Dialogs.m */,
|
|
||||||
CE515DE20FC6C12E00EC695D /* HSErrorReportWindow.h */,
|
|
||||||
CE515DE30FC6C12E00EC695D /* HSErrorReportWindow.m */,
|
|
||||||
CE003CB911242D00004B0AA7 /* NSEventAdditions.h */,
|
|
||||||
CE003CBA11242D00004B0AA7 /* NSEventAdditions.m */,
|
|
||||||
CE515DE60FC6C12E00EC695D /* ProgressController.h */,
|
|
||||||
CE515DE70FC6C12E00EC695D /* ProgressController.m */,
|
|
||||||
CE515DE80FC6C12E00EC695D /* PyApp.h */,
|
|
||||||
CE003CBE11242D00004B0AA7 /* PyRegistrable.h */,
|
|
||||||
CE515DE90FC6C12E00EC695D /* RecentDirectories.h */,
|
|
||||||
CE515DEA0FC6C12E00EC695D /* RecentDirectories.m */,
|
|
||||||
CE515DEB0FC6C12E00EC695D /* RegistrationInterface.h */,
|
|
||||||
CE515DEC0FC6C12E00EC695D /* RegistrationInterface.m */,
|
|
||||||
CE515DEF0FC6C12E00EC695D /* Utils.h */,
|
|
||||||
CE515DF00FC6C12E00EC695D /* Utils.m */,
|
|
||||||
CE515DF10FC6C12E00EC695D /* ValueTransformers.h */,
|
|
||||||
CE515DF20FC6C12E00EC695D /* ValueTransformers.m */,
|
|
||||||
);
|
|
||||||
name = cocoalib;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
CE515E140FC6C17900EC695D /* dgbase */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
CE003CCD11242D2C004B0AA7 /* DirectoryOutline.h */,
|
|
||||||
CE003CCE11242D2C004B0AA7 /* DirectoryOutline.m */,
|
|
||||||
CE003CCF11242D2C004B0AA7 /* PyDirectoryOutline.h */,
|
|
||||||
CE515E150FC6C19300EC695D /* AppDelegate.h */,
|
|
||||||
CE515E160FC6C19300EC695D /* AppDelegate.m */,
|
|
||||||
CE515E170FC6C19300EC695D /* Consts.h */,
|
|
||||||
CE6032BE0FE6784C007E33FF /* DetailsPanel.h */,
|
|
||||||
CE6032BF0FE6784C007E33FF /* DetailsPanel.m */,
|
|
||||||
CE515E180FC6C19300EC695D /* DirectoryPanel.h */,
|
|
||||||
CE515E190FC6C19300EC695D /* DirectoryPanel.m */,
|
|
||||||
CE515E1B0FC6C19300EC695D /* ResultWindow.h */,
|
|
||||||
CE515E1C0FC6C19300EC695D /* ResultWindow.m */,
|
|
||||||
CE0B3D6511243F83009A7A30 /* ResultOutline.h */,
|
|
||||||
CE0B3D6611243F83009A7A30 /* ResultOutline.m */,
|
|
||||||
CEDF07A1112493B200EE5BC0 /* StatsLabel.h */,
|
|
||||||
CEDF07A2112493B200EE5BC0 /* StatsLabel.m */,
|
|
||||||
CE515E1A0FC6C19300EC695D /* PyDupeGuru.h */,
|
|
||||||
CED0A591111C9FD10020AD7D /* PyDetailsPanel.h */,
|
|
||||||
CE0B3D6411243F83009A7A30 /* PyResultTree.h */,
|
|
||||||
CEDF07A0112493B200EE5BC0 /* PyStatsLabel.h */,
|
|
||||||
);
|
|
||||||
name = dgbase;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
CEFC294309C89E0000D9F998 /* images */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
CEFC295309C89FF200D9F998 /* details32.png */,
|
|
||||||
CEFC295409C89FF200D9F998 /* preferences32.png */,
|
|
||||||
CEFC294509C89E3D00D9F998 /* folder32.png */,
|
|
||||||
);
|
|
||||||
name = images;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
|
||||||
8D1107260486CEB800E47090 /* dupeguru */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "dupeguru" */;
|
|
||||||
buildPhases = (
|
|
||||||
8D1107290486CEB800E47090 /* Resources */,
|
|
||||||
8D11072C0486CEB800E47090 /* Sources */,
|
|
||||||
8D11072E0486CEB800E47090 /* Frameworks */,
|
|
||||||
CECC02B709A36E8200CC0A94 /* CopyFiles */,
|
|
||||||
CE6B288A0AFB7FC900508D93 /* AppleScript */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = dupeguru;
|
|
||||||
productInstallPath = "$(HOME)/Applications";
|
|
||||||
productName = dupeguru;
|
|
||||||
productReference = 8D1107320486CEB800E47090 /* dupeGuru ME.app */;
|
|
||||||
productType = "com.apple.product-type.application";
|
|
||||||
};
|
|
||||||
/* End PBXNativeTarget section */
|
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
|
||||||
29B97313FDCFA39411CA2CEA /* Project object */ = {
|
|
||||||
isa = PBXProject;
|
|
||||||
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */;
|
|
||||||
compatibilityVersion = "Xcode 3.0";
|
|
||||||
hasScannedForEncodings = 1;
|
|
||||||
mainGroup = 29B97314FDCFA39411CA2CEA /* dupeguru */;
|
|
||||||
projectDirPath = "";
|
|
||||||
projectRoot = "";
|
|
||||||
targets = (
|
|
||||||
8D1107260486CEB800E47090 /* dupeguru */,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/* End PBXProject section */
|
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
|
||||||
8D1107290486CEB800E47090 /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
CE381D0509915304003581CE /* dg_cocoa.plugin in Resources */,
|
|
||||||
CE073F6309CAE1A3005C1D2F /* dupeguru_me_help in Resources */,
|
|
||||||
CEEB135209C837A2004D2330 /* dupeguru.icns in Resources */,
|
|
||||||
CEFC294609C89E3D00D9F998 /* folder32.png in Resources */,
|
|
||||||
CEFC295509C89FF200D9F998 /* details32.png in Resources */,
|
|
||||||
CEFC295609C89FF200D9F998 /* preferences32.png in Resources */,
|
|
||||||
CE6E0E9F1054EB97008D9390 /* dsa_pub.pem in Resources */,
|
|
||||||
CE3FBDD31094637800B72D77 /* DetailsPanel.xib in Resources */,
|
|
||||||
CE3FBDD41094637800B72D77 /* DirectoryPanel.xib in Resources */,
|
|
||||||
CE900AD2109B238600754048 /* Preferences.xib in Resources */,
|
|
||||||
CE900AD7109B2A9B00754048 /* MainMenu.xib in Resources */,
|
|
||||||
CE4B59C81119919700C06C9E /* ErrorReportWindow.xib in Resources */,
|
|
||||||
CE4B59C91119919700C06C9E /* progress.xib in Resources */,
|
|
||||||
CE4B59CA1119919700C06C9E /* registration.xib in Resources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXResourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
|
||||||
8D11072C0486CEB800E47090 /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
8D11072D0486CEB800E47090 /* main.m in Sources */,
|
|
||||||
CE381C9609914ACE003581CE /* AppDelegate.m in Sources */,
|
|
||||||
CE381C9C09914ADF003581CE /* ResultWindow.m in Sources */,
|
|
||||||
CE68EE6809ABC48000971085 /* DirectoryPanel.m in Sources */,
|
|
||||||
CE515DF30FC6C12E00EC695D /* Dialogs.m in Sources */,
|
|
||||||
CE515DF40FC6C12E00EC695D /* HSErrorReportWindow.m in Sources */,
|
|
||||||
CE515DF60FC6C12E00EC695D /* ProgressController.m in Sources */,
|
|
||||||
CE515DF70FC6C12E00EC695D /* RecentDirectories.m in Sources */,
|
|
||||||
CE515DF80FC6C12E00EC695D /* RegistrationInterface.m in Sources */,
|
|
||||||
CE515DFA0FC6C12E00EC695D /* Utils.m in Sources */,
|
|
||||||
CE515DFB0FC6C12E00EC695D /* ValueTransformers.m in Sources */,
|
|
||||||
CE515E1D0FC6C19300EC695D /* AppDelegate.m in Sources */,
|
|
||||||
CE515E1E0FC6C19300EC695D /* DirectoryPanel.m in Sources */,
|
|
||||||
CE515E1F0FC6C19300EC695D /* ResultWindow.m in Sources */,
|
|
||||||
CE49DEF60FDFEB810098617B /* BRSingleLineFormatter.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;
|
|
||||||
};
|
|
||||||
/* End PBXSourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
|
||||||
C01FCF4C08A954540054247B /* release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
|
||||||
INFOPLIST_FILE = Info.plist;
|
|
||||||
INSTALL_PATH = "$(HOME)/Applications";
|
|
||||||
PRODUCT_NAME = "dupeGuru ME";
|
|
||||||
WRAPPER_EXTENSION = app;
|
|
||||||
};
|
|
||||||
name = release;
|
|
||||||
};
|
|
||||||
C01FCF5008A954540054247B /* release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ARCHS = (
|
|
||||||
i386,
|
|
||||||
x86_64,
|
|
||||||
ppc,
|
|
||||||
);
|
|
||||||
GCC_C_LANGUAGE_STANDARD = c99;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.5;
|
|
||||||
SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
|
|
||||||
};
|
|
||||||
name = release;
|
|
||||||
};
|
|
||||||
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 */
|
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
|
||||||
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "dupeguru" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
C01FCF4C08A954540054247B /* release */,
|
|
||||||
CED596C6111AF56D00C0CF2B /* dev */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = release;
|
|
||||||
};
|
|
||||||
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "dupeguru" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
C01FCF5008A954540054247B /* release */,
|
|
||||||
CED596C5111AF56D00C0CF2B /* dev */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = release;
|
|
||||||
};
|
|
||||||
/* End XCConfigurationList section */
|
|
||||||
};
|
|
||||||
rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "Utils.h"
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
||||||
[Utils setPluginName:@"dg_cocoa"];
|
|
||||||
NSString *pluginPath = [[NSBundle mainBundle]
|
|
||||||
pathForResource:@"dg_cocoa"
|
|
||||||
ofType:@"plugin"];
|
|
||||||
NSBundle *pluginBundle = [NSBundle bundleWithPath:pluginPath];
|
|
||||||
[pluginBundle load];
|
|
||||||
[pool release];
|
|
||||||
return NSApplicationMain(argc, (const char **) argv);
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "../base/AppDelegate.h"
|
#import "AppDelegateBase.h"
|
||||||
#import "PyDupeGuru.h"
|
|
||||||
|
|
||||||
@interface AppDelegate : AppDelegateBase {}
|
@interface AppDelegate : AppDelegateBase {}
|
||||||
- (IBAction)openWebsite:(id)sender;
|
- (void)clearPictureCache;
|
||||||
- (IBAction)toggleDirectories:(id)sender;
|
|
||||||
|
|
||||||
- (PyDupeGuru *)py;
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,79 +1,61 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
#import "ProgressController.h"
|
#import "ProgressController.h"
|
||||||
#import "RegistrationInterface.h"
|
|
||||||
#import "Utils.h"
|
#import "Utils.h"
|
||||||
#import "ValueTransformers.h"
|
#import "ValueTransformers.h"
|
||||||
#import "Consts.h"
|
#import "Consts.h"
|
||||||
#import "DetailsPanel.h"
|
#import "DetailsPanel.h"
|
||||||
#import "DirectoryPanel.h"
|
#import "DirectoryPanel.h"
|
||||||
|
#import "ResultWindow.h"
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
+ (void)initialize
|
+ (NSDictionary *)defaultPreferences
|
||||||
{
|
{
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:[super defaultPreferences]];
|
||||||
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithCapacity:10];
|
[d setObject:i2n(0) forKey:@"scanType"];
|
||||||
[d setObject:[NSNumber numberWithInt:95] forKey:@"minMatchPercentage"];
|
[d setObject:i2n(95) forKey:@"minMatchPercentage"];
|
||||||
[d setObject:[NSNumber numberWithInt:1] forKey:@"recreatePathType"];
|
[d setObject:b2n(NO) forKey:@"matchScaled"];
|
||||||
[d setObject:[NSNumber numberWithBool:NO] forKey:@"matchScaled"];
|
return d;
|
||||||
[d setObject:[NSNumber numberWithBool:YES] forKey:@"mixFileKind"];
|
|
||||||
[d setObject:[NSNumber numberWithBool:NO] forKey:@"useRegexpFilter"];
|
|
||||||
[d setObject:[NSNumber numberWithBool:NO] forKey:@"removeEmptyFolders"];
|
|
||||||
[d setObject:[NSNumber numberWithBool:NO] forKey:@"debug"];
|
|
||||||
[d setObject:[NSArray array] forKey:@"recentDirectories"];
|
|
||||||
[d setObject:[NSArray array] forKey:@"columnsOrder"];
|
|
||||||
[d setObject:[NSDictionary dictionary] forKey:@"columnsWidth"];
|
|
||||||
[[NSUserDefaultsController sharedUserDefaultsController] setInitialValues:d];
|
|
||||||
[ud registerDefaults:d];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
_directoryPanel = nil;
|
NSMutableIndexSet *i = [NSMutableIndexSet indexSetWithIndex:0];
|
||||||
|
VTIsIntIn *vtScanTypeIsFuzzy = [[[VTIsIntIn alloc] initWithValues:i reverse:NO] autorelease];
|
||||||
|
[NSValueTransformer setValueTransformer:vtScanTypeIsFuzzy forName:@"vtScanTypeIsFuzzy"];
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (DetailsPanel *)detailsPanel
|
- (NSString *)homepageURL
|
||||||
{
|
{
|
||||||
if (!_detailsPanel)
|
return @"http://www.hardcoded.net/dupeguru_pe/";
|
||||||
_detailsPanel = [[DetailsPanelPE alloc] initWithPy:py];
|
|
||||||
return _detailsPanel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)openWebsite:(id)sender
|
- (ResultWindowBase *)createResultWindow
|
||||||
{
|
{
|
||||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.hardcoded.net/dupeguru_pe"]];
|
return [[ResultWindow alloc] initWithParentApp:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleDirectories:(id)sender
|
- (DirectoryPanel *)createDirectoryPanel
|
||||||
{
|
{
|
||||||
[[self directoryPanel] toggleVisible:sender];
|
return [[DirectoryPanelPE alloc] initWithParentApp:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (DirectoryPanel *)directoryPanel
|
- (DetailsPanel *)createDetailsPanel
|
||||||
{
|
{
|
||||||
if (!_directoryPanel)
|
return [[DetailsPanel alloc] initWithApp:model];
|
||||||
_directoryPanel = [[DirectoryPanelPE alloc] initWithParentApp:self];
|
|
||||||
return _directoryPanel;
|
|
||||||
}
|
}
|
||||||
- (PyDupeGuru *)py { return (PyDupeGuru *)py; }
|
|
||||||
|
|
||||||
//Delegate
|
- (void)clearPictureCache
|
||||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
|
||||||
{
|
{
|
||||||
NSMenu *actionsMenu = [[[NSApp mainMenu] itemWithTitle:@"Actions"] submenu];
|
[(ResultWindow *)[self resultWindow] clearPictureCache];
|
||||||
// index 2 is just after "Clear Ingore List"
|
|
||||||
NSMenuItem *mi = [actionsMenu insertItemWithTitle:@"Clear Picture Cache" action:@selector(clearPictureCache:) keyEquivalent:@"P" atIndex:2];
|
|
||||||
[mi setTarget:result];
|
|
||||||
[mi setKeyEquivalentModifierMask:NSCommandKeyMask|NSShiftKeyMask];
|
|
||||||
[super applicationDidFinishLaunching:aNotification];
|
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "../base/Consts.h"
|
#import "../base/Consts.h"
|
||||||
|
|||||||
@@ -1,24 +1,32 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "../base/DetailsPanel.h"
|
#import "DetailsPanelBase.h"
|
||||||
|
#import "PyDupeGuru.h"
|
||||||
|
|
||||||
@interface DetailsPanelPE : DetailsPanel
|
@interface DetailsPanel : DetailsPanelBase
|
||||||
{
|
{
|
||||||
IBOutlet NSImageView *dupeImage;
|
NSImageView *dupeImage;
|
||||||
IBOutlet NSProgressIndicator *dupeProgressIndicator;
|
NSProgressIndicator *dupeProgressIndicator;
|
||||||
IBOutlet NSImageView *refImage;
|
NSImageView *refImage;
|
||||||
IBOutlet NSProgressIndicator *refProgressIndicator;
|
NSProgressIndicator *refProgressIndicator;
|
||||||
|
|
||||||
PyApp *pyApp;
|
PyDupeGuru *pyApp;
|
||||||
BOOL _needsRefresh;
|
BOOL _needsRefresh;
|
||||||
NSString *_dupePath;
|
NSString *_dupePath;
|
||||||
NSString *_refPath;
|
NSString *_refPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property (readwrite, retain) NSImageView *dupeImage;
|
||||||
|
@property (readwrite, retain) NSProgressIndicator *dupeProgressIndicator;
|
||||||
|
@property (readwrite, retain) NSImageView *refImage;
|
||||||
|
@property (readwrite, retain) NSProgressIndicator *refProgressIndicator;
|
||||||
|
|
||||||
|
- (id)initWithApp:(PyDupeGuru *)aApp;
|
||||||
@end
|
@end
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "Utils.h"
|
#import "Utils.h"
|
||||||
@@ -12,17 +12,29 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
#import "PyDupeGuru.h"
|
#import "PyDupeGuru.h"
|
||||||
#import "DetailsPanel.h"
|
#import "DetailsPanel.h"
|
||||||
#import "Consts.h"
|
#import "Consts.h"
|
||||||
|
#import "DetailsPanel_UI.h"
|
||||||
|
|
||||||
@implementation DetailsPanelPE
|
@implementation DetailsPanel
|
||||||
- (id)initWithPy:(PyApp *)aPy
|
|
||||||
|
@synthesize dupeImage;
|
||||||
|
@synthesize dupeProgressIndicator;
|
||||||
|
@synthesize refImage;
|
||||||
|
@synthesize refProgressIndicator;
|
||||||
|
|
||||||
|
- (id)initWithApp:(PyDupeGuru *)aApp
|
||||||
{
|
{
|
||||||
self = [super initWithPy:aPy];
|
self = [super initWithPyRef:[aApp detailsPanel]];
|
||||||
pyApp = aPy;
|
pyApp = aApp;
|
||||||
_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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSWindow *)createWindow
|
||||||
|
{
|
||||||
|
return createDetailsPanel_UI(self);
|
||||||
|
}
|
||||||
|
|
||||||
- (void)loadImageAsync:(NSString *)imagePath
|
- (void)loadImageAsync:(NSString *)imagePath
|
||||||
{
|
{
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||||
@@ -42,12 +54,12 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
return;
|
return;
|
||||||
[detailsTable reloadData];
|
[detailsTable reloadData];
|
||||||
|
|
||||||
NSString *refPath = [(PyDupeGuru *)pyApp getSelectedDupeRefPath];
|
NSString *refPath = [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 *)pyApp getSelectedDupePath];
|
NSString *dupePath = [pyApp getSelectedDupePath];
|
||||||
if (_dupePath != nil)
|
if (_dupePath != nil)
|
||||||
[_dupePath autorelease];
|
[_dupePath autorelease];
|
||||||
_dupePath = [dupePath retain];
|
_dupePath = [dupePath retain];
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
@@ -13,4 +13,5 @@ http://www.hardcoded.net/licenses/hs_license
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
- (IBAction)addiPhoto:(id)sender;
|
- (IBAction)addiPhoto:(id)sender;
|
||||||
|
- (IBAction)addAperture:(id)sender;
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,51 +1,40 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "DirectoryPanel.h"
|
#import "DirectoryPanel.h"
|
||||||
#import "ProgressController.h"
|
|
||||||
|
|
||||||
static NSString* jobAddIPhoto = @"jobAddIPhoto";
|
|
||||||
|
|
||||||
@implementation DirectoryPanelPE
|
@implementation DirectoryPanelPE
|
||||||
- (id)initWithParentApp:(id)aParentApp
|
- (id)initWithParentApp:(id)aParentApp
|
||||||
{
|
{
|
||||||
self = [super initWithParentApp:aParentApp];
|
self = [super initWithParentApp:aParentApp];
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobCompleted:) name:JobCompletedNotification object:nil];
|
_alwaysShowPopUp = YES;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)fillPopUpMenu
|
||||||
|
{
|
||||||
|
[super fillPopUpMenu];
|
||||||
|
NSMenu *m = [addButtonPopUp menu];
|
||||||
|
NSMenuItem *mi = [m insertItemWithTitle:NSLocalizedString(@"Add iPhoto Library", @"") action:@selector(addiPhoto:)
|
||||||
|
keyEquivalent:@"" atIndex:1];
|
||||||
|
[mi setTarget:self];
|
||||||
|
mi = [m insertItemWithTitle:NSLocalizedString(@"Add Aperture Library", @"") action:@selector(addAperture:)
|
||||||
|
keyEquivalent:@"" atIndex:2];
|
||||||
|
[mi setTarget:self];
|
||||||
|
}
|
||||||
|
|
||||||
- (IBAction)addiPhoto:(id)sender
|
- (IBAction)addiPhoto:(id)sender
|
||||||
{
|
{
|
||||||
[[ProgressController mainProgressController] setJobDesc:@"Adding iPhoto Library..."];
|
|
||||||
[[ProgressController mainProgressController] setJobId:jobAddIPhoto];
|
|
||||||
[[ProgressController mainProgressController] showSheetForParent:[self window]];
|
|
||||||
[self addDirectory:@"iPhoto Library"];
|
[self addDirectory:@"iPhoto Library"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)popupAddDirectoryMenu:(id)sender
|
- (IBAction)addAperture:(id)sender
|
||||||
{
|
{
|
||||||
NSMenu *m = [addButtonPopUp menu];
|
[self addDirectory:@"Aperture Library"];
|
||||||
while ([m numberOfItems] > 0)
|
|
||||||
[m removeItemAtIndex:0];
|
|
||||||
NSMenuItem *mi = [m addItemWithTitle:@"Add New Directory..." action:@selector(askForDirectory:) keyEquivalent:@""];
|
|
||||||
[mi setTarget:self];
|
|
||||||
mi = [m addItemWithTitle:@"Add iPhoto Directory" action:@selector(addiPhoto:) keyEquivalent:@""];
|
|
||||||
[mi setTarget:self];
|
|
||||||
[m addItem:[NSMenuItem separatorItem]];
|
|
||||||
[_recentDirectories fillMenu:m];
|
|
||||||
[addButtonPopUp selectItem:nil];
|
|
||||||
[[addButtonPopUp cell] performClickWithFrame:[sender frame] inView:[sender superview]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)jobCompleted:(NSNotification *)aNotification
|
|
||||||
{
|
|
||||||
if ([[ProgressController mainProgressController] jobId] == jobAddIPhoto) {
|
|
||||||
[outlineView reloadData];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>English</string>
|
<string>English</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
<string>dupeGuru</string>
|
||||||
<key>CFBundleHelpBookFolder</key>
|
<key>CFBundleHelpBookFolder</key>
|
||||||
<string>dupeguru_pe_help</string>
|
<string>dupeguru_pe_help</string>
|
||||||
<key>CFBundleHelpBookName</key>
|
<key>CFBundleHelpBookName</key>
|
||||||
@@ -13,23 +13,25 @@
|
|||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>dupeguru</string>
|
<string>dupeguru</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.hardcoded_software.dupeguru_pe</string>
|
<string>com.hardcoded-software.dupeguru-pe</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>${PRODUCT_NAME}</string>
|
<string>dupeGuru PE</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>hsft</string>
|
<string>hsft</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>{version}</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.8.6</string>
|
<string>{version}</string>
|
||||||
<key>NSMainNibFile</key>
|
<key>NSMainNibFile</key>
|
||||||
<string>MainMenu</string>
|
<string>MainMenu</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>© Hardcoded Software, 2010</string>
|
<string>© Hardcoded Software, 2012</string>
|
||||||
<key>SUFeedURL</key>
|
<key>SUFeedURL</key>
|
||||||
<string>http://www.hardcoded.net/updates/dupeguru_pe.appcast</string>
|
<string>http://www.hardcoded.net/updates/dupeguru_pe.appcast</string>
|
||||||
<key>SUPublicDSAKeyFile</key>
|
<key>SUPublicDSAKeyFile</key>
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
|
||||||
which should be included with this package. The terms are also available at
|
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
#import "../base/PyDupeGuru.h"
|
|
||||||
|
|
||||||
@interface PyDupeGuru : PyDupeGuruBase
|
|
||||||
- (void)clearPictureCache;
|
|
||||||
- (NSString *)getSelectedDupePath;
|
|
||||||
- (NSString *)getSelectedDupeRefPath;
|
|
||||||
- (void)setMatchScaled:(NSNumber *)match_scaled;
|
|
||||||
@end
|
|
||||||
@@ -1,16 +1,14 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import "../base/ResultWindow.h"
|
#import "../base/ResultWindow.h"
|
||||||
|
|
||||||
@interface ResultWindow : ResultWindowBase {}
|
@interface ResultWindow : ResultWindowBase {}
|
||||||
- (IBAction)clearPictureCache:(id)sender;
|
- (void)clearPictureCache;
|
||||||
- (IBAction)startDuplicateScan:(id)sender;
|
|
||||||
- (IBAction)toggleDirectories:(id)sender;
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -1,100 +1,57 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
|
|
||||||
This software is licensed under the "HS" License as described in the "LICENSE" file,
|
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
which should be included with this package. The terms are also available at
|
which should be included with this package. The terms are also available at
|
||||||
http://www.hardcoded.net/licenses/hs_license
|
http://www.hardcoded.net/licenses/bsd_license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "ResultWindow.h"
|
#import "ResultWindow.h"
|
||||||
#import "Dialogs.h"
|
#import "Dialogs.h"
|
||||||
#import "ProgressController.h"
|
|
||||||
#import "RegistrationInterface.h"
|
|
||||||
#import "Utils.h"
|
#import "Utils.h"
|
||||||
#import "AppDelegate.h"
|
#import "PyDupeGuru.h"
|
||||||
#import "Consts.h"
|
|
||||||
|
|
||||||
@implementation ResultWindow
|
@implementation ResultWindow
|
||||||
/* Override */
|
/* Override */
|
||||||
- (void)awakeFromNib
|
- (void)initResultColumns
|
||||||
{
|
{
|
||||||
[super awakeFromNib];
|
HSColumnDef defs[] = {
|
||||||
[[self window] setTitle:@"dupeGuru Picture Edition"];
|
{@"marked", 26, 26, 26, YES, [NSButtonCell class]},
|
||||||
NSMutableIndexSet *deltaColumns = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(2,5)];
|
{@"name", 162, 16, 0, YES, nil},
|
||||||
[deltaColumns removeIndex:3];
|
{@"folder_path", 142, 16, 0, YES, nil},
|
||||||
[deltaColumns removeIndex:4];
|
{@"size", 63, 16, 0, YES, nil},
|
||||||
[outline setDeltaColumns:deltaColumns];
|
{@"extension", 40, 16, 0, YES, nil},
|
||||||
|
{@"dimensions", 73, 16, 0, YES, nil},
|
||||||
|
{@"mtime", 120, 16, 0, YES, nil},
|
||||||
|
{@"percentage", 58, 16, 0, YES, nil},
|
||||||
|
{@"dupe_count", 80, 16, 0, YES, nil},
|
||||||
|
nil
|
||||||
|
};
|
||||||
|
[[table columns] initializeColumns:defs];
|
||||||
|
NSTableColumn *c = [matches tableColumnWithIdentifier:@"marked"];
|
||||||
|
[[c dataCell] setButtonType:NSSwitchButton];
|
||||||
|
[[c dataCell] setControlSize:NSSmallControlSize];
|
||||||
|
c = [matches tableColumnWithIdentifier:@"size"];
|
||||||
|
[[c dataCell] setAlignment:NSRightTextAlignment];
|
||||||
|
[[table columns] restoreColumns];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setScanOptions
|
||||||
|
{
|
||||||
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
|
[model setScanType:n2i([ud objectForKey:@"scanType"])];
|
||||||
|
[model setMinMatchPercentage:n2i([ud objectForKey:@"minMatchPercentage"])];
|
||||||
|
[model setMixFileKind:n2b([ud objectForKey:@"mixFileKind"])];
|
||||||
|
[model setIgnoreHardlinkMatches:n2b([ud objectForKey:@"ignoreHardlinkMatches"])];
|
||||||
|
[model setMatchScaled:n2b([ud objectForKey:@"matchScaled"])];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
- (IBAction)clearPictureCache:(id)sender
|
- (void)clearPictureCache
|
||||||
{
|
{
|
||||||
if ([Dialogs askYesNo:@"Do you really want to remove all your cached picture analysis?"] == NSAlertSecondButtonReturn) // NO
|
NSString *msg = NSLocalizedString(@"Do you really want to remove all your cached picture analysis?", @"");
|
||||||
|
if ([Dialogs askYesNo:msg] == NSAlertSecondButtonReturn) // NO
|
||||||
return;
|
return;
|
||||||
[(PyDupeGuru *)py clearPictureCache];
|
[model clearPictureCache];
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)resetColumnsToDefault:(id)sender
|
|
||||||
{
|
|
||||||
NSMutableArray *columnsOrder = [NSMutableArray array];
|
|
||||||
[columnsOrder addObject:@"0"];
|
|
||||||
[columnsOrder addObject:@"1"];
|
|
||||||
[columnsOrder addObject:@"2"];
|
|
||||||
[columnsOrder addObject:@"4"];
|
|
||||||
[columnsOrder addObject:@"7"];
|
|
||||||
NSMutableDictionary *columnsWidth = [NSMutableDictionary dictionary];
|
|
||||||
[columnsWidth setObject:i2n(121) forKey:@"0"];
|
|
||||||
[columnsWidth setObject:i2n(120) forKey:@"1"];
|
|
||||||
[columnsWidth setObject:i2n(63) forKey:@"2"];
|
|
||||||
[columnsWidth setObject:i2n(73) forKey:@"4"];
|
|
||||||
[columnsWidth setObject:i2n(58) forKey:@"7"];
|
|
||||||
[self restoreColumnsPosition:columnsOrder widths:columnsWidth];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)startDuplicateScan:(id)sender
|
|
||||||
{
|
|
||||||
if ([matches numberOfRows] > 0)
|
|
||||||
{
|
|
||||||
if ([Dialogs askYesNo:@"Are you sure you want to start a new duplicate scan?"] == NSAlertSecondButtonReturn) // NO
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
|
||||||
PyDupeGuru *_py = (PyDupeGuru *)py;
|
|
||||||
[_py setMinMatchPercentage:[ud objectForKey:@"minMatchPercentage"]];
|
|
||||||
[_py setMixFileKind:[ud objectForKey:@"mixFileKind"]];
|
|
||||||
[_py setMatchScaled:[ud objectForKey:@"matchScaled"]];
|
|
||||||
int r = n2i([py doScan]);
|
|
||||||
if (r != 0)
|
|
||||||
[[ProgressController mainProgressController] hide];
|
|
||||||
if (r == 1)
|
|
||||||
[Dialogs showMessage:@"You cannot make a duplicate scan with only reference directories."];
|
|
||||||
if (r == 3)
|
|
||||||
{
|
|
||||||
[Dialogs showMessage:@"The selected directories contain no scannable file."];
|
|
||||||
[app toggleDirectories:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)toggleDirectories:(id)sender
|
|
||||||
{
|
|
||||||
[(AppDelegate *)app toggleDirectories:sender];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Public */
|
|
||||||
- (void)initResultColumns
|
|
||||||
{
|
|
||||||
NSTableColumn *refCol = [matches tableColumnWithIdentifier:@"0"];
|
|
||||||
_resultColumns = [[NSMutableArray alloc] init];
|
|
||||||
[_resultColumns addObject:[matches tableColumnWithIdentifier:@"0"]]; // File Name
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:1 title:@"Directory" width:120 refCol:refCol]];
|
|
||||||
NSTableColumn *sizeCol = [self getColumnForIdentifier:2 title:@"Size (KB)" width:63 refCol:refCol];
|
|
||||||
[[sizeCol dataCell] setAlignment:NSRightTextAlignment];
|
|
||||||
[_resultColumns addObject:sizeCol];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:3 title:@"Kind" width:40 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:4 title:@"Dimensions" width:80 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:5 title:@"Creation" width:120 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:6 title:@"Modification" width:120 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:7 title:@"Match %" width:58 refCol:refCol]];
|
|
||||||
[_resultColumns addObject:[self getColumnForIdentifier:8 title:@"Dupe Count" width:80 refCol:refCol]];
|
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
@@ -1,41 +1,17 @@
|
|||||||
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
|
# Copyright 2012 Hardcoded Software (http://www.hardcoded.net)
|
||||||
#
|
#
|
||||||
# This software is licensed under the "HS" License as described in the "LICENSE" file,
|
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||||
# which should be included with this package. The terms are also available at
|
# which should be included with this package. The terms are also available at
|
||||||
# http://www.hardcoded.net/licenses/hs_license
|
# http://www.hardcoded.net/licenses/bsd_license
|
||||||
|
|
||||||
from core.app_cocoa_inter import PyDupeGuruBase, PyDetailsPanel
|
from hscommon.trans import install_gettext_trans_under_cocoa
|
||||||
from core_pe import app_cocoa as app_pe_cocoa
|
install_gettext_trans_under_cocoa()
|
||||||
|
|
||||||
# Fix py2app imports which chokes on relative imports and other stuff
|
from cocoa.inter import PySelectableList, PyColumns, PyTable
|
||||||
from core_pe import block, cache, matchbase, data, _block_osx
|
|
||||||
from lxml import etree, _elementpath
|
|
||||||
import gzip
|
|
||||||
|
|
||||||
class PyDupeGuru(PyDupeGuruBase):
|
from inter.all import *
|
||||||
def init(self):
|
from inter.app_pe import PyDupeGuru
|
||||||
self = super(PyDupeGuru, self).init()
|
|
||||||
self.py = app_pe_cocoa.DupeGuruPE()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def clearPictureCache(self):
|
|
||||||
self.py.scanner.clear_picture_cache()
|
|
||||||
|
|
||||||
#---Information
|
|
||||||
def getSelectedDupePath(self):
|
|
||||||
return unicode(self.py.selected_dupe_path())
|
|
||||||
|
|
||||||
def getSelectedDupeRefPath(self):
|
|
||||||
return unicode(self.py.selected_dupe_ref_path())
|
|
||||||
|
|
||||||
#---Properties
|
|
||||||
def setMatchScaled_(self,match_scaled):
|
|
||||||
self.py.scanner.match_scaled = match_scaled
|
|
||||||
|
|
||||||
def setMinMatchPercentage_(self,percentage):
|
|
||||||
self.py.scanner.threshold = int(percentage)
|
|
||||||
|
|
||||||
#---Registration
|
|
||||||
def appName(self):
|
|
||||||
return "dupeGuru Picture Edition"
|
|
||||||
|
|
||||||
|
# When built under virtualenv, the dependency collector misses this module, so we have to force it
|
||||||
|
# to see the module.
|
||||||
|
import distutils.sysconfig
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user