mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-09 20:27:13 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0aa2e2b298 | ||
|
|
1f5ad49591 | ||
|
|
3e45c7895c | ||
|
|
29301f440e | ||
|
|
2f86c0d9d2 | ||
|
|
e7dbd5a5cc | ||
|
|
62c00bb7f5 | ||
|
|
12f0d2a867 | ||
|
|
5af66b60e1 | ||
|
|
028a4fe5b2 | ||
|
|
fa6e711c2f | ||
|
|
c620c7bf39 | ||
|
|
f3066594fe | ||
|
|
4db1e28677 | ||
|
|
d6cbc52790 | ||
|
|
0943065498 | ||
|
|
42cc3f309f | ||
|
|
6d7dc24775 | ||
|
|
8faa17ad33 | ||
|
|
3d5232b308 | ||
|
|
c2bab5b369 | ||
|
|
eee1dcef13 | ||
|
|
8254543da7 | ||
|
|
2028efabc3 | ||
|
|
011c033478 | ||
|
|
477e9f85be | ||
|
|
0d5ae63013 | ||
|
|
aa60a7b975 | ||
|
|
c071569f3c | ||
|
|
1d717ffe99 | ||
|
|
c53fff058b | ||
|
|
c836bd48e0 | ||
|
|
ec675a5662 | ||
|
|
02e72493ef | ||
|
|
dd2ad410b8 | ||
|
|
92fb183c22 | ||
|
|
46eca09d69 | ||
|
|
f7bc2b6d06 | ||
|
|
c87cb4bacd | ||
|
|
b6ea6530b7 | ||
|
|
af4e69afc9 | ||
|
|
a292936d55 | ||
|
|
a000687a42 | ||
|
|
03aaa89a61 | ||
|
|
be71d957e1 | ||
|
|
651970899d | ||
|
|
ae67b00c4d | ||
|
|
51848b6cce | ||
|
|
8e0b3a5df3 | ||
|
|
692c43a3cc | ||
|
|
29430df66d | ||
|
|
9954a75cdd | ||
|
|
6ee7964fa6 | ||
|
|
db6608571a | ||
|
|
76d5051833 | ||
|
|
9cdcbb1988 | ||
|
|
33882942e4 | ||
|
|
6c2c690504 | ||
|
|
c78f232e4a | ||
|
|
5f39046a52 | ||
|
|
fff2be49fc | ||
|
|
95a2b46ae1 | ||
|
|
754324d253 | ||
|
|
b350b89376 | ||
|
|
4445f752f3 | ||
|
|
9e92947b2c | ||
|
|
92d44b12bd | ||
|
|
36c673c660 | ||
|
|
db509cb72f | ||
|
|
c0af2af7e7 | ||
|
|
35b2b5263c | ||
|
|
fa3df44535 | ||
|
|
08dd7d6951 | ||
|
|
d7316918a3 | ||
|
|
044fc3af69 | ||
|
|
d1eb20e7b3 | ||
|
|
c27edb2475 | ||
|
|
58d4030133 | ||
|
|
e8b1063647 | ||
|
|
3caa703ced | ||
|
|
f7e016a98c | ||
|
|
156e9b8e54 | ||
|
|
7aa00a961a | ||
|
|
4678efff72 | ||
|
|
f3b4e7f752 | ||
|
|
e39fa44cdb | ||
|
|
3fb4d1e4c1 | ||
|
|
06b8636817 | ||
|
|
4b653ee367 | ||
|
|
d2cdd46466 | ||
|
|
2d46fb885d | ||
|
|
b3e0c4a5d5 | ||
|
|
ab6609282d | ||
|
|
6ff21692f1 | ||
|
|
f64cc4c655 | ||
|
|
e31af96ef9 | ||
|
|
a431b34c43 | ||
|
|
562bb8a878 | ||
|
|
719dbf02d4 | ||
|
|
e2327fcf94 | ||
|
|
5a0c8f4a8b | ||
|
|
031a380c25 | ||
|
|
23c4e584d4 | ||
|
|
452febd73e | ||
|
|
645741aa23 | ||
|
|
97b141b653 | ||
|
|
67ac778d4c | ||
|
|
e45ed19dc7 | ||
|
|
f79c2346a5 | ||
|
|
e964c60830 | ||
|
|
f8ffb4abbd | ||
|
|
37931f08b5 | ||
|
|
9b75e37e1f | ||
|
|
232f1d008d | ||
|
|
3947bd65f2 | ||
|
|
1d39299a7d | ||
|
|
da1389b0b4 | ||
|
|
818c7dac3d | ||
|
|
15b05304cc | ||
|
|
e33c7f6be4 | ||
|
|
d23bf7b927 | ||
|
|
de1ebe6319 | ||
|
|
3e9e0802f6 | ||
|
|
b8f9263d18 | ||
|
|
946cd381a0 | ||
|
|
0a49d09878 | ||
|
|
f936277a4d | ||
|
|
ac16c5663b | ||
|
|
83058b01d3 | ||
|
|
3c8619db5d | ||
|
|
14226ef69c | ||
|
|
910691ad4b | ||
|
|
115d29b6f8 | ||
|
|
05ef7c83a8 | ||
|
|
2311fb9a6d | ||
|
|
c1b8ec8da1 | ||
|
|
a1efd1d024 | ||
|
|
088b6eac35 | ||
|
|
ea1068c465 | ||
|
|
978545af94 | ||
|
|
86c10e687b | ||
|
|
24ab9f9fdf | ||
|
|
5d69bbc4ca | ||
|
|
3c3b7ff61c | ||
|
|
5a6e266c8d | ||
|
|
6f724e34f5 | ||
|
|
7cec421419 | ||
|
|
967f541659 | ||
|
|
6e78d903e0 | ||
|
|
03a243fd20 | ||
|
|
0b8d3744bb | ||
|
|
a0bb3b31a3 | ||
|
|
1f79402a0a | ||
|
|
f7db4058a8 | ||
|
|
4d9854695a | ||
|
|
e90133ebd0 | ||
|
|
d4818b5a71 | ||
|
|
68a15c5c7c | ||
|
|
42db55fabd | ||
|
|
e6c1dfc51e | ||
|
|
4f3146dc7b | ||
|
|
4372edb7c4 | ||
|
|
d105388d82 | ||
|
|
fd812f25a5 | ||
|
|
9f5904eaf2 | ||
|
|
d246b19750 | ||
|
|
128a3ac4d9 | ||
|
|
abd908949a | ||
|
|
ab083a3baf | ||
|
|
45ee3aa0a3 | ||
|
|
d587016cc3 | ||
|
|
8edee4e755 | ||
|
|
8aeaa6681a | ||
|
|
8a0a68248c | ||
|
|
251ba407be | ||
|
|
bd28d688aa | ||
|
|
6ab679a725 | ||
|
|
6b7d636b0d | ||
|
|
f72749fbd6 | ||
|
|
9a032a57d0 | ||
|
|
244ff0d846 | ||
|
|
c65726fb12 | ||
|
|
8ef7f07195 | ||
|
|
2af2fc31a1 | ||
|
|
9bf0edb5df | ||
|
|
53f4baa9ac | ||
|
|
63cc9c4b36 | ||
|
|
8b60ef1156 | ||
|
|
751a80afb7 | ||
|
|
f2b2852b76 | ||
|
|
f36d8224a6 | ||
|
|
c5c2a80af9 | ||
|
|
b9dfd2a795 | ||
|
|
b381fe6c27 | ||
|
|
003dcface0 | ||
|
|
0e05f1a8b7 | ||
|
|
5a340804fe | ||
|
|
65f6c35c88 | ||
|
|
cfee75b6e9 | ||
|
|
0463af26b3 | ||
|
|
ced3c2d778 | ||
|
|
7e4e6932a0 | ||
|
|
13940c97e7 | ||
|
|
91d28cbbdd | ||
|
|
adc2d2b513 | ||
|
|
c80116e90d | ||
|
|
f667c39ade | ||
|
|
ce0ddb2e1f | ||
|
|
3a716c08f8 | ||
|
|
d69f46f7ee | ||
|
|
3cda75986b | ||
|
|
ffea2de826 | ||
|
|
ac6ccc3257 | ||
|
|
29ed8f3116 | ||
|
|
3f2dd34ee5 | ||
|
|
df7c8bf508 | ||
|
|
1e2fdc3e1b | ||
|
|
901f3fe63b | ||
|
|
a767d2199d | ||
|
|
ceb4432b49 | ||
|
|
b80c48886a | ||
|
|
55121d501b | ||
|
|
2c0c8b59a2 | ||
|
|
788419d64c | ||
|
|
735a38d134 | ||
|
|
8d80903cf2 | ||
|
|
432c98f5f7 | ||
|
|
f7495155af | ||
|
|
c2ee9a9a5e | ||
|
|
71bbd85ba4 | ||
|
|
3776a7bee1 | ||
|
|
fb51a0f892 | ||
|
|
2d74d9fa92 | ||
|
|
2014f47046 | ||
|
|
79c2edca2d | ||
|
|
175f1a9acd | ||
|
|
18e943a65b | ||
|
|
20f36eaa9a | ||
|
|
6cbff694ab | ||
|
|
54e106bb14 | ||
|
|
3d1992440b | ||
|
|
1cbaea0e3d | ||
|
|
400bdb5103 | ||
|
|
dd560fb78c | ||
|
|
e71cbfbde2 | ||
|
|
eb6c103396 | ||
|
|
45c585e5c2 | ||
|
|
0ab92c77bd | ||
|
|
2d1fd1b3a5 | ||
|
|
d4d831b9ac | ||
|
|
c713a3fb25 | ||
|
|
ff60125183 | ||
|
|
94d28a2593 | ||
|
|
43f8b50ef7 | ||
|
|
7c098753cf | ||
|
|
b46d283dc4 | ||
|
|
f9639eb695 | ||
|
|
9fc0006316 | ||
|
|
a51672cc82 | ||
|
|
32decc31db | ||
|
|
14d9aac98b | ||
|
|
327962bc01 | ||
|
|
4197bbe73c | ||
|
|
e3ef20eec3 | ||
|
|
8ddb80fca2 | ||
|
|
b8eb72f823 | ||
|
|
9003177bb7 | ||
|
|
591142ba84 | ||
|
|
7b4f7f75b0 | ||
|
|
31b2cb35f7 | ||
|
|
20eb2a9fb0 | ||
|
|
37d9bff77f | ||
|
|
f3f0bf3524 | ||
|
|
b9ca57a921 | ||
|
|
04546e39a5 | ||
|
|
e6cf98f743 | ||
|
|
a528a503d3 | ||
|
|
8345788e59 | ||
|
|
21c2b8f74a | ||
|
|
69c004093d | ||
|
|
5bec411bdc | ||
|
|
34339fbcd9 | ||
|
|
b49dc87a86 | ||
|
|
535d6c9dc5 | ||
|
|
221fad6c3e | ||
|
|
aeb3873f3c | ||
|
|
4f6c83cb3a | ||
|
|
2ba5cffe44 | ||
|
|
1e85a054ac | ||
|
|
ca60e50128 | ||
|
|
70c13a7588 | ||
|
|
6653bab492 | ||
|
|
741c235f08 | ||
|
|
70bc3557b4 | ||
|
|
7509e5c99b | ||
|
|
5a2c401e05 | ||
|
|
75427973f0 | ||
|
|
63d5c442a3 | ||
|
|
45f74386b8 | ||
|
|
6093530175 | ||
|
|
6bd064f289 | ||
|
|
31d57b0c37 | ||
|
|
22a185b52e | ||
|
|
0067bd0d16 | ||
|
|
6521202e68 | ||
|
|
67e87c7367 | ||
|
|
a4eeb91290 | ||
|
|
c0b1322c77 | ||
|
|
3c18282510 | ||
|
|
55674e31ea | ||
|
|
39a69b6850 | ||
|
|
2b8a5d6176 | ||
|
|
eb692e672e | ||
|
|
381a6a999b | ||
|
|
e276a830d3 | ||
|
|
e4ee9284b4 | ||
|
|
9e441f69b8 | ||
|
|
0d2b13dd75 | ||
|
|
2df07344c8 | ||
|
|
38ad5a8e2c | ||
|
|
bd15d2f331 | ||
|
|
80ec35fd0a | ||
|
|
fb8d834036 | ||
|
|
4191153d0b | ||
|
|
ace66b2b2f | ||
|
|
c8afcde4e5 | ||
|
|
8b9a0edeba | ||
|
|
923770d95a | ||
|
|
011b8d250a | ||
|
|
0f8aa1e644 | ||
|
|
9179a31a93 | ||
|
|
c9d32b4163 | ||
|
|
57c691c2e2 | ||
|
|
8a75c9078e | ||
|
|
fbcf84e63f | ||
|
|
ea5ab5e921 | ||
|
|
3a8773baec | ||
|
|
ab4dd1ac6d | ||
|
|
6f38b2aa0d | ||
|
|
929ec28436 | ||
|
|
04a19543a5 | ||
|
|
5d505f5271 | ||
|
|
60ff3ca0a1 | ||
|
|
e4f5d9b32f | ||
|
|
6b42ef3cb1 | ||
|
|
345b9d9297 | ||
|
|
4bad3c2823 | ||
|
|
38033d9dcb | ||
|
|
5c961d4f16 | ||
|
|
c21cdc2916 | ||
|
|
1a797b10c3 | ||
|
|
3bfa427134 | ||
|
|
9a8c4bd431 | ||
|
|
dc72b55361 | ||
|
|
ee4ef882dd | ||
|
|
23c2f4bb8c | ||
|
|
bd01955f39 | ||
|
|
3f1e1e57d8 | ||
|
|
ee343c7533 | ||
|
|
961071e3fa | ||
|
|
0219341ee8 | ||
|
|
5df3c7838d | ||
|
|
bf63147ca3 | ||
|
|
b7d7b99a82 | ||
|
|
5a0129018a | ||
|
|
e48a437c88 | ||
|
|
187c32d231 | ||
|
|
c680e31817 | ||
|
|
cc177eeb2b | ||
|
|
918db24c63 | ||
|
|
7e005f4559 | ||
|
|
5d49c1346a | ||
|
|
f6147850b9 | ||
|
|
731e50da32 | ||
|
|
5078c3e9cf | ||
|
|
de0a09899a | ||
|
|
4041ef8746 | ||
|
|
3b35219beb | ||
|
|
ca289e081e | ||
|
|
5cd00d71c7 | ||
|
|
ea924fbb26 | ||
|
|
da23a89910 | ||
|
|
e4b47090ba | ||
|
|
386ded05fd | ||
|
|
e3b6cbcee9 | ||
|
|
fd7651e895 | ||
|
|
8ca2f53828 | ||
|
|
fc385935ef | ||
|
|
ed1c5714a3 | ||
|
|
dfe03458d0 | ||
|
|
c55cd03db1 | ||
|
|
6a2b347b21 | ||
|
|
b96abb5fd4 | ||
|
|
4974a47c94 | ||
|
|
7e229652f1 | ||
|
|
81f2959417 | ||
|
|
9d46c688ed | ||
|
|
60a8ddd306 | ||
|
|
4817a87499 | ||
|
|
91ce1c3f6c | ||
|
|
0c9cba4e71 | ||
|
|
c833de2e0a | ||
|
|
6824e42c69 | ||
|
|
67a41534bd | ||
|
|
3d0d522f76 | ||
|
|
accd29804c | ||
|
|
0c62609e76 | ||
|
|
6148654604 | ||
|
|
40c1957484 | ||
|
|
6f754d9810 | ||
|
|
9a4c9e4306 | ||
|
|
c52b40657a | ||
|
|
b85e9d83b8 | ||
|
|
2e9684e9a7 | ||
|
|
a80a0d0a13 | ||
|
|
885eff1f9b | ||
|
|
706e80362d | ||
|
|
7bf2297125 | ||
|
|
99c1d18d95 | ||
|
|
a45668e7cb | ||
|
|
db2c88f2e5 | ||
|
|
b9095b2176 | ||
|
|
f46bbf83a8 | ||
|
|
52f7844aee | ||
|
|
64aeea0f08 | ||
|
|
ec9fac6629 | ||
|
|
b2dac594dc | ||
|
|
28029030b6 | ||
|
|
06db60d7df | ||
|
|
eb39e2e81a | ||
|
|
0867ccdc73 | ||
|
|
4b1fbf1eff | ||
|
|
8ead5f63da | ||
|
|
24fbecb2e6 | ||
|
|
a80732f13a | ||
|
|
923d7c63b5 | ||
|
|
7626af00e7 | ||
|
|
1d647fab10 | ||
|
|
c9fcbe0e93 | ||
|
|
6d9d443d0a | ||
|
|
4a142b5c02 | ||
|
|
dfba974572 | ||
|
|
c1fd31fa23 | ||
|
|
fd3e10f793 | ||
|
|
6a7358920b | ||
|
|
c0aa180e83 | ||
|
|
2a95523577 | ||
|
|
0fa149b74f | ||
|
|
82cf7df7e9 | ||
|
|
4c58c602ff | ||
|
|
14e4b71d20 | ||
|
|
6483ea233c | ||
|
|
895ed4f6ff | ||
|
|
5ff7e66981 | ||
|
|
a2ff814796 | ||
|
|
c1e42a2f3e | ||
|
|
9e1c07b195 | ||
|
|
c71c732fc0 | ||
|
|
b3e0ec60df | ||
|
|
513cc2cac7 | ||
|
|
b0feeb3d31 | ||
|
|
9c7b3475d8 | ||
|
|
99e4a4fb4f | ||
|
|
7b7b46a61e | ||
|
|
ed4a264108 | ||
|
|
c7fdad883a | ||
|
|
6e9f82212b | ||
|
|
22e2bdcd25 | ||
|
|
85c9ca518f | ||
|
|
29a93b20f4 | ||
|
|
b95bdca2a3 | ||
|
|
6fd5f66a15 | ||
|
|
9e235f7236 | ||
|
|
90d12ef77e | ||
|
|
0c2301766a | ||
|
|
8a7b6054ef | ||
|
|
7037151bfc | ||
|
|
ba26b19567 | ||
|
|
6587014014 | ||
|
|
80b16ae9ab | ||
|
|
07330a0ca8 | ||
|
|
c735399ca5 | ||
|
|
d68f4359ac | ||
|
|
d4cfa4b24f | ||
|
|
2912acb4f5 | ||
|
|
85683d8f18 | ||
|
|
8ca2a1bf5b | ||
|
|
8427697a4b | ||
|
|
275f89a340 | ||
|
|
49c8a02b5f | ||
|
|
1165e569d2 | ||
|
|
e66f22d094 | ||
|
|
d0b29fd38c | ||
|
|
28378ff19b | ||
|
|
b4f2f15698 | ||
|
|
2210c1f4c5 | ||
|
|
7be6352531 | ||
|
|
f2391cb150 | ||
|
|
09c02c5884 | ||
|
|
71d02abc28 | ||
|
|
367efe590e | ||
|
|
afcce49455 | ||
|
|
d63e4f8021 | ||
|
|
c9a4821d15 | ||
|
|
696990f035 | ||
|
|
ad0867ba7d | ||
|
|
48ebd2e954 | ||
|
|
3d82203893 | ||
|
|
a3766a0f96 | ||
|
|
2eb27a6492 | ||
|
|
991e213d02 | ||
|
|
834480ff68 | ||
|
|
678563a970 | ||
|
|
00e638f406 | ||
|
|
6b37bae2c2 | ||
|
|
a3a4aed0d2 | ||
|
|
8ec78f20d1 | ||
|
|
a821614939 | ||
|
|
e13d771560 | ||
|
|
49a0a3d86e | ||
|
|
56bc94f98c | ||
|
|
f0b527b680 | ||
|
|
ae5d56471f | ||
|
|
58494c5c2a | ||
|
|
c72e136370 | ||
|
|
b4c8da8f3d | ||
|
|
612e8c3f0b | ||
|
|
9763cef196 | ||
|
|
ebd83b4941 | ||
|
|
48d3ae61c5 | ||
|
|
cfba019e3d | ||
|
|
2037ab9a5e | ||
|
|
3287b02816 | ||
|
|
12deb6859e | ||
|
|
98393c66fc | ||
|
|
74bedf6dcd | ||
|
|
b5d0054f94 | ||
|
|
560c971aeb | ||
|
|
8fc465b63a | ||
|
|
ca0c68c9bf | ||
|
|
6c663ddf32 | ||
|
|
7129094e26 | ||
|
|
802d7d53b0 | ||
|
|
c4a074a92d | ||
|
|
fe35823b46 | ||
|
|
8439dc1c0f | ||
|
|
db8e132ef2 | ||
|
|
7e0c61bef0 | ||
|
|
2c4bb019ba | ||
|
|
0bd03b8d58 | ||
|
|
e62af384ea | ||
|
|
e23c70b5c4 | ||
|
|
bafb23ae44 | ||
|
|
c8639cc8cd | ||
|
|
87c9f9ef03 | ||
|
|
ab430b3574 | ||
|
|
b8ae14d3a8 | ||
|
|
ca0ed0ccb9 | ||
|
|
99201dc3db | ||
|
|
5074938437 | ||
|
|
637b8bed33 | ||
|
|
fcd75d72e0 | ||
|
|
5b82417698 | ||
|
|
45ebe2c905 | ||
|
|
ff2aa51a68 | ||
|
|
b6491f7bb8 | ||
|
|
e78200838f | ||
|
|
01852d8075 | ||
|
|
0ce115bc9c | ||
|
|
b430e2ef65 | ||
|
|
a99e976610 | ||
|
|
9b4807de26 | ||
|
|
56a0184258 | ||
|
|
b64390077a | ||
|
|
e7e9c24aaa | ||
|
|
9eba3c0cec | ||
|
|
69b30df8e6 | ||
|
|
ca2aab1198 | ||
|
|
1b45f621a7 | ||
|
|
421a51f008 | ||
|
|
cce21e7e87 | ||
|
|
f6236ca317 | ||
|
|
77e10c94e9 | ||
|
|
38cc1943ee | ||
|
|
3b1047e97d | ||
|
|
34c1a1ce10 | ||
|
|
b0e3e38791 | ||
|
|
124a63736f | ||
|
|
656e0eb28e | ||
|
|
688c46c793 | ||
|
|
ee2228a83c | ||
|
|
9f4dd3c2b8 | ||
|
|
c80a28077a | ||
|
|
47c631c58e | ||
|
|
ed4235d2f9 | ||
|
|
16e9fce5b5 | ||
|
|
2e22a0d5e8 | ||
|
|
1ae31c9374 | ||
|
|
973c43843d | ||
|
|
90709156c6 | ||
|
|
0d15e59d0a | ||
|
|
60fcb24159 | ||
|
|
a175e71906 | ||
|
|
c3b5dc747a | ||
|
|
e527c7e19f | ||
|
|
790327fbe8 | ||
|
|
d18bba6b03 | ||
|
|
0a752be53c | ||
|
|
b221cc1853 | ||
|
|
2935651578 | ||
|
|
ca2ae22f15 | ||
|
|
1c597eb0df | ||
|
|
28dd74444f | ||
|
|
28cf834e0b | ||
|
|
4dc102a3cf | ||
|
|
23a654d57a | ||
|
|
ffcbf87858 | ||
|
|
32a0e950fe | ||
|
|
f21ec37531 | ||
|
|
a8d02f43e2 | ||
|
|
7e863e7e96 | ||
|
|
588577bd96 | ||
|
|
ee64c5d832 | ||
|
|
e55cbbdc0e | ||
|
|
65c48004ba | ||
|
|
4211abd0b0 | ||
|
|
cf6dc3fa5e | ||
|
|
fb3da5c5bd | ||
|
|
e6bd921746 | ||
|
|
40dcfbd72c | ||
|
|
48d14840ee | ||
|
|
c9e0ab654f | ||
|
|
d8c0d2da8e | ||
|
|
e59f20668b | ||
|
|
8fe67b73da | ||
|
|
2fba03dc75 | ||
|
|
71f8d1db74 | ||
|
|
098131e252 | ||
|
|
afc5b72092 | ||
|
|
14f494a25d | ||
|
|
8bc665f355 | ||
|
|
6dd872f2d2 | ||
|
|
461bb65263 | ||
|
|
266bed7dc9 | ||
|
|
9777e4c52f | ||
|
|
dd89a98242 | ||
|
|
9b57d04181 | ||
|
|
8e74418a5c | ||
|
|
3350579675 | ||
|
|
46c14f3766 | ||
|
|
0aa65de6df | ||
|
|
06bb1a96f3 | ||
|
|
70e4dd16df | ||
|
|
b66034ef38 | ||
|
|
d3c86aeefd | ||
|
|
212feff636 | ||
|
|
b1b0cea27e | ||
|
|
5742608595 | ||
|
|
f46f97f8b6 | ||
|
|
7692f80f64 | ||
|
|
bb811238f3 | ||
|
|
68c657eb66 | ||
|
|
b5bae40a0c | ||
|
|
f7c68736f0 | ||
|
|
c481d55976 | ||
|
|
7aa7d61984 | ||
|
|
7d855a9c09 | ||
|
|
d59d141302 | ||
|
|
0c4ce1e756 | ||
|
|
9b8cadb91c | ||
|
|
9c9d6a7fdc | ||
|
|
fb31267cc1 | ||
|
|
b194d407d3 | ||
|
|
57813ea3fb | ||
|
|
bce89ca75b | ||
|
|
7204e73c22 | ||
|
|
541f5b8948 | ||
|
|
c5d471b31b | ||
|
|
ac26827f32 | ||
|
|
677cb4638f | ||
|
|
32348148b8 | ||
|
|
f79edc7bfb | ||
|
|
f7c7ee447d | ||
|
|
1220555936 | ||
|
|
032639234e | ||
|
|
cdf9a8c2a7 | ||
|
|
6fbdf7b400 | ||
|
|
4983e97884 | ||
|
|
cc7d50f505 | ||
|
|
f2f737fcd3 | ||
|
|
c47e6af6e4 | ||
|
|
5e6485b622 | ||
|
|
bb2ec9098b | ||
|
|
4e4cc65645 | ||
|
|
ddfac8d717 | ||
|
|
009683ddd4 | ||
|
|
8eefb90944 | ||
|
|
6bc8a53618 | ||
|
|
de73c05316 | ||
|
|
fba8b85c90 | ||
|
|
20557190d9 | ||
|
|
4e4e7ae015 | ||
|
|
36c3c94d3b | ||
|
|
64b733b1ea | ||
|
|
67a023a992 | ||
|
|
b6e7d4149d | ||
|
|
bcacee2147 | ||
|
|
b75dcd7ac4 | ||
|
|
e77d28f761 | ||
|
|
bc9645aa4f | ||
|
|
d52cbb7077 | ||
|
|
ba9df402b0 | ||
|
|
c78b5e580a | ||
|
|
ce2c662872 | ||
|
|
04b331ad55 | ||
|
|
35c026f92c | ||
|
|
6680a1f2a7 | ||
|
|
1ffbf7857f | ||
|
|
e2741ba604 | ||
|
|
5b7497093c | ||
|
|
b092ed8c2c | ||
|
|
2dd1a1b54d | ||
|
|
1952bc2b76 | ||
|
|
b6da0fdf06 | ||
|
|
fc545ed995 | ||
|
|
f85ab24993 | ||
|
|
4e1766485f | ||
|
|
8e5471d0fe | ||
|
|
a09878dcb4 | ||
|
|
7f7571a2d1 | ||
|
|
e704d42adb | ||
|
|
5ff9683cd7 | ||
|
|
7d36daa5b7 | ||
|
|
46956dbe9e | ||
|
|
586e18f1a7 | ||
|
|
c489909542 | ||
|
|
ad679e7c34 | ||
|
|
75f6db436b | ||
|
|
bdcef193b7 | ||
|
|
d377790e9d | ||
|
|
9a31297249 | ||
|
|
b4f7df4786 | ||
|
|
18d7dcc226 | ||
|
|
f7ef182d33 | ||
|
|
be36e17bd8 | ||
|
|
6b940116be | ||
|
|
da8ea72593 | ||
|
|
5effa853a3 | ||
|
|
0780a68fba | ||
|
|
b61192a050 | ||
|
|
9bc9a49b8f | ||
|
|
547f1fb786 | ||
|
|
b27560c51e | ||
|
|
8dd1d20a2d | ||
|
|
ad5badb0d7 | ||
|
|
18696e6bc2 | ||
|
|
4f2914a123 | ||
|
|
431c6522a6 | ||
|
|
b823eb7fe0 | ||
|
|
f8ceaf62b7 | ||
|
|
213fde04fe | ||
|
|
ddad10b615 | ||
|
|
f4e7386a0a | ||
|
|
9a42e12985 | ||
|
|
86ee790ea1 | ||
|
|
b21eab1bf1 | ||
|
|
b3f2ba035e | ||
|
|
10a620017d | ||
|
|
c9752195ac | ||
|
|
64000215a4 | ||
|
|
65d957f9f5 | ||
|
|
aef19c3b13 | ||
|
|
9968896bbd | ||
|
|
1a27ae39a3 | ||
|
|
ee4802e513 | ||
|
|
60dd1863b3 | ||
|
|
71408e7c3a | ||
|
|
659543461b | ||
|
|
50b7f02470 | ||
|
|
febdf4092e | ||
|
|
a3d89c8d78 | ||
|
|
c9b02603bc | ||
|
|
b9086f134a | ||
|
|
447c33b806 | ||
|
|
ae47ba78cd | ||
|
|
2f51358ea0 | ||
|
|
c91c513b60 | ||
|
|
189d90da91 | ||
|
|
5e20a6d9ef | ||
|
|
05edec8a8b | ||
|
|
75cc0be52a | ||
|
|
a9e59e1e87 | ||
|
|
e766a2c945 | ||
|
|
fbd5a78cf0 | ||
|
|
f39c1a2eb8 | ||
|
|
5335a611a0 | ||
|
|
c06068c931 | ||
|
|
62f81f2601 | ||
|
|
077e548219 | ||
|
|
d56bc733ad | ||
|
|
8fbd24c861 | ||
|
|
98049aeb78 | ||
|
|
c9718783af | ||
|
|
bfe01badc1 | ||
|
|
111ac84a65 | ||
|
|
d7540eea84 | ||
|
|
88408fc0ea | ||
|
|
6e796d7a22 | ||
|
|
f587713795 | ||
|
|
66fcba846b | ||
|
|
bdf20b0267 | ||
|
|
0292203ec0 | ||
|
|
112ad65882 | ||
|
|
6bc0880a0d | ||
|
|
90a63e1b14 | ||
|
|
6ce45d32da | ||
|
|
4eaf560ee6 | ||
|
|
4c0057e66a | ||
|
|
bcb22c7e46 | ||
|
|
0683b46239 | ||
|
|
61ccc39f81 | ||
|
|
533e1a1846 | ||
|
|
6afe22f351 | ||
|
|
039f2ce449 | ||
|
|
494f39dd86 | ||
|
|
c851522536 | ||
|
|
9b37ba20a3 | ||
|
|
6310bfe084 | ||
|
|
1a9670ae9c | ||
|
|
ee9da8e31e | ||
|
|
23e9fd927c | ||
|
|
22c481ff27 | ||
|
|
14fb769582 | ||
|
|
da0af13142 | ||
|
|
44fb4a9b83 | ||
|
|
1e6f6879ff | ||
|
|
ba33bb56d0 | ||
|
|
ab5e2dd774 | ||
|
|
0b1b1bbe0b | ||
|
|
1aebc8942d | ||
|
|
233df3bdab | ||
|
|
dd9c085ae3 | ||
|
|
1fed798120 | ||
|
|
fcf9c0bc84 | ||
|
|
fd9a753170 | ||
|
|
2a5f4b98ed | ||
|
|
ad33d1648c | ||
|
|
1f784f46c7 | ||
|
|
ed52b44f8b | ||
|
|
4b8cc3e6d1 | ||
|
|
6b8e10cb2e | ||
|
|
952bb4bb7c | ||
|
|
a0bcae2404 | ||
|
|
567f54c9f9 | ||
|
|
66f84ede6e | ||
|
|
5b1ca8a7f9 | ||
|
|
4fc894e430 | ||
|
|
15356b617b | ||
|
|
c8f6e12586 | ||
|
|
102cc92991 | ||
|
|
1e490f1796 | ||
|
|
088a5c7166 | ||
|
|
5837533678 | ||
|
|
20ec557655 | ||
|
|
b7d1dfa75f | ||
|
|
33e4a082d6 | ||
|
|
dd49ef51d5 | ||
|
|
13b2a886f3 | ||
|
|
c1dfb315a9 | ||
|
|
96958d26c7 | ||
|
|
8d635c7a5a | ||
|
|
627c300b4a | ||
|
|
00812025c2 | ||
|
|
0b21b8bab1 | ||
|
|
271bb00147 | ||
|
|
b346d761d8 | ||
|
|
21614e15b2 | ||
|
|
998c2f19cf | ||
|
|
750cd3c258 | ||
|
|
df358ccc42 | ||
|
|
1b5efdfc5b | ||
|
|
63dfb8a227 | ||
|
|
a3c0590218 | ||
|
|
c694c96a32 | ||
|
|
30bc304a20 | ||
|
|
974cf64e1d | ||
|
|
fce726a031 | ||
|
|
7187ba3074 | ||
|
|
bfbf8fb841 | ||
|
|
4ea0d4d043 | ||
|
|
8c13204afa | ||
|
|
e4c79d2101 | ||
|
|
90a45dca29 | ||
|
|
bcff25b959 | ||
|
|
dff6203fb3 | ||
|
|
b4dd6b670b | ||
|
|
f028533c07 | ||
|
|
dac61a14cd | ||
|
|
6884f93d76 | ||
|
|
17cc77e816 | ||
|
|
7ac59dd142 | ||
|
|
c277ae7f68 | ||
|
|
7c78753865 | ||
|
|
9fc07ac622 | ||
|
|
364a11491d | ||
|
|
0a26288d88 | ||
|
|
dcfb916d30 | ||
|
|
d1c7dab356 | ||
|
|
0e5b5c844c | ||
|
|
3eea14abe4 | ||
|
|
a7ca917abf | ||
|
|
9ca80df67a | ||
|
|
48a89fd828 | ||
|
|
1002f80617 | ||
|
|
fc51bead8e | ||
|
|
cea663f4eb | ||
|
|
8ecac8d03c | ||
|
|
f26d2f4ebf | ||
|
|
cb7f60aba8 | ||
|
|
b4dbe422ff | ||
|
|
aea19c5cd0 | ||
|
|
edc5542ee6 | ||
|
|
eb99b7d7ca | ||
|
|
006f709d37 | ||
|
|
f5322b39bc | ||
|
|
8702b1421f | ||
|
|
3070634aab | ||
|
|
b65041a780 | ||
|
|
6426c8aee2 | ||
|
|
16b6753f1d | ||
|
|
b96db40e3d | ||
|
|
7caf5fe20a | ||
|
|
44a958d3f8 | ||
|
|
4c562048dc | ||
|
|
f3b92c7445 | ||
|
|
675e9829bd | ||
|
|
4c0dbda558 | ||
|
|
e586273a3f | ||
|
|
d4a0f0cd21 | ||
|
|
8cfba4c242 | ||
|
|
cce13b4b45 | ||
|
|
05e435633f | ||
|
|
6ff3fa0e52 | ||
|
|
c3c996f1f7 | ||
|
|
ff98a9be5a | ||
|
|
96cef81267 | ||
|
|
75ceeffab8 | ||
|
|
71fffb6b68 | ||
|
|
e066228015 | ||
|
|
48a5d55af7 | ||
|
|
b9c2becd3e | ||
|
|
24f72c0b73 | ||
|
|
90e9b86497 | ||
|
|
560ea75a25 | ||
|
|
78fd972312 | ||
|
|
d947c849c8 | ||
|
|
8ce63a244c | ||
|
|
0306c35b54 | ||
|
|
ff8e5ae42e | ||
|
|
acf5e5b75f | ||
|
|
4f946f267e | ||
|
|
abfe2b4be6 | ||
|
|
bb3c08ce0b | ||
|
|
138888e620 | ||
|
|
4e1b429913 | ||
|
|
51f780b5f5 | ||
|
|
cffbf86351 | ||
|
|
709a9b3c61 | ||
|
|
c35621b669 | ||
|
|
4d1baac4db | ||
|
|
474623feb3 | ||
|
|
c17a3533ec | ||
|
|
85f3691432 | ||
|
|
2ae8d25d18 | ||
|
|
780bdc4421 | ||
|
|
a2e6f8f964 | ||
|
|
564668587c | ||
|
|
023cf46041 | ||
|
|
36bb41a945 | ||
|
|
0cbdb3b2d1 | ||
|
|
70f2d86b58 | ||
|
|
914175de70 | ||
|
|
7696b30067 | ||
|
|
478b5eaba3 | ||
|
|
52f0c0f587 | ||
|
|
2a4d3507b8 | ||
|
|
78a75924de | ||
|
|
a34dfee752 | ||
|
|
5ca9b4f370 | ||
|
|
2a2b320be3 | ||
|
|
071ab7d555 | ||
|
|
9738bbfbe5 | ||
|
|
e385321f95 | ||
|
|
21a9ae4c45 | ||
|
|
6a689dc3df | ||
|
|
af0ca52739 | ||
|
|
83ea50a836 | ||
|
|
2022eb5c12 | ||
|
|
6b132c4d3b | ||
|
|
f34775b038 | ||
|
|
f73d8a55de | ||
|
|
3d39bf8720 | ||
|
|
9a302328c6 | ||
|
|
cdb1ac32fa | ||
|
|
74af0dc93a | ||
|
|
2da1dc31fb | ||
|
|
6b55c81462 | ||
|
|
07630964c1 | ||
|
|
a6b7d456f4 | ||
|
|
7c5a07f709 | ||
|
|
7e25166ce1 | ||
|
|
10df31cbf9 | ||
|
|
10c067e44f | ||
|
|
f81e97726a | ||
|
|
8f040d6ff1 | ||
|
|
32fb13a9a9 | ||
|
|
4b86115d30 | ||
|
|
12a49d0ee1 | ||
|
|
b37f5b4143 | ||
|
|
cbb9602f9e | ||
|
|
997a305d35 | ||
|
|
f09193ce07 | ||
|
|
96fc34c00e | ||
|
|
bd06b8a867 | ||
|
|
7414d1407f | ||
|
|
19d5431f60 | ||
|
|
f2c01cbbbd | ||
|
|
c807358216 | ||
|
|
685c3c9853 | ||
|
|
db3514a2c1 | ||
|
|
6a43c544eb | ||
|
|
1b174b86ca | ||
|
|
f40fc321fe | ||
|
|
eaf4fa240f | ||
|
|
17a8be1ae6 | ||
|
|
16be03b162 | ||
|
|
2f58517ce5 | ||
|
|
9a3c2d5311 | ||
|
|
fed39694f6 | ||
|
|
c10b06e29b | ||
|
|
5c5412db67 | ||
|
|
1c35bd22f8 | ||
|
|
c28222e04d | ||
|
|
854481e395 | ||
|
|
44555ac724 | ||
|
|
dcec14decd | ||
|
|
f62d81e79f | ||
|
|
9ff056f073 | ||
|
|
1edb0bcc4a | ||
|
|
1899250a53 | ||
|
|
443e23e348 | ||
|
|
1e6595fca7 | ||
|
|
5ed475eedd | ||
|
|
17669fd09d | ||
|
|
51825db716 | ||
|
|
421ae6b316 | ||
|
|
9ab446b790 | ||
|
|
c65e3d4e6d | ||
|
|
10fad00c1e | ||
|
|
478f2d8134 | ||
|
|
a49b01d396 | ||
|
|
c26ebe0bd2 | ||
|
|
381a44148e | ||
|
|
c1f16de441 | ||
|
|
4a048657c5 | ||
|
|
44f5086600 | ||
|
|
ddddc3330d | ||
|
|
39ba9b9a4f | ||
|
|
bdb36f35cf | ||
|
|
0c9b4da473 | ||
|
|
935f1084d6 | ||
|
|
c8759babae | ||
|
|
ab3d648244 | ||
|
|
57dc0460a3 | ||
|
|
d5ee07fe1b | ||
|
|
89e3618d85 | ||
|
|
162381a73f | ||
|
|
08db857334 | ||
|
|
bb651d0c70 | ||
|
|
d9c094f644 | ||
|
|
32040bdd7e | ||
|
|
43f0e43de7 | ||
|
|
5122b15ffb | ||
|
|
468d8e7c33 | ||
|
|
93b1d1e591 | ||
|
|
1642149228 | ||
|
|
8fd564b034 | ||
|
|
7c3f94dd87 | ||
|
|
eacd408ce8 | ||
|
|
084da71420 | ||
|
|
3fa47ee9ec | ||
|
|
e811895570 | ||
|
|
47e890b8e1 | ||
|
|
f2bb27dc32 | ||
|
|
ef9d2d1775 | ||
|
|
be13a14330 | ||
|
|
45a2bcb71a | ||
|
|
48fb660e66 | ||
|
|
218919a8ba | ||
|
|
79ed04e403 | ||
|
|
3c995fe17b | ||
|
|
5ff8ddf5f3 | ||
|
|
8b2350fb22 | ||
|
|
15be647c16 | ||
|
|
8a8af0e4a9 | ||
|
|
4ff89eab04 | ||
|
|
ec032cb739 | ||
|
|
2b287a85e5 | ||
|
|
6ea2b4e0e2 | ||
|
|
f7e2e9c2ee | ||
|
|
5463e5ca55 | ||
|
|
396d4218f0 | ||
|
|
e3a07845d4 | ||
|
|
341a42bddd | ||
|
|
573444e640 | ||
|
|
ed9c6d7959 | ||
|
|
ed2c65dd6f | ||
|
|
0062caafdf | ||
|
|
b98f0b1427 | ||
|
|
b6a3d2b283 | ||
|
|
0d848e3d93 | ||
|
|
065197980b | ||
|
|
2a36488db7 | ||
|
|
3cd44284c4 | ||
|
|
bc0fa6a62e | ||
|
|
51c90223fc | ||
|
|
df1b56becf | ||
|
|
5a77c19791 | ||
|
|
eae3270385 | ||
|
|
daee101005 | ||
|
|
b32d503d65 | ||
|
|
ca7af88c59 | ||
|
|
6bd8e96ac0 | ||
|
|
d06d69966b | ||
|
|
004829ac9e | ||
|
|
0009603098 | ||
|
|
5479c77ae6 | ||
|
|
1f97616f3c | ||
|
|
e65fcdab42 | ||
|
|
428f5a656d | ||
|
|
0315ad38f9 | ||
|
|
6c1a6a2033 | ||
|
|
2a630a7210 | ||
|
|
528203d8e5 | ||
|
|
fd5a0cc468 | ||
|
|
359a29874b | ||
|
|
f9da7f73b3 | ||
|
|
49ae82d96a | ||
|
|
fbd23fb2c3 | ||
|
|
252b258159 | ||
|
|
dfcef3ca2d | ||
|
|
3d4a33d373 | ||
|
|
28397e672e | ||
|
|
bbbf439028 | ||
|
|
3c5dab1f34 | ||
|
|
e8499e1751 | ||
|
|
be26ab9201 | ||
|
|
7519b7ab63 | ||
|
|
aaf5c7581c | ||
|
|
ce4e8dc8b7 | ||
|
|
5c2e6c8ed8 | ||
|
|
4020f9c6c2 | ||
|
|
16bf635aca | ||
|
|
99bc5a018e | ||
|
|
411a634242 | ||
|
|
4ab421f183 | ||
|
|
06a553ba85 | ||
|
|
08b06b8632 | ||
|
|
79e8b6e242 | ||
|
|
6a34329765 | ||
|
|
f7dd6e4387 | ||
|
|
6a33acbb7c | ||
|
|
97e4b69eaa | ||
|
|
e96678eebc | ||
|
|
1bc8536f0e | ||
|
|
76df9be4fb | ||
|
|
322d8c67b3 | ||
|
|
d6ac30be75 | ||
|
|
a9f970459b | ||
|
|
76443a5d7f | ||
|
|
f0501f8494 | ||
|
|
efb752f1f4 | ||
|
|
d252e94892 | ||
|
|
0be23981f3 | ||
|
|
a4cadc5fb3 | ||
|
|
f902223597 | ||
|
|
9b926be557 | ||
|
|
db568af302 | ||
|
|
e7cf20a723 | ||
|
|
202243f711 | ||
|
|
c5f51ae826 | ||
|
|
61b9c7a1ee | ||
|
|
4520715b5b | ||
|
|
6ef829e9ec | ||
|
|
791e65c88b | ||
|
|
fddcde757f | ||
|
|
66180f8de4 | ||
|
|
bc5a21d545 | ||
|
|
fc8525e4d8 | ||
|
|
8dd61155bf | ||
|
|
5a89dfe8f4 | ||
|
|
80b70ec6b9 | ||
|
|
ac81ab458c | ||
|
|
23b76874df | ||
|
|
bc54fcf6c0 | ||
|
|
e0a3a683a5 | ||
|
|
c31786c4c1 | ||
|
|
13a5c7f215 | ||
|
|
ee2f361425 | ||
|
|
9eab2071dc | ||
|
|
61d7fe04e8 | ||
|
|
d3161422db | ||
|
|
d1ae1bd41f | ||
|
|
6384bed9cc | ||
|
|
be75e4948d | ||
|
|
337f5d9e73 | ||
|
|
4afa2bfada | ||
|
|
b94174f9e1 | ||
|
|
bf71045381 | ||
|
|
0ade0e1361 | ||
|
|
0f02e0d8dd | ||
|
|
7c786407a5 | ||
|
|
79ba8fecd5 | ||
|
|
213c81ed4d | ||
|
|
d7432c376d | ||
|
|
b8c7d6ea86 | ||
|
|
983f0a92b2 | ||
|
|
256cc902eb | ||
|
|
f7b766cc4a | ||
|
|
dca66f300d | ||
|
|
fd9dac8122 | ||
|
|
8b168e76af | ||
|
|
5b518797d7 | ||
|
|
e5275514dd | ||
|
|
0d35b38282 | ||
|
|
837dcbf19d | ||
|
|
e68d70cf1d | ||
|
|
1171e2c9af | ||
|
|
e5873caf04 | ||
|
|
34ee33aea0 | ||
|
|
2d93d12cad | ||
|
|
c43b80cb91 | ||
|
|
542e491bb4 | ||
|
|
9c9eb519df | ||
|
|
0a96cb8eb3 | ||
|
|
8e3e15080b | ||
|
|
90975c96f9 | ||
|
|
2fd85ab78f | ||
|
|
a0cf7d5787 | ||
|
|
e7fc9d9912 | ||
|
|
bdb0fb1310 | ||
|
|
006f7e0c15 | ||
|
|
93afa11ef9 | ||
|
|
040e7bc9ee | ||
|
|
6bb8514253 | ||
|
|
85283cff38 | ||
|
|
ef68408fff | ||
|
|
71104e4902 | ||
|
|
fd625c96d6 | ||
|
|
4f721fc1e2 | ||
|
|
373210e610 | ||
|
|
65362e727a | ||
|
|
5044ce4236 | ||
|
|
b4aca3410f | ||
|
|
b5971eae95 | ||
|
|
4b775796be | ||
|
|
e97af776a0 | ||
|
|
e83e5b7c29 | ||
|
|
179ef85f4e | ||
|
|
5b4603b6a7 | ||
|
|
fc2560eb32 | ||
|
|
2c23c0ffa7 | ||
|
|
fda974458e | ||
|
|
efe4d5f339 | ||
|
|
032fe7cc0c | ||
|
|
50c519ec69 | ||
|
|
af566ec08a | ||
|
|
cb7879eecf | ||
|
|
b1825334dd | ||
|
|
09727909b3 | ||
|
|
f98d3bd44b | ||
|
|
1545f7b60a | ||
|
|
2453e403ef | ||
|
|
2bb16c3617 | ||
|
|
4ef9202e92 | ||
|
|
22668b6b43 | ||
|
|
f9c786f3eb | ||
|
|
0f2ee1966e | ||
|
|
62e520a6a5 | ||
|
|
2f9cf6bc9b | ||
|
|
e03cfe8287 | ||
|
|
0d97dfacd6 | ||
|
|
6ac0191451 | ||
|
|
f5b360f66c | ||
|
|
1d5c21fa72 | ||
|
|
015aca9165 | ||
|
|
d3e1c39e35 | ||
|
|
ae47509aa7 | ||
|
|
5ff798306f | ||
|
|
c41ffc14c7 | ||
|
|
1e7a3bef0d | ||
|
|
243f564396 | ||
|
|
dae1e44a8f | ||
|
|
67bcc29baf | ||
|
|
cdb089778c | ||
|
|
5e43a02ffa | ||
|
|
7395d13c3e | ||
|
|
f6000468db | ||
|
|
09e52d82bf | ||
|
|
0e98f44a93 | ||
|
|
6d595c1344 | ||
|
|
5f6f997095 | ||
|
|
6662fabcb8 | ||
|
|
7577cf8645 | ||
|
|
8340791824 | ||
|
|
14f0d16fae | ||
|
|
e1ad4a2fce | ||
|
|
f67580e2b2 | ||
|
|
31f408478a | ||
|
|
fd93b6e5f7 | ||
|
|
39bb8e3418 | ||
|
|
ab6d5d6a90 | ||
|
|
6b3600d67b | ||
|
|
bf8ff44b84 | ||
|
|
72a3888062 | ||
|
|
4479233bea | ||
|
|
a7021e6b66 | ||
|
|
0f5dd9d469 | ||
|
|
388f008189 | ||
|
|
639a32ea7e | ||
|
|
5838f1aec8 | ||
|
|
d2399b6157 | ||
|
|
b1282b4362 | ||
|
|
bbd402fcff | ||
|
|
6f0ca9d94c | ||
|
|
a18fb1b686 | ||
|
|
6a72e1b6ff | ||
|
|
b3141f1a62 | ||
|
|
aea8f1f0dc | ||
|
|
8aecf880e5 | ||
|
|
7d5ce51910 | ||
|
|
cfc8ccafce | ||
|
|
e11dfc2875 | ||
|
|
b3ef31ad05 | ||
|
|
70410d6311 | ||
|
|
4cc7dec35b | ||
|
|
47ecd40c4f | ||
|
|
489f2bcfa0 | ||
|
|
16590e0603 | ||
|
|
f50cda9abc | ||
|
|
7543968203 | ||
|
|
54685b1f36 | ||
|
|
abdc601d92 | ||
|
|
9f50f08065 | ||
|
|
aabb1fa6f2 | ||
|
|
4b770714bd | ||
|
|
8175dbca53 | ||
|
|
c91332afb4 | ||
|
|
08fb1b33b4 | ||
|
|
cd6b6afc65 | ||
|
|
cd4a70ea78 | ||
|
|
eb24ba9835 | ||
|
|
468869821f | ||
|
|
0c85dbf9b0 | ||
|
|
2db47e413e | ||
|
|
b8fddc98bf | ||
|
|
467f798147 | ||
|
|
374f3d13f1 | ||
|
|
b0a35a183f | ||
|
|
74e349ed33 | ||
|
|
c57aa2ac5e | ||
|
|
acac29dcda | ||
|
|
675969af6d | ||
|
|
ce32177e1d | ||
|
|
e956d61a08 | ||
|
|
068cdb9817 | ||
|
|
0ec7d4cd9a | ||
|
|
6a8126337c | ||
|
|
12db25a829 | ||
|
|
a347bfb8b1 | ||
|
|
9255af210c | ||
|
|
0b3264a99d | ||
|
|
2bf16da441 | ||
|
|
5c4d890100 | ||
|
|
99282026fa | ||
|
|
8f4b2a7cfd | ||
|
|
c78feda3d6 | ||
|
|
a279d62c88 | ||
|
|
8d1f787179 | ||
|
|
7ab41cf195 | ||
|
|
df728b7a21 | ||
|
|
cba26faf9b | ||
|
|
c4c1cffc01 | ||
|
|
321706e5c6 | ||
|
|
47bf4c0baa | ||
|
|
fc628c1221 | ||
|
|
eb6a03704a | ||
|
|
7b855e6c68 | ||
|
|
fe0818c612 | ||
|
|
74dabcae8a | ||
|
|
daf7870bab | ||
|
|
64da7f8775 | ||
|
|
6cfa11a94d | ||
|
|
db3c6c1463 | ||
|
|
65a669a9a1 | ||
|
|
33f56bb203 | ||
|
|
1ff873e170 | ||
|
|
b0e0156880 | ||
|
|
896f36dffb | ||
|
|
eae975652e | ||
|
|
e46d1a793c | ||
|
|
1c9763eaa8 | ||
|
|
686370ac0e | ||
|
|
e4ca057e4c | ||
|
|
ac5a0e3dfc | ||
|
|
a580b522e3 | ||
|
|
af53c271c7 | ||
|
|
4476dad395 | ||
|
|
b7b2dd2915 | ||
|
|
efb6a0f5c6 | ||
|
|
deba653661 | ||
|
|
eb831dc4ac | ||
|
|
b0d355a71c | ||
|
|
af11b4e423 | ||
|
|
0cf86318ce | ||
|
|
4175b762c3 | ||
|
|
3ddcb5f118 | ||
|
|
d0ad865976 | ||
|
|
7013b34897 | ||
|
|
bbbae36e6a | ||
|
|
34c3a9530b | ||
|
|
78cb522874 | ||
|
|
e8a0d2acf6 | ||
|
|
f8c13f1e30 | ||
|
|
4b272a76fd | ||
|
|
f73d96af08 | ||
|
|
bb0835e2f3 | ||
|
|
ef4c108b9e | ||
|
|
319b9ed00f | ||
|
|
35b62ca53f | ||
|
|
4e1e6f3c0b | ||
|
|
c8f3445fa6 | ||
|
|
7aa1ae6bd4 | ||
|
|
58179939bb | ||
|
|
5d3eae9022 | ||
|
|
05f8a10b3d | ||
|
|
6843df410a | ||
|
|
f324577014 | ||
|
|
df8357e1fc | ||
|
|
35d14939c4 | ||
|
|
f2c19ad403 | ||
|
|
ed9fee3038 | ||
|
|
58e31b9cb4 | ||
|
|
401ee3fd94 | ||
|
|
43b601af9b | ||
|
|
5581dd71fa | ||
|
|
d8fb849691 | ||
|
|
5f4281b851 | ||
|
|
24ab176236 | ||
|
|
9c342ca19c | ||
|
|
3ee3acc948 | ||
|
|
9bdc5fa0fe | ||
|
|
d7a7d8e2ae | ||
|
|
5a9682b1bd | ||
|
|
3c97903ed0 | ||
|
|
c9cf21be80 | ||
|
|
f0818d94e9 | ||
|
|
831555cf5a | ||
|
|
e0483e316e | ||
|
|
c1fe45cbcd | ||
|
|
ef7ad8ee8e | ||
|
|
0ed7d46926 | ||
|
|
ac69879fba | ||
|
|
50e779ef5a | ||
|
|
0032af1468 | ||
|
|
cf1c369dc3 | ||
|
|
ece7996895 | ||
|
|
3cdfc1192a | ||
|
|
8b8998a2b9 | ||
|
|
677cf9e1df | ||
|
|
a08d4c6a06 | ||
|
|
e1e344dad1 | ||
|
|
b9427f84ae | ||
|
|
dc9d79bc3d | ||
|
|
503ce5502e | ||
|
|
0e4d476d76 | ||
|
|
cc2d579bc9 | ||
|
|
94bf2a36cf | ||
|
|
63127bf9c3 | ||
|
|
033765e866 | ||
|
|
1634e4562a | ||
|
|
ed66d78585 | ||
|
|
39e1514620 | ||
|
|
10b7eb83f1 | ||
|
|
672be3991d | ||
|
|
b350c2be91 | ||
|
|
68ab5bd4e7 | ||
|
|
bd21153300 | ||
|
|
9cca7ead17 | ||
|
|
4b1bf1fe65 | ||
|
|
6d353f378d | ||
|
|
cc2bb8f496 | ||
|
|
20853e3ad7 | ||
|
|
67def34e29 | ||
|
|
54549b61e1 | ||
|
|
3e7c3e952b | ||
|
|
e6411d7d90 | ||
|
|
9e7d0d3183 | ||
|
|
3ae4b1cf19 | ||
|
|
2841223c9d | ||
|
|
ba713b8b06 | ||
|
|
c53bff288a | ||
|
|
d85e8798c2 | ||
|
|
6dbb4eedb7 | ||
|
|
33b4b94426 | ||
|
|
3fc99095e2 | ||
|
|
8410198c37 | ||
|
|
d1b325052d | ||
|
|
4c4b42833a | ||
|
|
519a9300e2 | ||
|
|
91dda1f759 | ||
|
|
65e12269d1 | ||
|
|
9d2474b142 | ||
|
|
e804d44934 | ||
|
|
961628d8d3 | ||
|
|
7522751fd2 | ||
|
|
e3dc9fd96b | ||
|
|
c3ac076d13 | ||
|
|
1e02511022 | ||
|
|
071a772208 | ||
|
|
78711163c5 | ||
|
|
3f005ff476 | ||
|
|
ddd399631f | ||
|
|
c0ce830456 | ||
|
|
481555f11f | ||
|
|
20327d8fb6 | ||
|
|
a377e15368 | ||
|
|
52ca9628fd | ||
|
|
db1454c4fe | ||
|
|
6845bd4598 | ||
|
|
f02286e3f5 | ||
|
|
d473c251e6 | ||
|
|
64a401125d | ||
|
|
68ae1af13f | ||
|
|
f2165af48f | ||
|
|
62b1dbbc74 | ||
|
|
0881d86998 | ||
|
|
aff4883222 | ||
|
|
fbed77fc64 | ||
|
|
276e8b948d | ||
|
|
2edcfbda35 | ||
|
|
22cdd6e04d | ||
|
|
3518813145 | ||
|
|
a453412d5b | ||
|
|
55b438e8c1 | ||
|
|
ce749302a5 | ||
|
|
3356175f3e | ||
|
|
50ff20ba33 | ||
|
|
d3ba51104e | ||
|
|
981c184c20 | ||
|
|
e3a65a1847 | ||
|
|
64eb440ea3 | ||
|
|
994383f73a | ||
|
|
89bd5f5b93 | ||
|
|
ae25f489ad | ||
|
|
510eaabcfb | ||
|
|
875e177219 | ||
|
|
f8f62e1473 | ||
|
|
c1a70a3aa2 | ||
|
|
c514fd7a08 | ||
|
|
3cefc86fbf | ||
|
|
0c66606d1d | ||
|
|
05fc35a5b9 | ||
|
|
2425838ea6 | ||
|
|
20d117e129 | ||
|
|
8f08c33bcc | ||
|
|
d7f0bf6a24 | ||
|
|
5afe6fea3c | ||
|
|
9441739839 | ||
|
|
8727d96a06 | ||
|
|
b2ffe7e509 | ||
|
|
119c950b53 | ||
|
|
29a0de3636 | ||
|
|
aa3e3c7e71 | ||
|
|
eb00953c60 | ||
|
|
c4b255ae46 | ||
|
|
d3547dd892 | ||
|
|
7d1c7b2907 | ||
|
|
d0999fa5d5 | ||
|
|
c478afee0f | ||
|
|
ab196e75c1 | ||
|
|
50b5c21fcf | ||
|
|
47bc7e812d | ||
|
|
ad5f574fb7 | ||
|
|
9537a62ab1 | ||
|
|
dc7bd3355d | ||
|
|
d76d9bcfad | ||
|
|
440eed77cf | ||
|
|
b906fbea29 | ||
|
|
af148137d6 | ||
|
|
64357fde0d | ||
|
|
7512a45cda | ||
|
|
e034abef82 | ||
|
|
0b5db9c4bd | ||
|
|
3cd763aabb | ||
|
|
1a881fb501 | ||
|
|
2f68804279 | ||
|
|
60d15e2983 | ||
|
|
628488f7aa | ||
|
|
149308bc89 | ||
|
|
b4fa971007 | ||
|
|
b30f10ca46 | ||
|
|
da725f6ffc | ||
|
|
b0d62a8608 | ||
|
|
3da2d3718b | ||
|
|
4bd6aca0c0 | ||
|
|
e90c84ee81 | ||
|
|
45b8f83cdd | ||
|
|
c8e0aa9cb4 | ||
|
|
7a486db45f | ||
|
|
4634c6cf3b | ||
|
|
eb57d14673 | ||
|
|
cb939baca0 | ||
|
|
db8a277960 | ||
|
|
398d61097c | ||
|
|
4a43e4fda6 | ||
|
|
783159ee44 | ||
|
|
fa63551b42 | ||
|
|
18a0deb678 | ||
|
|
f7c46855b8 | ||
|
|
e67539c3b1 | ||
|
|
b8d06521b2 | ||
|
|
3225d980c5 | ||
|
|
9c3a579ff8 | ||
|
|
92b5b34d17 | ||
|
|
47e8696b9f | ||
|
|
7938384162 | ||
|
|
01aec1f78e | ||
|
|
67f35c7b74 | ||
|
|
d600df4585 | ||
|
|
f3a0710059 | ||
|
|
203e0d150b | ||
|
|
efd699a044 | ||
|
|
dbe0ca0adc | ||
|
|
5316cabf30 | ||
|
|
9a16921a32 | ||
|
|
f5edf2a4c2 | ||
|
|
cfdcfdbc99 | ||
|
|
ad99dca6ac | ||
|
|
fbcc81056b | ||
|
|
756877b94c | ||
|
|
68d30c8802 | ||
|
|
555b83a359 | ||
|
|
ff43be6d7b | ||
|
|
7defbce1dd | ||
|
|
ef5aead17d | ||
|
|
585b70a6a9 | ||
|
|
b24a78bdd5 | ||
|
|
f305621ade | ||
|
|
24198a7890 | ||
|
|
2d477b38bc | ||
|
|
48afc5639e | ||
|
|
7b932fbf4a | ||
|
|
39f3e7b292 | ||
|
|
49f3038737 | ||
|
|
f7a40a071b | ||
|
|
3a1ebcaf3f | ||
|
|
3da1c4381c | ||
|
|
9ff73e5e63 | ||
|
|
f3633c050b | ||
|
|
1ba828da18 | ||
|
|
109725d613 | ||
|
|
4bc8f01376 | ||
|
|
df20a6d6df | ||
|
|
e32963384e | ||
|
|
03a9865ccb | ||
|
|
7674b3aec6 | ||
|
|
9c9a5bcd51 | ||
|
|
8948f7f43c | ||
|
|
ef4e86a124 | ||
|
|
b9edc357f3 | ||
|
|
9353d0f95c | ||
|
|
7defdd2a00 | ||
|
|
621e79fe33 | ||
|
|
e759c5343b | ||
|
|
d879b25458 | ||
|
|
ccffde6966 | ||
|
|
1524f80418 | ||
|
|
bcbeb888f6 | ||
|
|
7730aeb652 | ||
|
|
272f938f2e | ||
|
|
5570a346c4 | ||
|
|
2b90652a16 | ||
|
|
a58dd4678c | ||
|
|
25f4574093 | ||
|
|
733b9b731d | ||
|
|
20637fc5e0 | ||
|
|
c33e2ee51f | ||
|
|
7ef59418dd | ||
|
|
0805e58a94 | ||
|
|
6ddeff2b21 | ||
|
|
7433c38d92 | ||
|
|
5f930ec840 | ||
|
|
0dd36b2275 | ||
|
|
629428739d | ||
|
|
7d7281480a | ||
|
|
666e2c0190 | ||
|
|
3968d35cac | ||
|
|
551677d6fd | ||
|
|
85b029fad0 | ||
|
|
841ed9ea89 | ||
|
|
74fd7eb793 | ||
|
|
26df9e861f | ||
|
|
338860d05b | ||
|
|
b7df6c1480 | ||
|
|
b2179f14ab | ||
|
|
99b43fed7f | ||
|
|
25dfcd38a7 | ||
|
|
a6d2c42402 | ||
|
|
fd8271396d | ||
|
|
19ca6a342e | ||
|
|
7819e174f1 | ||
|
|
7cd6d026a2 | ||
|
|
c48d306195 | ||
|
|
3a9845f784 | ||
|
|
94d3053232 | ||
|
|
b078169adf | ||
|
|
38c4dd408c | ||
|
|
8beb9dfed0 | ||
|
|
eef9549e96 | ||
|
|
82ac98ddc7 | ||
|
|
3d52ffcbdc | ||
|
|
3fad4635b0 | ||
|
|
b0b19328a8 | ||
|
|
9fd79488f8 | ||
|
|
cebe31857a | ||
|
|
1961829414 | ||
|
|
3e5bdec033 | ||
|
|
5789021a76 | ||
|
|
6bf0319c51 | ||
|
|
8f519caa77 | ||
|
|
a38be80a86 | ||
|
|
81b72fc571 | ||
|
|
91681d7846 | ||
|
|
d40619aca0 | ||
|
|
1328bc732f | ||
|
|
060f65b27e | ||
|
|
364669f527 | ||
|
|
f4042b9d4b | ||
|
|
c750b696d2 | ||
|
|
02cdee6c75 | ||
|
|
c4a824b5db | ||
|
|
7f31c0632d | ||
|
|
96ee92695c | ||
|
|
80b252a744 | ||
|
|
4f50f8b83f | ||
|
|
df895b9529 | ||
|
|
f9e9e51352 | ||
|
|
27e99344bd | ||
|
|
32572844fd | ||
|
|
a77e67e578 | ||
|
|
fb9cd1ef93 | ||
|
|
93722f6a7d | ||
|
|
76da7466ec | ||
|
|
6b3c95f09f | ||
|
|
81110ddba7 | ||
|
|
64fd9f615c | ||
|
|
213df88766 | ||
|
|
a26750d4f4 | ||
|
|
72edd7376e | ||
|
|
948e9ae139 | ||
|
|
ae357d1a2b | ||
|
|
59756ff1b3 | ||
|
|
2908e453e8 | ||
|
|
7b1ffc885f | ||
|
|
95364b1d31 | ||
|
|
f3257c0847 | ||
|
|
eb0fc35c8f | ||
|
|
3dccd93093 | ||
|
|
8929199082 | ||
|
|
20f6a57b3a | ||
|
|
db949728dd | ||
|
|
92d28e468e | ||
|
|
f95c3ca46a | ||
|
|
477b6bcdc5 | ||
|
|
3e40deba5f | ||
|
|
7d9bd25270 | ||
|
|
c201990cb0 | ||
|
|
289df53fb4 | ||
|
|
62788423a1 | ||
|
|
8617990206 | ||
|
|
2d8f2b328f | ||
|
|
833896e267 | ||
|
|
f12b4520f6 | ||
|
|
2564003c85 | ||
|
|
c36b8f2257 | ||
|
|
d413fbe0ed | ||
|
|
4237000fc9 | ||
|
|
5131aff352 | ||
|
|
f77f52802c | ||
|
|
d52ab34421 | ||
|
|
81e4c26b38 | ||
|
|
1eed1901e9 | ||
|
|
a8b1c7bce1 | ||
|
|
ac3d035ce7 | ||
|
|
c8575cac05 | ||
|
|
8d63c059c5 | ||
|
|
bc53288fca | ||
|
|
474d4281f1 | ||
|
|
73babcd7d6 | ||
|
|
5fe208d6a0 | ||
|
|
94ca0ed71b | ||
|
|
a323cfe08c | ||
|
|
29c3654928 | ||
|
|
27fefed0d8 | ||
|
|
235d974ce1 | ||
|
|
cb1d3168e3 | ||
|
|
64dc25dfb6 | ||
|
|
9a7484e57d | ||
|
|
b89ee3e4c1 | ||
|
|
ec5645d380 | ||
|
|
a0abf1e221 | ||
|
|
0e558d54e5 | ||
|
|
004cc5fe5a | ||
|
|
0b22b1d022 | ||
|
|
24ecf31bd3 | ||
|
|
c37c80771c | ||
|
|
a017c0c4a8 | ||
|
|
3a35ab449b | ||
|
|
da84964a33 | ||
|
|
65051855ee | ||
|
|
72c36789c6 | ||
|
|
bc07bd16bd | ||
|
|
bf691fa552 | ||
|
|
47ddfec1f9 | ||
|
|
fb8ba655e1 | ||
|
|
d64f56817d | ||
|
|
9363708e49 | ||
|
|
65a2e4d6b0 | ||
|
|
138d7bd9a2 | ||
|
|
97fe09fbdd | ||
|
|
487412fa22 | ||
|
|
ea47baa1fc | ||
|
|
90cd519916 | ||
|
|
669d18b218 | ||
|
|
fbcd2b3174 | ||
|
|
99a839456d | ||
|
|
0d9d60466d | ||
|
|
5a031efa7a | ||
|
|
1f64fce47c | ||
|
|
04529897dc | ||
|
|
7837e1ad81 | ||
|
|
c7c60b2b94 | ||
|
|
f9c2805379 | ||
|
|
035d772a4a | ||
|
|
f89c2e0ba9 | ||
|
|
94ce055bc2 | ||
|
|
9b3d4d4453 | ||
|
|
329e4dc3c3 | ||
|
|
d96e939eed | ||
|
|
c8437895dc | ||
|
|
95ba648dae | ||
|
|
9f847e5a24 | ||
|
|
a4eec5fef8 | ||
|
|
e51bf3f0c1 | ||
|
|
b907ea59f4 | ||
|
|
cebff75992 | ||
|
|
a3f9b65501 | ||
|
|
1f7afe7a49 | ||
|
|
ceecaa0184 | ||
|
|
78b4bbb06e | ||
|
|
0375e7a881 | ||
|
|
ebc831663b | ||
|
|
65ceb96e4f | ||
|
|
57ef2d2229 | ||
|
|
8759b175bd | ||
|
|
13f86b0cd4 | ||
|
|
c0fad49375 | ||
|
|
579cc98e4f | ||
|
|
4a98417ee8 | ||
|
|
4a5e90b5cb | ||
|
|
36a85e935b | ||
|
|
9034203a4f | ||
|
|
24e8f8aa43 | ||
|
|
632251b16c | ||
|
|
2406cbe7ed | ||
|
|
e6a0caa164 | ||
|
|
cd3e2fbbb8 | ||
|
|
47bd6e5ce3 | ||
|
|
f590c63e33 | ||
|
|
e8b74ece72 | ||
|
|
3d45699bc7 | ||
|
|
ae987f19dc | ||
|
|
09786cb8d2 | ||
|
|
17072f9ed4 | ||
|
|
82d3d1abc5 | ||
|
|
2cc40a7961 | ||
|
|
c523e706e9 | ||
|
|
cdca8047b9 | ||
|
|
09b8a3394e | ||
|
|
ed1a7a4bc9 | ||
|
|
0696fb405e | ||
|
|
265a410342 | ||
|
|
cbd0d6e6a2 | ||
|
|
25e0a59d5b | ||
|
|
ab34ee882e | ||
|
|
4f05657236 | ||
|
|
7720200679 | ||
|
|
117ec9970c | ||
|
|
766073f77a | ||
|
|
5f0a60f61c | ||
|
|
d608d34166 | ||
|
|
8bc1c7445d | ||
|
|
985b906c94 | ||
|
|
e8ac94f8da | ||
|
|
9465dd6941 | ||
|
|
28a00fff23 | ||
|
|
c418481981 | ||
|
|
2b87b9f3fc | ||
|
|
683eb993e8 | ||
|
|
e19d6a58c9 | ||
|
|
2dccee05bc | ||
|
|
ac2885afc6 | ||
|
|
972909902e | ||
|
|
b07f2ed186 | ||
|
|
ee6160e796 | ||
|
|
5940e6913d | ||
|
|
c85e33c034 | ||
|
|
21ef259e1f | ||
|
|
2b66683167 | ||
|
|
851a0e3d24 | ||
|
|
8b32f561ec | ||
|
|
1571b6daa2 | ||
|
|
2d69509d5e | ||
|
|
8edf05c000 | ||
|
|
0e6cef0268 | ||
|
|
60e4d8e080 | ||
|
|
6b58986ec3 | ||
|
|
e88ec01fca | ||
|
|
8284498722 | ||
|
|
e088280a5f | ||
|
|
7f78c323e9 | ||
|
|
77303c1aeb | ||
|
|
cc74f941c6 | ||
|
|
775c1c1c8b | ||
|
|
35a7456302 | ||
|
|
4006b35a5e | ||
|
|
b562280503 | ||
|
|
e3a075a6a6 | ||
|
|
8c534c6d33 | ||
|
|
82be6c243c | ||
|
|
5b8ee5dd1b | ||
|
|
050a132bd5 | ||
|
|
146bff6766 | ||
|
|
cd98bce331 | ||
|
|
a2d3723c7a | ||
|
|
ecb88a87ad | ||
|
|
3f2813932a | ||
|
|
c2223e3942 | ||
|
|
2dc0839d36 | ||
|
|
97ef1d1781 | ||
|
|
e64a9a7ab9 | ||
|
|
d4f3f83212 | ||
|
|
1488f3ed58 | ||
|
|
26945bde42 | ||
|
|
c911dd8aa6 | ||
|
|
1c81e0eee0 | ||
|
|
5366713790 | ||
|
|
e9940d060a | ||
|
|
605e905f3e | ||
|
|
11d30566bf | ||
|
|
0942f9fac3 | ||
|
|
7b303b6529 | ||
|
|
6a9040989d | ||
|
|
840e9c7671 | ||
|
|
1d9e2f8169 | ||
|
|
8c9f302b67 | ||
|
|
44f23cd8ac | ||
|
|
c88f781509 | ||
|
|
3dc578d585 | ||
|
|
238d8ff0be | ||
|
|
ca5dd0071c | ||
|
|
86fa759f9f | ||
|
|
0bcf8a6626 | ||
|
|
d735cd98e7 | ||
|
|
e25a550d3f | ||
|
|
8a2206656d | ||
|
|
ac68d670a2 | ||
|
|
e6539bbed5 | ||
|
|
9db0694677 | ||
|
|
a4d73330cc | ||
|
|
ea6a983e9b | ||
|
|
79cd8603a2 | ||
|
|
02cb054b71 | ||
|
|
007cfd589f | ||
|
|
90d24fd6bf | ||
|
|
4b7310d0cb | ||
|
|
59660032e1 | ||
|
|
d9c47ec763 | ||
|
|
a18ea750a6 | ||
|
|
c423411639 | ||
|
|
b5ad030e40 | ||
|
|
bfdbe2a34a | ||
|
|
ec5dcb47c9 | ||
|
|
436f1d1541 | ||
|
|
05873a030f | ||
|
|
78083eea48 | ||
|
|
7715da02e7 | ||
|
|
6798a5777a | ||
|
|
2f3ad961e7 | ||
|
|
dba047187f | ||
|
|
03e63f29dc | ||
|
|
b4cd9c4c45 | ||
|
|
b9b2649db9 | ||
|
|
df98483b06 | ||
|
|
37fbeaad02 | ||
|
|
794d8a754c | ||
|
|
b081debf88 | ||
|
|
36c6c9bfa6 | ||
|
|
bda5ea1c59 | ||
|
|
5ccf6eefc0 | ||
|
|
06261559fc | ||
|
|
477d2f9d82 | ||
|
|
0cb66ddc6e | ||
|
|
99df7f6acb | ||
|
|
0791fa0ad3 | ||
|
|
9811327672 | ||
|
|
60f2eaadf5 | ||
|
|
5478c640cc | ||
|
|
7fe3b115f1 | ||
|
|
3ee63b754c | ||
|
|
378b6c12f3 | ||
|
|
ee5817e480 | ||
|
|
a354b4acd5 | ||
|
|
8e87a4a941 | ||
|
|
42712166e9 | ||
|
|
2bc31de69d | ||
|
|
fcfeb29be5 | ||
|
|
a64a270e25 | ||
|
|
96eebc7d1d | ||
|
|
38fd675f51 | ||
|
|
35adaee1bb | ||
|
|
def314b5ad | ||
|
|
913a18eaaf | ||
|
|
d82b39de19 | ||
|
|
cde5d8709c | ||
|
|
ec210c49c5 | ||
|
|
07f320def9 | ||
|
|
f5fe15d6cb | ||
|
|
da146b0873 | ||
|
|
3b7a77ae0c | ||
|
|
9cb1e28ad1 | ||
|
|
9db7701691 | ||
|
|
27aca4c84a | ||
|
|
4e50244dca | ||
|
|
0c21724270 | ||
|
|
09306aa2cd | ||
|
|
1170673b92 | ||
|
|
712a991c86 | ||
|
|
e3cdcbc5ab | ||
|
|
62379b21c1 | ||
|
|
cfde67742c | ||
|
|
9b01561127 | ||
|
|
fb89450b90 | ||
|
|
5a8d491f10 | ||
|
|
7ffb44e394 | ||
|
|
34a5d9e61b | ||
|
|
e1d43e3651 | ||
|
|
c291ad83f1 | ||
|
|
c6a4eb28a8 | ||
|
|
dd4735026a | ||
|
|
dc0439227e | ||
|
|
00f470763f | ||
|
|
05b1cfca31 | ||
|
|
e40696a7af | ||
|
|
bc030c42d9 | ||
|
|
7ce72a31c5 | ||
|
|
cf8989938c | ||
|
|
e09b63183c | ||
|
|
76717c7177 | ||
|
|
6749fa57ad | ||
|
|
050b75a2df | ||
|
|
3f730c94ce | ||
|
|
22c931729b | ||
|
|
cb658826a3 | ||
|
|
3f8c311ea2 | ||
|
|
22aa4d359e | ||
|
|
c2880ec8e4 | ||
|
|
108822d4e1 | ||
|
|
f79a926b10 | ||
|
|
4eef234736 | ||
|
|
5abbdfc094 | ||
|
|
c44715bef4 | ||
|
|
b5f55100e9 | ||
|
|
4e0404fce6 | ||
|
|
f1874eca8a | ||
|
|
2e1d6f2412 | ||
|
|
0395287906 | ||
|
|
bda8a5fd6c | ||
|
|
ded413481c | ||
|
|
a5a6c07b08 | ||
|
|
204df99375 | ||
|
|
55dd67a0ad | ||
|
|
6040ee4ffc | ||
|
|
544ed99d98 | ||
|
|
7546640fba | ||
|
|
daf5b88834 | ||
|
|
c36f975bbf | ||
|
|
a5e34d9153 | ||
|
|
57a63440f9 | ||
|
|
e25a227e4d | ||
|
|
db7b340b32 | ||
|
|
7683755137 | ||
|
|
8de71663c2 | ||
|
|
211a7c7939 | ||
|
|
82ce66af16 | ||
|
|
df4bc13122 | ||
|
|
8f3661d5a4 | ||
|
|
bb31ac11ca | ||
|
|
4ef0065c66 | ||
|
|
476d168a46 | ||
|
|
093c6cdca3 | ||
|
|
345f5e14de | ||
|
|
5ddfef7eb7 | ||
|
|
81d784b5dc | ||
|
|
b0b9a7a294 | ||
|
|
e76ee3c96e | ||
|
|
24b495ca43 | ||
|
|
ff4fd1411e | ||
|
|
03eed5f4f9 | ||
|
|
1c89968b69 | ||
|
|
01ce469a0f | ||
|
|
c6347a3cf1 | ||
|
|
bf7e581445 | ||
|
|
0fe9253d3b | ||
|
|
6a9042fd9c | ||
|
|
9e62fdc0b8 | ||
|
|
a33b70f8bd | ||
|
|
bfc06ed81f | ||
|
|
c3c8ac7ff0 | ||
|
|
bfd0c00708 | ||
|
|
ef0c84072b | ||
|
|
066972ff75 | ||
|
|
608853d4fa | ||
|
|
1774305c2d | ||
|
|
3295966022 | ||
|
|
8f43f5a4a1 | ||
|
|
379f40c1be | ||
|
|
c2282af879 | ||
|
|
d057b1909f | ||
|
|
3cb3bfc008 | ||
|
|
2ba4e99114 | ||
|
|
f61a6ba5ff | ||
|
|
34a6389a92 | ||
|
|
b675adbbb0 | ||
|
|
e46da0e2f2 | ||
|
|
44b257a749 | ||
|
|
fe2599eb35 | ||
|
|
d791a189bc | ||
|
|
bbc521a2f3 | ||
|
|
56f6da606c | ||
|
|
3e098b7479 | ||
|
|
7e2fe6d5be | ||
|
|
c4b2cf9394 | ||
|
|
9ac823833a | ||
|
|
60132c085c | ||
|
|
5e14e65eb0 | ||
|
|
64063fb77d | ||
|
|
aa309ef27c | ||
|
|
4c81c9e21e | ||
|
|
f4912eb23b | ||
|
|
7f85b8b33f | ||
|
|
85c68f78da | ||
|
|
af03085d5f | ||
|
|
b7d747ee17 | ||
|
|
b6f100888a | ||
|
|
3cfd392e3e | ||
|
|
7ed98a8655 | ||
|
|
d78eb4a4e5 | ||
|
|
a3d168b646 | ||
|
|
5018df511b | ||
|
|
09345a9d53 | ||
|
|
8b17cd3cde | ||
|
|
403fcd8fff | ||
|
|
409be4020b | ||
|
|
24801a220c | ||
|
|
19b9f4e1c1 | ||
|
|
48d584df58 | ||
|
|
dd999a29fd | ||
|
|
75fdfb5a0b | ||
|
|
6b4db4e9ae | ||
|
|
2571b24f22 | ||
|
|
855a9eea44 | ||
|
|
189e299592 | ||
|
|
6cbaa35849 | ||
|
|
cb1cfa4d94 | ||
|
|
5bbf893a37 | ||
|
|
3ddbfb5dea | ||
|
|
5352d062d5 | ||
|
|
e15cbfca33 | ||
|
|
51b58f911e | ||
|
|
7acfbb9eed | ||
|
|
4bf9853a25 | ||
|
|
58c6db0a8f | ||
|
|
aed52a7829 | ||
|
|
a1e24a3f95 | ||
|
|
11cff29916 | ||
|
|
f184b9bcc4 | ||
|
|
bab0024e0c | ||
|
|
deed92cff7 | ||
|
|
a9bc949679 | ||
|
|
1411410f4e | ||
|
|
648fbe1b10 | ||
|
|
7042f89620 | ||
|
|
e235fffb5e | ||
|
|
6aadd13450 | ||
|
|
349de098af | ||
|
|
5527e585f6 | ||
|
|
a6e9814dfc | ||
|
|
f01a7d3933 | ||
|
|
d2cd3b899f | ||
|
|
ac2c0d5d84 | ||
|
|
f0d0b512ca | ||
|
|
c859ffd0f7 | ||
|
|
4eb57f3cc5 | ||
|
|
6218bfaf9e | ||
|
|
4d5cdcaeae | ||
|
|
6ad62a6f2f | ||
|
|
37d4b151d7 | ||
|
|
37c1d804f6 | ||
|
|
d200df3e9c | ||
|
|
eb43e416ae | ||
|
|
e6244f71bd | ||
|
|
403277ed06 | ||
|
|
dbe8a59c3c | ||
|
|
a98de54db4 | ||
|
|
7edb7266f2 | ||
|
|
d14e077511 | ||
|
|
211c14e316 | ||
|
|
4fbc140551 | ||
|
|
ee8e65c80d | ||
|
|
1db2e0282a | ||
|
|
2351f73c76 | ||
|
|
20a38c3aac | ||
|
|
bca25d0f5f | ||
|
|
b26853e4d7 | ||
|
|
39049f82fc | ||
|
|
a30250f758 | ||
|
|
629737cda3 | ||
|
|
ec41411675 | ||
|
|
becdf2bfeb | ||
|
|
1e723945f8 | ||
|
|
dfb7599bb6 | ||
|
|
e1d3363ad9 | ||
|
|
c36c750c5d | ||
|
|
c7581c0847 | ||
|
|
936d65291f | ||
|
|
a64030ad93 | ||
|
|
01d60ad4e9 | ||
|
|
06b64c6a87 | ||
|
|
0eff4e2bc0 | ||
|
|
44d6139db2 | ||
|
|
506e3004b7 | ||
|
|
9ce4c7a468 | ||
|
|
2fc724e469 | ||
|
|
afb69a7f03 | ||
|
|
cd2dd9f12d | ||
|
|
29963f74d6 | ||
|
|
565a017ebd | ||
|
|
0b85847524 | ||
|
|
cfc46edfb8 | ||
|
|
f250373673 | ||
|
|
b0acc13da7 | ||
|
|
d705af8965 | ||
|
|
a401dfddff | ||
|
|
f579fd925e | ||
|
|
e6d86eaa9b | ||
|
|
348e45a5cd | ||
|
|
2120e9cf2a | ||
|
|
3002acc895 | ||
|
|
0b3c313280 | ||
|
|
982ab4b4fa | ||
|
|
a0ee2f384b | ||
|
|
56a739655f | ||
|
|
150ee44da0 | ||
|
|
78795e3167 | ||
|
|
b0dd3cd818 | ||
|
|
ef12ec54d7 | ||
|
|
495b774965 | ||
|
|
1381bb5c54 | ||
|
|
f1d4794dd1 | ||
|
|
d58473c270 | ||
|
|
7adf32f36e | ||
|
|
5c3beb4160 | ||
|
|
b82c307007 | ||
|
|
a7010e7598 | ||
|
|
f850cbf780 | ||
|
|
4581fe4f22 | ||
|
|
35df54ae99 | ||
|
|
f8a5f91687 | ||
|
|
4b71557584 | ||
|
|
feda903f2f | ||
|
|
150e264f0d | ||
|
|
b592f6ae92 | ||
|
|
37ba8fb57d | ||
|
|
72f80cdbed | ||
|
|
c070f20669 | ||
|
|
0ddb17862c | ||
|
|
a4d5165a3d | ||
|
|
4fdddfb585 | ||
|
|
0575e96b60 | ||
|
|
d0a8749986 | ||
|
|
44fbf8afe2 | ||
|
|
1eb9202233 | ||
|
|
b7da92d225 | ||
|
|
e4f6d0db1f | ||
|
|
0a8bd9f687 | ||
|
|
f2608998f9 | ||
|
|
ffb08d32ce | ||
|
|
119e776ddc | ||
|
|
652930d5aa | ||
|
|
e3babe97e4 | ||
|
|
72d01865ae | ||
|
|
b9664681a3 | ||
|
|
fd5c545189 | ||
|
|
0639f4b00f | ||
|
|
ab49986238 | ||
|
|
a4fdc50d1d | ||
|
|
ed228b6183 | ||
|
|
e0e675531a | ||
|
|
44e0879224 | ||
|
|
1e6fa93ea2 | ||
|
|
2aaed6bffd | ||
|
|
2d367a8b47 | ||
|
|
52c18abc5e | ||
|
|
595c70ed56 | ||
|
|
9faec8decd | ||
|
|
90dc1f6cf8 | ||
|
|
ba01976151 | ||
|
|
4d7f5a8a09 | ||
|
|
2d01fa6f94 | ||
|
|
970f848b84 | ||
|
|
31feec0465 | ||
|
|
2466c14300 | ||
|
|
49927fcfb7 | ||
|
|
f612683dfb | ||
|
|
11e6ad2153 | ||
|
|
d041c69e0d | ||
|
|
b394052145 | ||
|
|
bce83e45b5 | ||
|
|
77bd8d3a76 | ||
|
|
118ece9f1c | ||
|
|
971956a6ba | ||
|
|
48cd362015 | ||
|
|
b12b1bf662 | ||
|
|
6829d502d7 | ||
|
|
e94ddb4179 | ||
|
|
aae415c1ee | ||
|
|
c79b2456f5 | ||
|
|
5104d323bb | ||
|
|
80c4be6e5f | ||
|
|
970859311d | ||
|
|
449581e7c4 | ||
|
|
02a8fc7f72 | ||
|
|
7bbeee8234 | ||
|
|
00532fe7ac | ||
|
|
073a934b2b | ||
|
|
ea7631de9e | ||
|
|
3635d0ec26 | ||
|
|
81e15c91f4 | ||
|
|
105bb7512f | ||
|
|
b63e50dacb | ||
|
|
417b5c8b97 | ||
|
|
b8afbb66a7 | ||
|
|
08ed16c493 | ||
|
|
734efdf343 | ||
|
|
1195678893 | ||
|
|
4ad2c2221f | ||
|
|
9e9eba695f | ||
|
|
c6e3cad944 | ||
|
|
3af0e3249a | ||
|
|
510a836b90 | ||
|
|
25f415045a | ||
|
|
0af9c18ef2 | ||
|
|
18cf198592 | ||
|
|
9bbe51fa64 | ||
|
|
4de3a34a3e | ||
|
|
c028425f14 | ||
|
|
7d03d8a58f | ||
|
|
db0e88d8a7 | ||
|
|
1b8aca26a3 | ||
|
|
b239053810 | ||
|
|
1acb130be1 |
@@ -4,3 +4,4 @@ Build
|
||||
Demos
|
||||
./Aristo
|
||||
WebSite
|
||||
.push-package
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
[submodule "External/narwhal"]
|
||||
path = External/narwhal
|
||||
url = git://github.com/tlrobinson/narwhal.git
|
||||
[submodule "External/jack"]
|
||||
path = External/jack
|
||||
url = git://github.com/tlrobinson/jack.git
|
||||
[submodule "External/browserjs"]
|
||||
path = External/browserjs
|
||||
url = git://github.com/tlrobinson/browserjs.git
|
||||
[submodule "External/ojunit"]
|
||||
path = External/ojunit
|
||||
url = git://github.com/280north/ojunit.git
|
||||
+18
-4
@@ -24,25 +24,29 @@
|
||||
@import "CPAlert.j"
|
||||
@import "CPAnimation.j"
|
||||
@import "CPApplication.j"
|
||||
@import "CPArrayController.j"
|
||||
@import "CPBezierPath.j"
|
||||
@import "CPBox.j"
|
||||
@import "CPBrowser.j"
|
||||
@import "CPButton.j"
|
||||
@import "CPButtonBar.j"
|
||||
@import "CPCheckBox.j"
|
||||
@import "CPCib.j"
|
||||
@import "CPCibBindingConnector.j"
|
||||
@import "CPCibConnector.j"
|
||||
@import "CPCibControlConnector.j"
|
||||
@import "CPCibLoading.j"
|
||||
@import "CPCibOutletConnector.j"
|
||||
@import "CPClipView.j"
|
||||
@import "CPCollectionViewItem.j"
|
||||
@import "CPCollectionView.j"
|
||||
@import "CPCollectionViewItem.j"
|
||||
@import "CPColor.j"
|
||||
@import "CPColorPanel.j"
|
||||
@import "CPColorWell.j"
|
||||
@import "CPCompatibility.j"
|
||||
@import "CPControl.j"
|
||||
@import "CPCookie.j"
|
||||
@import "CPCursor.j"
|
||||
@import "CPDocument.j"
|
||||
@import "CPDocumentController.j"
|
||||
@import "CPEvent.j"
|
||||
@@ -51,33 +55,43 @@
|
||||
@import "CPFont.j"
|
||||
@import "CPFontManager.j"
|
||||
@import "CPGeometry.j"
|
||||
@import "CPGraphics.j"
|
||||
@import "CPImage.j"
|
||||
@import "CPImageView.j"
|
||||
@import "CPKeyBinding.j"
|
||||
@import "CPMenu.j"
|
||||
@import "CPMenuItem.j"
|
||||
@import "CPOpenPanel.j"
|
||||
@import "CPOutlineView.j"
|
||||
@import "CPPanel.j"
|
||||
@import "CPPasteboard.j"
|
||||
@import "CPPopUpButton.j"
|
||||
@import "CPProgressIndicator.j"
|
||||
@import "CPRadio.j"
|
||||
@import "CPResponder.j"
|
||||
@import "CPSearchField.j"
|
||||
@import "CPScrollView.j"
|
||||
@import "CPScroller.j"
|
||||
@import "CPScrollView.j"
|
||||
@import "CPSearchField.j"
|
||||
@import "CPSecureTextField.j"
|
||||
@import "CPSegmentedControl.j"
|
||||
@import "CPShadow.j"
|
||||
@import "CPSlider.j"
|
||||
@import "CPSound.j"
|
||||
@import "CPSplitView.j"
|
||||
@import "CPTabView.j"
|
||||
@import "CPStepper.j"
|
||||
@import "CPTableColumn.j"
|
||||
@import "CPTableView.j"
|
||||
@import "CPTabView.j"
|
||||
@import "CPText.j"
|
||||
@import "CPTextField.j"
|
||||
@import "CPTokenField.j"
|
||||
@import "CPToolbar.j"
|
||||
@import "CPToolbarItem.j"
|
||||
@import "CPTreeNode.j"
|
||||
@import "CPView.j"
|
||||
@import "CPViewAnimation.j"
|
||||
@import "CPViewController.j"
|
||||
@import "CPWebView.j"
|
||||
@import "CPWindow.j"
|
||||
@import "CPWindowController.j"
|
||||
|
||||
|
||||
+68
-13
@@ -26,11 +26,16 @@
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
#import "CoreGraphics/CGGeometry.h"
|
||||
@import "CPView.j"
|
||||
@import "CPButton.j"
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPAccordionViewItem
|
||||
|
||||
<p>A CPAccordionViewItem represents a single section of a CPAccordionView.</p>
|
||||
*/
|
||||
@implementation CPAccordionViewItem : CPObject
|
||||
{
|
||||
CPString _identifier @accessors(property=identifier);
|
||||
@@ -43,6 +48,10 @@
|
||||
return [self initWithIdentifier:@""];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes a new CPAccordionViewItem with the specified identifier. This identifier may
|
||||
then be used in subsequent methods to provide abstract introspection of the CPAccordionView.
|
||||
*/
|
||||
- (id)initWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
@@ -55,6 +64,25 @@
|
||||
|
||||
@end
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPAccordionView
|
||||
|
||||
<p>CPAccordionView provides a container for CPAccordionViewItem objects and manages layout state
|
||||
for all sublayout items.</p>
|
||||
|
||||
<strong>Example</strong><br />
|
||||
<pre>
|
||||
var myAccordionView = [[CPAccordionView alloc] initWithFrame:CGRectMakeZero()];
|
||||
var firstItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"firstSection"]];
|
||||
[firstItem setView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
|
||||
var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection"]];
|
||||
[secondItem setView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
|
||||
[myAccordionView addItem:firstItem];
|
||||
[myAccordionView addItem:secondItem];
|
||||
[myAccordionView setAutoresizingMask: CPViewWidthSizable | CPViewHeightSizable];
|
||||
</pre>
|
||||
*/
|
||||
@implementation CPAccordionView : CPView
|
||||
{
|
||||
CPInteger _dirtyItemIndex;
|
||||
@@ -64,7 +92,10 @@
|
||||
CPMutableArray _itemViews;
|
||||
CPIndexSet _expandedItemIndexes;
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the receiver for usage with the specified bounding rectangle
|
||||
@return the initialized view
|
||||
*/
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
@@ -95,13 +126,17 @@
|
||||
{
|
||||
return _items;
|
||||
}
|
||||
|
||||
- (void)addItem:(CPAccordionItem)anItem
|
||||
/*!
|
||||
Append a CPAccordionViewItem to the receiver. Note that the CPAccordionViewItem must contain
|
||||
a valid CPView derived component or a TypeError will be generated when the contents of the
|
||||
ViewItem are disclosed.
|
||||
*/
|
||||
- (void)addItem:(CPAccordionViewItem)anItem
|
||||
{
|
||||
[self insertItem:anItem atIndex:_items.length];
|
||||
}
|
||||
|
||||
- (void)insertItem:(CPAccordionItem)anItem atIndex:(CPInteger)anIndex
|
||||
- (void)insertItem:(CPAccordionViewItem)anItem atIndex:(CPInteger)anIndex
|
||||
{
|
||||
// FIXME: SHIFT ITEMS RIGHT
|
||||
[_expandedItemIndexes addIndex:anIndex];
|
||||
@@ -122,7 +157,7 @@
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)removeItem:(CPAccordionItem)anItem
|
||||
- (void)removeItem:(CPAccordionViewItem)anItem
|
||||
{
|
||||
[self removeItemAtIndex:[_items indexOfObjectIdenticalTo:anItem]];
|
||||
}
|
||||
@@ -178,7 +213,7 @@
|
||||
|
||||
if ([itemView isCollapsed])
|
||||
[self expandItemAtIndex:anIndex];
|
||||
|
||||
|
||||
else
|
||||
[self collapseItemAtIndex:anIndex];
|
||||
}
|
||||
@@ -197,6 +232,20 @@
|
||||
return indexSet;
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)isEnabled forItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
var itemView = _itemViews[anIndex];
|
||||
if (!itemView)
|
||||
return;
|
||||
|
||||
if (!isEnabled)
|
||||
[self collapseItemAtIndex:anIndex];
|
||||
else
|
||||
[self expandItemAtIndex:anIndex];
|
||||
|
||||
[itemView setEnabled:isEnabled];
|
||||
}
|
||||
|
||||
- (void)_invalidateItemsStartingAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
if (_dirtyItemIndex === CPNotFound)
|
||||
@@ -221,7 +270,7 @@
|
||||
{
|
||||
if (_items.length <= 0)
|
||||
return [self setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), 0.0)];
|
||||
|
||||
|
||||
if (_dirtyItemIndex === CPNotFound)
|
||||
return;
|
||||
|
||||
@@ -301,6 +350,12 @@
|
||||
[_headerView setStringValue:aLabel];
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)isEnabled
|
||||
{
|
||||
if ([_headerView respondsToSelector:@selector(setEnabled:)])
|
||||
[_headerView setEnabled:isEnabled];
|
||||
}
|
||||
|
||||
- (void)setContentView:(CPView)aView
|
||||
{
|
||||
if (_contentView === aView)
|
||||
@@ -312,7 +367,7 @@
|
||||
|
||||
_contentView = aView;
|
||||
|
||||
[_contentView addObserver:self forKeyPath:@"frame" options:0 context:NULL];
|
||||
[_contentView addObserver:self forKeyPath:@"frame" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:NULL];
|
||||
|
||||
[self addSubview:_contentView];
|
||||
|
||||
@@ -336,7 +391,7 @@
|
||||
|
||||
[_contentView setFrameSize:_CGSizeMake(aWidth, contentHeight)];
|
||||
[self setFrame:_CGRectMake(0.0, aY, aWidth, contentHeight + headerHeight)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resizeSubviewsWithOldSize:(CGSize)aSize
|
||||
@@ -353,7 +408,7 @@
|
||||
/*
|
||||
else if (aKeyPath === "itemHeaderPrototype")
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
+577
-183
@@ -7,6 +7,8 @@
|
||||
*
|
||||
* 11/10/2008 Ross Boucher
|
||||
* - Make it conform to style guidelines, general cleanup and ehancements
|
||||
* 11/10/2010 Antoine Mercadal
|
||||
* - Enhancements, better compliance with Cocoa API
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -26,39 +28,34 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import <AppKit/CPApplication.j>
|
||||
@import <AppKit/CPButton.j>
|
||||
@import <AppKit/CPColor.j>
|
||||
@import <AppKit/CPFont.j>
|
||||
@import <AppKit/CPImage.j>
|
||||
@import <AppKit/CPImageView.j>
|
||||
@import <AppKit/CPPanel.j>
|
||||
@import <AppKit/CPTextField.j>
|
||||
@import "CPApplication.j"
|
||||
@import "CPButton.j"
|
||||
@import "CPColor.j"
|
||||
@import "CPFont.j"
|
||||
@import "CPImage.j"
|
||||
@import "CPImageView.j"
|
||||
@import "CPPanel.j"
|
||||
@import "CPTextField.j"
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPAlertStyle
|
||||
*/
|
||||
CPWarningAlertStyle = 0;
|
||||
CPWarningAlertStyle = 0;
|
||||
/*
|
||||
@global
|
||||
@group CPAlertStyle
|
||||
*/
|
||||
CPInformationalAlertStyle = 1;
|
||||
CPInformationalAlertStyle = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPAlertStyle
|
||||
*/
|
||||
CPCriticalAlertStyle = 2;
|
||||
|
||||
|
||||
var CPAlertWarningImage,
|
||||
CPAlertInformationImage,
|
||||
CPAlertErrorImage;
|
||||
CPCriticalAlertStyle = 2;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
|
||||
|
||||
CPAlert is an alert panel that can be displayed modally to present the
|
||||
user with a message and one or more options.
|
||||
|
||||
@@ -74,41 +71,83 @@ var CPAlertWarningImage,
|
||||
@delegate -(void)alertDidEnd:(CPAlert)theAlert returnCode:(int)returnCode;
|
||||
Called when the user dismisses the alert by clicking one of the buttons.
|
||||
@param theAlert the alert panel that the user dismissed
|
||||
@param returnCode the index of the button that the user clicked (starting from 0,
|
||||
@param returnCode the index of the button that the user clicked (starting from 0,
|
||||
representing the first button added to the alert which appears on the
|
||||
right, 1 representing the next button to the left and so on)
|
||||
*/
|
||||
@implementation CPAlert : CPObject
|
||||
@implementation CPAlert : CPView
|
||||
{
|
||||
CPPanel _alertPanel;
|
||||
BOOL _showHelp @accessors(property=showsHelp);
|
||||
BOOL _showSuppressionButton @accessors(property=showsSuppressionButton);
|
||||
|
||||
CPAlertStyle _alertStyle @accessors(property=alertStyle);
|
||||
CPString _title @accessors(property=title);
|
||||
CPView _accessoryView @accessors(property=accessoryView);
|
||||
CPImage _icon @accessors(property=icon);
|
||||
|
||||
CPArray _buttons @accessors(property=buttons,readonly);
|
||||
CPCheckBox _suppressionButton @accessors(property=suppressionButton,readonly);
|
||||
|
||||
id _delegate @accessors(property=delegate);
|
||||
id _modalDelegate;
|
||||
SEL _didEndSelector;
|
||||
|
||||
CPWindow _window @accessors(property=window,readonly);
|
||||
int _defaultWindowStyle;
|
||||
|
||||
CPTextField _messageLabel;
|
||||
CPImageView _alertImageView;
|
||||
CPTextField _informativeLabel;
|
||||
CPTextField _messageLabel;
|
||||
CPButton _alertHelpButton;
|
||||
|
||||
CPAlertStyle _alertStyle;
|
||||
CPString _windowTitle;
|
||||
int _windowStyle;
|
||||
int _buttonCount;
|
||||
CPArray _buttons;
|
||||
|
||||
id _delegate;
|
||||
BOOL _needsLayout;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
#pragma mark Creating Alerts
|
||||
|
||||
/*!
|
||||
Returns a CPAlert object with the provided info
|
||||
|
||||
@param aMessage the main body text of the alert
|
||||
@param defaultButton the title of the default button
|
||||
@param alternateButton if not nil, the title of a second button
|
||||
@param otherButton if not nil, the title of the third button
|
||||
@param informativeText if not nil the informative text of the alert
|
||||
@return fully initialized CPAlert
|
||||
*/
|
||||
+ (CPAlert)alertWithMessageText:(CPString)aMessage defaultButton:(CPString)defaultButtonTitle alternateButton:(CPString)alternateButtonTitle otherButton:(CPString)otherButtonTitle informativeTextWithFormat:(CPString)informativeText
|
||||
{
|
||||
if (self != CPAlert)
|
||||
return;
|
||||
var alert = [[CPAlert alloc] init];
|
||||
|
||||
var bundle = [CPBundle bundleForClass:[self class]];
|
||||
[alert setMessageText:aMessage];
|
||||
[alert addButtonWithTitle:defaultButtonTitle];
|
||||
|
||||
CPAlertWarningImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-warning.png"]
|
||||
size:CGSizeMake(32.0, 32.0)];
|
||||
|
||||
CPAlertInformationImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-information.png"]
|
||||
size:CGSizeMake(32.0, 32.0)];
|
||||
|
||||
CPAlertErrorImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAlert/dialog-error.png"]
|
||||
size:CGSizeMake(32.0, 32.0)];
|
||||
if (alternateButtonTitle)
|
||||
[alert addButtonWithTitle:alternateButtonTitle];
|
||||
|
||||
if (otherButtonTitle)
|
||||
[alert addButtonWithTitle:otherButtonTitle];
|
||||
|
||||
if (informativeText)
|
||||
[alert setInformativeText:informativeText];
|
||||
|
||||
return alert;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return an CPAlert with type error
|
||||
|
||||
@param anErrorMessage the message of the alert
|
||||
@return fully initialized CPAlert
|
||||
*/
|
||||
+ (CPAlert)alertWithError:(CPString)anErrorMessage
|
||||
{
|
||||
var alert = [[CPAlert alloc] init];
|
||||
|
||||
[alert setMessageText:anErrorMessage];
|
||||
[alert setAlertStyle:CPCriticalAlertStyle];
|
||||
|
||||
return alert;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -116,162 +155,384 @@ var CPAlertWarningImage,
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_buttonCount = 0;
|
||||
_buttons = [CPArray array];
|
||||
_alertStyle = CPWarningAlertStyle;
|
||||
self = [super init];
|
||||
|
||||
[self setWindowStyle:nil];
|
||||
if (self)
|
||||
{
|
||||
_buttons = [];
|
||||
_alertStyle = CPWarningAlertStyle;
|
||||
_showHelp = NO;
|
||||
_needsLayout = YES;
|
||||
_defaultWindowStyle = CPTitledWindowMask;
|
||||
|
||||
_messageLabel = [CPTextField labelWithTitle:@"Alert"];
|
||||
_alertImageView = [[CPImageView alloc] init];
|
||||
_informativeLabel = [[CPTextField alloc] init];
|
||||
_suppressionButton = [CPCheckBox checkBoxWithTitle:@"Do not show this message again"];
|
||||
|
||||
_alertHelpButton = [[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 16.0, 16.0)];
|
||||
[_alertHelpButton setTarget:self];
|
||||
[_alertHelpButton setAction:@selector(_showHelp:)];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Accessors
|
||||
|
||||
/*!
|
||||
Sets the window appearance.
|
||||
@param styleMask - Either CPHUDBackgroundWindowMask or nil for standard.
|
||||
set the theme to use
|
||||
|
||||
@param the theme to use
|
||||
*/
|
||||
- (void)setWindowStyle:(int)styleMask
|
||||
- (void)setTheme:(CPTheme)aTheme
|
||||
{
|
||||
_windowStyle = styleMask;
|
||||
|
||||
_alertPanel = [[CPPanel alloc] initWithContentRect:CGRectMake(0.0, 0.0, 400.0, 110.0) styleMask:styleMask ? styleMask | CPTitledWindowMask : CPTitledWindowMask];
|
||||
[_alertPanel setFloatingPanel:YES];
|
||||
[_alertPanel center];
|
||||
if (aTheme === [self theme])
|
||||
return;
|
||||
|
||||
[_messageLabel setTextColor:(styleMask & CPHUDBackgroundWindowMask) ? [CPColor whiteColor] : [CPColor blackColor]];
|
||||
if (aTheme === [CPTheme defaultHudTheme])
|
||||
_defaultWindowStyle = CPTitledWindowMask | CPHUDBackgroundWindowMask;
|
||||
else
|
||||
_defaultWindowStyle = CPTitledWindowMask;
|
||||
|
||||
var count = [_buttons count];
|
||||
for(var i=0; i < count; i++)
|
||||
{
|
||||
var button = _buttons[i];
|
||||
|
||||
[button setFrameSize:CGSizeMake([button frame].size.width, (styleMask == CPHUDBackgroundWindowMask) ? 20.0 : 24.0)];
|
||||
[button setBezelStyle:(styleMask & CPHUDBackgroundWindowMask) ? CPHUDBezelStyle : CPRoundedBezelStyle];
|
||||
|
||||
[[_alertPanel contentView] addSubview:button];
|
||||
}
|
||||
|
||||
if (!_messageLabel)
|
||||
{
|
||||
var bounds = [[_alertPanel contentView] bounds];
|
||||
|
||||
_messageLabel = [[CPTextField alloc] initWithFrame:CGRectMake(57.0, 10.0, CGRectGetWidth(bounds) - 73.0, 62.0)];
|
||||
[_messageLabel setFont:[CPFont boldSystemFontOfSize:13.0]];
|
||||
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
||||
[_messageLabel setAlignment:CPJustifiedTextAlignment];
|
||||
|
||||
_alertImageView = [[CPImageView alloc] initWithFrame:CGRectMake(15.0, 12.0, 32.0, 32.0)];
|
||||
}
|
||||
|
||||
[[_alertPanel contentView] addSubview:_messageLabel];
|
||||
[[_alertPanel contentView] addSubview:_alertImageView];
|
||||
_window = nil; // will be regenerated at next layout
|
||||
_needsLayout = YES;
|
||||
[super setTheme:aTheme];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the window's title. If this is not defined, a default title based on your warning level will be used.
|
||||
@param aTitle the title to use in place of the default. Set to nil to use default.
|
||||
/*! @deprecated
|
||||
*/
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
- (void)setWindowStyle:(int)aStyle
|
||||
{
|
||||
_windowTitle = aTitle;
|
||||
CPLog.warn("DEPRECATED: setWindowStyle: is deprecated. use setTheme: instead");
|
||||
[self setTheme:(aStyle === CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Gets the window's title.
|
||||
*/
|
||||
- (CPString)title
|
||||
{
|
||||
return _windowTitle;
|
||||
}
|
||||
|
||||
/*!
|
||||
Gets the window's style.
|
||||
/*! @deprecated
|
||||
*/
|
||||
- (int)windowStyle
|
||||
{
|
||||
return _windowStyle;
|
||||
CPLog.warn("DEPRECATED: windowStyle: is deprecated. use theme instead");
|
||||
return _defaultWindowStyle;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
set the text of the alert's message
|
||||
|
||||
@param aText CPString containing the text
|
||||
*/
|
||||
- (void)setMessageText:(CPString)aText
|
||||
{
|
||||
[_messageLabel setStringValue:aText];
|
||||
_needsLayout = YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the receiver’s delegate.
|
||||
@param delegate - Delegate for the alert. nil removes the delegate.
|
||||
*/
|
||||
- (void)setDelegate:(id)delegate
|
||||
{
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Gets the receiver's delegate.
|
||||
*/
|
||||
- (void)delegate
|
||||
{
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the alert style of the receiver.
|
||||
@param style - Alert style for the alert.
|
||||
*/
|
||||
- (void)setAlertStyle:(CPAlertStyle)style
|
||||
{
|
||||
_alertStyle = style;
|
||||
}
|
||||
|
||||
/*!
|
||||
Gets the alert style.
|
||||
*/
|
||||
- (CPAlertStyle)alertStyle
|
||||
{
|
||||
return _alertStyle;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set’s the receiver’s message text, or title, to a given text.
|
||||
@param messageText - Message text for the alert.
|
||||
*/
|
||||
- (void)setMessageText:(CPString)messageText
|
||||
{
|
||||
[_messageLabel setStringValue:messageText];
|
||||
}
|
||||
|
||||
/*!
|
||||
Return's the receiver's message text body.
|
||||
return the content of the message text
|
||||
@return CPString containing the message text
|
||||
*/
|
||||
- (CPString)messageText
|
||||
{
|
||||
return [_messageLabel stringValue];
|
||||
}
|
||||
|
||||
/*!
|
||||
set the text of the alert's informative text
|
||||
|
||||
@param aText CPString containing the informative text
|
||||
*/
|
||||
- (void)setInformativeText:(CPString)aText
|
||||
{
|
||||
[_informativeLabel setStringValue:aText];
|
||||
_needsLayout = YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
return the content of the message text
|
||||
|
||||
@return CPString containing the message text
|
||||
*/
|
||||
- (CPString)informativeText
|
||||
{
|
||||
return [_informativeLabel stringValue];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the title of the alert window.
|
||||
This API is not present in Cocoa.
|
||||
@param aTitle CPString containing the window title
|
||||
*/
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
{
|
||||
_title = aTitle;
|
||||
[_window setTitle:aTitle];
|
||||
}
|
||||
|
||||
/*!
|
||||
set the accessory view
|
||||
|
||||
@param aView the accessory view
|
||||
*/
|
||||
- (void)setAccessoryView:(CPView)aView
|
||||
{
|
||||
_accessoryView = aView;
|
||||
_needsLayout = YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
set if alert shows the suppression button
|
||||
|
||||
@param shouldShowSuppressionButton YES or NO
|
||||
*/
|
||||
- (void)setShowsSuppressionButton:(BOOL)shouldShowSuppressionButton
|
||||
{
|
||||
_showSuppressionButton = shouldShowSuppressionButton;
|
||||
_needsLayout = YES;
|
||||
}
|
||||
|
||||
#pragma mark Accessing Buttons
|
||||
|
||||
/*!
|
||||
Adds a button with a given title to the receiver.
|
||||
Buttons will be added starting from the right hand side of the \c CPAlert panel.
|
||||
The first button will have the index 0, the second button 1 and so on.
|
||||
|
||||
You really shouldn't need more than 3 buttons.
|
||||
*/
|
||||
- (void)addButtonWithTitle:(CPString)title
|
||||
{
|
||||
var bounds = [[_alertPanel contentView] bounds],
|
||||
button = [[CPButton alloc] initWithFrame:CGRectMake(CGRectGetWidth(bounds) - ((_buttonCount + 1) * 90.0), CGRectGetHeight(bounds) - 34.0, 80.0, (_windowStyle == CPHUDBackgroundWindowMask) ? 20.0 : 24.0)];
|
||||
|
||||
[button setTitle:title];
|
||||
[button setTarget:self];
|
||||
[button setTag:_buttonCount];
|
||||
[button setAction:@selector(_notifyDelegate:)];
|
||||
|
||||
[button setBezelStyle:(_windowStyle == CPHUDBackgroundWindowMask) ? CPHUDBezelStyle : CPRoundRectBezelStyle];
|
||||
[[_alertPanel contentView] addSubview:button];
|
||||
|
||||
if (_buttonCount == 0)
|
||||
[_alertPanel setDefaultButton:button];
|
||||
The first button will automatically be given a key equivalent of Return,
|
||||
and any button titled "Cancel" will be given a key equivalent of Escape.
|
||||
|
||||
_buttonCount++;
|
||||
[_buttons addObject:button];
|
||||
You really shouldn't need more than 3 buttons.
|
||||
|
||||
@param title the title of the button
|
||||
*/
|
||||
- (void)addButtonWithTitle:(CPString)aTitle
|
||||
{
|
||||
var bounds = [[_window contentView] bounds],
|
||||
count = [_buttons count],
|
||||
|
||||
button = [[CPButton alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[button setTitle:aTitle];
|
||||
[button setTag:count];
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(_takeReturnCodeFrom:)];
|
||||
|
||||
[[_window contentView] addSubview:button];
|
||||
|
||||
if (count == 0)
|
||||
[button setKeyEquivalent:CPCarriageReturnCharacter];
|
||||
else if ([aTitle lowercaseString] === @"cancel")
|
||||
[button setKeyEquivalent:CPEscapeFunctionKey];
|
||||
|
||||
[_buttons insertObject:button atIndex:0];
|
||||
}
|
||||
|
||||
#pragma mark Layout
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_layoutMessageView
|
||||
{
|
||||
var inset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
sizeWithFontCorrection = 6.0,
|
||||
messageLabelWidth,
|
||||
messageLabelTextSize;
|
||||
|
||||
[_messageLabel setTextColor:[self currentValueForThemeAttribute:@"message-text-color"]];
|
||||
[_messageLabel setFont:[self currentValueForThemeAttribute:@"message-text-font"]];
|
||||
[_messageLabel setTextShadowColor:[self currentValueForThemeAttribute:@"message-text-shadow-color"]];
|
||||
[_messageLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"message-text-shadow-offset"]];
|
||||
[_messageLabel setAlignment:[self currentValueForThemeAttribute:@"message-text-alignment"]];
|
||||
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
||||
|
||||
messageLabelWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right;
|
||||
messageLabelTextSize = [[_messageLabel stringValue] sizeWithFont:[_messageLabel font] inWidth:messageLabelWidth];
|
||||
|
||||
[_messageLabel setFrame:CGRectMake(inset.left, inset.top, messageLabelTextSize.width, messageLabelTextSize.height + sizeWithFontCorrection)];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_layoutInformativeView
|
||||
{
|
||||
var inset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
|
||||
sizeWithFontCorrection = 6.0,
|
||||
informativeLabelWidth,
|
||||
informativeLabelOriginY,
|
||||
informativeLabelTextSize;
|
||||
|
||||
[_informativeLabel setTextColor:[self currentValueForThemeAttribute:@"informative-text-color"]];
|
||||
[_informativeLabel setFont:[self currentValueForThemeAttribute:@"informative-text-font"]];
|
||||
[_informativeLabel setTextShadowColor:[self currentValueForThemeAttribute:@"informative-text-shadow-color"]];
|
||||
[_informativeLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
|
||||
[_informativeLabel setAlignment:[self currentValueForThemeAttribute:@"informative-text-alignment"]];
|
||||
[_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping];
|
||||
|
||||
informativeLabelWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right,
|
||||
informativeLabelOriginY = [_messageLabel frameOrigin].y + [_messageLabel frameSize].height + defaultElementsMargin,
|
||||
informativeLabelTextSize = [[_informativeLabel stringValue] sizeWithFont:[_informativeLabel font] inWidth:informativeLabelWidth];
|
||||
|
||||
[_informativeLabel setFrame:CGRectMake(inset.left, informativeLabelOriginY, informativeLabelTextSize.width, informativeLabelTextSize.height + sizeWithFontCorrection)];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_layoutAccessoryView
|
||||
{
|
||||
if (!_accessoryView)
|
||||
return;
|
||||
|
||||
var inset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
|
||||
accessoryViewWidth = CGRectGetWidth([[_window contentView] frame]) - inset.left - inset.right,
|
||||
accessoryViewOriginY = CGRectGetMaxY([_informativeLabel frame]) + defaultElementsMargin;
|
||||
|
||||
[_accessoryView setFrameOrigin:CGPointMake(inset.left, accessoryViewOriginY)];
|
||||
[[_window contentView] addSubview:_accessoryView];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_layoutSuppressionButton
|
||||
{
|
||||
if (!_showSuppressionButton)
|
||||
return;
|
||||
|
||||
var inset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
suppressionViewXOffset = [self currentValueForThemeAttribute:@"suppression-button-x-offset"],
|
||||
suppressionViewYOffset = [self currentValueForThemeAttribute:@"suppression-button-y-offset"],
|
||||
defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
|
||||
suppressionButtonViewOriginY = CGRectGetMaxY([(_accessoryView || _informativeLabel) frame]) + defaultElementsMargin + suppressionViewYOffset;
|
||||
|
||||
[_suppressionButton setTextColor:[self currentValueForThemeAttribute:@"suppression-button-text-color"]];
|
||||
[_suppressionButton setFont:[self currentValueForThemeAttribute:@"suppression-button-text-font"]];
|
||||
[_suppressionButton setTextShadowColor:[self currentValueForThemeAttribute:@"suppression-button-text-shadow-color"]];
|
||||
[_suppressionButton setTextShadowOffset:[self currentValueForThemeAttribute:@"suppression-button-text-shadow-offset"]];
|
||||
[_suppressionButton sizeToFit];
|
||||
|
||||
[_suppressionButton setFrameOrigin:CGPointMake(inset.left + suppressionViewXOffset, suppressionButtonViewOriginY)];
|
||||
[[_window contentView] addSubview:_suppressionButton];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (CGSize)_layoutButtonsFromView:(CPView)lastView
|
||||
{
|
||||
var inset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
minimumSize = [self currentValueForThemeAttribute:@"size"],
|
||||
buttonOffset = [self currentValueForThemeAttribute:@"button-offset"],
|
||||
helpLeftOffset = [self currentValueForThemeAttribute:@"help-image-left-offset"],
|
||||
aRepresentativeButton = [_buttons objectAtIndex:0],
|
||||
defaultElementsMargin = [self currentValueForThemeAttribute:@"default-elements-margin"],
|
||||
panelSize = [[_window contentView] frame].size,
|
||||
buttonsOriginY,
|
||||
offsetX;
|
||||
|
||||
[aRepresentativeButton setTheme:[self theme]];
|
||||
[aRepresentativeButton sizeToFit];
|
||||
|
||||
panelSize.height = CGRectGetMaxY([lastView frame]) + defaultElementsMargin + [aRepresentativeButton frameSize].height;
|
||||
if (panelSize.height < minimumSize.height)
|
||||
panelSize.height = minimumSize.height;
|
||||
|
||||
buttonsOriginY = panelSize.height - [aRepresentativeButton frameSize].height + buttonOffset;
|
||||
offsetX = panelSize.width - inset.right;
|
||||
|
||||
for (var i = [_buttons count] - 1; i >= 0 ; i--)
|
||||
{
|
||||
var button = _buttons[i];
|
||||
[button setTheme:[self theme]];
|
||||
[button sizeToFit];
|
||||
|
||||
var buttonFrame = [button frame],
|
||||
width = MAX(80.0, CGRectGetWidth(buttonFrame)),
|
||||
height = CGRectGetHeight(buttonFrame);
|
||||
|
||||
offsetX -= width;
|
||||
[button setFrame:CGRectMake(offsetX, buttonsOriginY, width, height)];
|
||||
offsetX -= 10;
|
||||
}
|
||||
|
||||
if (_showHelp)
|
||||
{
|
||||
var helpImage = [self currentValueForThemeAttribute:@"help-image"],
|
||||
helpImagePressed = [self currentValueForThemeAttribute:@"help-image-pressed"],
|
||||
helpImageSize = helpImage ? [helpImage size] : CGSizeMakeZero(),
|
||||
helpFrame = CGRectMake(helpLeftOffset, buttonsOriginY, helpImageSize.width, helpImageSize.height);
|
||||
|
||||
[_alertHelpButton setImage:helpImage];
|
||||
[_alertHelpButton setAlternateImage:helpImagePressed];
|
||||
[_alertHelpButton setBordered:NO];
|
||||
[_alertHelpButton setFrame:helpFrame];
|
||||
}
|
||||
|
||||
panelSize.height += [aRepresentativeButton frameSize].height + inset.bottom + buttonOffset;
|
||||
return panelSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)layout
|
||||
{
|
||||
if (!_needsLayout)
|
||||
return;
|
||||
|
||||
if (!_window)
|
||||
[self _createWindowWithStyle:nil];
|
||||
|
||||
var iconOffset = [self currentValueForThemeAttribute:@"image-offset"],
|
||||
theImage = _icon,
|
||||
finalSize;
|
||||
|
||||
if (!theImage)
|
||||
switch (_alertStyle)
|
||||
{
|
||||
case CPWarningAlertStyle:
|
||||
theImage = [self currentValueForThemeAttribute:@"warning-image"];
|
||||
break;
|
||||
case CPInformationalAlertStyle:
|
||||
theImage = [self currentValueForThemeAttribute:@"information-image"];
|
||||
break;
|
||||
case CPCriticalAlertStyle:
|
||||
theImage = [self currentValueForThemeAttribute:@"error-image"];
|
||||
break;
|
||||
}
|
||||
|
||||
[_alertImageView setImage:theImage];
|
||||
|
||||
var imageSize = theImage ? [theImage size] : CGSizeMakeZero();
|
||||
[_alertImageView setFrame:CGRectMake(iconOffset.x, iconOffset.y, imageSize.width, imageSize.height)];
|
||||
|
||||
[self _layoutMessageView];
|
||||
[self _layoutInformativeView];
|
||||
[self _layoutAccessoryView];
|
||||
[self _layoutSuppressionButton];
|
||||
|
||||
var lastView = _informativeLabel;
|
||||
if (_showSuppressionButton)
|
||||
lastView = _suppressionButton;
|
||||
else if (_accessoryView)
|
||||
lastView = _accessoryView
|
||||
|
||||
finalSize = [self _layoutButtonsFromView:lastView];
|
||||
if ([_window styleMask] & CPDocModalWindowMask)
|
||||
finalSize.height -= 26; // adjust the absence of title bar
|
||||
|
||||
[_window setFrameSize:finalSize];
|
||||
[_window center];
|
||||
|
||||
_needsLayout = NO;
|
||||
}
|
||||
|
||||
#pragma mark Displaying Alerts
|
||||
|
||||
/*!
|
||||
Displays the \c CPAlert panel as a modal dialog. The user will not be
|
||||
able to interact with any other controls until s/he has dismissed the alert
|
||||
@@ -279,34 +540,167 @@ var CPAlertWarningImage,
|
||||
*/
|
||||
- (void)runModal
|
||||
{
|
||||
var theTitle;
|
||||
|
||||
switch (_alertStyle)
|
||||
if (!([_window styleMask] & _defaultWindowStyle))
|
||||
{
|
||||
case CPWarningAlertStyle: [_alertImageView setImage:CPAlertWarningImage];
|
||||
theTitle = @"Warning";
|
||||
break;
|
||||
case CPInformationalAlertStyle: [_alertImageView setImage:CPAlertInformationImage];
|
||||
theTitle = @"Information";
|
||||
break;
|
||||
case CPCriticalAlertStyle: [_alertImageView setImage:CPAlertErrorImage];
|
||||
theTitle = @"Error";
|
||||
break;
|
||||
_needsLayout = YES;
|
||||
[self _createWindowWithStyle:_defaultWindowStyle];
|
||||
}
|
||||
|
||||
[_alertPanel setTitle:_windowTitle ? _windowTitle : theTitle];
|
||||
|
||||
[CPApp runModalForWindow:_alertPanel];
|
||||
|
||||
[self layout];
|
||||
[CPApp runModalForWindow:_window];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_notifyDelegate:(id)button
|
||||
{
|
||||
[CPApp abortModal];
|
||||
[_alertPanel close];
|
||||
/*!
|
||||
Runs the receiver modally as an alert sheet attached to a specified window.
|
||||
|
||||
if (_delegate && [_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
|
||||
[_delegate alertDidEnd:self returnCode:[button tag]];
|
||||
@param window The parent window for the sheet.
|
||||
@param modalDelegate The delegate for the modal-dialog session.
|
||||
@param alertDidEndSelector Message the alert sends to modalDelegate after the sheet is dismissed.
|
||||
@param contextInfo Contextual data passed to modalDelegate in didEndSelector message.
|
||||
*/
|
||||
- (void)beginSheetModalForWindow:(CPWindow)aWindow modalDelegate:(id)modalDelegate didEndSelector:(SEL)alertDidEndSelector contextInfo:(id)contextInfo
|
||||
{
|
||||
if (!([_window styleMask] & CPDocModalWindowMask))
|
||||
{
|
||||
_needsLayout = YES;
|
||||
[self _createWindowWithStyle:CPDocModalWindowMask];
|
||||
}
|
||||
|
||||
[self layout];
|
||||
|
||||
_modalDelegate = modalDelegate;
|
||||
_didEndSelector = alertDidEndSelector;
|
||||
|
||||
[CPApp beginSheet:_window modalForWindow:aWindow modalDelegate:self didEndSelector:@selector(_alertDidEnd:returnCode:contextInfo:) contextInfo:contextInfo];
|
||||
}
|
||||
|
||||
/*!
|
||||
Runs the receiver modally as an alert sheet attached to a specified window.
|
||||
|
||||
@param window The parent window for the sheet.
|
||||
*/
|
||||
- (void)beginSheetModalForWindow:(CPWindow)aWindow
|
||||
{
|
||||
[self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_createWindowWithStyle:(int)forceStyle
|
||||
{
|
||||
var frame = CGRectMakeZero();
|
||||
frame.size = [self currentValueForThemeAttribute:@"size"];
|
||||
|
||||
_window = [[CPWindow alloc] initWithContentRect:frame styleMask:forceStyle || _defaultWindowStyle];
|
||||
|
||||
if (_title)
|
||||
[_window setTitle:_title];
|
||||
|
||||
var contentView = [_window contentView],
|
||||
count = [_buttons count];
|
||||
|
||||
if (count)
|
||||
while (count--)
|
||||
[contentView addSubview:_buttons[count]];
|
||||
else
|
||||
[self addButtonWithTitle:@"OK"];
|
||||
|
||||
[contentView addSubview:_messageLabel];
|
||||
[contentView addSubview:_alertImageView];
|
||||
[contentView addSubview:_informativeLabel];
|
||||
|
||||
if (_showHelp)
|
||||
[contentView addSubview:_alertHelpButton];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (@action)_showHelp:(id)aSender
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(alertShowHelp:)])
|
||||
[_delegate alertShowHelp:self];
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (@action)_takeReturnCodeFrom:(id)aSender
|
||||
{
|
||||
if ([_window isSheet])
|
||||
[CPApp endSheet:_window returnCode:[aSender tag]];
|
||||
else
|
||||
{
|
||||
[CPApp abortModal];
|
||||
[_window close];
|
||||
|
||||
[self _alertDidEnd:_window returnCode:[aSender tag] contextInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_alertDidEnd:(CPWindow)aWindow returnCode:(int)returnCode contextInfo:(id)contextInfo
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
|
||||
[_delegate alertDidEnd:self returnCode:returnCode];
|
||||
|
||||
if (_didEndSelector)
|
||||
objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
|
||||
|
||||
_modalDelegate = nil;
|
||||
_didEndSelector = nil;
|
||||
}
|
||||
|
||||
#pragma mark Theme Attributes
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"alert";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[CGSizeMake(400.0, 110.0), CGInsetMake(15, 15, 15, 50), 6, 10,
|
||||
CPJustifiedTextAlignment, [CPColor blackColor], [CPFont boldSystemFontOfSize:13.0], [CPNull null], CGSizeMakeZero(),
|
||||
CPJustifiedTextAlignment, [CPColor blackColor], [CPFont systemFontOfSize:12.0], [CPNull null], CGSizeMakeZero(),
|
||||
CGPointMake(15, 12),
|
||||
[CPNull null],
|
||||
[CPNull null],
|
||||
[CPNull null],
|
||||
[CPNull null],
|
||||
[CPNull null],
|
||||
[CPNull null],
|
||||
0.0,
|
||||
0.0,
|
||||
3.0,
|
||||
[CPColor blackColor],
|
||||
[CPFont systemFontOfSize:12.0],
|
||||
[CPNull null],
|
||||
0.0
|
||||
]
|
||||
forKeys:[@"size", @"content-inset", @"informative-offset", @"button-offset",
|
||||
@"message-text-alignment", @"message-text-color", @"message-text-font", @"message-text-shadow-color", @"message-text-shadow-offset",
|
||||
@"informative-text-alignment", @"informative-text-color", @"informative-text-font", @"informative-text-shadow-color", @"informative-text-shadow-offset",
|
||||
@"image-offset",
|
||||
@"information-image",
|
||||
@"warning-image",
|
||||
@"error-image",
|
||||
@"help-image",
|
||||
@"help-image-left-offset",
|
||||
@"help-image-pressed",
|
||||
@"suppression-button-y-offset",
|
||||
@"suppression-button-x-offset",
|
||||
@"default-elements-margin",
|
||||
@"suppression-button-text-color",
|
||||
@"suppression-button-text-font",
|
||||
@"suppression-button-text-shadow-color",
|
||||
@"suppression-button-text-shadow-offset"
|
||||
]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+116
-40
@@ -48,14 +48,14 @@ CPAnimationLinear = 3;
|
||||
|
||||
ACTUAL_FRAME_RATE = 0;
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPAnimation
|
||||
|
||||
Manages an animation. Contains timing and progress information.
|
||||
|
||||
@par Delegate Methods
|
||||
|
||||
|
||||
@delegate -(BOOL)animationShouldStart:(CPAnimation)animation;
|
||||
Called at the beginning of \c -startAnimation.
|
||||
@param animation the animation that will start
|
||||
@@ -81,13 +81,13 @@ ACTUAL_FRAME_RATE = 0;
|
||||
{
|
||||
CPTimeInterval _lastTime;
|
||||
CPTimeInterval _duration;
|
||||
|
||||
|
||||
CPAnimationCurve _animationCurve;
|
||||
CAMediaTimingFunction _timingFunction;
|
||||
|
||||
|
||||
float _frameRate;
|
||||
float _progress;
|
||||
|
||||
|
||||
id _delegate;
|
||||
CPTimer _timer;
|
||||
}
|
||||
@@ -101,7 +101,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
- (id)initWithDuration:(float)aDuration animationCurve:(CPAnimationCurve)anAnimationCurve
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_progress = 0.0;
|
||||
@@ -110,14 +110,14 @@ ACTUAL_FRAME_RATE = 0;
|
||||
|
||||
[self setAnimationCurve:anAnimationCurve];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the animation's pace.
|
||||
@param anAnimationCurve the animation's pace
|
||||
@throws CPInvalidArgumentException if an invalid animation curve is specified
|
||||
@throws CPInvalidArgumentException if an invalid animation curve is specified
|
||||
*/
|
||||
- (void)setAnimationCurve:(CPAnimationCurve)anAnimationCurve
|
||||
{
|
||||
@@ -125,13 +125,13 @@ ACTUAL_FRAME_RATE = 0;
|
||||
{
|
||||
case CPAnimationEaseInOut: timingFunctionName = kCAMediaTimingFunctionEaseInEaseOut;
|
||||
break;
|
||||
|
||||
|
||||
case CPAnimationEaseIn: timingFunctionName = kCAMediaTimingFunctionEaseIn;
|
||||
break;
|
||||
|
||||
|
||||
case CPAnimationEaseOut: timingFunctionName = kCAMediaTimingFunctionEaseOut;
|
||||
break;
|
||||
|
||||
|
||||
case CPAnimationLinear: timingFunctionName = kCAMediaTimingFunctionLinear;
|
||||
break;
|
||||
|
||||
@@ -139,7 +139,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
reason:"Invalid value provided for animation curve"];
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
_animationCurve = anAnimationCurve;
|
||||
_timingFunction = [CAMediaTimingFunction functionWithName:timingFunctionName];
|
||||
}
|
||||
@@ -161,7 +161,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
{
|
||||
if (aDuration < 0)
|
||||
[CPException raise:CPInvalidArgumentException reason:"aDuration can't be negative"];
|
||||
|
||||
|
||||
_duration = aDuration;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
{
|
||||
if (frameRate < 0)
|
||||
[CPException raise:CPInvalidArgumentException reason:"frameRate can't be negative"];
|
||||
|
||||
|
||||
_frameRate = frameRate;
|
||||
}
|
||||
|
||||
@@ -221,13 +221,13 @@ ACTUAL_FRAME_RATE = 0;
|
||||
// If we're already animating, or our delegate stops us, animate.
|
||||
if (_timer || _delegate && [_delegate respondsToSelector:@selector(animationShouldStart:)] && ![_delegate animationShouldStart:self])
|
||||
return;
|
||||
|
||||
|
||||
if (_progress === 1.0)
|
||||
_progress = 0.0;
|
||||
|
||||
|
||||
ACTUAL_FRAME_RATE = 0;
|
||||
_lastTime = new Date();
|
||||
|
||||
|
||||
_timer = [CPTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(animationTimerDidFire:) userInfo:nil repeats:YES];
|
||||
}
|
||||
|
||||
@@ -242,14 +242,14 @@ ACTUAL_FRAME_RATE = 0;
|
||||
_lastTime = currentTime;
|
||||
|
||||
++ACTUAL_FRAME_RATE;
|
||||
|
||||
|
||||
[self setCurrentProgress:progress];
|
||||
|
||||
|
||||
if (progress === 1.0)
|
||||
{
|
||||
[_timer invalidate];
|
||||
_timer = nil;
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(animationDidEnd:)])
|
||||
[_delegate animationDidEnd:self];
|
||||
}
|
||||
@@ -262,10 +262,10 @@ ACTUAL_FRAME_RATE = 0;
|
||||
{
|
||||
if (!_timer)
|
||||
return;
|
||||
|
||||
|
||||
[_timer invalidate];
|
||||
_timer = nil;
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(animationDidStop:)])
|
||||
[_delegate animationDidStop:self];
|
||||
}
|
||||
@@ -302,17 +302,17 @@ ACTUAL_FRAME_RATE = 0;
|
||||
- (float)currentValue
|
||||
{
|
||||
var t = [self currentProgress];
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)])
|
||||
return [_delegate animation:self valueForProgress:t];
|
||||
|
||||
|
||||
var c1 = [],
|
||||
c2 = [];
|
||||
|
||||
|
||||
[_timingFunction getControlPointAtIndex:1 values:c1];
|
||||
[_timingFunction getControlPointAtIndex:2 values:c2];
|
||||
|
||||
return CubicBezierAtTime(t,c1[0],c1[1],c2[0],c2[1],_duration);
|
||||
|
||||
return CubicBezierAtTime(t, c1[0], c1[1], c2[0], c2[1], _duration);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -320,28 +320,104 @@ ACTUAL_FRAME_RATE = 0;
|
||||
// currently used function to determine time
|
||||
// 1:1 conversion to js from webkit source files
|
||||
// UnitBezier.h, WebCore_animation_AnimationBase.cpp
|
||||
var CubicBezierAtTime = function CubicBezierAtTime(t,p1x,p1y,p2x,p2y,duration)
|
||||
var CubicBezierAtTime = function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration)
|
||||
{
|
||||
var ax=0,bx=0,cx=0,ay=0,by=0,cy=0;
|
||||
var ax = 0,
|
||||
bx = 0,
|
||||
cx = 0,
|
||||
ay = 0,
|
||||
by = 0,
|
||||
cy = 0;
|
||||
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
|
||||
function sampleCurveX(t) {return ((ax*t+bx)*t+cx)*t;};
|
||||
function sampleCurveY(t) {return ((ay*t+by)*t+cy)*t;};
|
||||
function sampleCurveDerivativeX(t) {return (3.0*ax*t+2.0*bx)*t+cx;};
|
||||
function sampleCurveX(t)
|
||||
{
|
||||
return ((ax * t + bx) * t + cx) * t;
|
||||
}
|
||||
|
||||
function sampleCurveY(t)
|
||||
{
|
||||
return ((ay * t + by) * t + cy) * t;
|
||||
}
|
||||
|
||||
function sampleCurveDerivativeX(t)
|
||||
{
|
||||
return (3.0 * ax * t + 2.0 * bx) * t + cx;
|
||||
}
|
||||
|
||||
// The epsilon value to pass given that the animation is going to run over |duration| seconds. The longer the animation, the more precision is needed in the timing function result to avoid ugly discontinuities.
|
||||
function solveEpsilon(duration) {return 1.0/(200.0*duration);};
|
||||
function solve(x,epsilon) {return sampleCurveY(solveCurveX(x,epsilon));};
|
||||
function solveEpsilon(duration)
|
||||
{
|
||||
return 1.0 / (200.0 * duration);
|
||||
}
|
||||
|
||||
function solve(x, epsilon)
|
||||
{
|
||||
return sampleCurveY(solveCurveX(x, epsilon));
|
||||
}
|
||||
|
||||
// Given an x value, find a parametric value it came from.
|
||||
function solveCurveX(x,epsilon) {var t0,t1,t2,x2,d2,i;
|
||||
function fabs(n) {if(n>=0) {return n;}else {return 0-n;}};
|
||||
function solveCurveX(x, epsilon)
|
||||
{
|
||||
var t0,
|
||||
t1,
|
||||
t2 = x,
|
||||
x2,
|
||||
d2,
|
||||
i = 0;
|
||||
|
||||
// First try a few iterations of Newton's method -- normally very fast.
|
||||
for(t2=x, i=0; i<8; i++) {x2=sampleCurveX(t2)-x; if(fabs(x2)<epsilon) {return t2;} d2=sampleCurveDerivativeX(t2); if(fabs(d2)<1e-6) {break;} t2=t2-x2/d2;}
|
||||
for (; i < 8; i++)
|
||||
{
|
||||
x2 = sampleCurveX(t2) - x;
|
||||
|
||||
if (ABS(x2) < epsilon)
|
||||
return t2;
|
||||
|
||||
d2 = sampleCurveDerivativeX(t2);
|
||||
|
||||
if (ABS(d2) < 1e-6)
|
||||
break;
|
||||
|
||||
t2 = t2 - x2 / d2;
|
||||
}
|
||||
|
||||
// Fall back to the bisection method for reliability.
|
||||
t0=0.0; t1=1.0; t2=x; if(t2<t0) {return t0;} if(t2>t1) {return t1;}
|
||||
while(t0<t1) {x2=sampleCurveX(t2); if(fabs(x2-x)<epsilon) {return t2;} if(x>x2) {t0=t2;}else {t1=t2;} t2=(t1-t0)*.5+t0;}
|
||||
t0 = 0.0;
|
||||
t1 = 1.0;
|
||||
t2 = x;
|
||||
|
||||
if (t2 < t0)
|
||||
return t0;
|
||||
|
||||
if (t2 > t1)
|
||||
return t1;
|
||||
|
||||
while (t0 < t1)
|
||||
{
|
||||
x2 = sampleCurveX(t2);
|
||||
|
||||
if (ABS(x2 - x) < epsilon)
|
||||
return t2;
|
||||
|
||||
if (x > x2)
|
||||
t0 = t2;
|
||||
|
||||
else
|
||||
t1 = t2;
|
||||
|
||||
t2 = (t1 - t0) * 0.5 + t0;
|
||||
}
|
||||
|
||||
return t2; // Failure.
|
||||
};
|
||||
// Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
|
||||
cx=3.0*p1x; bx=3.0*(p2x-p1x)-cx; ax=1.0-cx-bx; cy=3.0*p1y; by=3.0*(p2y-p1y)-cy; ay=1.0-cy-by;
|
||||
cx = 3.0 * p1x;
|
||||
bx = 3.0 * (p2x - p1x) - cx;
|
||||
ax = 1.0 - cx - bx;
|
||||
cy = 3.0 * p1y;
|
||||
by = 3.0 * (p2y - p1y) - cy;
|
||||
ay = 1.0 - cy - by;
|
||||
|
||||
// Convert from input time to parametric value in curve, then from that to output time.
|
||||
return solve(t, solveEpsilon(duration));
|
||||
};
|
||||
|
||||
+463
-219
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,690 @@
|
||||
|
||||
|
||||
/*
|
||||
* CPArrayController.j
|
||||
* AppKit
|
||||
*
|
||||
* Adapted from Cocotron, by Johannes Fortmann
|
||||
*
|
||||
* Created by Ross Boucher.
|
||||
* Copyright 2009, 280 North, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
|
||||
@import "CPObjectController.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
|
||||
|
||||
@implementation CPArrayController : CPObjectController
|
||||
{
|
||||
BOOL _avoidsEmptySelection;
|
||||
BOOL _clearsFilterPredicateOnInsertion;
|
||||
BOOL _filterRestrictsInsertion;
|
||||
BOOL _preservesSelection;
|
||||
BOOL _selectsInsertedObjects;
|
||||
BOOL _alwaysUsesMultipleValuesMarker;
|
||||
|
||||
id _selectionIndexes;
|
||||
id _sortDescriptors;
|
||||
id _filterPredicate;
|
||||
id _arrangedObjects;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self !== [CPArrayController class])
|
||||
return;
|
||||
|
||||
[self exposeBinding:@"contentArray"];
|
||||
[self exposeBinding:@"contentSet"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingContentArray
|
||||
{
|
||||
return [CPSet setWithObjects:"content"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingArrangedObjects
|
||||
{
|
||||
return [CPSet setWithObjects:"content", "filterPredicate", "sortDescriptors"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingSelection
|
||||
{
|
||||
return [CPSet setWithObjects:"selectionIndexes"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingSelectionIndex
|
||||
{
|
||||
return [CPSet setWithObjects:"selectionIndexes"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingSelectionIndexes
|
||||
{
|
||||
// When the arranged objects change, selection preservation may cause the indexes
|
||||
// to change.
|
||||
return [CPSet setWithObjects:"arrangedObjects"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingSelectedObjects
|
||||
{
|
||||
// Don't need to depend on arrangedObjects here because selectionIndexes already does.
|
||||
return [CPSet setWithObjects:"selectionIndexes"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingCanRemove
|
||||
{
|
||||
return [CPSet setWithObjects:"selectionIndexes"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingCanSelectNext
|
||||
{
|
||||
return [CPSet setWithObjects:"selectionIndexes"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingCanSelectPrevious
|
||||
{
|
||||
return [CPSet setWithObjects:"selectionIndexes"];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_sortDescriptors = [CPArray array];
|
||||
_selectionIndexes = [CPIndexSet indexSet];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)prepareContent
|
||||
{
|
||||
[self _setContentArray:[[self newObject]]];
|
||||
}
|
||||
|
||||
- (BOOL)preservesSelection
|
||||
{
|
||||
return _preservesSelection;
|
||||
}
|
||||
|
||||
- (void)setPreservesSelection:(BOOL)value
|
||||
{
|
||||
_preservesSelection = value;
|
||||
}
|
||||
|
||||
- (BOOL)selectsInsertedObjects
|
||||
{
|
||||
return _selectsInsertedObjects;
|
||||
}
|
||||
|
||||
- (void)setSelectsInsertedObjects:(BOOL)value
|
||||
{
|
||||
_selectsInsertedObjects = value;
|
||||
}
|
||||
|
||||
- (BOOL)avoidsEmptySelection
|
||||
{
|
||||
return _avoidsEmptySelection;
|
||||
}
|
||||
|
||||
- (void)setAvoidsEmptySelection:(BOOL)value
|
||||
{
|
||||
_avoidsEmptySelection = value;
|
||||
}
|
||||
|
||||
- (void)setContent:(id)value
|
||||
{
|
||||
if (value === nil)
|
||||
value = [];
|
||||
|
||||
if (![value isKindOfClass:[CPArray class]])
|
||||
value = [value];
|
||||
|
||||
var oldSelectedObjects = nil,
|
||||
oldSelectionIndexes = nil;
|
||||
|
||||
if ([self preservesSelection])
|
||||
oldSelectedObjects = [self selectedObjects];
|
||||
else
|
||||
oldSelectionIndexes = [self selectionIndexes];
|
||||
|
||||
/*
|
||||
When the contents are changed, the selected indexes may no longer refer to the
|
||||
same items. This would cause problems when setSelectedObjects is called below.
|
||||
Any KVO observation would try to retrieve the 'before' value which could be
|
||||
wrong or even throw an exception for no longer existing indexes.
|
||||
|
||||
To avoid that, use the internal __setSelectedObjects which fires no notifications.
|
||||
The selectionIndexes notifications will fire later since they depend on the
|
||||
content key. This pattern is also applied for many other methods throughout this
|
||||
class.
|
||||
*/
|
||||
|
||||
if (_clearsFilterPredicateOnInsertion)
|
||||
[self willChangeValueForKey:@"filterPredicate"];
|
||||
|
||||
// Don't use [super setContent:] as that would fire the contentObject change.
|
||||
// We need to be in control of when notifications fire.
|
||||
_contentObject = value;
|
||||
|
||||
if (_clearsFilterPredicateOnInsertion)
|
||||
[self __setFilterPredicate:nil]; // Causes a _rearrangeObjects.
|
||||
else
|
||||
[self _rearrangeObjects];
|
||||
|
||||
if ([self preservesSelection])
|
||||
[self __setSelectedObjects:oldSelectedObjects];
|
||||
else
|
||||
[self __setSelectionIndexes:oldSelectionIndexes];
|
||||
|
||||
if (_clearsFilterPredicateOnInsertion)
|
||||
[self didChangeValueForKey:@"filterPredicate"];
|
||||
}
|
||||
|
||||
- (void)_setContentArray:(id)anArray
|
||||
{
|
||||
[self setContent:anArray];
|
||||
}
|
||||
|
||||
- (void)_setContentSet:(id)aSet
|
||||
{
|
||||
[self setContent:[aSet allObjects]];
|
||||
}
|
||||
|
||||
- (id)contentArray
|
||||
{
|
||||
return [self content];
|
||||
}
|
||||
|
||||
- (id)contentSet
|
||||
{
|
||||
return [CPSet setWithArray:[self content]];
|
||||
}
|
||||
|
||||
- (CPArray)arrangeObjects:(CPArray)objects
|
||||
{
|
||||
var filterPredicate = [self filterPredicate],
|
||||
sortDescriptors = [self sortDescriptors];
|
||||
|
||||
if (filterPredicate && sortDescriptors)
|
||||
{
|
||||
var sortedObjects = [objects filteredArrayUsingPredicate:filterPredicate];
|
||||
[sortedObjects sortUsingDescriptors:sortDescriptors];
|
||||
return sortedObjects;
|
||||
}
|
||||
else if (filterPredicate)
|
||||
return [objects filteredArrayUsingPredicate:filterPredicate];
|
||||
else if (sortDescriptors)
|
||||
return [objects sortedArrayUsingDescriptors:sortDescriptors];
|
||||
|
||||
return [objects copy];
|
||||
}
|
||||
|
||||
- (void)rearrangeObjects
|
||||
{
|
||||
[self willChangeValueForKey:@"arrangedObjects"];
|
||||
[self _rearrangeObjects];
|
||||
[self didChangeValueForKey:@"arrangedObjects"];
|
||||
}
|
||||
|
||||
/*
|
||||
Like rearrangeObjects but don't fire any change notifications.
|
||||
@ignore
|
||||
*/
|
||||
- (void)_rearrangeObjects
|
||||
{
|
||||
/*
|
||||
Rearranging reapplies the selection criteria and may cause objects to disappear,
|
||||
so take care of the selection.
|
||||
*/
|
||||
var oldSelectedObjects = nil,
|
||||
oldSelectionIndexes = nil;
|
||||
|
||||
if ([self preservesSelection])
|
||||
oldSelectedObjects = [self selectedObjects];
|
||||
else
|
||||
oldSelectionIndexes = [self selectionIndexes];
|
||||
|
||||
[self __setArrangedObjects:[self arrangeObjects:[self contentArray]]];
|
||||
|
||||
if ([self preservesSelection])
|
||||
[self __setSelectedObjects:oldSelectedObjects];
|
||||
else
|
||||
[self __setSelectionIndexes:oldSelectionIndexes];
|
||||
}
|
||||
|
||||
- (void)__setArrangedObjects:(id)value
|
||||
{
|
||||
if (_arrangedObjects === value)
|
||||
return;
|
||||
|
||||
_arrangedObjects = [[_CPObservableArray alloc] initWithArray:value];
|
||||
}
|
||||
|
||||
- (id)arrangedObjects
|
||||
{
|
||||
return _arrangedObjects;
|
||||
}
|
||||
|
||||
- (CPArray)sortDescriptors
|
||||
{
|
||||
return _sortDescriptors;
|
||||
}
|
||||
|
||||
- (void)setSortDescriptors:(CPArray)value
|
||||
{
|
||||
if (_sortDescriptors === value)
|
||||
return;
|
||||
|
||||
_sortDescriptors = [value copy];
|
||||
// Use the non-notification version since arrangedObjects already depends
|
||||
// on sortDescriptors.
|
||||
[self _rearrangeObjects];
|
||||
}
|
||||
|
||||
- (CPPredicate)filterPredicate
|
||||
{
|
||||
return _filterPredicate;
|
||||
}
|
||||
|
||||
- (void)setFilterPredicate:(CPPredicate)value
|
||||
{
|
||||
[self __setFilterPredicate:value];
|
||||
}
|
||||
|
||||
/*
|
||||
Like setFilterPredicate but don't fire any change notifications.
|
||||
@ignore
|
||||
*/
|
||||
- (void)__setFilterPredicate:(CPPredicate)value
|
||||
{
|
||||
if (_filterPredicate === value)
|
||||
return;
|
||||
|
||||
_filterPredicate = value;
|
||||
// Use the non-notification version since arrangedObjects already depends
|
||||
// on filterPredicate.
|
||||
[self _rearrangeObjects];
|
||||
}
|
||||
|
||||
- (BOOL)alwaysUsesMultipleValuesMarker
|
||||
{
|
||||
return _alwaysUsesMultipleValuesMarker;
|
||||
}
|
||||
|
||||
//Selection
|
||||
|
||||
- (unsigned)selectionIndex
|
||||
{
|
||||
return [_selectionIndexes firstIndex];
|
||||
}
|
||||
|
||||
- (BOOL)setSelectionIndex:(unsigned)index
|
||||
{
|
||||
return [self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
||||
}
|
||||
|
||||
- (CPIndexSet)selectionIndexes
|
||||
{
|
||||
return _selectionIndexes;
|
||||
}
|
||||
|
||||
- (BOOL)setSelectionIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
[self _selectionWillChange]
|
||||
[self __setSelectionIndexes:indexes];
|
||||
[self _selectionDidChange];
|
||||
}
|
||||
|
||||
/*
|
||||
Like setSelectionIndex but don't fire any change notifications.
|
||||
@ignore
|
||||
*/
|
||||
- (BOOL)__setSelectionIndex:(int)theIndex
|
||||
{
|
||||
[self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:theIndex]];
|
||||
}
|
||||
|
||||
/*
|
||||
Like setSelectionIndexes but don't fire any change notifications.
|
||||
@ignore
|
||||
*/
|
||||
- (BOOL)__setSelectionIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
if (!indexes)
|
||||
indexes = [CPIndexSet indexSet];
|
||||
|
||||
if (![indexes count])
|
||||
{
|
||||
if (_avoidsEmptySelection && [[self arrangedObjects] count])
|
||||
indexes = [CPIndexSet indexSetWithIndex:0];
|
||||
}
|
||||
else
|
||||
{
|
||||
var objectsCount = [[self arrangedObjects] count];
|
||||
// Remove out of bounds indexes.
|
||||
[indexes removeIndexesInRange:CPMakeRange(objectsCount, [indexes lastIndex] + 1)];
|
||||
// When avoiding empty selection and the deleted selection was at the bottom, select the last item.
|
||||
if (![indexes count] && _avoidsEmptySelection && objectsCount)
|
||||
indexes = [CPIndexSet indexSetWithIndex:objectsCount - 1];
|
||||
}
|
||||
|
||||
if ([_selectionIndexes isEqualToIndexSet:indexes])
|
||||
return NO;
|
||||
|
||||
_selectionIndexes = [indexes copy];
|
||||
|
||||
// Push back the new selection to the model for selectionIndexes if we have one.
|
||||
// There won't be an infinite loop because of the equality check above.
|
||||
var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"];
|
||||
[[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CPArray)selectedObjects
|
||||
{
|
||||
var objects = [[self arrangedObjects] objectsAtIndexes:[self selectionIndexes]];
|
||||
|
||||
return [_CPObservableArray arrayWithArray:(objects || [])];
|
||||
}
|
||||
|
||||
- (BOOL)setSelectedObjects:(CPArray)objects
|
||||
{
|
||||
[self willChangeValueForKey:@"selectionIndexes"];
|
||||
[self _selectionWillChange];
|
||||
|
||||
[self __setSelectedObjects:objects];
|
||||
|
||||
[self didChangeValueForKey:@"selectionIndexes"];
|
||||
[self _selectionDidChange];
|
||||
}
|
||||
|
||||
/*
|
||||
Like setSelectedObjects but don't fire any change notifications.
|
||||
@ignore
|
||||
*/
|
||||
- (BOOL)__setSelectedObjects:(CPArray)objects
|
||||
{
|
||||
var set = [CPIndexSet indexSet],
|
||||
count = [objects count],
|
||||
arrangedObjects = [self arrangedObjects];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var index = [arrangedObjects indexOfObject:[objects objectAtIndex:i]];
|
||||
|
||||
if (index !== CPNotFound)
|
||||
[set addIndex:index];
|
||||
}
|
||||
|
||||
[self __setSelectionIndexes:set];
|
||||
return YES;
|
||||
}
|
||||
|
||||
//Moving selection
|
||||
|
||||
- (BOOL)canSelectPrevious
|
||||
{
|
||||
return [[self selectionIndexes] firstIndex] > 0
|
||||
}
|
||||
|
||||
- (void)selectPrevious:(id)sender
|
||||
{
|
||||
var index = [[self selectionIndexes] firstIndex] - 1;
|
||||
|
||||
if (index >= 0)
|
||||
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
||||
}
|
||||
|
||||
- (BOOL)canSelectNext
|
||||
{
|
||||
return [[self selectionIndexes] firstIndex] < [[self arrangedObjects] count] - 1;
|
||||
}
|
||||
|
||||
- (void)selectNext:(id)sender
|
||||
{
|
||||
var index = [[self selectionIndexes] firstIndex] + 1;
|
||||
|
||||
if (index < [[self arrangedObjects] count])
|
||||
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
||||
}
|
||||
|
||||
//Add/Remove
|
||||
|
||||
- (void)addObject:(id)object
|
||||
{
|
||||
if (![self canAdd])
|
||||
return;
|
||||
|
||||
if (_clearsFilterPredicateOnInsertion)
|
||||
[self willChangeValueForKey:@"filterPredicate"];
|
||||
|
||||
[self willChangeValueForKey:@"content"];
|
||||
[_contentObject addObject:object];
|
||||
|
||||
if (_clearsFilterPredicateOnInsertion)
|
||||
[self __setFilterPredicate:nil];
|
||||
|
||||
if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object])
|
||||
{
|
||||
var pos = [_arrangedObjects insertObject:object inArraySortedByDescriptors:_sortDescriptors];
|
||||
|
||||
// selectionIndexes change notification will be fired as a result of the
|
||||
// content change. Don't fire manually.
|
||||
if (_selectsInsertedObjects)
|
||||
[self __setSelectionIndex:pos];
|
||||
else
|
||||
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:1];
|
||||
}
|
||||
else
|
||||
[self _rearrangeObjects];
|
||||
|
||||
[self didChangeValueForKey:@"content"];
|
||||
if (_clearsFilterPredicateOnInsertion)
|
||||
[self didChangeValueForKey:@"filterPredicate"];
|
||||
}
|
||||
|
||||
- (void)insertObject:(id)anObject atArrangedObjectIndex:(int)anIndex
|
||||
{
|
||||
if (![self canAdd])
|
||||
return;
|
||||
|
||||
if (_clearsFilterPredicateOnInsertion)
|
||||
[self willChangeValueForKey:@"filterPredicate"];
|
||||
|
||||
[self willChangeValueForKey:@"content"];
|
||||
[_contentObject insertObject:anObject atIndex:anIndex];
|
||||
|
||||
if (_clearsFilterPredicateOnInsertion)
|
||||
[self __setFilterPredicate:nil];
|
||||
|
||||
[[self arrangedObjects] insertObject:anObject atIndex:anIndex];
|
||||
|
||||
// selectionIndexes change notification will be fired as a result of the
|
||||
// content change. Don't fire manually.
|
||||
if ([self selectsInsertedObjects])
|
||||
[self __setSelectionIndex:anIndex];
|
||||
else
|
||||
[[self selectionIndexes] shiftIndexesStartingAtIndex:anIndex by:1];
|
||||
|
||||
if ([self avoidsEmptySelection] && [[self selectionIndexes] count] <= 0 && [_contentObject count] > 0)
|
||||
[self __setSelectionIndexes:[CPIndexSet indexSetWithIndex:0]];
|
||||
|
||||
[self didChangeValueForKey:@"content"];
|
||||
if (_clearsFilterPredicateOnInsertion)
|
||||
[self didChangeValueForKey:@"filterPredicate"];
|
||||
}
|
||||
|
||||
- (void)removeObject:(id)object
|
||||
{
|
||||
[self willChangeValueForKey:@"content"];
|
||||
[_contentObject removeObject:object];
|
||||
|
||||
if (_filterPredicate === nil || [_filterPredicate evaluateWithObject:object])
|
||||
{
|
||||
// selectionIndexes change notification will be fired as a result of the
|
||||
// content change. Don't fire manually.
|
||||
var pos = [_arrangedObjects indexOfObject:object];
|
||||
|
||||
[_arrangedObjects removeObjectAtIndex:pos];
|
||||
[_selectionIndexes shiftIndexesStartingAtIndex:pos by:-1];
|
||||
}
|
||||
|
||||
[self didChangeValueForKey:@"content"];
|
||||
}
|
||||
|
||||
- (void)add:(id)sender
|
||||
{
|
||||
if (![self canAdd])
|
||||
return;
|
||||
|
||||
[self insert:sender];
|
||||
}
|
||||
|
||||
- (void)insert:(id)sender
|
||||
{
|
||||
if (![self canInsert])
|
||||
return;
|
||||
|
||||
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject];
|
||||
|
||||
[self addObject:newObject];
|
||||
}
|
||||
|
||||
- (void)remove:(id)sender
|
||||
{
|
||||
[self removeObjects:[[self arrangedObjects] objectsAtIndexes:[self selectionIndexes]]];
|
||||
}
|
||||
|
||||
- (void)removeObjectsAtArrangedObjectIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
[self _removeObjects:[[self arrangedObjects] objectsAtIndexes:indexes]];
|
||||
}
|
||||
|
||||
|
||||
- (void)addObjects:(CPArray)objects
|
||||
{
|
||||
if (![self canAdd])
|
||||
return;
|
||||
|
||||
var contentArray = [self contentArray],
|
||||
count = [objects count];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
[contentArray addObject:[objects objectAtIndex:i]];
|
||||
|
||||
[self setContent:contentArray];
|
||||
}
|
||||
|
||||
- (void)removeObjects:(CPArray)objects
|
||||
{
|
||||
[self _removeObjects:objects];
|
||||
}
|
||||
|
||||
- (void)_removeObjects:(CPArray)objects
|
||||
{
|
||||
[self willChangeValueForKey:@"content"];
|
||||
[_contentObject removeObjectsInArray:objects];
|
||||
|
||||
var arrangedObjects = [self arrangedObjects],
|
||||
position = [arrangedObjects indexOfObject:[objects objectAtIndex:0]];
|
||||
|
||||
[arrangedObjects removeObjectsInArray:objects];
|
||||
|
||||
var objectsCount = [arrangedObjects count],
|
||||
selectionIndexes = [CPIndexSet indexSet];
|
||||
|
||||
if ([self preservesSelection] || [self avoidsEmptySelection])
|
||||
{
|
||||
selectionIndexes = [CPIndexSet indexSetWithIndex:position];
|
||||
|
||||
// Remove the selection if there are no objects
|
||||
if (objectsCount <= 0)
|
||||
selectionIndexes = [CPIndexSet indexSet];
|
||||
|
||||
// Shift selection to last object if position is out of bounds
|
||||
else if (position >= objectsCount)
|
||||
selectionIndexes = [CPIndexSet indexSetWithIndex:objectsCount - 1];
|
||||
}
|
||||
|
||||
_selectionIndexes = selectionIndexes;
|
||||
|
||||
[self didChangeValueForKey:@"content"];
|
||||
}
|
||||
|
||||
- (BOOL)canInsert
|
||||
{
|
||||
return [self isEditable];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPArrayControllerAvoidsEmptySelection = @"CPArrayControllerAvoidsEmptySelection",
|
||||
CPArrayControllerClearsFilterPredicateOnInsertion = @"CPArrayControllerClearsFilterPredicateOnInsertion",
|
||||
CPArrayControllerFilterRestrictsInsertion = @"CPArrayControllerFilterRestrictsInsertion",
|
||||
CPArrayControllerPreservesSelection = @"CPArrayControllerPreservesSelection",
|
||||
CPArrayControllerSelectsInsertedObjects = @"CPArrayControllerSelectsInsertedObjects",
|
||||
CPArrayControllerAlwaysUsesMultipleValuesMarker = @"CPArrayControllerAlwaysUsesMultipleValuesMarker";
|
||||
|
||||
@implementation CPArrayController (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_avoidsEmptySelection = [aCoder decodeBoolForKey:CPArrayControllerAvoidsEmptySelection];
|
||||
_clearsFilterPredicateOnInsertion = [aCoder decodeBoolForKey:CPArrayControllerClearsFilterPredicateOnInsertion];
|
||||
_filterRestrictsInsertion = [aCoder decodeBoolForKey:CPArrayControllerFilterRestrictsInsertion];
|
||||
_preservesSelection = [aCoder decodeBoolForKey:CPArrayControllerPreservesSelection];
|
||||
_selectsInsertedObjects = [aCoder decodeBoolForKey:CPArrayControllerSelectsInsertedObjects];
|
||||
_alwaysUsesMultipleValuesMarker = [aCoder decodeBoolForKey:CPArrayControllerAlwaysUsesMultipleValuesMarker];
|
||||
|
||||
if (![self content] && [self automaticallyPreparesContent])
|
||||
[self prepareContent];
|
||||
else if (![self content])
|
||||
[self _setContentArray:[]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeBool:_avoidsEmptySelection forKey:CPArrayControllerAvoidsEmptySelection];
|
||||
[aCoder encodeBool:_clearsFilterPredicateOnInsertion forKey:CPArrayControllerClearsFilterPredicateOnInsertion];
|
||||
[aCoder encodeBool:_filterRestrictsInsertion forKey:CPArrayControllerFilterRestrictsInsertion];
|
||||
[aCoder encodeBool:_preservesSelection forKey:CPArrayControllerPreservesSelection];
|
||||
[aCoder encodeBool:_selectsInsertedObjects forKey:CPArrayControllerSelectsInsertedObjects];
|
||||
[aCoder encodeBool:_alwaysUsesMultipleValuesMarker forKey:CPArrayControllerAlwaysUsesMultipleValuesMarker];
|
||||
}
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
[self _selectionWillChange];
|
||||
[self _selectionDidChange];
|
||||
}
|
||||
|
||||
@end
|
||||
+12
-11
@@ -4,7 +4,7 @@
|
||||
* Created by Ross Boucher.
|
||||
* Copyright 2009, 280 North, Inc.
|
||||
*
|
||||
* Adapted from Kevin Wojniak, portions Copyright 2009 Kevin Wojniak.
|
||||
* Adapted from Kevin Wojniak, portions Copyright 2009 Kevin Wojniak.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -20,26 +20,27 @@
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
* Copyright 2009 280 North, Inc.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@import <AppKit/CPGraphicsContext.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import "CPGraphicsContext.j"
|
||||
|
||||
|
||||
// Class defaults
|
||||
|
||||
var DefaultLineWidth = 1.0;
|
||||
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPBezierPath
|
||||
|
||||
A CPBezierPath allows you to create paths for drawing to the screen using a simpler API than CoreGraphics.
|
||||
Paths can form any shape, including regular polgyons like squares and triangles; circles, arcs; or complex
|
||||
line segments.
|
||||
|
||||
line segments.
|
||||
|
||||
A path can be stroked and filled using the relevant method. The currently active fill and stroke color will
|
||||
be used, which can be set by calling setFill: and setStroke: on any CPColor object (or set: for both).
|
||||
*/
|
||||
@@ -64,9 +65,9 @@ var DefaultLineWidth = 1.0;
|
||||
+ (CPBezierPath)bezierPathWithOvalInRect:(CGRect)rect
|
||||
{
|
||||
var path = [self bezierPath];
|
||||
|
||||
|
||||
[path appendBezierPathWithOvalInRect:rect];
|
||||
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -76,9 +77,9 @@ var DefaultLineWidth = 1.0;
|
||||
+ (CPBezierPath)bezierPathWithRect:(CGRect)rect
|
||||
{
|
||||
var path = [self bezierPath];
|
||||
|
||||
|
||||
[path appendBezierPathWithRect:rect];
|
||||
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -183,7 +184,6 @@ var DefaultLineWidth = 1.0;
|
||||
CGContextBeginPath(ctx);
|
||||
CGContextAddPath(ctx, _path);
|
||||
CGContextSetLineWidth(ctx, [self lineWidth]);
|
||||
CGContextClosePath(ctx);
|
||||
CGContextStrokePath(ctx);
|
||||
}
|
||||
|
||||
@@ -280,6 +280,7 @@ var DefaultLineWidth = 1.0;
|
||||
{
|
||||
CGPathAddPath(_path, nil, other._path);
|
||||
}
|
||||
|
||||
/*!
|
||||
Remove all path elements; clears path.
|
||||
*/
|
||||
|
||||
+176
-52
@@ -19,25 +19,33 @@
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
@import "CPView.j"
|
||||
|
||||
// CPBoxType
|
||||
CPBoxPrimary = 0;
|
||||
CPBoxSecondary = 1;
|
||||
CPBoxSeparator = 2;
|
||||
CPBoxOldStyle = 3;
|
||||
CPBoxCustom = 4;
|
||||
|
||||
// CPBorderType
|
||||
CPNoBorder = 0;
|
||||
CPLineBorder = 1;
|
||||
CPLineBorder = 1;
|
||||
CPBezelBorder = 2;
|
||||
CPGrooveBorder = 3;
|
||||
|
||||
@implementation CPBox : CPView
|
||||
{
|
||||
CPBoxType _boxType;
|
||||
CPBorderType _borderType;
|
||||
|
||||
|
||||
CPColor _borderColor;
|
||||
CPColor _fillColor;
|
||||
|
||||
|
||||
float _cornerRadius;
|
||||
float _borderWidth;
|
||||
|
||||
|
||||
CPSize _contentMargin;
|
||||
CPView _contentView;
|
||||
}
|
||||
@@ -50,18 +58,19 @@ CPGrooveBorder = 3;
|
||||
[box setFrameFromContentFrame:[aView frame]];
|
||||
|
||||
[enclosingView replaceSubview:aView with:box];
|
||||
|
||||
|
||||
[box setContentView:aView];
|
||||
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)frameRect
|
||||
{
|
||||
self = [super initWithFrame:frameRect];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_borderType = CPBezelBorder;
|
||||
_fillColor = [CPColor clearColor];
|
||||
_borderColor = [CPColor blackColor];
|
||||
|
||||
@@ -69,7 +78,9 @@ CPGrooveBorder = 3;
|
||||
_contentMargin = CGSizeMake(0.0, 0.0);
|
||||
|
||||
_contentView = [[CPView alloc] initWithFrame:[self bounds]];
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
[self setAutoresizesSubviews:YES];
|
||||
[self addSubview:_contentView];
|
||||
}
|
||||
|
||||
@@ -88,9 +99,26 @@ CPGrooveBorder = 3;
|
||||
return _borderType;
|
||||
}
|
||||
|
||||
- (void)setBorderType:(CPBorderType)value
|
||||
- (void)setBorderType:(CPBorderType)aBorderType
|
||||
{
|
||||
_borderType = value;
|
||||
if (_borderType === aBorderType)
|
||||
return;
|
||||
|
||||
_borderType = aBorderType;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (CPBoxType)boxType
|
||||
{
|
||||
return _boxType;
|
||||
}
|
||||
|
||||
- (void)setBoxType:(CPBoxType)aBoxType
|
||||
{
|
||||
if (_boxType === aBoxType)
|
||||
return;
|
||||
|
||||
_boxType = aBoxType;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
@@ -161,9 +189,10 @@ CPGrooveBorder = 3;
|
||||
return;
|
||||
|
||||
[aView setFrame:CGRectInset([self bounds], _contentMargin.width + _borderWidth, _contentMargin.height + _borderWidth)];
|
||||
[aView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
[self replaceSubview:_contentView with:aView];
|
||||
|
||||
_contentView = aView;
|
||||
|
||||
_contentView = aView;
|
||||
}
|
||||
|
||||
- (CPSize)contentViewMargins
|
||||
@@ -173,9 +202,9 @@ CPGrooveBorder = 3;
|
||||
|
||||
- (void)setContentViewMargins:(CPSize)size
|
||||
{
|
||||
if(size.width < 0 || size.height < 0)
|
||||
if (size.width < 0 || size.height < 0)
|
||||
[CPException raise:CPGenericException reason:@"Margins must be positive"];
|
||||
|
||||
|
||||
_contentMargin = CGSizeMakeCopy(size);
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
@@ -189,56 +218,151 @@ CPGrooveBorder = 3;
|
||||
- (void)sizeToFit
|
||||
{
|
||||
var contentFrame = [_contentView frame];
|
||||
|
||||
[self setFrameSize:CGSizeMake(contentFrame.size.width + _contentMargin.width * 2,
|
||||
|
||||
[self setFrameSize:CGSizeMake(contentFrame.size.width + _contentMargin.width * 2,
|
||||
contentFrame.size.height + _contentMargin.height * 2)];
|
||||
|
||||
|
||||
[_contentView setFrameOrigin:CGPointMake(_contentMargin.width, _contentMargin.height)];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CPRect)rect
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
aContext = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
border2 = _borderWidth/2,
|
||||
if (_borderType === CPNoBorder)
|
||||
return;
|
||||
|
||||
strokeRect = CGRectMake(bounds.origin.x + border2,
|
||||
bounds.origin.y + border2,
|
||||
bounds.size.width - _borderWidth,
|
||||
bounds.size.height - _borderWidth),
|
||||
|
||||
fillRect = CGRectMake(bounds.origin.x + border2,
|
||||
bounds.origin.y + border2,
|
||||
bounds.size.width - _borderWidth,
|
||||
bounds.size.height - _borderWidth);
|
||||
var bounds = [self bounds];
|
||||
|
||||
CGContextSetFillColor(aContext, [self fillColor]);
|
||||
CGContextSetStrokeColor(aContext, [self borderColor]);
|
||||
CGContextSetLineWidth(aContext, _borderWidth);
|
||||
|
||||
switch(_borderType)
|
||||
switch (_boxType)
|
||||
{
|
||||
case CPLineBorder: CGContextFillRoundedRectangleInRect(aContext, fillRect, _cornerRadius, YES, YES, YES, YES);
|
||||
CGContextStrokeRoundedRectangleInRect(aContext, strokeRect, _cornerRadius, YES, YES, YES, YES);
|
||||
break;
|
||||
case CPBoxSeparator:
|
||||
// NSBox does not include a horitontal flag for the seperator type. We have to determine
|
||||
// the type of seperator to draw by the width and height of the frame.
|
||||
if (CGRectGetWidth(bounds) === 5.0)
|
||||
return [self _drawVerticalSeperatorInRect:bounds];
|
||||
else if (CGRectGetHeight(bounds) === 5.0)
|
||||
return [self _drawHorizontalSeperatorInRect:bounds];
|
||||
|
||||
case CPBezelBorder: CGContextFillRoundedRectangleInRect(aContext, fillRect, _cornerRadius, YES, YES, YES, YES);
|
||||
CGContextSetStrokeColor(aContext, [CPColor colorWithWhite:190.0/255.0 alpha:1.0]);
|
||||
CGContextBeginPath(aContext);
|
||||
CGContextMoveToPoint(aContext, strokeRect.origin.x, strokeRect.origin.y);
|
||||
CGContextAddLineToPoint(aContext, CGRectGetMinX(strokeRect), CGRectGetMaxY(strokeRect)),
|
||||
CGContextAddLineToPoint(aContext, CGRectGetMaxX(strokeRect), CGRectGetMaxY(strokeRect)),
|
||||
CGContextAddLineToPoint(aContext, CGRectGetMaxX(strokeRect), CGRectGetMinY(strokeRect)),
|
||||
CGContextStrokePath(aContext);
|
||||
CGContextSetStrokeColor(aContext, [CPColor colorWithWhite:142.0/255.0 alpha:1.0]);
|
||||
CGContextBeginPath(aContext);
|
||||
CGContextMoveToPoint(aContext, bounds.origin.x, strokeRect.origin.y);
|
||||
CGContextAddLineToPoint(aContext, CGRectGetMaxX(bounds), CGRectGetMinY(strokeRect));
|
||||
CGContextStrokePath(aContext);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
switch (_borderType)
|
||||
{
|
||||
case CPBezelBorder:
|
||||
[self _drawBezelBorderInRect:bounds];
|
||||
break;
|
||||
|
||||
default:
|
||||
case CPLineBorder:
|
||||
[self _drawLineBorderInRect:bounds];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_drawHorizontalSeperatorInRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextSetStrokeColor(context, [self borderColor]);
|
||||
CGContextSetLineWidth(context, 1.0);
|
||||
|
||||
CGContextMoveToPoint(context, CGRectGetMinX(aRect), CGRectGetMinY(aRect) + 0.5);
|
||||
CGContextAddLineToPoint(context, CGRectGetWidth(aRect), CGRectGetMinY(aRect) + 0.5);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
- (void)_drawVerticalSeperatorInRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextSetStrokeColor(context, [self borderColor]);
|
||||
CGContextSetLineWidth(context, 1.0);
|
||||
|
||||
CGContextMoveToPoint(context, CGRectGetMinX(aRect) + 0.5, CGRectGetMinY(aRect));
|
||||
CGContextAddLineToPoint(context, CGRectGetMinX(aRect) + 0.5, CGRectGetHeight(aRect));
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
- (void)_drawBezelBorderInRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
sides = [CPMinYEdge, CPMaxXEdge, CPMaxYEdge, CPMinXEdge],
|
||||
sideGray = 190.0 / 255.0,
|
||||
grays = [142.0 / 255.0, sideGray, sideGray, sideGray],
|
||||
borderWidth = _borderWidth;
|
||||
|
||||
while (borderWidth--)
|
||||
aRect = CPDrawTiledRects(aRect, aRect, sides, grays);
|
||||
|
||||
CGContextSetFillColor(context, [self fillColor]);
|
||||
CGContextFillRect(context, aRect);
|
||||
}
|
||||
|
||||
- (void)_drawLineBorderInRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
aRect = CGRectInset(aRect, _borderWidth / 2.0, _borderWidth / 2.0);
|
||||
|
||||
CGContextSetFillColor(context, [self fillColor]);
|
||||
CGContextSetStrokeColor(context, [self borderColor]);
|
||||
|
||||
CGContextSetLineWidth(context, _borderWidth);
|
||||
CGContextFillRoundedRectangleInRect(context, aRect, _cornerRadius, YES, YES, YES, YES);
|
||||
CGContextStrokeRoundedRectangleInRect(context, aRect, _cornerRadius, YES, YES, YES, YES);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
CPBoxBorderTypeKey = @"CPBoxBorderTypeKey",
|
||||
CPBoxBorderColorKey = @"CPBoxBorderColorKey",
|
||||
CPBoxFillColorKey = @"CPBoxFillColorKey",
|
||||
CPBoxCornerRadiusKey = @"CPBoxCornerRadiusKey",
|
||||
CPBoxBorderWidthKey = @"CPBoxBorderWidthKey",
|
||||
CPBoxContentMarginKey = @"CPBoxContentMarginKey";
|
||||
|
||||
@implementation CPBox (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_boxType = [aCoder decodeIntForKey:CPBoxTypeKey];
|
||||
_borderType = [aCoder decodeIntForKey:CPBoxBorderTypeKey];
|
||||
|
||||
_borderColor = [aCoder decodeObjectForKey:CPBoxBorderColorKey];
|
||||
_fillColor = [aCoder decodeObjectForKey:CPBoxFillColorKey];
|
||||
|
||||
_cornerRadius = [aCoder decodeFloatForKey:CPBoxCornerRadiusKey];
|
||||
_borderWidth = [aCoder decodeFloatForKey:CPBoxBorderWidthKey];
|
||||
|
||||
_contentMargin = [aCoder decodeSizeForKey:CPBoxContentMarginKey];
|
||||
|
||||
_contentView = [self subviews][0];
|
||||
|
||||
[self setAutoresizesSubviews:YES];
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeInt:_boxType forKey:CPBoxTypeKey];
|
||||
[aCoder encodeInt:_borderType forKey:CPBoxBorderTypeKey];
|
||||
|
||||
[aCoder encodeObject:_borderColor forKey:CPBoxBorderColorKey];
|
||||
[aCoder encodeObject:_fillColor forKey:CPBoxFillColorKey];
|
||||
|
||||
[aCoder encodeFloat:_cornerRadius forKey:CPBoxCornerRadiusKey];
|
||||
[aCoder encodeFloat:_borderWidth forKey:CPBoxBorderWidthKey];
|
||||
|
||||
[aCoder encodeSize:_contentMargin forKey:CPBoxContentMarginKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,955 @@
|
||||
/*
|
||||
* CPBrowser.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Ross Boucher.
|
||||
* Copyright 2010, 280 North, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
|
||||
@import "CPControl.j"
|
||||
@import "CPImage.j"
|
||||
@import "CPTableView.j"
|
||||
@import "CPTextField.j"
|
||||
@import "CPScrollView.j"
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPBrowser
|
||||
*/
|
||||
|
||||
@implementation CPBrowser : CPControl
|
||||
{
|
||||
id _delegate;
|
||||
CPString _pathSeparator;
|
||||
|
||||
CPView _contentView;
|
||||
CPScrollView _horizontalScrollView;
|
||||
CPView _prototypeView;
|
||||
|
||||
CPArray _tableViews;
|
||||
CPArray _tableDelegates;
|
||||
|
||||
id _rootItem;
|
||||
|
||||
BOOL _delegateSupportsImages;
|
||||
|
||||
SEL _doubleAction @accessors(property=doubleAction);
|
||||
|
||||
BOOL _allowsMultipleSelection;
|
||||
BOOL _allowsEmptySelection;
|
||||
|
||||
Class _tableViewClass @accessors(property=tableViewClass);
|
||||
|
||||
float _rowHeight;
|
||||
float _imageWidth;
|
||||
float _leafWidth;
|
||||
float _minColumnWidth;
|
||||
float _defaultColumnWidth @accessors(property=defaultColumnWidth);
|
||||
|
||||
CPArray _columnWidths;
|
||||
}
|
||||
|
||||
+ (CPImage)branchImage
|
||||
{
|
||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPBrowser class]]
|
||||
pathForResource:"browser-leaf.png"]
|
||||
size:CGSizeMake(9,9)];
|
||||
}
|
||||
|
||||
+ (CPImage)highlightedBranchImage
|
||||
{
|
||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPBrowser class]]
|
||||
pathForResource:"browser-leaf-highlighted.png"]
|
||||
size:CGSizeMake(9,9)];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_rowHeight = 23.0;
|
||||
_defaultColumnWidth = 140.0;
|
||||
_minColumnWidth = 80.0;
|
||||
_imageWidth = 23.0;
|
||||
_leafWidth = 13.0;
|
||||
_columnWidths = [];
|
||||
|
||||
_pathSeparator = "/";
|
||||
_tableViews = [];
|
||||
_tableDelegates = [];
|
||||
_allowsMultipleSelection = YES;
|
||||
_allowsEmptySelection = YES;
|
||||
_tableViewClass = [_CPBrowserTableView class];
|
||||
|
||||
_prototypeView = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
[_prototypeView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_prototypeView setValue:[CPColor whiteColor] forThemeAttribute:"text-color" inState:CPThemeStateSelectedDataView];
|
||||
[_prototypeView setLineBreakMode:CPLineBreakByTruncatingTail];
|
||||
|
||||
_horizontalScrollView = [[CPScrollView alloc] initWithFrame:[self bounds]];
|
||||
|
||||
[_horizontalScrollView setHasVerticalScroller:NO];
|
||||
[_horizontalScrollView setAutohidesScrollers:YES];
|
||||
[_horizontalScrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
_contentView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 0, CGRectGetHeight([self bounds]))];
|
||||
[_contentView setAutoresizingMask:CPViewHeightSizable];
|
||||
|
||||
[_horizontalScrollView setDocumentView:_contentView];
|
||||
|
||||
[self addSubview:_horizontalScrollView];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setPrototypeView:(CPView)aPrototypeView
|
||||
{
|
||||
_prototypeView = [CPKeyedUnarchiver unarchiveObjectWithData:
|
||||
[CPKeyedArchiver archivedDataWithRootObject:aPrototypeView]];
|
||||
}
|
||||
|
||||
- (CPView)prototypeView
|
||||
{
|
||||
return [CPKeyedUnarchiver unarchiveObjectWithData:
|
||||
[CPKeyedArchiver archivedDataWithRootObject:_prototypeView]];
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id)anObject
|
||||
{
|
||||
_delegate = anObject;
|
||||
_delegateSupportsImages = [_delegate respondsToSelector:@selector(browser:imageValueForItem:)];
|
||||
|
||||
[self loadColumnZero];
|
||||
}
|
||||
|
||||
- (id)delegate
|
||||
{
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
- (CPTableView)tableViewInColumn:(unsigned)index
|
||||
{
|
||||
return _tableViews[index];
|
||||
}
|
||||
|
||||
- (unsigned)columnOfTableView:(CPTableView)aTableView
|
||||
{
|
||||
return [_tableViews indexOfObject:aTableView];
|
||||
}
|
||||
|
||||
- (void)loadColumnZero
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(rootItemForBrowser:)])
|
||||
_rootItem = [_delegate rootItemForBrowser:self];
|
||||
else
|
||||
_rootItem = nil;
|
||||
|
||||
[self setLastColumn:-1];
|
||||
[self addColumn];
|
||||
}
|
||||
|
||||
- (void)setLastColumn:(int)columnIndex
|
||||
{
|
||||
if (columnIndex >= _tableViews.length)
|
||||
return;
|
||||
|
||||
var oldValue = _tableViews.length - 1,
|
||||
indexPlusOne = columnIndex + 1; // unloads all later columns.
|
||||
|
||||
[[_tableViews.slice(indexPlusOne) valueForKey:"enclosingScrollView"]
|
||||
makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||||
|
||||
_tableViews = _tableViews.slice(0, indexPlusOne);
|
||||
_tableDelegates = _tableDelegates.slice(0, indexPlusOne);
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(browser:didChangeLastColumn:toColumn:)])
|
||||
[_delegate browser:self didChangeLastColumn:oldValue toColumn:columnIndex];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (int)lastColumn
|
||||
{
|
||||
return _tableViews.length - 1;
|
||||
}
|
||||
|
||||
- (void)addColumn
|
||||
{
|
||||
var lastIndex = [self lastColumn],
|
||||
lastColumn = _tableViews[lastIndex],
|
||||
selectionIndexes = [lastColumn selectedRowIndexes];
|
||||
|
||||
if (lastIndex >= 0 && [selectionIndexes count] > 1)
|
||||
[CPException raise:CPInvalidArgumentException
|
||||
reason:"Can't add column, column "+lastIndex+" has invalid selection."];
|
||||
|
||||
var index = lastIndex + 1,
|
||||
item = index === 0 ? _rootItem : [_tableDelegates[lastIndex] childAtIndex:[selectionIndexes firstIndex]];
|
||||
|
||||
if (index > 0 && item && [self isLeafItem:item])
|
||||
return;
|
||||
|
||||
var table = [[_tableViewClass alloc] initWithFrame:CGRectMakeZero() browser:self];
|
||||
|
||||
[table setHeaderView:nil];
|
||||
[table setCornerView:nil];
|
||||
[table setAllowsMultipleSelection:_allowsMultipleSelection];
|
||||
[table setAllowsEmptySelection:_allowsEmptySelection];
|
||||
[table registerForDraggedTypes:[self registeredDraggedTypes]];
|
||||
|
||||
[self _addTableColumnsToTableView:table forColumnIndex:index];
|
||||
|
||||
var delegate = [[_CPBrowserTableDelegate alloc] init];
|
||||
|
||||
[delegate _setDelegate:_delegate];
|
||||
[delegate _setBrowser:self];
|
||||
[delegate _setIndex:index];
|
||||
[delegate _setItem:item];
|
||||
|
||||
_tableViews[index] = table;
|
||||
_tableDelegates[index] = delegate;
|
||||
|
||||
[table setDelegate:delegate];
|
||||
[table setDataSource:delegate];
|
||||
[table setTarget:delegate];
|
||||
[table setAction:@selector(_tableViewClicked:)];
|
||||
[table setDoubleAction:@selector(_tableViewDoubleClicked:)];
|
||||
[table setDraggingDestinationFeedbackStyle:CPTableViewDraggingDestinationFeedbackStyleRegular];
|
||||
|
||||
var scrollView = [[_CPBrowserScrollView alloc] initWithFrame:CGRectMakeZero()];
|
||||
[scrollView _setBrowser:self];
|
||||
[scrollView setDocumentView:table];
|
||||
[scrollView setHasHorizontalScroller:NO];
|
||||
[scrollView setAutoresizingMask:CPViewHeightSizable];
|
||||
|
||||
[_contentView addSubview:scrollView];
|
||||
|
||||
[self tile];
|
||||
|
||||
[self scrollColumnToVisible:index];
|
||||
}
|
||||
|
||||
- (void)_addTableColumnsToTableView:(CPTableView)aTableView forColumnIndex:(unsigned)index
|
||||
{
|
||||
if (_delegateSupportsImages)
|
||||
{
|
||||
var column = [[CPTableColumn alloc] initWithIdentifier:@"Image"],
|
||||
view = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[view setImageScaling:CPScaleProportionally];
|
||||
|
||||
[column setDataView:view];
|
||||
[column setResizingMask:CPTableColumnNoResizing];
|
||||
|
||||
[aTableView addTableColumn:column];
|
||||
}
|
||||
|
||||
var column = [[CPTableColumn alloc] initWithIdentifier:@"Content"];
|
||||
|
||||
[column setDataView:_prototypeView];
|
||||
[column setResizingMask:CPTableColumnNoResizing];
|
||||
|
||||
[aTableView addTableColumn:column];
|
||||
|
||||
var column = [[CPTableColumn alloc] initWithIdentifier:@"Leaf"],
|
||||
view = [[_CPBrowserLeafView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[view setBranchImage:[[self class] branchImage]];
|
||||
[view setHighlightedBranchImage:[[self class] highlightedBranchImage]];
|
||||
|
||||
[column setDataView:view];
|
||||
[column setResizingMask:CPTableColumnNoResizing];
|
||||
|
||||
[aTableView addTableColumn:column];
|
||||
}
|
||||
|
||||
- (void)reloadColumn:(int)column
|
||||
{
|
||||
[[self tableViewInColumn:column] reloadData];
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
{
|
||||
var xOrigin = 0,
|
||||
scrollerWidth = [CPScroller scrollerWidth],
|
||||
height = CGRectGetHeight([_contentView bounds]);
|
||||
|
||||
for (var i = 0, count = _tableViews.length; i < count; i++)
|
||||
{
|
||||
var tableView = _tableViews[i],
|
||||
scrollView = [tableView enclosingScrollView],
|
||||
width = [self widthOfColumn:i],
|
||||
tableHeight = CGRectGetHeight([tableView bounds]);
|
||||
|
||||
[[tableView tableColumnWithIdentifier:"Image"] setWidth:_imageWidth];
|
||||
[[tableView tableColumnWithIdentifier:"Content"] setWidth:width - (_leafWidth + _delegateSupportsImages ? _imageWidth : 0) - scrollerWidth - scrollerWidth];
|
||||
[[tableView tableColumnWithIdentifier:"Leaf"] setWidth:_leafWidth];
|
||||
|
||||
[tableView setRowHeight:_rowHeight];
|
||||
[tableView setFrameSize:CGSizeMake(width - scrollerWidth, tableHeight)];
|
||||
[scrollView setFrameOrigin:CGPointMake(xOrigin, 0)];
|
||||
[scrollView setFrameSize:CGSizeMake(width, height)];
|
||||
|
||||
xOrigin += width;
|
||||
}
|
||||
|
||||
[_contentView setFrameSize:CGSizeMake(xOrigin, height)];
|
||||
}
|
||||
|
||||
- (unsigned)rowAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var column = [self columnAtPoint:aPoint];
|
||||
if (column === -1)
|
||||
return -1;
|
||||
|
||||
var tableView = _tableViews[column];
|
||||
return [tableView rowAtPoint:[tableView convertPoint:aPoint fromView:self]];
|
||||
}
|
||||
|
||||
- (unsigned)columnAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var adjustedPoint = [_contentView convertPoint:aPoint fromView:self];
|
||||
|
||||
for (var i = 0, count = _tableViews.length; i < count; i++)
|
||||
{
|
||||
var frame = [[_tableViews[i] enclosingScrollView] frame];
|
||||
if (CGRectContainsPoint(frame, adjustedPoint))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
- (CGRect)rectOfRow:(unsigned)aRow inColumn:(unsigned)aColumn
|
||||
{
|
||||
var tableView = _tableViews[aColumn],
|
||||
rect = [tableView rectOfRow:aRow];
|
||||
|
||||
rect.origin = [self convertPoint:rect.origin fromView:tableView];
|
||||
return rect;
|
||||
}
|
||||
|
||||
// ITEMS
|
||||
|
||||
- (id)itemAtRow:(int)row inColumn:(int)column
|
||||
{
|
||||
return [_tableDelegates[column] childAtIndex:row];
|
||||
}
|
||||
|
||||
- (BOOL)isLeafItem:(id)item
|
||||
{
|
||||
return [_delegate respondsToSelector:@selector(browser:isLeafItem:)] && [_delegate browser:self isLeafItem:item];
|
||||
}
|
||||
|
||||
- (id)parentForItemsInColumn:(int)column
|
||||
{
|
||||
return [_tableDelegates[column] _item];
|
||||
}
|
||||
|
||||
- (CPSet)selectedItems
|
||||
{
|
||||
var selectedColumn = [self selectedColumn],
|
||||
selectedIndexes = [self selectedRowIndexesInColumn:selectedColumn],
|
||||
set = [CPSet set],
|
||||
index = [selectedIndexes firstIndex];
|
||||
|
||||
while (index !== CPNotFound)
|
||||
{
|
||||
[set addObject:[self itemAtRow:index inColumn:selectedColumn]];
|
||||
index = [selectedIndexes indexGreaterThanIndex:index];
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
- (id)selectedItem
|
||||
{
|
||||
var selectedColumn = [self selectedColumn],
|
||||
selectedRow = [self selectedRowInColumn:selectedColumn];
|
||||
|
||||
return [self itemAtRow:selectedRow inColumn:selectedColumn];
|
||||
}
|
||||
|
||||
// CLICK EVENTS
|
||||
|
||||
- (void)trackMouse:(CPEvent)anEvent
|
||||
{
|
||||
}
|
||||
|
||||
- (void)_column:(unsigned)columnIndex clickedRow:(unsigned)rowIndex
|
||||
{
|
||||
[self setLastColumn:columnIndex];
|
||||
|
||||
if (rowIndex >= 0)
|
||||
[self addColumn];
|
||||
|
||||
[self doClick:self];
|
||||
}
|
||||
|
||||
- (void)sendAction
|
||||
{
|
||||
[self sendAction:_action to:_target];
|
||||
}
|
||||
|
||||
- (void)doClick:(id)sender
|
||||
{
|
||||
[self sendAction:_action to:_target];
|
||||
}
|
||||
|
||||
- (void)doDoubleClick:(id)sender
|
||||
{
|
||||
[self sendAction:_doubleAction to:_target];
|
||||
}
|
||||
|
||||
- (void)keyDown:(CPEvent)anEvent
|
||||
{
|
||||
var column = [self selectedColumn];
|
||||
if (column === -1)
|
||||
return;
|
||||
|
||||
[_tableViews[column] keyDown:anEvent];
|
||||
}
|
||||
|
||||
// SIZING
|
||||
|
||||
- (float)columnContentWidthForColumnWidth:(float)aWidth
|
||||
{
|
||||
return aWidth - (_leafWidth + _delegateSupportsImages ? _imageWidth : 0) - [CPScroller scrollerWidth];
|
||||
}
|
||||
|
||||
- (float)columnWidthForColumnContentWidth:(float)aWidth
|
||||
{
|
||||
return aWidth + (_leafWidth + _delegateSupportsImages ? _imageWidth : 0) + [CPScroller scrollerWidth];
|
||||
}
|
||||
|
||||
- (void)setImageWidth:(float)aWidth
|
||||
{
|
||||
_imageWidth = aWidth;
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (float)imageWidth
|
||||
{
|
||||
return _imageWidth;
|
||||
}
|
||||
|
||||
- (void)setMinColumnWidth:(float)minWidth
|
||||
{
|
||||
_minColumnWidth = minWidth;
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (float)minColumnWidth
|
||||
{
|
||||
return _minColumnWidth;
|
||||
}
|
||||
|
||||
- (void)setWidth:(float)aWidth ofColumn:(unsigned)column
|
||||
{
|
||||
_columnWidths[column] = aWidth;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(browser:didResizeColumn:)])
|
||||
[_delegate browser:self didResizeColumn:column];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (float)widthOfColumn:(unsigned)column
|
||||
{
|
||||
var width = _columnWidths[column];
|
||||
|
||||
if (width == null)
|
||||
width = _defaultColumnWidth;
|
||||
|
||||
return MAX([CPScroller scrollerWidth], MAX(_minColumnWidth, width));
|
||||
}
|
||||
|
||||
- (void)setRowHeight:(float)aHeight
|
||||
{
|
||||
_rowHeight = aHeight;
|
||||
}
|
||||
|
||||
- (float)rowHeight
|
||||
{
|
||||
return _rowHeight;
|
||||
}
|
||||
|
||||
// SCROLLERS
|
||||
|
||||
- (void)scrollColumnToVisible:(unsigned)columnIndex
|
||||
{
|
||||
[_contentView scrollRectToVisible:[[[self tableViewInColumn:columnIndex] enclosingScrollView] frame]];
|
||||
}
|
||||
|
||||
- (void)scrollRowToVisible:(unsigned)rowIndex inColumn:(unsigned)columnIndex
|
||||
{
|
||||
[self scrollColumnToVisible:columnIndex];
|
||||
[[self tableViewInColumn:columnIndex] scrollRowToVisible:rowIndex];
|
||||
}
|
||||
|
||||
- (BOOL)autohidesScroller
|
||||
{
|
||||
return [_horizontalScrollView autohidesScrollers];
|
||||
}
|
||||
|
||||
- (void)setAutohidesScroller:(BOOL)shouldHide
|
||||
{
|
||||
[_horizontalScrollView setAutohidesScrollers:shouldHide];
|
||||
}
|
||||
|
||||
// SELECTION
|
||||
|
||||
- (unsigned)selectedRowInColumn:(unsigned)columnIndex
|
||||
{
|
||||
if (columnIndex > [self lastColumn] || columnIndex < 0)
|
||||
return -1;
|
||||
|
||||
return [_tableViews[columnIndex] selectedRow];
|
||||
}
|
||||
|
||||
- (unsigned)selectedColumn
|
||||
{
|
||||
var column = [self lastColumn],
|
||||
row = [self selectedRowInColumn:column];
|
||||
|
||||
if (row >= 0)
|
||||
return column;
|
||||
else
|
||||
return column - 1;
|
||||
}
|
||||
|
||||
- (void)selectRow:(unsigned)row inColumn:(unsigned)column
|
||||
{
|
||||
var selectedIndexes = row === -1 ? [CPIndexSet indexSet] : [CPIndexSet indexSetWithIndex:row];
|
||||
[self selectRowIndexes:selectedIndexes inColumn:column];
|
||||
}
|
||||
|
||||
- (BOOL)allowsMultipleSelection
|
||||
{
|
||||
return _allowsMultipleSelection;
|
||||
}
|
||||
|
||||
- (void)setAllowsMultipleSelection:(BOOL)shouldAllow
|
||||
{
|
||||
if (_allowsMultipleSelection === shouldAllow)
|
||||
return;
|
||||
|
||||
_allowsMultipleSelection = shouldAllow;
|
||||
[_tableViews makeObjectsPerformSelector:@selector(setAllowsMultipleSelection:) withObject:shouldAllow];
|
||||
}
|
||||
|
||||
- (BOOL)allowsEmptySelection
|
||||
{
|
||||
return _allowsEmptySelection;
|
||||
}
|
||||
|
||||
- (void)setAllowsEmptySelection:(BOOL)shouldAllow
|
||||
{
|
||||
if (_allowsEmptySelection === shouldAllow)
|
||||
return;
|
||||
|
||||
_allowsEmptySelection = shouldAllow;
|
||||
[_tableViews makeObjectsPerformSelector:@selector(setAllowsEmptySelection:) withObject:shouldAllow];
|
||||
}
|
||||
|
||||
- (CPIndexSet)selectedRowIndexesInColumn:(unsigned)column
|
||||
{
|
||||
if (column < 0 || column > [self lastColumn] +1)
|
||||
return [CPIndexSet indexSet];
|
||||
|
||||
return [[self tableViewInColumn:column] selectedRowIndexes];
|
||||
}
|
||||
|
||||
- (void)selectRowIndexes:(CPIndexSet)indexSet inColumn:(unsigned)column
|
||||
{
|
||||
if (column < 0 || column > [self lastColumn] + 1)
|
||||
return;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(browser:selectionIndexesForProposedSelection:inColumn:)])
|
||||
indexSet = [_delegate browser:self selectionIndexesForProposedSelection:indexSet inColumn:column];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(browser:shouldSelectRowIndexes:inColumn:)] &&
|
||||
![_delegate browser:self shouldSelectRowIndexes:indexSet inColumn:column])
|
||||
return;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(browserSelectionIsChanging:)])
|
||||
[_delegate browserSelectionIsChanging:self];
|
||||
|
||||
if (column > [self lastColumn])
|
||||
[self addColumn];
|
||||
|
||||
[self setLastColumn:column];
|
||||
|
||||
[[self tableViewInColumn:column] selectRowIndexes:indexSet byExtendingSelection:NO];
|
||||
|
||||
[self scrollColumnToVisible:column];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(browserSelectionDidChange:)])
|
||||
[_delegate browserSelectionDidChange:self];
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(CPColor)aColor
|
||||
{
|
||||
[super setBackgroundColor:aColor];
|
||||
[_contentView setBackgroundColor:aColor];
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
// DRAG AND DROP
|
||||
|
||||
- (void)registerForDraggedTypes:(CPArray)types
|
||||
{
|
||||
[super registerForDraggedTypes:types];
|
||||
[_tableViews makeObjectsPerformSelector:@selector(registerForDraggedTypes:) withObject:types];
|
||||
}
|
||||
|
||||
- (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(browser:canDragRowsWithIndexes:inColumn:withEvent:)])
|
||||
return [_delegate browser:self canDragRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CPImage)draggingImageForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(browser:draggingImageForRowsWithIndexes:inColumn:withEvent:offset:)])
|
||||
return [_delegate browser:self draggingImageForRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent offset:dragImageOffset];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPView)draggingViewForRowsWithIndexes:(CPIndexSet)rowIndexes inColumn:(int)columnIndex withEvent:(CPEvent)dragEvent offset:(CGPoint)dragImageOffset
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(browser:draggingViewForRowsWithIndexes:inColumn:withEvent:offset:)])
|
||||
return [_delegate browser:self draggingViewForRowsWithIndexes:rowIndexes inColumn:columnIndex withEvent:dragEvent offset:dragImageOffset];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPBrowserResizeControlBackgroundImage = nil;
|
||||
|
||||
@implementation _CPBrowserResizeControl : CPView
|
||||
{
|
||||
CGPoint _mouseDownX;
|
||||
CPBrowser _browser;
|
||||
unsigned _index;
|
||||
unsigned _width;
|
||||
}
|
||||
|
||||
+ (CPImage)backgroundImage
|
||||
{
|
||||
if (!_CPBrowserResizeControlBackgroundImage)
|
||||
{
|
||||
var path = [[CPBundle bundleForClass:[self class]] pathForResource:"browser-resize-control.png"];
|
||||
_CPBrowserResizeControlBackgroundImage = [[CPImage alloc] initWithContentsOfFile:path
|
||||
size:CGSizeMake(15, 14)];
|
||||
}
|
||||
|
||||
return _CPBrowserResizeControlBackgroundImage;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
[self setBackgroundColor:[CPColor colorWithPatternImage:[[self class] backgroundImage]]];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
_mouseDownX = [anEvent locationInWindow].x;
|
||||
_browser = [[self superview] _browser];
|
||||
_index = [_browser columnOfTableView:[[self superview] documentView]];
|
||||
_width = [_browser widthOfColumn:_index];
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
var deltaX = [anEvent locationInWindow].x - _mouseDownX;
|
||||
[_browser setWidth:_width + deltaX ofColumn:_index];
|
||||
}
|
||||
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPBrowserScrollView : CPScrollView
|
||||
{
|
||||
_CPBrowserResizeControl _resizeControl;
|
||||
CPBrowser _browser @accessors;
|
||||
}
|
||||
|
||||
- (void)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_resizeControl = [[_CPBrowserResizeControl alloc] initWithFrame:CGRectMakeZero()];
|
||||
[self addSubview:_resizeControl];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)reflectScrolledClipView:(CPClipView)aClipView
|
||||
{
|
||||
[super reflectScrolledClipView:aClipView];
|
||||
|
||||
var frame = [_verticalScroller frame];
|
||||
frame.size.height = CGRectGetHeight([self bounds]) - 14.0 - frame.origin.y;
|
||||
[_verticalScroller setFrameSize:frame.size];
|
||||
|
||||
var resizeFrame = CGRectMake(CGRectGetMinX(frame), CGRectGetMaxY(frame), [CPScroller scrollerWidth], 14.0);
|
||||
[_resizeControl setFrame:resizeFrame];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPBrowserTableView : CPTableView
|
||||
{
|
||||
CPBrowser _browser;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame browser:(CPBrowser)aBrowser
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
_browser = aBrowser;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
[super mouseDown:anEvent];
|
||||
[[self window] makeFirstResponder:_browser];
|
||||
}
|
||||
|
||||
- (CPView)browserView
|
||||
{
|
||||
return _browser;
|
||||
}
|
||||
|
||||
- (BOOL)canDragRowsWithIndexes:(CPIndexSet)rowIndexes atPoint:(CGPoint)mouseDownPoint
|
||||
{
|
||||
return [_browser canDragRowsWithIndexes:rowIndexes inColumn:[_browser columnOfTableView:self] withEvent:[CPApp currentEvent]];
|
||||
}
|
||||
|
||||
- (CPImage)dragImageForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CPPointPointer)dragImageOffset
|
||||
{
|
||||
return [_browser draggingImageForRowsWithIndexes:dragRows inColumn:[_browser columnOfTableView:self] withEvent:dragEvent offset:dragImageOffset] ||
|
||||
[super dragImageForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragImageOffset];
|
||||
}
|
||||
|
||||
- (CPView)dragViewForRowsWithIndexes:(CPIndexSet)dragRows tableColumns:(CPArray)theTableColumns event:(CPEvent)dragEvent offset:(CPPoint)dragViewOffset
|
||||
{
|
||||
var count = theTableColumns.length;
|
||||
while (count--)
|
||||
{
|
||||
if ([theTableColumns[count] identifier] === "Leaf")
|
||||
[theTableColumns removeObject:theTableColumns[count]];
|
||||
}
|
||||
|
||||
return [_browser draggingViewForRowsWithIndexes:dragRows inColumn:[_browser columnOfTableView:self] withEvent:dragEvent offset:dragViewOffset] ||
|
||||
[super dragViewForRowsWithIndexes:dragRows tableColumns:theTableColumns event:dragEvent offset:dragViewOffset];
|
||||
}
|
||||
|
||||
- (void)moveUp:(id)sender
|
||||
{
|
||||
[super moveUp:sender];
|
||||
[_browser selectRow:[self selectedRow] inColumn:[_browser selectedColumn]];
|
||||
}
|
||||
|
||||
- (void)moveDown:(id)sender
|
||||
{
|
||||
[super moveDown:sender];
|
||||
[_browser selectRow:[self selectedRow] inColumn:[_browser selectedColumn]];
|
||||
}
|
||||
|
||||
- (void)moveLeft:(id)sender
|
||||
{
|
||||
var previousColumn = [_browser selectedColumn] - 1,
|
||||
selectedRow = [_browser selectedRowInColumn:previousColumn];
|
||||
|
||||
[_browser selectRow:selectedRow inColumn:previousColumn];
|
||||
}
|
||||
|
||||
- (void)moveRight:(id)sender
|
||||
{
|
||||
[_browser selectRow:0 inColumn:[_browser selectedColumn] + 1];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@implementation _CPBrowserTableDelegate : CPObject
|
||||
{
|
||||
CPBrowser _browser @accessors;
|
||||
unsigned _index @accessors;
|
||||
id _delegate @accessors;
|
||||
id _item @accessors;
|
||||
}
|
||||
|
||||
- (unsigned)numberOfRowsInTableView:(CPTableView)aTableView
|
||||
{
|
||||
return [_delegate browser:_browser numberOfChildrenOfItem:_item];
|
||||
}
|
||||
|
||||
- (void)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)column row:(unsigned)row
|
||||
{
|
||||
if ([column identifier] === "Image")
|
||||
return [_delegate browser:_browser imageValueForItem:[self childAtIndex:row]];
|
||||
else if ([column identifier] === "Leaf")
|
||||
return ![_browser isLeafItem:[self childAtIndex:row]];
|
||||
else
|
||||
return [_delegate browser:_browser objectValueForItem:[self childAtIndex:row]];
|
||||
}
|
||||
|
||||
- (void)_tableViewDoubleClicked:(CPTableView)aTableView
|
||||
{
|
||||
[_browser doDoubleClick:self];
|
||||
}
|
||||
|
||||
- (void)_tableViewClicked:(CPTableView)aTableView
|
||||
{
|
||||
var selectedIndexes = [aTableView selectedRowIndexes];
|
||||
[_browser _column:_index clickedRow:[selectedIndexes count] === 1 ? [selectedIndexes firstIndex] : -1];
|
||||
}
|
||||
|
||||
- (id)childAtIndex:(unsigned)index
|
||||
{
|
||||
return [_delegate browser:_browser child:index ofItem:_item];
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(CPTableView)aTableView acceptDrop:(id)info row:(int)row dropOperation:(CPTableViewDropOperation)operation
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(browser:acceptDrop:atRow:column:dropOperation:)])
|
||||
return [_delegate browser:_browser acceptDrop:info atRow:row column:_index dropOperation:operation];
|
||||
else
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (CPDragOperation)tableView:(CPTableView)aTableView validateDrop:(id)info proposedRow:(int)row proposedDropOperation:(CPTableViewDropOperation)operation
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(browser:validateDrop:proposedRow:column:dropOperation:)])
|
||||
return [_delegate browser:_browser validateDrop:info proposedRow:row column:_index dropOperation:operation];
|
||||
else
|
||||
return CPDragOperationNone;
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(CPTableView)aTableView writeRowsWithIndexes:(CPIndexSet)rowIndexes toPasteboard:(CPPasteboard)pboard
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(browser:writeRowsWithIndexes:inColumn:toPasteboard:)])
|
||||
return [_delegate browser:_browser writeRowsWithIndexes:rowIndexes inColumn:_index toPasteboard:pboard];
|
||||
else
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)respondsToSelector:(SEL)aSelector
|
||||
{
|
||||
if (aSelector === @selector(browser:writeRowsWithIndexes:inColumn:toPasteboard:))
|
||||
return [_delegate respondsToSelector:@selector(browser:writeRowsWithIndexes:inColumn:toPasteboard:)];
|
||||
else
|
||||
return [super respondsToSelector:aSelector];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPBrowserLeafView : CPView
|
||||
{
|
||||
BOOL _isLeaf @accessors(readonly, property=isLeaf);
|
||||
CPImage _branchImage @accessors(property=branchImage);
|
||||
CPImage _highlightedBranchImage @accessors(property=highlightedBranchImage);
|
||||
}
|
||||
|
||||
- (BOOL)objectValue
|
||||
{
|
||||
return _isLeaf;
|
||||
}
|
||||
|
||||
- (void)setObjectValue:(id)aValue
|
||||
{
|
||||
_isLeaf = !!aValue;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if (aName === "image-view")
|
||||
return CGRectInset([self bounds], 1, 1);
|
||||
|
||||
return [super rectForEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if (aName === "image-view")
|
||||
return [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
return [super createEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var imageView = [self layoutEphemeralSubviewNamed:@"image-view"
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:nil],
|
||||
isHighlighted = [self themeState] & CPThemeStateSelectedDataView;
|
||||
|
||||
[imageView setImage: _isLeaf ? (isHighlighted ? _highlightedBranchImage : _branchImage) : nil];
|
||||
[imageView setImageScaling:CPScaleNone];
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeBool:_isLeaf forKey:"_CPBrowserLeafViewIsLeafKey"];
|
||||
[aCoder encodeObject:_branchImage forKey:"_CPBrowserLeafViewBranchImageKey"];
|
||||
[aCoder encodeObject:_highlightedBranchImage forKey:"_CPBrowserLeafViewHighlightedBranchImageKey"];
|
||||
}
|
||||
|
||||
- (void)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
_isLeaf = [aCoder decodeBoolForKey:"_CPBrowserLeafViewIsLeafKey"];
|
||||
_branchImage = [aCoder decodeObjectForKey:"_CPBrowserLeafViewBranchImageKey"];
|
||||
_highlightedBranchImage = [aCoder decodeObjectForKey:"_CPBrowserLeafViewHighlightedBranchImageKey"];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
+242
-103
@@ -24,31 +24,8 @@
|
||||
@import "CGGeometry.j"
|
||||
|
||||
@import "CPControl.j"
|
||||
@import "CPStringDrawing.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
CPScaleProportionally = 0;
|
||||
CPScaleToFit = 1;
|
||||
CPScaleNone = 2;
|
||||
|
||||
|
||||
/* @group CPCellImagePosition */
|
||||
|
||||
CPNoImage = 0;
|
||||
CPImageOnly = 1;
|
||||
CPImageLeft = 2;
|
||||
CPImageRight = 3;
|
||||
CPImageBelow = 4;
|
||||
CPImageAbove = 5;
|
||||
CPImageOverlaps = 6;
|
||||
|
||||
|
||||
/* @group CPButtonState */
|
||||
|
||||
CPOnState = 1;
|
||||
CPOffState = 0;
|
||||
CPMixedState = -1;
|
||||
|
||||
/* @group CPBezelStyle */
|
||||
|
||||
@@ -93,9 +70,18 @@ CPPushInCellMask = CPPushInButtonMask;
|
||||
CPChangeGrayCellMask = CPGrayButtonMask;
|
||||
CPChangeBackgroundCellMask = CPBackgroundButtonMask;
|
||||
|
||||
CPButtonStateMixed = CPThemeState("mixed");
|
||||
CPButtonStateMixed = CPThemeState("mixed");
|
||||
CPButtonStateBezelStyleRounded = CPThemeState("rounded");
|
||||
|
||||
/*!
|
||||
// add all future correspondance between bezel styles and theme state here.
|
||||
var CPButtonBezelStyleStateMap = [CPDictionary dictionaryWithObjects:[CPButtonStateBezelStyleRounded, nil]
|
||||
forKeys:[CPRoundedBezelStyle, CPRoundRectBezelStyle]];
|
||||
|
||||
|
||||
CPButtonDefaultHeight = 24.0;
|
||||
CPButtonImageOffset = 3.0;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPButton
|
||||
|
||||
@@ -106,12 +92,9 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
@implementation CPButton : CPControl
|
||||
{
|
||||
BOOL _allowsMixedState;
|
||||
|
||||
|
||||
CPString _title;
|
||||
CPString _alternateTitle;
|
||||
|
||||
CPImage _image;
|
||||
CPImage _alternateImage;
|
||||
|
||||
CPInteger _showsStateBy;
|
||||
CPInteger _highlightsBy;
|
||||
@@ -120,6 +103,9 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
// NS-style Display Properties
|
||||
CPBezelStyle _bezelStyle;
|
||||
CPControlSize _controlSize;
|
||||
|
||||
CPString _keyEquivalent;
|
||||
unsigned _keyEquivalentModifierMask;
|
||||
}
|
||||
|
||||
+ (id)buttonWithTitle:(CPString)aTitle
|
||||
@@ -138,21 +124,26 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
return button;
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"button";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMakeZero(), nil]
|
||||
forKeys:[@"bezel-inset", @"content-inset", @"bezel-color"]];
|
||||
return [CPDictionary dictionaryWithObjects:[[CPNull null], 0.0, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null]]
|
||||
forKeys:[@"image", @"image-offset", @"bezel-inset", @"content-inset", @"bezel-color"]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes and returns a newly allocated CPButton object with a specified frame rectangle.
|
||||
@param aFrame The frame rectangle for the created button object.
|
||||
@return An initialized CPView object or nil if the object couldn't be created.
|
||||
*/
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
// Should we instead override the defaults?
|
||||
@@ -160,19 +151,23 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
[self setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"];
|
||||
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
||||
[self setValue:CPScaleNone forThemeAttribute:@"image-scaling"];
|
||||
|
||||
|
||||
_controlSize = CPRegularControlSize;
|
||||
|
||||
// [self setBezelStyle:CPRoundRectBezelStyle];
|
||||
|
||||
_keyEquivalent = @"";
|
||||
_keyEquivalentModifierMask = 0;
|
||||
|
||||
[self setBezelStyle:CPRoundRectBezelStyle];
|
||||
[self setBordered:YES];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// Setting the state
|
||||
/*!
|
||||
Returns \c YES if the button has a 'mixed' state in addition to on and off.
|
||||
Returns a Boolean value indicating whether the button allows a mixed state.
|
||||
@return \c YES if the button has a 'mixed' state in addition to on and off.
|
||||
*/
|
||||
- (BOOL)allowsMixedState
|
||||
{
|
||||
@@ -192,10 +187,14 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
|
||||
_allowsMixedState = aFlag;
|
||||
|
||||
if (!_allowsMixedState)
|
||||
[self unsetThemeState:CPButtonStateMixed];
|
||||
if (!_allowsMixedState && [self state] === CPMixedState)
|
||||
[self setState:CPOnState];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the value of the button using an Objective-J object.
|
||||
@param anObjectValue The value of the button interpreted as an Objective-J object.
|
||||
*/
|
||||
- (void)setObjectValue:(id)anObjectValue
|
||||
{
|
||||
if (!anObjectValue || anObjectValue === @"" || ([anObjectValue intValue] === 0))
|
||||
@@ -204,7 +203,7 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
else if (![anObjectValue isKindOfClass:[CPNumber class]])
|
||||
anObjectValue = CPOnState;
|
||||
|
||||
else if (anObjectValue > CPOnState)
|
||||
else if (anObjectValue >= CPOnState)
|
||||
anObjectValue = CPOnState
|
||||
|
||||
else if (anObjectValue < CPOffState)
|
||||
@@ -230,6 +229,13 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the button's next state.
|
||||
@return The button's state. A button can have two or three states.
|
||||
If it has two, this value is either \c CPOffState (the normal or unpressed state)
|
||||
or \c CPOnState (the alternate or pressed state).
|
||||
If it has three, this value can be \c CPOnState (the feature is in effect everywhere), \c CPOffState (the feature is in effect nowhere), or \c CPMixedState (the feature is in effect somewhere).
|
||||
*/
|
||||
- (CPInteger)nextState
|
||||
{
|
||||
if ([self allowsMixedState])
|
||||
@@ -242,8 +248,16 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
return 1 - [self state];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the button's next state to \c aState.
|
||||
@param aState Possible states are any of the CPButton globals:
|
||||
\c CPOffState, \c CPOnState, \c CPMixedState
|
||||
*/
|
||||
- (void)setNextState
|
||||
{
|
||||
if ([self infoForBinding:CPValueBinding])
|
||||
[self setAllowsMixedState:NO];
|
||||
|
||||
[self setState:[self nextState]];
|
||||
}
|
||||
|
||||
@@ -265,17 +279,27 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
return [self intValue];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the title displayed by the button when in its normal state.
|
||||
@param aTitle The string to set as the button's title. This title is always shown on buttons
|
||||
that don’t use their alternate contents when highlighting or displaying their alternate state.
|
||||
*/
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
{
|
||||
if (_title === aTitle)
|
||||
return;
|
||||
|
||||
|
||||
_title = aTitle;
|
||||
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the title displayed on the button when it’s in its normal state.
|
||||
@return The title displayed on the receiver when it’s in its normal state
|
||||
or the empty string if the button doesn’t display a title.
|
||||
*/
|
||||
- (CPString)title
|
||||
{
|
||||
return _title;
|
||||
@@ -285,7 +309,7 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
{
|
||||
if (_alternateTitle === aTitle)
|
||||
return;
|
||||
|
||||
|
||||
_alternateTitle = aTitle;
|
||||
|
||||
[self setNeedsLayout];
|
||||
@@ -299,18 +323,12 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
|
||||
- (void)setImage:(CPImage)anImage
|
||||
{
|
||||
if (_image === anImage)
|
||||
return;
|
||||
|
||||
_image = anImage;
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setValue:anImage forThemeAttribute:@"image"];
|
||||
}
|
||||
|
||||
- (CPImage)image
|
||||
{
|
||||
return _image;
|
||||
return [self valueForThemeAttribute:@"image" inState:CPThemeStateNormal];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -319,13 +337,7 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
*/
|
||||
- (void)setAlternateImage:(CPImage)anImage
|
||||
{
|
||||
if (_alternateImage === anImage)
|
||||
return;
|
||||
|
||||
_alternateImage = anImage;
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateHighlighted];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -333,7 +345,17 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
*/
|
||||
- (CPImage)alternateImage
|
||||
{
|
||||
return _alternateImage;
|
||||
return [self valueForThemeAttribute:@"image" inState:CPThemeStateHighlighted];
|
||||
}
|
||||
|
||||
- (void)setImageOffset:(float)theImageOffset
|
||||
{
|
||||
[self setValue:theImageOffset forThemeAttribute:@"image-offset"];
|
||||
}
|
||||
|
||||
- (float)imageOffset
|
||||
{
|
||||
return [self valueForThemeAttribute:@"image-offset"];
|
||||
}
|
||||
|
||||
- (void)setShowsStateBy:(CPInteger)aMask
|
||||
@@ -394,13 +416,13 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
[self setShowsStateBy:CPContentsCellMask];
|
||||
break;
|
||||
|
||||
case CPSwitchButton: [CPException raise:CPInvalidArgumentException
|
||||
case CPSwitchButton: [CPException raise:CPInvalidArgumentException
|
||||
reason:"The CPSwitchButton type is not supported in Cappuccino, use the CPCheckBox class instead."];
|
||||
|
||||
case CPRadioButton: [CPException raise:CPInvalidArgumentException
|
||||
case CPRadioButton: [CPException raise:CPInvalidArgumentException
|
||||
reason:"The CPRadioButton type is not supported in Cappuccino, use the CPRadio class instead."];
|
||||
|
||||
default: [CPException raise:CPInvalidArgumentException
|
||||
default: [CPException raise:CPInvalidArgumentException
|
||||
reason:"Unknown button type."];
|
||||
}
|
||||
|
||||
@@ -416,7 +438,7 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
|
||||
_imageDimsWhenDisabled = imageShouldDimWhenDisabled;
|
||||
|
||||
if (_imageDimsWhenDisabled)
|
||||
if ([self hasThemeState:CPThemeStateDisabled])
|
||||
{
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsLayout];
|
||||
@@ -431,7 +453,7 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
||||
{
|
||||
[self highlight:YES];
|
||||
|
||||
|
||||
return [super startTrackingAt:aPoint];
|
||||
}
|
||||
|
||||
@@ -452,15 +474,16 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
if (_CGInsetIsEmpty(contentInset))
|
||||
return bounds;
|
||||
|
||||
bounds = _CGRectMakeCopy(bounds);
|
||||
bounds.origin.x += contentInset.left;
|
||||
bounds.origin.y += contentInset.top;
|
||||
bounds.size.width -= contentInset.left + contentInset.right;
|
||||
bounds.size.height -= contentInset.top + contentInset.bottom;
|
||||
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
- (CGRect)bezelRectForBounds:(CFRect)bounds
|
||||
- (CGRect)bezelRectForBounds:(CGRect)bounds
|
||||
{
|
||||
if (![self isBordered])
|
||||
return _CGRectMakeZero();
|
||||
@@ -470,6 +493,7 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
if (_CGInsetIsEmpty(bezelInset))
|
||||
return bounds;
|
||||
|
||||
bounds = _CGRectMakeCopy(bounds);
|
||||
bounds.origin.x += bezelInset.left;
|
||||
bounds.origin.y += bezelInset.top;
|
||||
bounds.size.width -= bezelInset.left + bezelInset.right;
|
||||
@@ -483,8 +507,20 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
*/
|
||||
- (void)sizeToFit
|
||||
{
|
||||
var size = [([self title] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]],
|
||||
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
[self layoutSubviews];
|
||||
|
||||
var size,
|
||||
contentView = [self ephemeralSubviewNamed:@"content-view"];
|
||||
|
||||
if (contentView)
|
||||
{
|
||||
[contentView sizeToFit];
|
||||
size = [contentView frameSize];
|
||||
}
|
||||
else
|
||||
size = [([self title] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]];
|
||||
|
||||
var contentInset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
minSize = [self currentValueForThemeAttribute:@"min-size"],
|
||||
maxSize = [self currentValueForThemeAttribute:@"max-size"];
|
||||
|
||||
@@ -498,16 +534,19 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
size.height = MIN(size.height, maxSize.height);
|
||||
|
||||
[self setFrameSize:size];
|
||||
|
||||
if (contentView)
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if (aName === "bezel-view")
|
||||
return [self bezelRectForBounds:[self bounds]];
|
||||
|
||||
|
||||
else if (aName === "content-view")
|
||||
return [self contentRectForBounds:[self bounds]];
|
||||
|
||||
|
||||
return [super rectForEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
@@ -518,13 +557,11 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
[view setHitTests:NO];
|
||||
|
||||
|
||||
return view;
|
||||
}
|
||||
else
|
||||
return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
return [super createEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
@@ -532,9 +569,8 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:@"content-view"];
|
||||
|
||||
if (bezelView)
|
||||
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||
|
||||
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||
|
||||
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
|
||||
positioned:CPWindowAbove
|
||||
@@ -543,7 +579,8 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
if (contentView)
|
||||
{
|
||||
[contentView setText:([self hasThemeState:CPThemeStateHighlighted] && _alternateTitle) ? _alternateTitle : _title];
|
||||
[contentView setImage:([self hasThemeState:CPThemeStateHighlighted] && _alternateImage) ? _alternateImage : _image];
|
||||
[contentView setImage:[self currentValueForThemeAttribute:@"image"]];
|
||||
[contentView setImageOffset:[self currentValueForThemeAttribute:@"image-offset"]];
|
||||
|
||||
[contentView setFont:[self currentValueForThemeAttribute:@"font"]];
|
||||
[contentView setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
|
||||
@@ -554,17 +591,10 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
[contentView setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
|
||||
[contentView setImagePosition:[self currentValueForThemeAttribute:@"image-position"]];
|
||||
[contentView setImageScaling:[self currentValueForThemeAttribute:@"image-scaling"]];
|
||||
[contentView setDimsImage:[self hasThemeState:CPThemeStateDisabled] && _imageDimsWhenDisabled];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDefaultButton:(BOOL)shouldBeDefaultButton
|
||||
{
|
||||
if (shouldBeDefaultButton)
|
||||
[self setThemeState:CPThemeStateDefault];
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateDefault];
|
||||
}
|
||||
|
||||
- (void)setBordered:(BOOL)shouldBeBordered
|
||||
{
|
||||
if (shouldBeBordered)
|
||||
@@ -578,26 +608,118 @@ CPButtonStateMixed = CPThemeState("mixed");
|
||||
return [self hasThemeState:CPThemeStateBordered];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the keyboard shortcut for this button. For special keys see
|
||||
CPEvent.j CP...FunctionKey and CPText.j CP...Character.
|
||||
|
||||
@param aString the keyboard shortcut as a string
|
||||
*/
|
||||
- (void)setKeyEquivalent:(CPString)aString
|
||||
{
|
||||
_keyEquivalent = aString || @"";
|
||||
|
||||
// Check if the key equivalent is the enter key
|
||||
// Treat \r and \n as the same key equivalent. See issue #710.
|
||||
if (aString === CPNewlineCharacter || aString === CPCarriageReturnCharacter)
|
||||
[self setThemeState:CPThemeStateDefault];
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateDefault];
|
||||
}
|
||||
|
||||
- (void)viewWillMoveToWindow:(CPWindow)aWindow
|
||||
{
|
||||
var selfWindow = [self window];
|
||||
|
||||
if (selfWindow === aWindow || aWindow === nil)
|
||||
return;
|
||||
|
||||
if ([selfWindow defaultButton] === self)
|
||||
[selfWindow setDefaultButton:nil];
|
||||
|
||||
if ([self keyEquivalent] === CPNewlineCharacter || [self keyEquivalent] === CPCarriageReturnCharacter)
|
||||
[aWindow setDefaultButton:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the keyboard shortcut for this button.
|
||||
*/
|
||||
- (CPString)keyEquivalent
|
||||
{
|
||||
return _keyEquivalent;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the mask used with this button's key equivalent.
|
||||
*/
|
||||
- (void)setKeyEquivalentModifierMask:(unsigned)aMask
|
||||
{
|
||||
_keyEquivalentModifierMask = aMask;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the mask to be used with this button's key equivalent.
|
||||
*/
|
||||
- (unsigned)keyEquivalentModifierMask
|
||||
{
|
||||
return _keyEquivalentModifierMask;
|
||||
}
|
||||
|
||||
/*!
|
||||
Checks the button's key equivalent against that in the event, and if they
|
||||
match simulates a button click.
|
||||
*/
|
||||
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
||||
{
|
||||
// Don't handle the key equivalent for the default window because the window will handle it for us
|
||||
if ([[self window] defaultButton] === self)
|
||||
return NO;
|
||||
|
||||
if (![anEvent _triggersKeyEquivalent:[self keyEquivalent] withModifierMask:[self keyEquivalentModifierMask]])
|
||||
return NO;
|
||||
|
||||
[self performClick:nil];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPButton (NS)
|
||||
|
||||
- (void)setBezelStyle:(unsigned)aBezelStyle
|
||||
{
|
||||
if (aBezelStyle === _bezelStyle)
|
||||
return;
|
||||
|
||||
var currentState = [CPButtonBezelStyleStateMap objectForKey:_bezelStyle],
|
||||
newState = [CPButtonBezelStyleStateMap objectForKey:aBezelStyle];
|
||||
|
||||
if (currentState)
|
||||
[self unsetThemeState:currentState];
|
||||
|
||||
if (newState)
|
||||
[self setThemeState:newState];
|
||||
|
||||
_bezelStyle = aBezelStyle;
|
||||
}
|
||||
|
||||
- (unsigned)bezelStyle
|
||||
{
|
||||
return _bezelStyle;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPButtonImageKey = @"CPButtonImageKey",
|
||||
CPButtonAlternateImageKey = @"CPButtonAlternateImageKey",
|
||||
CPButtonTitleKey = @"CPButtonTitleKey",
|
||||
CPButtonAlternateTitleKey = @"CPButtonAlternateTitleKey",
|
||||
CPButtonIsBorderedKey = @"CPButtonIsBorderedKey";
|
||||
var CPButtonImageKey = @"CPButtonImageKey",
|
||||
CPButtonAlternateImageKey = @"CPButtonAlternateImageKey",
|
||||
CPButtonTitleKey = @"CPButtonTitleKey",
|
||||
CPButtonAlternateTitleKey = @"CPButtonAlternateTitleKey",
|
||||
CPButtonIsBorderedKey = @"CPButtonIsBorderedKey",
|
||||
CPButtonAllowsMixedStateKey = @"CPButtonAllowsMixedStateKey",
|
||||
CPButtonImageDimsWhenDisabledKey = @"CPButtonImageDimsWhenDisabledKey",
|
||||
CPButtonImagePositionKey = @"CPButtonImagePositionKey",
|
||||
CPButtonKeyEquivalentKey = @"CPButtonKeyEquivalentKey",
|
||||
CPButtonKeyEquivalentMaskKey = @"CPButtonKeyEquivalentMaskKey";
|
||||
|
||||
@implementation CPButton (CPCoding)
|
||||
|
||||
@@ -608,21 +730,31 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_controlSize = CPRegularControlSize;
|
||||
|
||||
[self setImage:[aCoder decodeObjectForKey:CPButtonImageKey]];
|
||||
[self setAlternateImage:[aCoder decodeObjectForKey:CPButtonAlternateImageKey]];
|
||||
|
||||
[self setTitle:[aCoder decodeObjectForKey:CPButtonTitleKey]];
|
||||
[self setAlternateTitle:[aCoder decodeObjectForKey:CPButtonAlternateTitleKey]];
|
||||
|
||||
_title = [aCoder decodeObjectForKey:CPButtonTitleKey];
|
||||
_alternateTitle = [aCoder decodeObjectForKey:CPButtonAlternateTitleKey];
|
||||
|
||||
if ([aCoder containsValueForKey:CPButtonAllowsMixedStateKey])
|
||||
_allowsMixedState = [aCoder decodeBoolForKey:CPButtonAllowsMixedStateKey];
|
||||
|
||||
[self setImageDimsWhenDisabled:[aCoder decodeObjectForKey:CPButtonImageDimsWhenDisabledKey]];
|
||||
|
||||
if ([aCoder containsValueForKey:CPButtonImagePositionKey])
|
||||
[self setImagePosition:[aCoder decodeIntForKey:CPButtonImagePositionKey]];
|
||||
|
||||
if ([aCoder containsValueForKey:CPButtonKeyEquivalentKey])
|
||||
[self setKeyEquivalent:CFData.decodeBase64ToUtf16String([aCoder decodeObjectForKey:CPButtonKeyEquivalentKey])];
|
||||
|
||||
_keyEquivalentModifierMask = [aCoder decodeIntForKey:CPButtonKeyEquivalentMaskKey];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -634,11 +766,18 @@ var CPButtonImageKey = @"CPButtonImageKey",
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeObject:_image forKey:CPButtonImageKey];
|
||||
[aCoder encodeObject:_alternateImage forKey:CPButtonAlternateImageKey];
|
||||
|
||||
[aCoder encodeObject:_title forKey:CPButtonTitleKey];
|
||||
[aCoder encodeObject:_alternateTitle forKey:CPButtonAlternateTitleKey];
|
||||
|
||||
[aCoder encodeBool:_allowsMixedState forKey:CPButtonAllowsMixedStateKey];
|
||||
|
||||
[aCoder encodeBool:[self imageDimsWhenDisabled] forKey:CPButtonImageDimsWhenDisabledKey];
|
||||
[aCoder encodeInt:[self imagePosition] forKey:CPButtonImagePositionKey];
|
||||
|
||||
if (_keyEquivalent)
|
||||
[aCoder encodeObject:CFData.encodeBase64Utf16String(_keyEquivalent) forKey:CPButtonKeyEquivalentKey];
|
||||
|
||||
[aCoder encodeInt:_keyEquivalentModifierMask forKey:CPButtonKeyEquivalentMaskKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+244
-28
@@ -1,61 +1,277 @@
|
||||
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
@implementation CPButtonBar : CPControl
|
||||
@implementation CPButtonBar : CPView
|
||||
{
|
||||
BOOL _hasResizeControl;
|
||||
BOOL _resizeControlIsLeftAligned;
|
||||
CPArray _buttons;
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
+ (id)plusButton
|
||||
{
|
||||
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
||||
image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"plus_button.png"] size:CGSizeMake(11, 12)];
|
||||
|
||||
[button setBordered:NO];
|
||||
[button setImage:image];
|
||||
[button setImagePosition:CPImageOnly];
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
+ (id)minusButton
|
||||
{
|
||||
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
||||
image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"minus_button.png"] size:CGSizeMake(11, 4)];
|
||||
|
||||
[button setBordered:NO];
|
||||
[button setImage:image];
|
||||
[button setImagePosition:CPImageOnly];
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
+ (id)actionPopupButton
|
||||
{
|
||||
var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
|
||||
image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"action_button.png"] size:CGSizeMake(22, 14)];
|
||||
|
||||
[button addItemWithTitle:nil];
|
||||
[[button lastItem] setImage:image];
|
||||
[button setImagePosition:CPImageOnly];
|
||||
[button setValue:CGInsetMake(0, 0, 0, 0) forThemeAttribute:"content-inset"];
|
||||
|
||||
[button setPullsDown:YES];
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"button-bar";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[nil]
|
||||
forKeys:[@"bezel-color"]];
|
||||
return [CPDictionary dictionaryWithObjects:[CGInsetMake(0.0, 0.0, 0.0, 0.0), CGSizeMakeZero(), [CPNull null], [CPNull null], [CPNull null], [CPNull null]]
|
||||
forKeys:[@"resize-control-inset", @"resize-control-size", @"resize-control-color", @"bezel-color", @"button-bezel-color", @"button-text-color"]];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_buttons = [];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
var view = [self superview],
|
||||
subview = self;
|
||||
|
||||
while (view)
|
||||
{
|
||||
if ([view isKindOfClass:[CPSplitView class]])
|
||||
{
|
||||
var viewIndex = [[view subviews] indexOfObject:subview];
|
||||
[view setButtonBar:self forDividerAtIndex:viewIndex];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
subview = view;
|
||||
view = [view superview];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setButtons:(CPArray)buttons
|
||||
{
|
||||
_buttons = [CPArray arrayWithArray:buttons];
|
||||
|
||||
for (var i = 0, count = [_buttons count]; i < count; i++)
|
||||
[_buttons[i] setBordered:YES];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (CPArray)buttons
|
||||
{
|
||||
return [CPArray arrayWithArray:_buttons];
|
||||
}
|
||||
|
||||
- (void)setHasResizeControl:(BOOL)shouldHaveResizeControl
|
||||
{
|
||||
if (_hasResizeControl === shouldHaveResizeControl)
|
||||
return;
|
||||
|
||||
_hasResizeControl = !!shouldHaveResizeControl;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (BOOL)hasResizeControl
|
||||
{
|
||||
return _hasResizeControl;
|
||||
}
|
||||
|
||||
- (void)setResizeControlIsLeftAligned:(BOOL)shouldBeLeftAligned
|
||||
{
|
||||
if (_resizeControlIsLeftAligned === shouldBeLeftAligned)
|
||||
return;
|
||||
|
||||
_resizeControlIsLeftAligned = !!shouldBeLeftAligned;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (BOOL)resizeControlIsLeftAligned
|
||||
{
|
||||
return _resizeControlIsLeftAligned;
|
||||
}
|
||||
|
||||
- (CGRect)resizeControlFrame
|
||||
{
|
||||
var inset = [self currentValueForThemeAttribute:@"resize-control-inset"],
|
||||
size = [self currentValueForThemeAttribute:@"resize-control-size"],
|
||||
currentSize = [self bounds],
|
||||
leftOrigin = _resizeControlIsLeftAligned ? 0 : currentSize.size.width - size.width - inset.right - inset.left;
|
||||
|
||||
return CGRectMake(leftOrigin, 0, size.width + inset.left + inset.right, size.height + inset.top + inset.bottom);
|
||||
}
|
||||
|
||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if (aName === "bezel-view")
|
||||
return [self bounds];
|
||||
|
||||
if (aName === "resize-control-view")
|
||||
{
|
||||
var inset = [self currentValueForThemeAttribute:@"resize-control-inset"],
|
||||
size = [self currentValueForThemeAttribute:@"resize-control-size"],
|
||||
currentSize = [self bounds];
|
||||
|
||||
if (_resizeControlIsLeftAligned)
|
||||
return CGRectMake(inset.left, inset.top, size.width, size.height);
|
||||
else
|
||||
return CGRectMake(currentSize.size.width - size.width - inset.right, inset.top, size.width, size.height);
|
||||
}
|
||||
|
||||
return [super rectForEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if (aName === "bezel-view")
|
||||
{
|
||||
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
if (aName === "resize-control-view")
|
||||
return [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[view setHitTests:NO];
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
return [super createEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:@""];
|
||||
|
||||
if (bezelView)
|
||||
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||
|
||||
var normalColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateNormal],
|
||||
highlightedColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateHighlighted],
|
||||
disabledColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateDisabled],
|
||||
textColor = [self valueForThemeAttribute:@"button-text-color" inState:CPThemeStateNormal];
|
||||
|
||||
var buttonsNotHidden = [CPArray arrayWithArray:_buttons],
|
||||
count = [buttonsNotHidden count];
|
||||
|
||||
while (count--)
|
||||
if ([buttonsNotHidden[count] isHidden])
|
||||
[buttonsNotHidden removeObject:buttonsNotHidden[count]];
|
||||
|
||||
var currentButtonOffset = _resizeControlIsLeftAligned ? CGRectGetMaxX([self bounds]) + 1 : -1,
|
||||
bounds = [self bounds],
|
||||
height = CGRectGetHeight(bounds) - 1,
|
||||
frameWidth = CGRectGetWidth(bounds),
|
||||
resizeRect = _hasResizeControl ? [self rectForEphemeralSubviewNamed:"resize-control-view"] : CGRectMakeZero(),
|
||||
resizeWidth = CGRectGetWidth(resizeRect),
|
||||
availableWidth = frameWidth - resizeWidth - 1;
|
||||
|
||||
for (var i = 0, count = [buttonsNotHidden count]; i < count; i++)
|
||||
{
|
||||
var button = buttonsNotHidden[i],
|
||||
width = CGRectGetWidth([button frame]);
|
||||
|
||||
if (availableWidth > width)
|
||||
availableWidth -= width;
|
||||
else
|
||||
break;
|
||||
|
||||
if (_resizeControlIsLeftAligned)
|
||||
{
|
||||
[button setFrame:CGRectMake(currentButtonOffset - width, 1, width, height)];
|
||||
currentButtonOffset -= width - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
[button setFrame:CGRectMake(currentButtonOffset, 1, width, height)];
|
||||
currentButtonOffset += width - 1;
|
||||
}
|
||||
|
||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal | CPThemeStateBordered];
|
||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted | CPThemeStateBordered];
|
||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled | CPThemeStateBordered];
|
||||
[button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered];
|
||||
|
||||
// FIXME shouldn't need this
|
||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
|
||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
|
||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled | CPThemeStateBordered | CPPopUpButtonStatePullsDown];
|
||||
|
||||
[self addSubview:button];
|
||||
}
|
||||
|
||||
if (_hasResizeControl)
|
||||
{
|
||||
var resizeControlView = [self layoutEphemeralSubviewNamed:@"resize-control-view"
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[resizeControlView setAutoresizingMask: _resizeControlIsLeftAligned ? CPViewMaxXMargin : CPViewMinXMargin];
|
||||
[resizeControlView setBackgroundColor:[self currentValueForThemeAttribute:@"resize-control-color"]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addSubview:(CPView)aSubview
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
[super addSubview:aSubview];
|
||||
|
||||
[aSubview setAutoresizingMask:CPViewMinXMargin];
|
||||
[super setFrameSize:aSize];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPButtonBarHasResizeControlKey = @"CPButtonBarHasResizeControlKey",
|
||||
CPButtonBarResizeControlIsLeftAlignedKey = @"CPButtonBarResizeControlIsLeftAlignedKey",
|
||||
CPButtonBarButtonsKey = @"CPButtonBarButtonsKey";
|
||||
|
||||
@implementation CPButtonBar (CPCoding)
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeBool:_hasResizeControl forKey:CPButtonBarHasResizeControlKey];
|
||||
[aCoder encodeBool:_resizeControlIsLeftAligned forKey:CPButtonBarResizeControlIsLeftAlignedKey];
|
||||
[aCoder encodeObject:_buttons forKey:CPButtonBarButtonsKey];
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
_buttons = [aCoder decodeObjectForKey:CPButtonBarButtonsKey] || [];
|
||||
_hasResizeControl = [aCoder decodeBoolForKey:CPButtonBarHasResizeControlKey];
|
||||
_resizeControlIsLeftAligned = [aCoder decodeBoolForKey:CPButtonBarResizeControlIsLeftAlignedKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+85
-2
@@ -23,6 +23,8 @@
|
||||
@import "CPButton.j"
|
||||
|
||||
|
||||
CPCheckBoxImageOffset = 4.0;
|
||||
|
||||
@implementation CPCheckBox : CPButton
|
||||
{
|
||||
}
|
||||
@@ -37,11 +39,19 @@
|
||||
return [self buttonWithTitle:aTitle];
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"check-box";
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
{
|
||||
if (theBinding === CPValueBinding)
|
||||
return [_CPCheckBoxValueBinder class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
@@ -55,10 +65,83 @@
|
||||
[self setImagePosition:CPImageLeft];
|
||||
[self setAlignment:CPLeftTextAlignment];
|
||||
|
||||
[self setBordered:YES];
|
||||
[self setBordered:NO];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)takeStateFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
|
||||
{
|
||||
var count = objects.length,
|
||||
value = [objects[0] valueForKeyPath:aKeyPath] ? CPOnState : CPOffState;
|
||||
|
||||
[self setAllowsMixedState:NO];
|
||||
[self setState:value];
|
||||
|
||||
while (count-- > 1)
|
||||
{
|
||||
if (value !== ([objects[count] valueForKeyPath:aKeyPath] ? CPOnState : CPOffState))
|
||||
{
|
||||
[self setAllowsMixedState:YES];
|
||||
[self setState:CPMixedState];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
|
||||
{
|
||||
[self takeStateFromKeyPath:aKeyPath ofObjects:objects];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCheckBoxValueBinder : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)theBinding
|
||||
{
|
||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
||||
options = [_info objectForKey:CPOptionsKey],
|
||||
newValue = [destination valueForKeyPath:keyPath],
|
||||
isPlaceholder = CPIsControllerMarker(newValue);
|
||||
|
||||
if (isPlaceholder)
|
||||
{
|
||||
switch (newValue)
|
||||
{
|
||||
case CPMultipleValuesMarker:
|
||||
newValue = CPMixedState;
|
||||
break;
|
||||
|
||||
case CPNoSelectionMarker:
|
||||
newValue = CPOffState;
|
||||
break;
|
||||
|
||||
case CPNotApplicableMarker:
|
||||
if ([options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
|
||||
[CPException raise:CPGenericException reason:@"can't transform non applicable key on: "+_source+" value: "+newValue];
|
||||
|
||||
newValue = CPOffState;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newValue === CPMixedState)
|
||||
{
|
||||
[_source setAllowsMixedState:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cocoa will always set allowsMixedState to NO
|
||||
// This behavior will be fine for Cappuccino as well if we (like Cocoa)
|
||||
// default the CPConditionallySetsEnabledBindingOption to YES
|
||||
[_source setAllowsMixedState:NO];
|
||||
}
|
||||
}
|
||||
|
||||
[_source setState:newValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+64
-36
@@ -22,14 +22,12 @@
|
||||
|
||||
@import "CPView.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPClipView
|
||||
|
||||
CPClipView allows you to define a clip rect and display only that portion of its containing view.
|
||||
CPClipView allows you to define a clip rect and display only that portion of its containing view.
|
||||
It is used to hold the document view in a CPScrollView.
|
||||
*/
|
||||
@implementation CPClipView : CPView
|
||||
@@ -47,7 +45,7 @@
|
||||
return;
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
|
||||
if (_documentView)
|
||||
{
|
||||
[defaultCenter
|
||||
@@ -59,29 +57,29 @@
|
||||
removeObserver:self
|
||||
name:CPViewBoundsDidChangeNotification
|
||||
object:_documentView];
|
||||
|
||||
|
||||
[_documentView removeFromSuperview];
|
||||
}
|
||||
|
||||
|
||||
_documentView = aView;
|
||||
|
||||
|
||||
if (_documentView)
|
||||
{
|
||||
[self addSubview:_documentView];
|
||||
|
||||
[_documentView setPostsFrameChangedNotifications:YES];
|
||||
[_documentView setPostsBoundsChangedNotifications:YES];
|
||||
|
||||
[defaultCenter
|
||||
[_documentView setPostsFrameChangedNotifications:YES];
|
||||
[_documentView setPostsBoundsChangedNotifications:YES];
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(viewFrameChanged:)
|
||||
name:CPViewFrameDidChangeNotification
|
||||
name:CPViewFrameDidChangeNotification
|
||||
object:_documentView];
|
||||
|
||||
[defaultCenter
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(viewBoundsChanged:)
|
||||
name:CPViewBoundsDidChangeNotification
|
||||
name:CPViewBoundsDidChangeNotification
|
||||
object:_documentView];
|
||||
}
|
||||
}
|
||||
@@ -106,7 +104,7 @@
|
||||
return _CGPointMakeZero();
|
||||
|
||||
var documentFrame = [_documentView frame];
|
||||
|
||||
|
||||
aPoint.x = MAX(0.0, MIN(aPoint.x, MAX(_CGRectGetWidth(documentFrame) - _CGRectGetWidth(_bounds), 0.0)));
|
||||
aPoint.y = MAX(0.0, MIN(aPoint.y, MAX(_CGRectGetHeight(documentFrame) - _CGRectGetHeight(_bounds), 0.0)));
|
||||
|
||||
@@ -117,12 +115,16 @@
|
||||
{
|
||||
if (_CGPointEqualToPoint(_bounds.origin, aPoint))
|
||||
return;
|
||||
|
||||
|
||||
[super setBoundsOrigin:aPoint];
|
||||
|
||||
var superview = [self superview];
|
||||
|
||||
if([superview isKindOfClass:[CPScrollView class]])
|
||||
var superview = [self superview],
|
||||
|
||||
// This is hack to avoid having to import CPScrollView.
|
||||
// FIXME: Should CPScrollView be finding out about this on its own somehow?
|
||||
scrollViewClass = objj_getClass("CPScrollView");
|
||||
|
||||
if ([superview isKindOfClass:scrollViewClass])
|
||||
[superview reflectScrolledClipView:self];
|
||||
}
|
||||
|
||||
@@ -162,37 +164,63 @@
|
||||
- (void)_constrainScrollPoint
|
||||
{
|
||||
var oldScrollPoint = [self bounds].origin;
|
||||
|
||||
// Call scrollToPoint: because the current scroll point may no longer make
|
||||
|
||||
// Call scrollToPoint: because the current scroll point may no longer make
|
||||
// sense given the new frame of the document view.
|
||||
[self scrollToPoint:oldScrollPoint];
|
||||
|
||||
// scrollToPoint: takes care of reflectScrollClipView: for us, so bail if
|
||||
|
||||
// scrollToPoint: takes care of reflectScrollClipView: for us, so bail if
|
||||
// the scroll points are not equal (meaning scrollToPoint: didn't early bail).
|
||||
if (!CGPointEqualToPoint(oldScrollPoint, [self bounds].origin))
|
||||
return;
|
||||
|
||||
// ... and we're in a scroll view of course.
|
||||
var superview = [self superview];
|
||||
|
||||
if ([superview isKindOfClass:[CPScrollView class]])
|
||||
var superview = [self superview],
|
||||
|
||||
// This is hack to avoid having to import CPScrollView.
|
||||
// FIXME: Should CPScrollView be finding out about this on its own somehow?
|
||||
scrollViewClass = objj_getClass("CPScrollView");
|
||||
|
||||
if ([superview isKindOfClass:scrollViewClass])
|
||||
[superview reflectScrolledClipView:self];
|
||||
}
|
||||
|
||||
- (BOOL)autoscroll:(CPEvent)anEvent
|
||||
- (BOOL)autoscroll:(CPEvent)anEvent
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
eventLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
eventLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
superview = [self superview],
|
||||
deltaX = 0,
|
||||
deltaY = 0;
|
||||
|
||||
if (CPRectContainsPoint(bounds, eventLocation))
|
||||
if (CGRectContainsPoint(bounds, eventLocation))
|
||||
return NO;
|
||||
|
||||
var newRect = CGRectMakeZero();
|
||||
if (![superview isKindOfClass:[CPScrollView class]] || [superview hasVerticalScroller])
|
||||
{
|
||||
if (eventLocation.y < CGRectGetMinY(bounds))
|
||||
deltaY = CGRectGetMinY(bounds) - eventLocation.y;
|
||||
else if (eventLocation.y > CGRectGetMaxY(bounds))
|
||||
deltaY = CGRectGetMaxY(bounds) - eventLocation.y;
|
||||
if (deltaY < -bounds.size.height)
|
||||
deltaY = -bounds.size.height;
|
||||
if (deltaY > bounds.size.height)
|
||||
deltaY = bounds.size.height;
|
||||
}
|
||||
|
||||
newRect.origin = eventLocation;
|
||||
newRect.size = CPSizeMake(10, 10);
|
||||
if (![superview isKindOfClass:[CPScrollView class]] || [superview hasHorizontalScroller])
|
||||
{
|
||||
if (eventLocation.x < CGRectGetMinX(bounds))
|
||||
deltaX = CGRectGetMinX(bounds) - eventLocation.x;
|
||||
else if (eventLocation.x > CGRectGetMaxX(bounds))
|
||||
deltaX = CGRectGetMaxX(bounds) - eventLocation.x;
|
||||
if (deltaX < -bounds.size.width)
|
||||
deltaX = -bounds.size.width;
|
||||
if (deltaX > bounds.size.width)
|
||||
deltaX = bounds.size.width;
|
||||
}
|
||||
|
||||
return [_documentView scrollRectToVisible:newRect];
|
||||
return [self scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -206,14 +234,14 @@ var CPClipViewDocumentViewKey = @"CPScrollViewDocumentView";
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
[self setDocumentView:[aCoder decodeObjectForKey:CPClipViewDocumentViewKey]];
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
|
||||
[aCoder encodeObject:_documentView forKey:CPClipViewDocumentViewKey];
|
||||
}
|
||||
|
||||
|
||||
+312
-87
@@ -30,16 +30,16 @@
|
||||
@import "CPCollectionViewItem.j"
|
||||
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPCollectionView
|
||||
|
||||
This class displays an array as a grid of objects, where each object is represented by a view.
|
||||
The view is controlled by creating a CPCollectionViewItem and specifying its view, then
|
||||
This class displays an array as a grid of objects, where each object is represented by a view.
|
||||
The view is controlled by creating a CPCollectionViewItem and specifying its view, then
|
||||
setting that item as the collection view prototype.
|
||||
|
||||
|
||||
@par Delegate Methods
|
||||
|
||||
|
||||
@delegate -(void)collectionViewDidChangeSelection:(CPCollectionView)collectionView;
|
||||
Called when the selection in the collection view has changed.
|
||||
@param collectionView the collection view who's selection changed
|
||||
@@ -67,35 +67,35 @@
|
||||
{
|
||||
CPArray _content;
|
||||
CPArray _items;
|
||||
|
||||
|
||||
CPData _itemData;
|
||||
CPCollectionViewItem _itemPrototype;
|
||||
CPCollectionViewItem _itemForDragging;
|
||||
CPMutableArray _cachedItems;
|
||||
|
||||
|
||||
unsigned _maxNumberOfRows;
|
||||
unsigned _maxNumberOfColumns;
|
||||
|
||||
|
||||
CGSize _minItemSize;
|
||||
CGSize _maxItemSize;
|
||||
|
||||
CPArray _backgroundColors;
|
||||
|
||||
float _tileWidth;
|
||||
|
||||
|
||||
BOOL _isSelectable;
|
||||
BOOL _allowsMultipleSelection;
|
||||
BOOL _allowsEmptySelection;
|
||||
CPIndexSet _selectionIndexes;
|
||||
|
||||
|
||||
CGSize _itemSize;
|
||||
|
||||
|
||||
float _horizontalMargin;
|
||||
float _verticalMargin;
|
||||
|
||||
|
||||
unsigned _numberOfRows;
|
||||
unsigned _numberOfColumns;
|
||||
|
||||
|
||||
id _delegate;
|
||||
|
||||
CPEvent _mouseDownEvent;
|
||||
@@ -104,14 +104,14 @@
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_items = [];
|
||||
_content = [];
|
||||
|
||||
|
||||
_cachedItems = [];
|
||||
|
||||
|
||||
_itemSize = CGSizeMakeZero();
|
||||
_minItemSize = CGSizeMakeZero();
|
||||
_maxItemSize = CGSizeMakeZero();
|
||||
@@ -120,12 +120,12 @@
|
||||
|
||||
_verticalMargin = 5.0;
|
||||
_tileWidth = -1.0;
|
||||
|
||||
|
||||
_selectionIndexes = [CPIndexSet indexSet];
|
||||
_allowsEmptySelection = YES;
|
||||
_isSelectable = YES;
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -196,18 +196,18 @@
|
||||
|
||||
// Setting the Content
|
||||
/*!
|
||||
Sets the content of the collection view to the content in \c anArray.
|
||||
This array can be of any type, and each element will be passed to the \c -setRepresentedObject: method.
|
||||
Sets the content of the collection view to the content in \c anArray.
|
||||
This array can be of any type, and each element will be passed to the \c -setRepresentedObject: method.
|
||||
It's the responsibility of your custom collection view item to interpret the object.
|
||||
@param anArray the content array
|
||||
*/
|
||||
- (void)setContent:(CPArray)anArray
|
||||
{
|
||||
if (_content == anArray)
|
||||
return;
|
||||
|
||||
// reset the _selectionIndexes
|
||||
[self setSelectionIndexes:[CPIndexSet indexSet]];
|
||||
|
||||
_content = anArray;
|
||||
|
||||
|
||||
[self reloadContent];
|
||||
}
|
||||
|
||||
@@ -236,13 +236,13 @@
|
||||
{
|
||||
if (_isSelectable == isSelectable)
|
||||
return;
|
||||
|
||||
|
||||
_isSelectable = isSelectable;
|
||||
|
||||
|
||||
if (!_isSelectable)
|
||||
{
|
||||
var index = CPNotFound;
|
||||
|
||||
|
||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
||||
[_items[index] setSelected:NO];
|
||||
}
|
||||
@@ -297,23 +297,26 @@
|
||||
*/
|
||||
- (void)setSelectionIndexes:(CPIndexSet)anIndexSet
|
||||
{
|
||||
if (_selectionIndexes == anIndexSet || !_isSelectable)
|
||||
if ([_selectionIndexes isEqual:anIndexSet] || !_isSelectable)
|
||||
return;
|
||||
|
||||
|
||||
var index = CPNotFound;
|
||||
|
||||
|
||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
||||
[_items[index] setSelected:NO];
|
||||
|
||||
|
||||
_selectionIndexes = anIndexSet;
|
||||
|
||||
|
||||
var index = CPNotFound;
|
||||
|
||||
|
||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
||||
[_items[index] setSelected:YES];
|
||||
|
||||
|
||||
var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"];
|
||||
[[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)])
|
||||
[_delegate collectionViewDidChangeSelection:self]
|
||||
[_delegate collectionViewDidChangeSelection:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -321,21 +324,23 @@
|
||||
*/
|
||||
- (CPIndexSet)selectionIndexes
|
||||
{
|
||||
return _selectionIndexes;
|
||||
return [_selectionIndexes copy];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)reloadContent
|
||||
{
|
||||
{
|
||||
// Remove current views
|
||||
var count = _items.length;
|
||||
|
||||
|
||||
while (count--)
|
||||
{
|
||||
[[_items[count] view] removeFromSuperview];
|
||||
[_items[count] setSelected:NO];
|
||||
|
||||
_cachedItems.push(_items[count]);
|
||||
}
|
||||
|
||||
|
||||
_items = [];
|
||||
|
||||
if (!_itemPrototype || !_content)
|
||||
@@ -348,10 +353,14 @@
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
_items.push([self newItemForRepresentedObject:_content[index]]);
|
||||
|
||||
|
||||
[self addSubview:[_items[index] view]];
|
||||
}
|
||||
|
||||
|
||||
index = CPNotFound;
|
||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
|
||||
[_items[index] setSelected:YES];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@@ -359,49 +368,49 @@
|
||||
- (void)tile
|
||||
{
|
||||
var width = CGRectGetWidth([self bounds]);
|
||||
|
||||
|
||||
if (![_content count] || width == _tileWidth)
|
||||
return;
|
||||
|
||||
// We try to fit as many views per row as possible. Any remaining space is then
|
||||
|
||||
// We try to fit as many views per row as possible. Any remaining space is then
|
||||
// either proportioned out to the views (if their minSize != maxSize) or used as
|
||||
// margin
|
||||
var itemSize = CGSizeMakeCopy(_minItemSize);
|
||||
|
||||
|
||||
_numberOfColumns = MAX(1.0, FLOOR(width / itemSize.width));
|
||||
|
||||
|
||||
if (_maxNumberOfColumns > 0)
|
||||
_numberOfColumns = MIN(_maxNumberOfColumns, _numberOfColumns);
|
||||
|
||||
|
||||
var remaining = width - _numberOfColumns * itemSize.width,
|
||||
itemsNeedSizeUpdate = NO;
|
||||
|
||||
|
||||
if (remaining > 0 && itemSize.width < _maxItemSize.width)
|
||||
itemSize.width = MIN(_maxItemSize.width, itemSize.width + FLOOR(remaining / _numberOfColumns));
|
||||
|
||||
|
||||
// When we ONE column and a non-integral width, the FLOORing above can cause the item width to be smaller than the total width.
|
||||
if (_maxNumberOfColumns == 1 && itemSize.width < _maxItemSize.width && itemSize.width < width)
|
||||
itemSize.width = MIN(_maxItemSize.width, width);
|
||||
|
||||
|
||||
if (!CGSizeEqualToSize(_itemSize, itemSize))
|
||||
{
|
||||
_itemSize = itemSize;
|
||||
itemsNeedSizeUpdate = YES;
|
||||
}
|
||||
|
||||
|
||||
var index = 0,
|
||||
count = _items.length;
|
||||
|
||||
|
||||
if (_maxNumberOfColumns > 0 && _maxNumberOfRows > 0)
|
||||
count = MIN(count, _maxNumberOfColumns * _maxNumberOfRows);
|
||||
|
||||
|
||||
_numberOfRows = CEIL(count / _numberOfColumns);
|
||||
|
||||
_horizontalMargin = FLOOR((width - _numberOfColumns * itemSize.width) / (_numberOfColumns + 1));
|
||||
|
||||
|
||||
var x = _horizontalMargin,
|
||||
y = -itemSize.height;
|
||||
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
if (index % _numberOfColumns == 0)
|
||||
@@ -409,19 +418,28 @@
|
||||
x = _horizontalMargin;
|
||||
y += _verticalMargin + itemSize.height;
|
||||
}
|
||||
|
||||
|
||||
var view = [_items[index] view];
|
||||
|
||||
|
||||
[view setFrameOrigin:CGPointMake(x, y)];
|
||||
|
||||
|
||||
if (itemsNeedSizeUpdate)
|
||||
[view setFrameSize:_itemSize];
|
||||
|
||||
|
||||
x += itemSize.width + _horizontalMargin;
|
||||
}
|
||||
|
||||
|
||||
var superview = [self superview],
|
||||
proposedHeight = y + itemSize.height + _verticalMargin;
|
||||
|
||||
if ([superview isKindOfClass:[CPClipView class]])
|
||||
{
|
||||
var superviewSize = [superview bounds].size;
|
||||
proposedHeight = MAX(superviewSize.height, proposedHeight);
|
||||
}
|
||||
|
||||
_tileWidth = width;
|
||||
[self setFrameSize:CGSizeMake(width, y + itemSize.height + _verticalMargin)];
|
||||
[self setFrameSize:CGSizeMake(width, proposedHeight)];
|
||||
_tileWidth = -1.0;
|
||||
}
|
||||
|
||||
@@ -439,9 +457,9 @@
|
||||
{
|
||||
if (_maxNumberOfRows == aMaxNumberOfRows)
|
||||
return;
|
||||
|
||||
|
||||
_maxNumberOfRows = aMaxNumberOfRows;
|
||||
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@@ -461,9 +479,9 @@
|
||||
{
|
||||
if (_maxNumberOfColumns == aMaxNumberOfColumns)
|
||||
return;
|
||||
|
||||
|
||||
_maxNumberOfColumns = aMaxNumberOfColumns;
|
||||
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@@ -500,9 +518,9 @@
|
||||
{
|
||||
if (CGSizeEqualToSize(_minItemSize, aSize))
|
||||
return;
|
||||
|
||||
|
||||
_minItemSize = CGSizeMakeCopy(aSize);
|
||||
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@@ -522,9 +540,9 @@
|
||||
{
|
||||
if (CGSizeEqualToSize(_maxItemSize, aSize))
|
||||
return;
|
||||
|
||||
|
||||
_maxItemSize = CGSizeMakeCopy(aSize);
|
||||
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@@ -576,14 +594,51 @@
|
||||
index = row * _numberOfColumns + column;
|
||||
|
||||
if (index >= 0 && index < _items.length)
|
||||
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
||||
{
|
||||
if (_allowsMultipleSelection && ([anEvent modifierFlags] & CPCommandKeyMask || [anEvent modifierFlags] & CPShiftKeyMask))
|
||||
{
|
||||
if ([anEvent modifierFlags] & CPCommandKeyMask)
|
||||
{
|
||||
var indexes = [_selectionIndexes copy];
|
||||
|
||||
if ([indexes containsIndex:index])
|
||||
[indexes removeIndex:index];
|
||||
else
|
||||
[indexes addIndex:index];
|
||||
}
|
||||
else if ([anEvent modifierFlags] & CPShiftKeyMask)
|
||||
{
|
||||
var firstSelectedIndex = [[self selectionIndexes] firstIndex],
|
||||
newSelectedRange = nil;
|
||||
|
||||
if (index < firstSelectedIndex)
|
||||
newSelectedRange = CPMakeRange(index, (firstSelectedIndex - index) + 1);
|
||||
else
|
||||
newSelectedRange = CPMakeRange(firstSelectedIndex, (index - firstSelectedIndex) + 1);
|
||||
|
||||
indexes = [[self selectionIndexes] copy];
|
||||
[indexes addIndexesInRange:newSelectedRange];
|
||||
}
|
||||
}
|
||||
else
|
||||
indexes = [CPIndexSet indexSetWithIndex:index];
|
||||
|
||||
[self setSelectionIndexes:indexes];
|
||||
}
|
||||
else if (_allowsEmptySelection)
|
||||
[self setSelectionIndexes:[CPIndexSet indexSet]];
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
var locationInWindow = [anEvent locationInWindow],
|
||||
mouseDownLocationInWindow = [_mouseDownEvent locationInWindow];
|
||||
|
||||
// FIXME: This is because Safari's drag hysteresis is 3px x 3px
|
||||
if ((ABS(locationInWindow.x - mouseDownLocationInWindow.x) < 3) &&
|
||||
(ABS(locationInWindow.y - mouseDownLocationInWindow.y) < 3))
|
||||
return;
|
||||
|
||||
if (![_delegate respondsToSelector:@selector(collectionView:dragTypesForItemsAtIndexes:)])
|
||||
return;
|
||||
|
||||
@@ -591,13 +646,15 @@
|
||||
if (![_selectionIndexes count])
|
||||
return;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(collectionView:canDragItemsAtIndexes:withEvent:)] &&
|
||||
![_delegate collectionView:self canDragItemsAtIndexes:_selectionIndexes withEvent:_mouseDownEvent])
|
||||
return;
|
||||
|
||||
// Set up the pasteboard
|
||||
var dragTypes = [_delegate collectionView:self dragTypesForItemsAtIndexes:_selectionIndexes];
|
||||
|
||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:dragTypes owner:self];
|
||||
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
if (!_itemForDragging)
|
||||
_itemForDragging = [self newItemForRepresentedObject:_content[[_selectionIndexes firstIndex]]];
|
||||
else
|
||||
@@ -638,9 +695,9 @@
|
||||
{
|
||||
if (_verticalMargin == aVerticalMargin)
|
||||
return;
|
||||
|
||||
|
||||
_verticalMargin = aVerticalMargin;
|
||||
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@@ -670,13 +727,173 @@
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
- (CPCollectionViewItem)itemAtIndex:(unsigned)anIndex
|
||||
{
|
||||
return [_items objectAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (CGRect)frameForItemAtIndex:(unsigned)anIndex
|
||||
{
|
||||
return [[[self itemAtIndex:anIndex] view] frame];
|
||||
}
|
||||
|
||||
- (CGRect)frameForItemsAtIndexes:(CPIndexSet)anIndexSet
|
||||
{
|
||||
var indexArray = [],
|
||||
frame = CGRectNull;
|
||||
|
||||
[anIndexSet getIndexes:indexArray maxCount:-1 inIndexRange:nil];
|
||||
|
||||
var index = 0,
|
||||
count = [indexArray count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
frame = CGRectUnion(frame, [self frameForItemAtIndex:indexArray[index]]);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
|
||||
CPCollectionViewMaxItemSizeKey = @"CPCollectionViewMaxItemSizeKey",
|
||||
CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey",
|
||||
CPCollectionViewSelectableKey = @"CPCollectionViewSelectableKey",
|
||||
CPCollectionViewBackgroundColorsKey = @"CPCollectionViewBackgroundColorsKey";
|
||||
@implementation CPCollectionView (KeyboardInteraction)
|
||||
|
||||
- (void)_modifySelectionWithNewIndex:(int)anIndex direction:(int)aDirection expand:(BOOL)shouldExpand
|
||||
{
|
||||
anIndex = MIN(MAX(anIndex, 0), [[self items] count] - 1);
|
||||
|
||||
if (_allowsMultipleSelection && shouldExpand)
|
||||
{
|
||||
var indexes = [_selectionIndexes copy],
|
||||
bottomAnchor = [indexes firstIndex],
|
||||
topAnchor = [indexes lastIndex];
|
||||
|
||||
// if the direction is backward (-1) check with the bottom anchor
|
||||
if (aDirection === -1)
|
||||
[indexes addIndexesInRange:CPMakeRange(anIndex, bottomAnchor - anIndex + 1)];
|
||||
else
|
||||
[indexes addIndexesInRange:CPMakeRange(topAnchor, anIndex - topAnchor + 1)];
|
||||
}
|
||||
else
|
||||
indexes = [CPIndexSet indexSetWithIndex:anIndex];
|
||||
|
||||
[self setSelectionIndexes:indexes];
|
||||
[self _scrollToSelection];
|
||||
}
|
||||
|
||||
- (void)_scrollToSelection
|
||||
{
|
||||
var frame = [self frameForItemsAtIndexes:[self selectionIndexes]];
|
||||
|
||||
if (!CGRectIsNull(frame))
|
||||
[self scrollRectToVisible:frame];
|
||||
}
|
||||
|
||||
- (void)moveLeft:(id)sender
|
||||
{
|
||||
var index = [[self selectionIndexes] firstIndex];
|
||||
if (index === CPNotFound)
|
||||
index = [[self items] count];
|
||||
|
||||
[self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:NO];
|
||||
}
|
||||
|
||||
- (void)moveLeftAndModifySelection:(id)sender
|
||||
{
|
||||
var index = [[self selectionIndexes] firstIndex];
|
||||
if (index === CPNotFound)
|
||||
index = [[self items] count];
|
||||
|
||||
[self _modifySelectionWithNewIndex:index - 1 direction:-1 expand:YES];
|
||||
}
|
||||
|
||||
- (void)moveRight:(id)sender
|
||||
{
|
||||
[self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:NO];
|
||||
}
|
||||
|
||||
- (void)moveRightAndModifySelection:(id)sender
|
||||
{
|
||||
[self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + 1 direction:1 expand:YES];
|
||||
}
|
||||
|
||||
- (void)moveDown:(id)sender
|
||||
{
|
||||
[self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:NO];
|
||||
}
|
||||
|
||||
- (void)moveDownAndModifySelection:(id)sender
|
||||
{
|
||||
[self _modifySelectionWithNewIndex:[[self selectionIndexes] lastIndex] + [self numberOfColumns] direction:1 expand:YES];
|
||||
}
|
||||
|
||||
- (void)moveUp:(id)sender
|
||||
{
|
||||
var index = [[self selectionIndexes] firstIndex];
|
||||
if (index == CPNotFound)
|
||||
index = [[self items] count];
|
||||
|
||||
[self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:NO];
|
||||
}
|
||||
|
||||
- (void)moveUpAndModifySelection:(id)sender
|
||||
{
|
||||
var index = [[self selectionIndexes] firstIndex];
|
||||
if (index == CPNotFound)
|
||||
index = [[self items] count];
|
||||
|
||||
[self _modifySelectionWithNewIndex:index - [self numberOfColumns] direction:-1 expand:YES];
|
||||
}
|
||||
|
||||
- (void)deleteBackward:(id)sender
|
||||
{
|
||||
if ([[self delegate] respondsToSelector:@selector(collectionView:shouldDeleteItemsAtIndexes:)])
|
||||
{
|
||||
[[self delegate] collectionView:self shouldDeleteItemsAtIndexes:[self selectionIndexes]];
|
||||
|
||||
var index = [[self selectionIndexes] firstIndex];
|
||||
if (index > [[self content] count] - 1)
|
||||
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:[[self content] count] - 1]];
|
||||
|
||||
[self _scrollToSelection];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)keyDown:(CPEvent)anEvent
|
||||
{
|
||||
[self interpretKeyEvents:[anEvent]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPCollectionView (Deprecated)
|
||||
|
||||
- (CGRect)rectForItemAtIndex:(int)anIndex
|
||||
{
|
||||
_CPReportLenientDeprecation([self class], _cmd, @selector(frameForItemAtIndex:));
|
||||
|
||||
// Don't re-compute anything just grab the current frame
|
||||
// This allows subclasses to override tile without messing this up.
|
||||
return [self frameForItemAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (CGRect)rectForItemsAtIndexes:(CPIndexSet)anIndexSet
|
||||
{
|
||||
_CPReportLenientDeprecation([self class], _cmd, @selector(frameForItemsAtIndexes:));
|
||||
|
||||
return [self frameForItemsAtIndexes:anIndexSet];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
|
||||
CPCollectionViewMaxItemSizeKey = @"CPCollectionViewMaxItemSizeKey",
|
||||
CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey",
|
||||
CPCollectionViewMaxNumberOfRowsKey = @"CPCollectionViewMaxNumberOfRowsKey",
|
||||
CPCollectionViewMaxNumberOfColumnsKey = @"CPCollectionViewMaxNumberOfColumnsKey",
|
||||
CPCollectionViewSelectableKey = @"CPCollectionViewSelectableKey",
|
||||
CPCollectionViewAllowsMultipleSelectionKey = @"CPCollectionViewAllowsMultipleSelectionKey",
|
||||
CPCollectionViewBackgroundColorsKey = @"CPCollectionViewBackgroundColorsKey";
|
||||
|
||||
|
||||
@implementation CPCollectionView (CPCoding)
|
||||
@@ -693,20 +910,24 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
|
||||
_cachedItems = [];
|
||||
|
||||
_itemSize = CGSizeMakeZero();
|
||||
|
||||
|
||||
_minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey] || CGSizeMakeZero();
|
||||
_maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey] || CGSizeMakeZero();
|
||||
|
||||
|
||||
_maxNumberOfRows = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfRowsKey] || 0;
|
||||
_maxNumberOfColumns = [aCoder decodeIntForKey:CPCollectionViewMaxNumberOfColumnsKey] || 0;
|
||||
|
||||
_verticalMargin = [aCoder decodeFloatForKey:CPCollectionViewVerticalMarginKey];
|
||||
|
||||
|
||||
_isSelectable = [aCoder decodeBoolForKey:CPCollectionViewSelectableKey];
|
||||
_allowsMultipleSelection = [aCoder decodeBoolForKey:CPCollectionViewAllowsMultipleSelectionKey];
|
||||
|
||||
[self setBackgroundColors:[aCoder decodeObjectForKey:CPCollectionViewBackgroundColorsKey]];
|
||||
|
||||
|
||||
_tileWidth = -1.0;
|
||||
|
||||
_selectionIndexes = [CPIndexSet indexSet];
|
||||
|
||||
|
||||
_allowsEmptySelection = YES;
|
||||
}
|
||||
|
||||
@@ -719,12 +940,16 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
|
||||
|
||||
if (!CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
|
||||
[aCoder encodeSize:_minItemSize forKey:CPCollectionViewMinItemSizeKey];
|
||||
|
||||
|
||||
if (!CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
|
||||
[aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey];
|
||||
|
||||
|
||||
[aCoder encodeInt:_maxNumberOfRows forKey:CPCollectionViewMaxNumberOfRowsKey];
|
||||
[aCoder encodeInt:_maxNumberOfColumns forKey:CPCollectionViewMaxNumberOfColumnsKey];
|
||||
|
||||
[aCoder encodeBool:_isSelectable forKey:CPCollectionViewSelectableKey];
|
||||
|
||||
[aCoder encodeBool:_allowsMultipleSelection forKey:CPCollectionViewAllowsMultipleSelectionKey];
|
||||
|
||||
[aCoder encodeFloat:_verticalMargin forKey:CPCollectionViewVerticalMarginKey];
|
||||
|
||||
[aCoder encodeObject:_backgroundColors forKey:CPCollectionViewBackgroundColorsKey];
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
@import "CPViewController.j"
|
||||
|
||||
|
||||
/*!
|
||||
Represents an object inside a CPCollectionView.
|
||||
*/
|
||||
|
||||
+97
-144
@@ -32,7 +32,7 @@ var _redComponent = 0,
|
||||
_greenComponent = 1,
|
||||
_blueComponent = 2,
|
||||
_alphaCompnent = 3;
|
||||
|
||||
|
||||
var _hueComponent = 0,
|
||||
_saturationComponent = 1,
|
||||
_brightnessComponent = 2;
|
||||
@@ -54,27 +54,21 @@ var cachedBlackColor,
|
||||
cachedShadowColor,
|
||||
cachedClearColor;
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@code CPColor
|
||||
|
||||
\c CPColor can be used to represent color
|
||||
in an RGB or HSB model with an optional transparency value.</p>
|
||||
|
||||
<p>It also provides some class helper methods that
|
||||
returns instances of commonly used colors.</p>
|
||||
|
||||
<p>The class does not have a \c -set: method
|
||||
like NextStep based frameworks to change the color of
|
||||
the current context. To change the color of the current
|
||||
context, use CGContextSetFillColor().
|
||||
*/
|
||||
@implementation CPColor : CPObject
|
||||
{
|
||||
CPArray _components;
|
||||
|
||||
CPImage _patternImage;
|
||||
CPString _cssString;
|
||||
CPString _cssString;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -82,12 +76,12 @@ var cachedBlackColor,
|
||||
Each component should be between the range of 0.0 to 1.0. For
|
||||
the alpha component, a value of 1.0 is opaque, and 0.0 means
|
||||
completely transparent.
|
||||
|
||||
|
||||
@param red the red component of the color
|
||||
@param green the green component of the color
|
||||
@param blue the blue component of the color
|
||||
@param alpha the alpha component
|
||||
|
||||
|
||||
@return a color initialized to the values specified
|
||||
*/
|
||||
+ (CPColor)colorWithRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
|
||||
@@ -97,17 +91,17 @@ var cachedBlackColor,
|
||||
|
||||
/*!
|
||||
@deprecated in favor of colorWithRed:green:blue:alpha:
|
||||
|
||||
|
||||
Creates a color in the RGB color space, with an alpha value.
|
||||
Each component should be between the range of 0.0 to 1.0. For
|
||||
the alpha component, a value of 1.0 is opaque, and 0.0 means
|
||||
completely transparent.
|
||||
|
||||
|
||||
@param red the red component of the color
|
||||
@param green the green component of the color
|
||||
@param blue the blue component of the color
|
||||
@param alpha the alpha component
|
||||
|
||||
|
||||
@return a color initialized to the values specified
|
||||
*/
|
||||
+ (CPColor)colorWithCalibratedRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
|
||||
@@ -119,10 +113,10 @@ var cachedBlackColor,
|
||||
/*!
|
||||
Creates a new color object with \c white for the RGB components.
|
||||
For the alpha component, a value of 1.0 is opaque, and 0.0 means completely transparent.
|
||||
|
||||
|
||||
@param white a float between 0.0 and 1.0
|
||||
@param alpha the alpha component between 0.0 and 1.0
|
||||
|
||||
|
||||
@return a color initialized to the values specified
|
||||
*/
|
||||
+ (CPColor)colorWithWhite:(float)white alpha:(float)alpha
|
||||
@@ -132,13 +126,13 @@ var cachedBlackColor,
|
||||
|
||||
/*!
|
||||
@deprecated in favor of colorWithWhite:apha:
|
||||
|
||||
|
||||
Creates a new color object with \c white for the RGB components.
|
||||
For the alpha component, a value of 1.0 is opaque, and 0.0 means completely transparent.
|
||||
|
||||
|
||||
@param white a float between 0.0 and 1.0
|
||||
@param alpha the alpha component between 0.0 and 1.0
|
||||
|
||||
|
||||
@return a color initialized to the values specified
|
||||
*/
|
||||
+ (CPColor)colorWithCalibratedWhite:(float)white alpha:(float)alpha
|
||||
@@ -148,11 +142,11 @@ var cachedBlackColor,
|
||||
|
||||
/*!
|
||||
Creates a new color in HSB space.
|
||||
|
||||
|
||||
@param hue the hue value
|
||||
@param saturation the saturation value
|
||||
@param brightness the brightness value
|
||||
|
||||
|
||||
@return the initialized color
|
||||
*/
|
||||
+ (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness
|
||||
@@ -162,16 +156,16 @@ var cachedBlackColor,
|
||||
|
||||
+ (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
|
||||
{
|
||||
if(saturation === 0.0)
|
||||
if (saturation === 0.0)
|
||||
return [CPColor colorWithCalibratedWhite:brightness / 100.0 alpha:alpha];
|
||||
|
||||
|
||||
var f = hue % 60,
|
||||
p = (brightness * (100 - saturation)) / 10000,
|
||||
q = (brightness * (6000 - saturation * f)) / 600000,
|
||||
t = (brightness * (6000 - saturation * (60 -f))) / 600000,
|
||||
b = brightness / 100.0;
|
||||
|
||||
switch(FLOOR(hue / 60))
|
||||
|
||||
switch (FLOOR(hue / 60))
|
||||
{
|
||||
case 0: return [CPColor colorWithCalibratedRed: b green: t blue: p alpha: alpha];
|
||||
case 1: return [CPColor colorWithCalibratedRed: q green: b blue: p alpha: alpha];
|
||||
@@ -186,7 +180,7 @@ var cachedBlackColor,
|
||||
Creates an RGB color from a hexadecimal string. For example,
|
||||
the a string of "FFFFFF" would return a white CPColor.
|
||||
"FF0000" would return a pure red, "00FF00" would return a
|
||||
pure blue, and "0000FF" would return a pure green.
|
||||
pure blue, and "0000FF" would return a pure green.
|
||||
|
||||
@param hex a 6 character long string of hex
|
||||
|
||||
@@ -194,7 +188,8 @@ var cachedBlackColor,
|
||||
*/
|
||||
+ (CPColor)colorWithHexString:(string)hex
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA: hexToRGB(hex)];
|
||||
var rgba = hexToRGB(hex);
|
||||
return rgba ? [[CPColor alloc] _initWithRGBA: rgba] : null;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -397,7 +392,7 @@ var cachedBlackColor,
|
||||
|
||||
/*!
|
||||
Creates a CPColor from a valid CSS RGB string. Example, "rgb(32,64,129)".
|
||||
|
||||
|
||||
@param aString a CSS color string
|
||||
@return a color initialized to the value in the css string
|
||||
*/
|
||||
@@ -409,23 +404,23 @@ var cachedBlackColor,
|
||||
/* @ignore */
|
||||
- (id)_initWithCSSString:(CPString)aString
|
||||
{
|
||||
if(aString.indexOf("rgb") == CPNotFound)
|
||||
if (aString.indexOf("rgb") == CPNotFound)
|
||||
return nil;
|
||||
|
||||
|
||||
self = [super init];
|
||||
|
||||
var startingIndex = aString.indexOf("(");
|
||||
var parts = aString.substring(startingIndex+1).split(',');
|
||||
|
||||
|
||||
var startingIndex = aString.indexOf("("),
|
||||
parts = aString.substring(startingIndex + 1).split(',');
|
||||
|
||||
_components = [
|
||||
parseInt(parts[0], 10) / 255.0,
|
||||
parseInt(parts[1], 10) / 255.0,
|
||||
parseInt(parts[2], 10) / 255.0,
|
||||
parts[3] ? parseInt(parts[3], 10) / 255.0 : 1.0
|
||||
]
|
||||
|
||||
parts[3] ? parseInt(parts[3], 10) / 255.0 : 1.0
|
||||
];
|
||||
|
||||
_cssString = aString;
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -433,55 +428,20 @@ var cachedBlackColor,
|
||||
- (id)_initWithRGBA:(CPArray)components
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_components = components;
|
||||
|
||||
if (!CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0 && window.Base64 && window.CRC32)
|
||||
{
|
||||
var bytes = [0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x8,0x3,0x0,0x0,0x0,0x28,0xcb,0x34,0xbb,0x0,0x0,0x3,0x0,0x50,0x4c,0x54,0x45,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x17,0x89,0x99,0x55,0x0,0x0,0x0,0x1,0x74,0x52,0x4e,0x53,0x0,0x40,0xe6,0xd8,0x66,0x0,0x0,0x0,0x10,0x49,0x44,0x41,0x54,0x78,0xda,0x62,0x60,0x0,0x0,0x0,0x0,0xff,0xff,0x3,0x0,0x0,0x2,0x0,0x1,0x24,0x7f,0x24,0xf1,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,0xff];
|
||||
var r_off = 41;
|
||||
var g_off = 42;
|
||||
var b_off = 43;
|
||||
var a_off = 821;
|
||||
var plte_crc_off = 809;
|
||||
var trns_crc_off = 822;
|
||||
var plte_type_off = 37;
|
||||
var trns_type_off = 817;
|
||||
|
||||
bytes[r_off] = Math.round(_components[0]*255);
|
||||
bytes[g_off] = Math.round(_components[1]*255);
|
||||
bytes[b_off] = Math.round(_components[2]*255);
|
||||
bytes[a_off] = Math.round(_components[3]*255);
|
||||
|
||||
// calculate new CRCs
|
||||
var new_plte_crc = integerToBytes(CRC32.getCRC(bytes, plte_type_off, 4+768), 4);
|
||||
var new_trns_crc = integerToBytes(CRC32.getCRC(bytes, trns_type_off, 4+1), 4);
|
||||
|
||||
// overwrite old CRCs with new ones
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
bytes[plte_crc_off+i] = new_plte_crc[i];
|
||||
bytes[trns_crc_off+i] = new_trns_crc[i];
|
||||
}
|
||||
|
||||
// Base64 encode, strip whitespace and build data URL
|
||||
var base64image = Base64.encode(bytes); //.replace(/[\s]/g, "");
|
||||
|
||||
_cssString = "url(\"data:image/png;base64," + base64image + "\")";
|
||||
}
|
||||
else
|
||||
{
|
||||
var hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0;
|
||||
|
||||
_cssString = (hasAlpha ? "rgba(" : "rgb(") +
|
||||
parseInt(_components[0] * 255.0) + ", " +
|
||||
parseInt(_components[1] * 255.0) + ", " +
|
||||
parseInt(_components[2] * 255.0) +
|
||||
(hasAlpha ? (", " + _components[3]) : "") + ")";
|
||||
}
|
||||
|
||||
var hasAlpha = CPFeatureIsCompatible(CPCSSRGBAFeature) && _components[3] != 1.0;
|
||||
|
||||
_cssString = (hasAlpha ? "rgba(" : "rgb(") +
|
||||
parseInt(_components[0] * 255.0) + ", " +
|
||||
parseInt(_components[1] * 255.0) + ", " +
|
||||
parseInt(_components[2] * 255.0) +
|
||||
(hasAlpha ? (", " + _components[3]) : "") + ")";
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -489,13 +449,14 @@ var cachedBlackColor,
|
||||
- (id)_initWithPatternImage:(CPImage)anImage
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_patternImage = anImage;
|
||||
_cssString = "url(\"" + [_patternImage filename] + "\")";
|
||||
_components = [0.0, 0.0, 0.0, 1.0];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -557,17 +518,17 @@ var cachedBlackColor,
|
||||
|
||||
/*!
|
||||
Returns a new color with the same RGB as the receiver but a new alpha component.
|
||||
|
||||
|
||||
@param anAlphaComponent the alpha component for the new color
|
||||
|
||||
|
||||
@return a new color object
|
||||
*/
|
||||
- (CPColor)colorWithAlphaComponent:(float)anAlphaComponent
|
||||
{
|
||||
var components = _components.slice();
|
||||
|
||||
|
||||
components[components.length - 1] = anAlphaComponent;
|
||||
|
||||
|
||||
return [[[self class] alloc] _initWithRGBA:components];
|
||||
}
|
||||
|
||||
@@ -586,37 +547,40 @@ var cachedBlackColor,
|
||||
var red = ROUND(_components[_redComponent] * 255.0),
|
||||
green = ROUND(_components[_greenComponent] * 255.0),
|
||||
blue = ROUND(_components[_blueComponent] * 255.0);
|
||||
|
||||
|
||||
var max = MAX(red, green, blue),
|
||||
min = MIN(red, green, blue),
|
||||
delta = max - min;
|
||||
|
||||
|
||||
var brightness = max / 255.0,
|
||||
saturation = (max != 0) ? delta / max : 0;
|
||||
|
||||
|
||||
var hue;
|
||||
if(saturation == 0)
|
||||
|
||||
if (saturation == 0)
|
||||
{
|
||||
hue = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var rr = (max - red) / delta;
|
||||
var gr = (max - green) / delta;
|
||||
var br = (max - blue) / delta;
|
||||
|
||||
if (red == max)
|
||||
var rr = (max - red) / delta,
|
||||
gr = (max - green) / delta,
|
||||
br = (max - blue) / delta;
|
||||
|
||||
if (red == max)
|
||||
hue = br - gr;
|
||||
else if (green == max)
|
||||
else if (green == max)
|
||||
hue = 2 + rr - br;
|
||||
else
|
||||
else
|
||||
hue = 4 + gr - rr;
|
||||
|
||||
|
||||
hue /= 6;
|
||||
if (hue < 0)
|
||||
if (hue < 0)
|
||||
hue++;
|
||||
}
|
||||
|
||||
|
||||
return [
|
||||
ROUND(hue * 360.0),
|
||||
ROUND(hue * 360.0),
|
||||
ROUND(saturation * 100.0),
|
||||
ROUND(brightness * 100.0)
|
||||
];
|
||||
@@ -664,7 +628,7 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
||||
|
||||
@implementation CPColor (CoreGraphicsExtensions)
|
||||
|
||||
/*!
|
||||
/*!
|
||||
Set's the receiver to be the fill and stroke color in the current graphics context
|
||||
*/
|
||||
- (void)set
|
||||
@@ -673,16 +637,16 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
||||
[self setStroke];
|
||||
}
|
||||
|
||||
/*!
|
||||
/*!
|
||||
Set's the receiver to be the fill color in the current graphics context
|
||||
*/
|
||||
- (void)setFill
|
||||
{
|
||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
CGContextSetFillColor(ctx, self);
|
||||
CGContextSetFillColor(ctx, self);
|
||||
}
|
||||
|
||||
/*!
|
||||
/*!
|
||||
Set's the receiver to be the stroke color in the current graphics context
|
||||
*/
|
||||
- (void)setStroke
|
||||
@@ -715,7 +679,7 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
{
|
||||
if ([aCoder containsValueForKey:CPColorPatternImageKey])
|
||||
return [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
|
||||
|
||||
|
||||
return [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
|
||||
}
|
||||
|
||||
@@ -733,6 +697,7 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var hexCharacters = "0123456789ABCDEF";
|
||||
|
||||
/*!
|
||||
@@ -741,51 +706,39 @@ var hexCharacters = "0123456789ABCDEF";
|
||||
@class CPColor
|
||||
@return an array of rgb components
|
||||
*/
|
||||
function hexToRGB(hex)
|
||||
var hexToRGB = function(hex)
|
||||
{
|
||||
if ( hex.length == 3 )
|
||||
if (hex.length == 3)
|
||||
hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
|
||||
if(hex.length != 6)
|
||||
|
||||
if (hex.length != 6)
|
||||
return null;
|
||||
|
||||
hex = hex.toUpperCase();
|
||||
|
||||
for(var i=0; i<hex.length; i++)
|
||||
if(hexCharacters.indexOf(hex.charAt(i)) == -1)
|
||||
for (var i = 0; i < hex.length; i++)
|
||||
if (hexCharacters.indexOf(hex.charAt(i)) == -1)
|
||||
return null;
|
||||
|
||||
var red = (hexCharacters.indexOf(hex.charAt(0)) * 16 + hexCharacters.indexOf(hex.charAt(1))) / 255.0;
|
||||
var green = (hexCharacters.indexOf(hex.charAt(2)) * 16 + hexCharacters.indexOf(hex.charAt(3))) / 255.0;
|
||||
var blue = (hexCharacters.indexOf(hex.charAt(4)) * 16 + hexCharacters.indexOf(hex.charAt(5))) / 255.0;
|
||||
|
||||
|
||||
var red = (hexCharacters.indexOf(hex.charAt(0)) * 16 + hexCharacters.indexOf(hex.charAt(1))) / 255.0,
|
||||
green = (hexCharacters.indexOf(hex.charAt(2)) * 16 + hexCharacters.indexOf(hex.charAt(3))) / 255.0,
|
||||
blue = (hexCharacters.indexOf(hex.charAt(4)) * 16 + hexCharacters.indexOf(hex.charAt(5))) / 255.0;
|
||||
|
||||
return [red, green, blue, 1.0];
|
||||
}
|
||||
};
|
||||
|
||||
function integerToBytes(integer, length) {
|
||||
if (!length)
|
||||
length = (integer == 0) ? 1 : Math.round((Math.log(integer)/Math.log(2))/8+0.5);
|
||||
|
||||
var bytes = new Array(length);
|
||||
for (var i = length-1; i >= 0; i--) {
|
||||
bytes[i] = integer & 255;
|
||||
integer = integer >> 8
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function rgbToHex(r,g,b) {
|
||||
var rgbToHex = function(r,g,b)
|
||||
{
|
||||
return byteToHex(r) + byteToHex(g) + byteToHex(b);
|
||||
}
|
||||
};
|
||||
|
||||
function byteToHex(n) {
|
||||
if (!n || isNaN(n)) return "00";
|
||||
n = ROUND(MIN(255,MAX(0,256*n)));
|
||||
return hexCharacters.charAt((n - n % 16) / 16) +
|
||||
hexCharacters.charAt(n % 16);
|
||||
}
|
||||
var byteToHex = function(n)
|
||||
{
|
||||
if (!n || isNaN(n))
|
||||
return "00";
|
||||
|
||||
// Toll-Free bridge CPColor to CGColor.
|
||||
//CGColor.prototype.isa = CPColor;
|
||||
//[CPColor initialize];
|
||||
n = FLOOR(MIN(255, MAX(0, 256 * n)));
|
||||
|
||||
//http://dev.mootools.net/browser/trunk/Source/Utilities/Color.js?rev=1184
|
||||
return hexCharacters.charAt((n - n % 16) / 16) +
|
||||
hexCharacters.charAt(n % 16);
|
||||
};
|
||||
|
||||
+92
-88
@@ -20,11 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPApplication.j"
|
||||
@import "CPButton.j"
|
||||
@import "CPCookie.j"
|
||||
@import "CPPanel.j"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification";
|
||||
|
||||
var PREVIEW_HEIGHT = 20.0,
|
||||
@@ -35,7 +37,7 @@ var PREVIEW_HEIGHT = 20.0,
|
||||
|
||||
var SharedColorPanel = nil,
|
||||
ColorPickerClasses = [];
|
||||
|
||||
|
||||
/*
|
||||
A color wheel
|
||||
@global
|
||||
@@ -52,7 +54,7 @@ CPSliderColorPickerMode = 2;
|
||||
CPColorPickerViewWidth = 265,
|
||||
CPColorPickerViewHeight = 370;
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPColorPanel
|
||||
|
||||
@@ -61,7 +63,7 @@ CPColorPickerViewHeight = 370;
|
||||
obtain the panel, call the \c +sharedColorPanel method.
|
||||
*/
|
||||
@implementation CPColorPanel : CPPanel
|
||||
{
|
||||
{
|
||||
_CPColorPanelToolbar _toolbar;
|
||||
_CPColorPanelSwatches _swatchView;
|
||||
_CPColorPanelPreview _previewView;
|
||||
@@ -80,8 +82,8 @@ CPColorPickerViewHeight = 370;
|
||||
int _mode;
|
||||
}
|
||||
|
||||
/*!
|
||||
A list of color pickers is collected here, and any color panel created will contain
|
||||
/*!
|
||||
A list of color pickers is collected here, and any color panel created will contain
|
||||
any picker in this list up to this point. In other words, call before creating a color panel.
|
||||
*/
|
||||
+ (void)provideColorPickerClass:(Class)aColorPickerSubclass
|
||||
@@ -96,7 +98,7 @@ CPColorPickerViewHeight = 370;
|
||||
{
|
||||
if (!SharedColorPanel)
|
||||
SharedColorPanel = [[CPColorPanel alloc] init];
|
||||
|
||||
|
||||
return SharedColorPanel;
|
||||
}
|
||||
|
||||
@@ -107,7 +109,7 @@ CPColorPickerViewHeight = 370;
|
||||
+ (void)setPickerMode:(CPColorPanelMode)mode
|
||||
{
|
||||
var panel = [CPColorPanel sharedColorPanel];
|
||||
[panel setMode: mode];
|
||||
[panel setMode:mode];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -116,7 +118,7 @@ CPColorPickerViewHeight = 370;
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
self = [super initWithContentRect:CGRectMake(500.0, 50.0, 219.0, 370.0)
|
||||
self = [super initWithContentRect:CGRectMake(500.0, 50.0, 219.0, 370.0)
|
||||
styleMask:(CPTitledWindowMask | CPClosableWindowMask | CPResizableWindowMask)];
|
||||
|
||||
if (self)
|
||||
@@ -125,14 +127,14 @@ CPColorPickerViewHeight = 370;
|
||||
|
||||
[self setTitle:@"Color Panel"];
|
||||
[self setLevel:CPFloatingWindowLevel];
|
||||
|
||||
|
||||
[self setFloatingPanel:YES];
|
||||
[self setBecomesKeyOnlyIfNeeded:YES];
|
||||
|
||||
|
||||
[self setMinSize:CGSizeMake(219.0, 342.0)];
|
||||
[self setMaxSize:CGSizeMake(323.0, 537.0)];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -140,15 +142,15 @@ CPColorPickerViewHeight = 370;
|
||||
Sets the color of the panel, and updates the picker. Also posts a \c CPColorPanelDidChangeNotification.
|
||||
*/
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
{
|
||||
_color = aColor;
|
||||
[_previewView setBackgroundColor: _color];
|
||||
|
||||
|
||||
[CPApp sendAction:@selector(changeColor:) to:nil from:self];
|
||||
|
||||
if (_target && _action)
|
||||
[CPApp sendAction:_action to:_target from:self];
|
||||
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPColorPanelColorDidChangeNotification
|
||||
object:self];
|
||||
@@ -165,7 +167,7 @@ CPColorPickerViewHeight = 370;
|
||||
- (void)setColor:(CPColor)aColor updatePicker:(BOOL)bool
|
||||
{
|
||||
[self setColor:aColor];
|
||||
|
||||
|
||||
if (bool)
|
||||
[_activePicker setColor:_color];
|
||||
}
|
||||
@@ -232,30 +234,30 @@ CPColorPickerViewHeight = 370;
|
||||
{
|
||||
var picker = _colorPickers[[sender tag]],
|
||||
view = [picker provideNewView:NO];
|
||||
|
||||
|
||||
if (!view)
|
||||
view = [picker provideNewView:YES];
|
||||
|
||||
|
||||
if (view == _currentView)
|
||||
return;
|
||||
|
||||
|
||||
if (_currentView)
|
||||
[view setFrame:[_currentView frame]];
|
||||
else
|
||||
{
|
||||
var height = (TOOLBAR_HEIGHT+10+PREVIEW_HEIGHT+5+SWATCH_HEIGHT+32),
|
||||
var height = (TOOLBAR_HEIGHT + 10 + PREVIEW_HEIGHT + 5 + SWATCH_HEIGHT + 32),
|
||||
bounds = [[self contentView] bounds];
|
||||
|
||||
[view setFrameSize: CPSizeMake(bounds.size.width - 10, bounds.size.height - height)];
|
||||
[view setFrameOrigin: CPPointMake(5, height)];
|
||||
[view setFrameSize:CPSizeMake(bounds.size.width - 10, bounds.size.height - height)];
|
||||
[view setFrameOrigin:CPPointMake(5, height)];
|
||||
}
|
||||
|
||||
|
||||
[_currentView removeFromSuperview];
|
||||
[[self contentView] addSubview:view];
|
||||
|
||||
|
||||
_currentView = view;
|
||||
_activePicker = picker;
|
||||
|
||||
|
||||
[picker setColor:[self color]];
|
||||
}
|
||||
|
||||
@@ -285,7 +287,7 @@ CPColorPickerViewHeight = 370;
|
||||
_colorPickers = [];
|
||||
|
||||
var count = [ColorPickerClasses count];
|
||||
for (var i=0; i<count; i++)
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var currentPickerClass = ColorPickerClasses[i],
|
||||
currentPicker = [[currentPickerClass alloc] initWithPickerMask:0 colorPanel:self];
|
||||
@@ -297,29 +299,29 @@ CPColorPickerViewHeight = 370;
|
||||
bounds = [contentView bounds];
|
||||
|
||||
_toolbar = [[CPView alloc] initWithFrame:CGRectMake(0, 6, CGRectGetWidth(bounds), TOOLBAR_HEIGHT)];
|
||||
[_toolbar setAutoresizingMask: CPViewWidthSizable];
|
||||
[_toolbar setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
var totalToolbarWidth = count * ICON_WIDTH + (count - 1) * ICON_PADDING,
|
||||
leftOffset = (CGRectGetWidth(bounds) - totalToolbarWidth) / 2.0,
|
||||
buttonForLater = nil;
|
||||
|
||||
for (var i=0; i<count; i++)
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var image = [_colorPickers[i] provideNewButtonImage],
|
||||
highlightImage = [_colorPickers[i] provideNewAlternateButtonImage],
|
||||
button = [[CPButton alloc] initWithFrame:CGRectMake(leftOffset + i*(ICON_WIDTH+ICON_PADDING), 0, ICON_WIDTH, ICON_WIDTH)];
|
||||
|
||||
button = [[CPButton alloc] initWithFrame:CGRectMake(leftOffset + i * (ICON_WIDTH + ICON_PADDING), 0, ICON_WIDTH, ICON_WIDTH)];
|
||||
|
||||
[button setTag:i];
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(_setPicker:)];
|
||||
[button setBordered:NO];
|
||||
[button setAutoresizingMask:CPViewMinXMargin|CPViewMaxXMargin];
|
||||
|
||||
[button setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
|
||||
|
||||
[button setImage:image];
|
||||
[button setAlternateImage:highlightImage];
|
||||
|
||||
|
||||
[_toolbar addSubview:button];
|
||||
|
||||
|
||||
if (!buttonForLater)
|
||||
buttonForLater = button;
|
||||
}
|
||||
@@ -330,7 +332,7 @@ CPColorPickerViewHeight = 370;
|
||||
_previewView = [[_CPColorPanelPreview alloc] initWithFrame:CGRectInset([previewBox bounds], 2.0, 2.0)];
|
||||
|
||||
[_previewView setColorPanel:self];
|
||||
[_previewView setAutoresizingMask:CPViewWidthSizable];
|
||||
[_previewView setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[previewBox setBackgroundColor:[CPColor colorWithWhite:0.8 alpha:1.0]];
|
||||
[previewBox setAutoresizingMask:CPViewWidthSizable];
|
||||
@@ -338,7 +340,7 @@ CPColorPickerViewHeight = 370;
|
||||
[previewBox addSubview:_previewView];
|
||||
|
||||
var _previewLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 10, 60, 15)];
|
||||
[_previewLabel setStringValue: "Preview:"];
|
||||
[_previewLabel setStringValue:"Preview:"];
|
||||
[_previewLabel setTextColor:[CPColor blackColor]];
|
||||
[_previewLabel setAlignment:CPRightTextAlignment];
|
||||
|
||||
@@ -350,8 +352,8 @@ CPColorPickerViewHeight = 370;
|
||||
|
||||
_swatchView = [[_CPColorPanelSwatches alloc] initWithFrame:CGRectInset([swatchBox bounds], 1.0, 1.0)];
|
||||
|
||||
[_swatchView setColorPanel: self];
|
||||
[_swatchView setAutoresizingMask: CPViewWidthSizable];
|
||||
[_swatchView setColorPanel:self];
|
||||
[_swatchView setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[swatchBox addSubview:_swatchView];
|
||||
|
||||
@@ -367,7 +369,7 @@ CPColorPickerViewHeight = 370;
|
||||
[opacityLabel setAlignment:CPRightTextAlignment];
|
||||
|
||||
_opacitySlider = [[CPSlider alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 34, CGRectGetWidth(bounds) - 86, 20.0)];
|
||||
|
||||
|
||||
[_opacitySlider setMinValue:0.0];
|
||||
[_opacitySlider setMaxValue:1.0];
|
||||
|
||||
@@ -415,37 +417,37 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
CPCookie _swatchCookie;
|
||||
}
|
||||
|
||||
-(id)initWithFrame:(CPRect)aFrame
|
||||
- (id)initWithFrame:(CPRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
[self setBackgroundColor: [CPColor grayColor]];
|
||||
|
||||
|
||||
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
|
||||
|
||||
var whiteColor = [CPColor whiteColor];
|
||||
|
||||
_swatchCookie = [[CPCookie alloc] initWithName: CPColorPanelSwatchesCookie];
|
||||
var colorList = [self startingColorList];
|
||||
|
||||
|
||||
_swatches = [];
|
||||
|
||||
for(var i=0; i < 50; i++)
|
||||
|
||||
for (var i = 0; i < 50; i++)
|
||||
{
|
||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||
var view = [[CPView alloc] initWithFrame: CPRectMake(13*i+1, 1, 12, 12)],
|
||||
var view = [[CPView alloc] initWithFrame: CPRectMake(13 * i + 1, 1, 12, 12)],
|
||||
fillView = [[CPView alloc] initWithFrame:CGRectInset([view bounds], 1.0, 1.0)];
|
||||
|
||||
|
||||
[view setBackgroundColor:whiteColor];
|
||||
[fillView setBackgroundColor: (i < colorList.length) ? colorList[i] : whiteColor];
|
||||
|
||||
[view addSubview:fillView];
|
||||
|
||||
[self addSubview: view];
|
||||
|
||||
|
||||
_swatches.push(view);
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -457,7 +459,8 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
- (CPArray)startingColorList
|
||||
{
|
||||
var cookieValue = [_swatchCookie value];
|
||||
if(cookieValue == "")
|
||||
|
||||
if (cookieValue == "")
|
||||
{
|
||||
return [
|
||||
[CPColor blackColor],
|
||||
@@ -471,11 +474,11 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
[CPColor yellowColor]
|
||||
];
|
||||
}
|
||||
|
||||
var cookieValue = eval(cookieValue);
|
||||
var result = [];
|
||||
|
||||
for(var i=0; i<cookieValue.length; i++)
|
||||
|
||||
var cookieValue = eval(cookieValue),
|
||||
result = [];
|
||||
|
||||
for (var i = 0; i < cookieValue.length; i++)
|
||||
result.push([CPColor colorWithHexString: cookieValue[i]]);
|
||||
|
||||
return result;
|
||||
@@ -485,12 +488,12 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
{
|
||||
var result = [];
|
||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||
for(var i=0; i<_swatches.length; i++)
|
||||
for (var i = 0; i < _swatches.length; i++)
|
||||
result.push([[[_swatches[i] subviews][0] backgroundColor] hexString]);
|
||||
|
||||
|
||||
var future = new Date();
|
||||
future.setYear(2019);
|
||||
|
||||
|
||||
[_swatchCookie setValue: JSON.stringify(result) expires:future domain: nil];
|
||||
}
|
||||
|
||||
@@ -518,9 +521,10 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
if(point.x > [self bounds].size.width - 1 || point.x < 1)
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
bounds = [self bounds];
|
||||
|
||||
if (!CGRectContainsPoint(bounds, point) || point.x > [self bounds].size.width - 1 || point.x < 1)
|
||||
return NO;
|
||||
|
||||
[_colorPanel setColor: [self colorAtIndex:FLOOR(point.x / 13)] updatePicker: YES];
|
||||
@@ -530,25 +534,25 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
if(point.x > [self bounds].size.width - 1 || point.x < 1)
|
||||
if (point.x > [self bounds].size.width - 1 || point.x < 1)
|
||||
return NO;
|
||||
|
||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
|
||||
|
||||
|
||||
var swatch = _swatches[FLOOR(point.x / 13)];
|
||||
|
||||
|
||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||
_dragColor = [[swatch subviews][0] backgroundColor];
|
||||
|
||||
|
||||
var bounds = CPRectCreateCopy([swatch bounds]);
|
||||
|
||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||
var dragView = [[CPView alloc] initWithFrame: bounds];
|
||||
var dragView = [[CPView alloc] initWithFrame: bounds],
|
||||
dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
|
||||
|
||||
|
||||
[dragView setBackgroundColor:[CPColor blackColor]];
|
||||
[dragFillView setBackgroundColor:_dragColor];
|
||||
|
||||
|
||||
[dragView addSubview:dragFillView];
|
||||
|
||||
[self dragView: dragView
|
||||
@@ -562,20 +566,20 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
|
||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||
{
|
||||
if(aType == CPColorDragType)
|
||||
[aPasteboard setData:_dragColor forType:aType];
|
||||
if (aType == CPColorDragType)
|
||||
[aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:_dragColor] forType:aType];
|
||||
}
|
||||
|
||||
- (void)performDragOperation:(id <CPDraggingInfo>)aSender
|
||||
{
|
||||
{
|
||||
var location = [self convertPoint:[aSender draggingLocation] fromView:nil],
|
||||
pasteboard = [aSender draggingPasteboard],
|
||||
swatch = nil;
|
||||
|
||||
if(![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1)
|
||||
if (![pasteboard availableTypeFromArray:[CPColorDragType]] || location.x > [self bounds].size.width - 1 || location.x < 1)
|
||||
return NO;
|
||||
|
||||
[self setColor:[pasteboard dataForType:CPColorDragType] atIndex: FLOOR(location.x / 13)];
|
||||
|
||||
[self setColor:[CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]] atIndex: FLOOR(location.x / 13)];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -589,9 +593,9 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
- (id)initWithFrame:(CPRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -609,11 +613,11 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
{
|
||||
var pasteboard = [aSender draggingPasteboard];
|
||||
|
||||
if(![pasteboard availableTypeFromArray:[CPColorDragType]])
|
||||
if (![pasteboard availableTypeFromArray:[CPColorDragType]])
|
||||
return NO;
|
||||
|
||||
var color = [pasteboard dataForType:CPColorDragType];
|
||||
[_colorPanel setColor: color updatePicker: YES];
|
||||
|
||||
var color = [CPKeyedUnarchiver unarchiveObjectWithData:[pasteboard dataForType:CPColorDragType]];
|
||||
[_colorPanel setColor:color updatePicker:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isOpaque
|
||||
@@ -625,19 +629,19 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
|
||||
|
||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPColorDragType] owner:self];
|
||||
|
||||
var bounds = CPRectMake(0, 0, 15, 15);
|
||||
|
||||
|
||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||
var dragView = [[CPView alloc] initWithFrame: bounds];
|
||||
var dragView = [[CPView alloc] initWithFrame: bounds],
|
||||
dragFillView = [[CPView alloc] initWithFrame:CGRectInset(bounds, 1.0, 1.0)];
|
||||
|
||||
|
||||
[dragView setBackgroundColor:[CPColor blackColor]];
|
||||
[dragFillView setBackgroundColor:[self backgroundColor]];
|
||||
|
||||
|
||||
[dragView addSubview:dragFillView];
|
||||
|
||||
|
||||
[self dragView: dragView
|
||||
at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
|
||||
offset: CPPointMake(0.0, 0.0)
|
||||
@@ -649,8 +653,8 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
|
||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||
{
|
||||
if(aType == CPColorDragType)
|
||||
[aPasteboard setData:[self backgroundColor] forType:aType];
|
||||
if (aType == CPColorDragType)
|
||||
[aPasteboard setData:[CPKeyedArchiver archivedDataWithRootObject:[self backgroundColor]] forType:aType];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+39
-38
@@ -21,9 +21,11 @@
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
@import "CPColorPanel.j"
|
||||
|
||||
/*!
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPColorPicker
|
||||
|
||||
@@ -100,18 +102,18 @@
|
||||
|
||||
CPColor _cachedColor;
|
||||
}
|
||||
|
||||
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
|
||||
|
||||
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
|
||||
{
|
||||
return [super initWithPickerMask:mask colorPanel: owningColorPanel];
|
||||
}
|
||||
|
||||
-(id)initView
|
||||
- (id)initView
|
||||
{
|
||||
aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight);
|
||||
|
||||
_pickerView = [[CPView alloc] initWithFrame:aFrame];
|
||||
[_pickerView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||
[_pickerView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
_brightnessSlider = [[CPSlider alloc] initWithFrame:CGRectMake(0, (aFrame.size.height - 34), aFrame.size.width, 15)];
|
||||
|
||||
@@ -134,17 +136,17 @@
|
||||
[_pickerView addSubview:_brightnessSlider];
|
||||
}
|
||||
|
||||
-(void)brightnessSliderDidChange:(id)sender
|
||||
- (void)brightnessSliderDidChange:(id)sender
|
||||
{
|
||||
[self updateColor];
|
||||
}
|
||||
|
||||
-(void)colorWheelDidChange:(id)sender
|
||||
- (void)colorWheelDidChange:(id)sender
|
||||
{
|
||||
[self updateColor];
|
||||
}
|
||||
|
||||
-(void)updateColor
|
||||
- (void)updateColor
|
||||
{
|
||||
var hue = [_hueSaturationView angle],
|
||||
saturation = [_hueSaturationView distance],
|
||||
@@ -221,7 +223,7 @@
|
||||
float _radius;
|
||||
}
|
||||
|
||||
-(id)initWithFrame:(CPRect)aFrame
|
||||
- (id)initWithFrame:(CPRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
@@ -239,8 +241,10 @@
|
||||
_blackWheelImage.style.filter = "alpha(opacity=0)"
|
||||
_blackWheelImage.style.position = "absolute";
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.appendChild(_wheelImage);
|
||||
_DOMElement.appendChild(_blackWheelImage);
|
||||
#endif
|
||||
|
||||
[self setWheelSize:aFrame.size];
|
||||
|
||||
@@ -257,19 +261,19 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)setWheelBrightness:(float)brightness
|
||||
- (void)setWheelBrightness:(float)brightness
|
||||
{
|
||||
_blackWheelImage.style.opacity = 1.0 - brightness;
|
||||
_blackWheelImage.style.filter = "alpha(opacity=" + (1.0 - brightness)*100 + ")"
|
||||
_blackWheelImage.style.filter = "alpha(opacity=" + (1.0 - brightness) * 100 + ")"
|
||||
}
|
||||
|
||||
-(void)setFrameSize:(CPSize)aSize
|
||||
- (void)setFrameSize:(CPSize)aSize
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
[self setWheelSize:aSize];
|
||||
}
|
||||
|
||||
-(void)setWheelSize:(CPSize)aSize
|
||||
- (void)setWheelSize:(CPSize)aSize
|
||||
{
|
||||
var min = MIN(aSize.width, aSize.height);
|
||||
|
||||
@@ -292,57 +296,55 @@
|
||||
[self setAngle:[self degreesToRadians:_angle] distance:(_distance / 100.0) * _radius];
|
||||
}
|
||||
|
||||
-(void)setDelegate:(id)aDelegate
|
||||
- (void)setDelegate:(id)aDelegate
|
||||
{
|
||||
_delegate = aDelegate;
|
||||
}
|
||||
|
||||
-(id)delegate
|
||||
- (id)delegate
|
||||
{
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
-(float)angle
|
||||
- (float)angle
|
||||
{
|
||||
return _angle;
|
||||
}
|
||||
|
||||
-(float)distance
|
||||
- (float)distance
|
||||
{
|
||||
return _distance;
|
||||
}
|
||||
|
||||
-(void)mouseDown:(CPEvent)anEvent
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
[self reposition:anEvent];
|
||||
}
|
||||
|
||||
-(void)mouseDragged:(CPEvent)anEvent
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
[self reposition:anEvent];
|
||||
}
|
||||
|
||||
-(void)reposition:(CPEvent)anEvent
|
||||
- (void)reposition:(CPEvent)anEvent
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
var midX = CGRectGetMidX(bounds);
|
||||
var midY = CGRectGetMidY(bounds);
|
||||
|
||||
var distance = MIN(SQRT((location.x - midX)*(location.x - midX) + (location.y - midY)*(location.y - midY)), _radius);
|
||||
var angle = ATAN2(location.y - midY, location.x - midX);
|
||||
location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
midX = CGRectGetMidX(bounds),
|
||||
midY = CGRectGetMidY(bounds),
|
||||
distance = MIN(SQRT((location.x - midX) * (location.x - midX) + (location.y - midY) * (location.y - midY)), _radius),
|
||||
angle = ATAN2(location.y - midY, location.x - midX);
|
||||
|
||||
[self setAngle:angle distance:distance];
|
||||
|
||||
[_delegate colorWheelDidChange:self];
|
||||
}
|
||||
|
||||
-(void)setAngle:(int)angle distance:(float)distance
|
||||
- (void)setAngle:(int)angle distance:(float)distance
|
||||
{
|
||||
var bounds = [self bounds];
|
||||
var midX = CGRectGetMidX(bounds);
|
||||
var midY = CGRectGetMidY(bounds);
|
||||
var bounds = [self bounds],
|
||||
midX = CGRectGetMidX(bounds),
|
||||
midY = CGRectGetMidY(bounds);
|
||||
|
||||
_angle = [self radiansToDegrees:angle];
|
||||
_distance = (distance / _radius) * 100.0;
|
||||
@@ -350,23 +352,22 @@
|
||||
[_crosshair setFrameOrigin:CPPointMake(COS(angle) * distance + midX - 2.0, SIN(angle) * distance + midY - 2.0)];
|
||||
}
|
||||
|
||||
-(void)setPositionToColor:(CPColor)aColor
|
||||
- (void)setPositionToColor:(CPColor)aColor
|
||||
{
|
||||
var hsb = [aColor hsbComponents],
|
||||
bounds = [self bounds];
|
||||
|
||||
var angle = [self degreesToRadians:hsb[0]],
|
||||
bounds = [self bounds],
|
||||
angle = [self degreesToRadians:hsb[0]],
|
||||
distance = (hsb[1] / 100.0) * _radius;
|
||||
|
||||
[self setAngle:angle distance:distance];
|
||||
}
|
||||
|
||||
-(int)radiansToDegrees:(float)radians
|
||||
- (int)radiansToDegrees:(float)radians
|
||||
{
|
||||
return ((-radians / PI) * 180 + 360) % 360;
|
||||
}
|
||||
|
||||
-(float)degreesToRadians:(float)degrees
|
||||
- (float)degreesToRadians:(float)degrees
|
||||
{
|
||||
return -(((degrees - 360) / 180) * PI);
|
||||
}
|
||||
|
||||
+36
-30
@@ -29,7 +29,7 @@
|
||||
|
||||
var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiveNotification";
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPColorWell
|
||||
|
||||
@@ -41,7 +41,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
{
|
||||
BOOL _active;
|
||||
BOOL _bordered;
|
||||
|
||||
|
||||
CPColor _color;
|
||||
CPView _wellView;
|
||||
}
|
||||
@@ -49,19 +49,19 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_active = NO;
|
||||
_bordered = YES;
|
||||
_color = [CPColor whiteColor];
|
||||
|
||||
|
||||
[self drawBezelWithHighlight:NO];
|
||||
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||
|
||||
|
||||
[self _registerForNotifications];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -97,9 +97,9 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
{
|
||||
if (_bordered == bordered)
|
||||
return;
|
||||
|
||||
|
||||
_bordered = bordered;
|
||||
|
||||
|
||||
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||
}
|
||||
|
||||
@@ -120,9 +120,9 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
{
|
||||
if (_color == aColor)
|
||||
return;
|
||||
|
||||
|
||||
_color = aColor;
|
||||
|
||||
|
||||
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
|
||||
// Activating and Deactivating Color Wells
|
||||
/*!
|
||||
Activates the color well, displays the color panel, and makes the panel's current color the same as its own.
|
||||
Activates the color well, displays the color panel, and makes the panel's current color the same as its own.
|
||||
If exclusive is \c YES, deactivates any other CPColorWells. \c NO, keeps them active.
|
||||
@param shouldBeExclusive whether other color wells should be deactivated.
|
||||
*/
|
||||
@@ -152,9 +152,9 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
|
||||
if ([self isActive])
|
||||
return;
|
||||
|
||||
|
||||
_active = YES;
|
||||
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(colorPanelDidChangeColor:)
|
||||
@@ -169,9 +169,9 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
{
|
||||
if (![self isActive])
|
||||
return;
|
||||
|
||||
|
||||
_active = NO;
|
||||
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
removeObserver:self
|
||||
name:CPColorPanelColorDidChangeNotification
|
||||
@@ -202,19 +202,19 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
{
|
||||
_wellView = [[CPView alloc] initWithFrame:aRect];
|
||||
[_wellView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
|
||||
[self addSubview:_wellView];
|
||||
}
|
||||
else
|
||||
[_wellView setFrame:aRect];
|
||||
|
||||
|
||||
[_wellView setBackgroundColor:_color];
|
||||
}
|
||||
|
||||
- (void)colorPanelDidChangeColor:(CPNotification)aNotification
|
||||
{
|
||||
[self takeColorFrom:[aNotification object]];
|
||||
|
||||
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
}
|
||||
|
||||
@@ -231,25 +231,31 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
[self drawBezelWithHighlight:YES];
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
[self drawBezelWithHighlight:CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil])];
|
||||
}
|
||||
|
||||
-(void)mouseUp:(CPEvent)anEvent
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
{
|
||||
[self drawBezelWithHighlight:NO];
|
||||
|
||||
if (!CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil]))
|
||||
if (!CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil]) || ![self isEnabled])
|
||||
return;
|
||||
|
||||
|
||||
[self activate:YES];
|
||||
|
||||
var colorPanel = [CPColorPanel sharedColorPanel];
|
||||
|
||||
|
||||
[colorPanel setColor:_color];
|
||||
[colorPanel orderFront:self];
|
||||
}
|
||||
@@ -268,11 +274,11 @@ var CPColorWellColorKey = "CPColorWellColorKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_active = NO;
|
||||
_bordered = [aCoder decodeObjectForKey:CPColorWellBorderedKey];
|
||||
_bordered = [aCoder decodeBoolForKey:CPColorWellBorderedKey];
|
||||
_color = [aCoder decodeObjectForKey:CPColorWellColorKey];
|
||||
|
||||
[self drawBezelWithHighlight:NO];
|
||||
@@ -280,7 +286,7 @@ var CPColorWellColorKey = "CPColorWellColorKey",
|
||||
|
||||
[self _registerForNotifications];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -290,17 +296,17 @@ var CPColorWellColorKey = "CPColorWellColorKey",
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
// We do this in order to avoid encoding the _wellView, which
|
||||
// We do this in order to avoid encoding the _wellView, which
|
||||
// should just automatically be created programmatically as needed.
|
||||
var actualSubviews = _subviews;
|
||||
|
||||
|
||||
_subviews = [_subviews copy];
|
||||
[_subviews removeObjectIdenticalTo:_wellView];
|
||||
|
||||
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
|
||||
_subviews = actualSubviews;
|
||||
|
||||
|
||||
[aCoder encodeObject:_color forKey:CPColorWellColorKey];
|
||||
[aCoder encodeObject:_bordered forKey:CPColorWellBorderedKey];
|
||||
}
|
||||
|
||||
+44
-25
@@ -23,6 +23,7 @@
|
||||
@import "CPEvent.j"
|
||||
@import "CPPlatform.j"
|
||||
|
||||
|
||||
// Browser Engines
|
||||
CPUnknownBrowserEngine = 0;
|
||||
CPGeckoBrowserEngine = 1;
|
||||
@@ -31,6 +32,11 @@ CPKHTMLBrowserEngine = 3;
|
||||
CPOperaBrowserEngine = 4;
|
||||
CPWebKitBrowserEngine = 5;
|
||||
|
||||
// Operating Systems
|
||||
CPMacOperatingSystem = 0;
|
||||
CPWindowsOperatingSystem = 1;
|
||||
CPOtherOperatingSystem = 2;
|
||||
|
||||
// Features
|
||||
CPCSSRGBAFeature = 1 << 5;
|
||||
|
||||
@@ -60,8 +66,6 @@ CPInputTypeCanBeChangedFeature = 1 << 25;
|
||||
CPHTML5DragAndDropSourceYOffBy1 = 1 << 26;
|
||||
|
||||
|
||||
|
||||
|
||||
var USER_AGENT = "",
|
||||
PLATFORM_ENGINE = CPUnknownBrowserEngine,
|
||||
PLATFORM_FEATURES = 0;
|
||||
@@ -70,27 +74,27 @@ var USER_AGENT = "",
|
||||
|
||||
PLATFORM_FEATURES |= CPInputTypeCanBeChangedFeature;
|
||||
|
||||
if (typeof window != "undfined" && typeof window.navigator != "undefined")
|
||||
if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
|
||||
USER_AGENT = window.navigator.userAgent;
|
||||
|
||||
// Opera
|
||||
if (window.opera)
|
||||
if (typeof window !== "undefined" && window.opera)
|
||||
{
|
||||
PLATFORM_ENGINE = CPOperaBrowserEngine;
|
||||
|
||||
|
||||
PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature;
|
||||
}
|
||||
|
||||
// Internet Explorer
|
||||
else if (window.attachEvent) // Must follow Opera check.
|
||||
else if (typeof window !== "undefined" && window.attachEvent) // Must follow Opera check.
|
||||
{
|
||||
PLATFORM_ENGINE = CPInternetExplorerBrowserEngine;
|
||||
|
||||
|
||||
// Features we can only be sure of with IE (no known independent tests)
|
||||
PLATFORM_FEATURES |= CPVMLFeature;
|
||||
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
||||
|
||||
|
||||
PLATFORM_FEATURES |= CPOpacityRequiresFilterFeature;
|
||||
|
||||
PLATFORM_FEATURES &= ~CPInputTypeCanBeChangedFeature;
|
||||
@@ -100,15 +104,18 @@ else if (window.attachEvent) // Must follow Opera check.
|
||||
else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
{
|
||||
PLATFORM_ENGINE = CPWebKitBrowserEngine;
|
||||
|
||||
|
||||
// Features we can only be sure of with WebKit (no known independent tests)
|
||||
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
||||
PLATFORM_FEATURES |= CPHTMLContentEditableFeature;
|
||||
PLATFORM_FEATURES |= CPHTMLDragAndDropFeature;
|
||||
|
||||
if (USER_AGENT.indexOf("Chrome") === -1)
|
||||
PLATFORM_FEATURES |= CPHTMLDragAndDropFeature;
|
||||
|
||||
PLATFORM_FEATURES |= CPJavascriptClipboardEventsFeature;
|
||||
PLATFORM_FEATURES |= CPJavascriptClipboardAccessFeature;
|
||||
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
||||
|
||||
|
||||
var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length,
|
||||
versionEnd = USER_AGENT.indexOf(" ", versionStart),
|
||||
versionString = USER_AGENT.substring(versionStart, versionEnd),
|
||||
@@ -116,7 +123,7 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
majorVersion = parseInt(versionString.substring(0, versionDivision)),
|
||||
minorVersion = parseInt(versionString.substr(versionDivision + 1));
|
||||
|
||||
if((USER_AGENT.indexOf("Safari") !== CPNotFound && (majorVersion > 525 || (majorVersion === 525 && minorVersion > 14))) || USER_AGENT.indexOf("Chrome") !== CPNotFound)
|
||||
if ((USER_AGENT.indexOf("Safari") !== CPNotFound && (majorVersion > 525 || (majorVersion === 525 && minorVersion > 14))) || USER_AGENT.indexOf("Chrome") !== CPNotFound)
|
||||
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||
|
||||
// FIXME this is a terrible hack to get around this bug:
|
||||
@@ -138,12 +145,12 @@ else if (USER_AGENT.indexOf("KHTML") != -1) // Must follow WebKit check.
|
||||
else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
|
||||
{
|
||||
PLATFORM_ENGINE = CPGeckoBrowserEngine;
|
||||
|
||||
|
||||
PLATFORM_FEATURES |= CPJavaScriptCanvasDrawFeature;
|
||||
|
||||
|
||||
var index = USER_AGENT.indexOf("Firefox"),
|
||||
version = (index === -1) ? 2.0 : parseFloat(USER_AGENT.substring(index + "Firefox".length + 1));
|
||||
|
||||
|
||||
if (version >= 3.0)
|
||||
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
||||
|
||||
@@ -159,16 +166,16 @@ if (typeof document != "undefined")
|
||||
if (canvasElement && canvasElement.getContext)
|
||||
{
|
||||
PLATFORM_FEATURES |= CPHTMLCanvasFeature;
|
||||
|
||||
|
||||
// Detect Canvas setTransform/transform support
|
||||
var context = document.createElement("canvas").getContext("2d");
|
||||
|
||||
|
||||
if (context && context.setTransform && context.transform)
|
||||
PLATFORM_FEATURES |= CPJavaScriptCanvasTransformFeature;
|
||||
}
|
||||
|
||||
|
||||
var DOMElement = document.createElement("div");
|
||||
|
||||
|
||||
// Detect whether we have innerText or textContent (or neither)
|
||||
if (DOMElement.innerText != undefined)
|
||||
PLATFORM_FEATURES |= CPJavascriptInnerTextFeature;
|
||||
@@ -183,25 +190,37 @@ function CPFeatureIsCompatible(aFeature)
|
||||
|
||||
function CPBrowserIsEngine(anEngine)
|
||||
{
|
||||
return PLATFORM_ENGINE == anEngine;
|
||||
return PLATFORM_ENGINE === anEngine;
|
||||
}
|
||||
|
||||
if (USER_AGENT.indexOf("Mac") != -1)
|
||||
function CPBrowserIsOperatingSystem(anOperatingSystem)
|
||||
{
|
||||
return OPERATING_SYSTEM === anOperatingSystem;
|
||||
}
|
||||
|
||||
OPERATING_SYSTEM = CPOtherOperatingSystem;
|
||||
|
||||
if (USER_AGENT.indexOf("Mac") !== -1)
|
||||
{
|
||||
OPERATING_SYSTEM = CPMacOperatingSystem;
|
||||
|
||||
CPPlatformActionKeyMask = CPCommandKeyMask;
|
||||
|
||||
CPUndoKeyEquivalent = @"Z";
|
||||
CPUndoKeyEquivalent = @"z";
|
||||
CPRedoKeyEquivalent = @"Z";
|
||||
|
||||
CPUndoKeyEquivalentModifierMask = CPCommandKeyMask;
|
||||
CPRedoKeyEquivalentModifierMask = CPCommandKeyMask | CPShiftKeyMask;
|
||||
CPRedoKeyEquivalentModifierMask = CPCommandKeyMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (USER_AGENT.indexOf("Windows") !== -1)
|
||||
OPERATING_SYSTEM = CPWindowsOperatingSystem;
|
||||
|
||||
CPPlatformActionKeyMask = CPControlKeyMask;
|
||||
|
||||
CPUndoKeyEquivalent = @"Z";
|
||||
CPRedoKeyEquivalent = @"Y";
|
||||
CPUndoKeyEquivalent = @"z";
|
||||
CPRedoKeyEquivalent = @"y";
|
||||
|
||||
CPUndoKeyEquivalentModifierMask = CPControlKeyMask;
|
||||
CPRedoKeyEquivalentModifierMask = CPControlKeyMask;
|
||||
|
||||
+249
-104
@@ -23,51 +23,45 @@
|
||||
@import "CPFont.j"
|
||||
@import "CPShadow.j"
|
||||
@import "CPView.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
#include "Platform/Platform.h"
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPTextAlignment
|
||||
*/
|
||||
CPLeftTextAlignment = 0;
|
||||
/*
|
||||
@global
|
||||
@group CPTextAlignment
|
||||
*/
|
||||
CPRightTextAlignment = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPTextAlignment
|
||||
*/
|
||||
CPCenterTextAlignment = 2;
|
||||
/*
|
||||
@global
|
||||
@group CPTextAlignment
|
||||
*/
|
||||
CPJustifiedTextAlignment = 3;
|
||||
/*
|
||||
@global
|
||||
@group CPTextAlignment
|
||||
*/
|
||||
CPNaturalTextAlignment = 4;
|
||||
CPLeftTextAlignment = 0;
|
||||
CPRightTextAlignment = 1;
|
||||
CPCenterTextAlignment = 2;
|
||||
CPJustifiedTextAlignment = 3;
|
||||
CPNaturalTextAlignment = 4;
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPControlSize
|
||||
*/
|
||||
CPRegularControlSize = 0;
|
||||
/*
|
||||
@global
|
||||
@group CPControlSize
|
||||
*/
|
||||
CPSmallControlSize = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPControlSize
|
||||
*/
|
||||
CPMiniControlSize = 2;
|
||||
CPRegularControlSize = 0;
|
||||
CPSmallControlSize = 1;
|
||||
CPMiniControlSize = 2;
|
||||
|
||||
CPLineBreakByWordWrapping = 0;
|
||||
CPLineBreakByCharWrapping = 1;
|
||||
CPLineBreakByClipping = 2;
|
||||
CPLineBreakByTruncatingHead = 3;
|
||||
CPLineBreakByTruncatingTail = 4;
|
||||
CPLineBreakByTruncatingMiddle = 5;
|
||||
|
||||
CPTopVerticalTextAlignment = 1,
|
||||
CPCenterVerticalTextAlignment = 2,
|
||||
CPBottomVerticalTextAlignment = 3;
|
||||
|
||||
CPScaleProportionally = 0;
|
||||
CPScaleToFit = 1;
|
||||
CPScaleNone = 2;
|
||||
|
||||
CPNoImage = 0;
|
||||
CPImageOnly = 1;
|
||||
CPImageLeft = 2;
|
||||
CPImageRight = 3;
|
||||
CPImageBelow = 4;
|
||||
CPImageAbove = 5;
|
||||
CPImageOverlaps = 6;
|
||||
|
||||
CPOnState = 1;
|
||||
CPOffState = 0;
|
||||
CPMixedState = -1;
|
||||
|
||||
CPControlNormalBackgroundColor = "CPControlNormalBackgroundColor";
|
||||
CPControlSelectedBackgroundColor = "CPControlSelectedBackgroundColor";
|
||||
@@ -80,7 +74,7 @@ CPControlTextDidEndEditingNotification = "CPControlTextDidEndEditingNotific
|
||||
|
||||
var CPControlBlackColor = [CPColor blackColor];
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPControl
|
||||
|
||||
@@ -89,7 +83,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
@implementation CPControl : CPView
|
||||
{
|
||||
id _value;
|
||||
|
||||
|
||||
// Target-Action Support
|
||||
id _target;
|
||||
SEL _action;
|
||||
@@ -112,7 +106,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
CPLineBreakByClipping,
|
||||
[CPColor blackColor],
|
||||
[CPFont systemFontOfSize:12.0],
|
||||
nil,
|
||||
[CPNull null],
|
||||
_CGSizeMakeZero(),
|
||||
CPImageLeft,
|
||||
CPScaleToFit,
|
||||
@@ -131,16 +125,48 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
@"max-size"]];
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self === [CPControl class])
|
||||
{
|
||||
[self exposeBinding:@"value"];
|
||||
[self exposeBinding:@"objectValue"];
|
||||
[self exposeBinding:@"stringValue"];
|
||||
[self exposeBinding:@"integerValue"];
|
||||
[self exposeBinding:@"intValue"];
|
||||
[self exposeBinding:@"doubleValue"];
|
||||
[self exposeBinding:@"floatValue"];
|
||||
|
||||
[self exposeBinding:@"enabled"];
|
||||
}
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
{
|
||||
if (theBinding === CPValueBinding)
|
||||
return [_CPValueBinder class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
}
|
||||
|
||||
- (void)_reverseSetBinding
|
||||
{
|
||||
var binderClass = [[self class] _binderClassForBinding:CPValueBinding],
|
||||
theBinding = [binderClass getBinding:CPValueBinding forObject:self];
|
||||
|
||||
[theBinding reverseSetValueFor:@"objectValue"];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_sendActionOn = CPLeftMouseUpMask;
|
||||
_trackingMouseDownFlags = 0;
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -185,15 +211,16 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
*/
|
||||
- (void)sendAction:(SEL)anAction to:(id)anObject
|
||||
{
|
||||
[self _reverseSetBinding];
|
||||
[CPApp sendAction:anAction to:anObject from:self];
|
||||
}
|
||||
|
||||
- (int)sendActionOn:(int)mask
|
||||
{
|
||||
var previousMask = _sendActionOn;
|
||||
|
||||
|
||||
_sendActionOn = mask;
|
||||
|
||||
|
||||
return previousMask;
|
||||
}
|
||||
|
||||
@@ -206,7 +233,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
{
|
||||
if (_toolTip == aToolTip)
|
||||
return;
|
||||
|
||||
|
||||
_toolTip = aToolTip;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
@@ -241,7 +268,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
// Some subclasses should redefine this with CPLeftMouseDraggedMask
|
||||
if (flag)
|
||||
_sendActionOn |= CPPeriodicMask;
|
||||
else
|
||||
else
|
||||
_sendActionOn &= ~CPPeriodicMask;
|
||||
}
|
||||
|
||||
@@ -253,18 +280,22 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
- (void)trackMouse:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type],
|
||||
currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
|
||||
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
[self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:YES];
|
||||
|
||||
|
||||
_trackingMouseDownFlags = 0;
|
||||
|
||||
if (isWithinFrame)
|
||||
[self setThemeState:CPThemeStateHovered];
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
[self unsetThemeState:CPThemeStateHovered];
|
||||
|
||||
if (type === CPLeftMouseDown)
|
||||
{
|
||||
_trackingMouseDownFlags = [anEvent modifierFlags];
|
||||
@@ -276,20 +307,20 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
{
|
||||
if (!_trackingWasWithinFrame)
|
||||
_continuousTracking = [self startTrackingAt:currentLocation];
|
||||
|
||||
|
||||
else if (_continuousTracking)
|
||||
_continuousTracking = [self continueTracking:_previousTrackingLocation at:currentLocation];
|
||||
}
|
||||
else
|
||||
[self stopTracking:_previousTrackingLocation at:currentLocation mouseIsUp:NO];
|
||||
}
|
||||
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
}
|
||||
|
||||
|
||||
if ((_sendActionOn & (1 << type)) && isWithinFrame)
|
||||
[self sendAction:_action to:_target];
|
||||
|
||||
|
||||
_trackingWasWithinFrame = isWithinFrame;
|
||||
_previousTrackingLocation = currentLocation;
|
||||
}
|
||||
@@ -303,11 +334,30 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)performClick:(id)sender
|
||||
- (void)performClick:(id)sender
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
[self highlight:YES];
|
||||
[self setState:[self nextState]];
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
|
||||
try
|
||||
{
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
[CPTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(unhighlightButtonTimerDidFinish:) userInfo:nil repeats:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)unhighlightButtonTimerDidFinish:(id)sender
|
||||
{
|
||||
[self highlight:NO];
|
||||
}
|
||||
|
||||
@@ -319,7 +369,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
||||
{
|
||||
[self highlight:YES];
|
||||
|
||||
|
||||
return (_sendActionOn & CPPeriodicMask) || (_sendActionOn & CPLeftMouseDraggedMask);
|
||||
}
|
||||
|
||||
@@ -337,10 +387,29 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
|
||||
[self trackMouse:anEvent];
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
[self setThemeState:CPThemeStateHovered];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(CPEvent)anEvent
|
||||
{
|
||||
var currentLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
isWithinFrame = [self tracksMouseOutsideOfFrame] || CGRectContainsPoint([self bounds], currentLocation);
|
||||
|
||||
// Make sure we're not still in the frame because Cappuccino will sent mouseExited events
|
||||
// for all of the (ephemeral) subviews of a view as well.
|
||||
if (!isWithinFrame)
|
||||
[self unsetThemeState:CPThemeStateHovered];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the receiver's object value
|
||||
*/
|
||||
@@ -355,7 +424,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
- (void)setObjectValue:(id)anObject
|
||||
{
|
||||
_value = anObject;
|
||||
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
@@ -429,7 +498,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the receiver's int value
|
||||
Returns the receiver's string value
|
||||
*/
|
||||
- (CPString)stringValue
|
||||
{
|
||||
@@ -437,7 +506,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
}
|
||||
|
||||
/*!
|
||||
Set's the receiver's int value
|
||||
Set's the receiver's string value
|
||||
*/
|
||||
- (void)setStringValue:(CPString)anObject
|
||||
{
|
||||
@@ -484,54 +553,124 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
[self setStringValue:[sender stringValue]];
|
||||
}
|
||||
|
||||
- (void)textDidBeginEditing:(CPNotification)note
|
||||
- (void)textDidBeginEditing:(CPNotification)note
|
||||
{
|
||||
//this looks to prevent false propagation of notifications for other objects
|
||||
if([note object] != self)
|
||||
if ([note object] != self)
|
||||
return;
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidBeginEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||
}
|
||||
|
||||
- (void)textDidChange:(CPNotification)note
|
||||
- (void)textDidChange:(CPNotification)note
|
||||
{
|
||||
//this looks to prevent false propagation of notifications for other objects
|
||||
if([note object] != self)
|
||||
if ([note object] != self)
|
||||
return;
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidChangeNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||
}
|
||||
|
||||
- (void)textDidEndEditing:(CPNotification)note
|
||||
- (void)textDidEndEditing:(CPNotification)note
|
||||
{
|
||||
//this looks to prevent false propagation of notifications for other objects
|
||||
if([note object] != self)
|
||||
if ([note object] != self)
|
||||
return;
|
||||
|
||||
[self _reverseSetBinding];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidEndEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||
}
|
||||
|
||||
#define BRIDGE(UPPERCASE, LOWERCASE, ATTRIBUTENAME) \
|
||||
/*! Sets the value for ATTRIBUTENAME */\
|
||||
- (void)set##UPPERCASE:(id)aValue\
|
||||
{\
|
||||
[self setValue:aValue forThemeAttribute:ATTRIBUTENAME];\
|
||||
}\
|
||||
/*! Returns the current value for ATTRIBUTENAME */\
|
||||
- (id)LOWERCASE\
|
||||
{\
|
||||
return [self valueForThemeAttribute:ATTRIBUTENAME];\
|
||||
- (void)setAlignment:(CPTextAlignment)alignment
|
||||
{
|
||||
[self setValue:alignment forThemeAttribute:@"alignment"];
|
||||
}
|
||||
|
||||
BRIDGE(Alignment, alignment, "alignment")
|
||||
BRIDGE(VerticalAlignment, verticalAlignment, "vertical-alignment")
|
||||
BRIDGE(LineBreakMode, lineBreakMode, "line-break-mode")
|
||||
BRIDGE(TextColor, textColor, "text-color")
|
||||
BRIDGE(Font, font, "font")
|
||||
BRIDGE(TextShadowColor, textShadowColor, "text-shadow-color")
|
||||
BRIDGE(TextShadowOffset, textShadowOffset, "text-shadow-offset")
|
||||
BRIDGE(ImagePosition, imagePosition, "image-position")
|
||||
BRIDGE(ImageScaling, imageScaling, "image-scaling")
|
||||
- (CPTextAlignment)alignment
|
||||
{
|
||||
return [self valueForThemeAttribute:@"alignment"];
|
||||
}
|
||||
|
||||
- (void)setVerticalAlignment:(CPTextVerticalAlignment)alignment
|
||||
{
|
||||
[self setValue:alignment forThemeAttribute:@"vertical-alignment"];
|
||||
}
|
||||
|
||||
- (CPTextVerticalAlignment)verticalAlignment
|
||||
{
|
||||
return [self valueForThemeAttribute:@"vertical-alignment"];
|
||||
}
|
||||
|
||||
- (void)setLineBreakMode:(CPLineBreakMode)mode
|
||||
{
|
||||
[self setValue:mode forThemeAttribute:@"line-break-mode"];
|
||||
}
|
||||
|
||||
- (CPLineBreakMode)lineBreakMode
|
||||
{
|
||||
return [self valueForThemeAttribute:@"line-break-mode"];
|
||||
}
|
||||
|
||||
- (void)setTextColor:(CPColor)aColor
|
||||
{
|
||||
[self setValue:aColor forThemeAttribute:@"text-color"];
|
||||
}
|
||||
|
||||
- (CPColor)textColor
|
||||
{
|
||||
return [self valueForThemeAttribute:@"text-color"];
|
||||
}
|
||||
|
||||
- (void)setTextShadowColor:(CPColor)aColor
|
||||
{
|
||||
[self setValue:aColor forThemeAttribute:@"text-shadow-color"];
|
||||
}
|
||||
|
||||
- (CPColor)textShadowColor
|
||||
{
|
||||
return [self valueForThemeAttribute:@"text-shadow-color"];
|
||||
}
|
||||
|
||||
- (void)setTextShadowOffset:(float)offset
|
||||
{
|
||||
[self setValue:offset forThemeAttribute:@"text-shadow-offset"];
|
||||
}
|
||||
|
||||
- (float)textShadowOffset
|
||||
{
|
||||
return [self valueForThemeAttribute:@"text-shadow-offset"];
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
[self setValue:aFont forThemeAttribute:@"font"];
|
||||
}
|
||||
|
||||
- (CPFont)font
|
||||
{
|
||||
return [self valueForThemeAttribute:@"font"];
|
||||
}
|
||||
|
||||
- (void)setImagePosition:(CPCellImagePosition)position
|
||||
{
|
||||
[self setValue:position forThemeAttribute:@"image-position"];
|
||||
}
|
||||
|
||||
- (CPCellImagePosition)imagePosition
|
||||
{
|
||||
return [self valueForThemeAttribute:@"image-position"];
|
||||
}
|
||||
|
||||
- (void)setImageScaling:(CPImageScaling)scaling
|
||||
{
|
||||
[self setValue:scaling forThemeAttribute:@"image-scaling"];
|
||||
}
|
||||
|
||||
- (CPImageScaling)imageScaling
|
||||
{
|
||||
return [self valueForThemeAttribute:@"image-scaling"];
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)isEnabled
|
||||
{
|
||||
@@ -569,10 +708,12 @@ BRIDGE(ImageScaling, imageScaling, "image-scaling")
|
||||
var CPControlValueKey = "CPControlValueKey",
|
||||
CPControlControlStateKey = @"CPControlControlStateKey",
|
||||
CPControlIsEnabledKey = "CPControlIsEnabledKey",
|
||||
|
||||
|
||||
CPControlTargetKey = "CPControlTargetKey",
|
||||
CPControlActionKey = "CPControlActionKey",
|
||||
CPControlSendActionOnKey = "CPControlSendActionOnKey";
|
||||
CPControlSendActionOnKey = "CPControlSendActionOnKey",
|
||||
|
||||
CPControlSendsActionOnEndEditingKey = "CPControlSendsActionOnEndEditingKey";
|
||||
|
||||
var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||
|
||||
@@ -595,8 +736,9 @@ var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||
[self setAction:[aCoder decodeObjectForKey:CPControlActionKey]];
|
||||
|
||||
[self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]];
|
||||
[self setSendsActionOnEndEditing:[aCoder decodeBoolForKey:CPControlSendsActionOnEndEditingKey]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -608,6 +750,9 @@ var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
if (_sendsActionOnEndEditing)
|
||||
[aCoder encodeBool:_sendsActionOnEndEditing forKey:CPControlSendsActionOnEndEditingKey];
|
||||
|
||||
if (_value !== nil)
|
||||
[aCoder encodeObject:_value forKey:CPControlValueKey];
|
||||
|
||||
@@ -629,7 +774,7 @@ var _CPControlSizeIdentifiers = [],
|
||||
_CPControlSizeIdentifiers[CPRegularControlSize] = "Regular";
|
||||
_CPControlSizeIdentifiers[CPSmallControlSize] = "Small";
|
||||
_CPControlSizeIdentifiers[CPMiniControlSize] = "Mini";
|
||||
|
||||
|
||||
function _CPControlIdentifierForControlSize(aControlSize)
|
||||
{
|
||||
return _CPControlSizeIdentifiers[aControlSize];
|
||||
@@ -640,21 +785,21 @@ function _CPControlColorWithPatternImage(sizes, aClassName)
|
||||
var index = 1,
|
||||
count = arguments.length,
|
||||
identifier = "";
|
||||
|
||||
|
||||
for (; index < count; ++index)
|
||||
identifier += arguments[index];
|
||||
|
||||
|
||||
var color = _CPControlCachedColorWithPatternImages[identifier];
|
||||
|
||||
|
||||
if (!color)
|
||||
{
|
||||
var bundle = [CPBundle bundleForClass:[CPControl class]];
|
||||
|
||||
|
||||
color = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:aClassName + "/" + identifier + ".png"] size:sizes[identifier]]];
|
||||
|
||||
_CPControlCachedColorWithPatternImages[identifier] = color;
|
||||
}
|
||||
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
@@ -663,17 +808,17 @@ function _CPControlThreePartImagePattern(isVertical, sizes, aClassName)
|
||||
var index = 2,
|
||||
count = arguments.length,
|
||||
identifier = "";
|
||||
|
||||
|
||||
for (; index < count; ++index)
|
||||
identifier += arguments[index];
|
||||
|
||||
var color = _CPControlCachedThreePartImagePattern[identifier];
|
||||
|
||||
|
||||
if (!color)
|
||||
{
|
||||
var bundle = [CPBundle bundleForClass:[CPControl class]],
|
||||
path = aClassName + "/" + identifier;
|
||||
|
||||
|
||||
sizes = sizes[identifier];
|
||||
|
||||
color = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:[
|
||||
@@ -681,9 +826,9 @@ function _CPControlThreePartImagePattern(isVertical, sizes, aClassName)
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "1.png"] size:sizes[1]],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "2.png"] size:sizes[2]]
|
||||
] isVertical:isVertical]];
|
||||
|
||||
|
||||
_CPControlCachedThreePartImagePattern[identifier] = color;
|
||||
}
|
||||
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// CPController.j
|
||||
// AppKit
|
||||
//
|
||||
// Created by Ross Boucher 1/15/09
|
||||
// Copyright 280 North
|
||||
//
|
||||
// Adapted from GNUStep
|
||||
// Copyright (C) 2007 Free Software Foundation, Inc
|
||||
// Released under the LGPL.
|
||||
//
|
||||
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
var CPControllerDeclaredKeysKey = @"CPControllerDeclaredKeysKey";
|
||||
|
||||
@implementation CPController : CPObject
|
||||
{
|
||||
CPArray _editors;
|
||||
CPArray _declaredKeys;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_editors = [];
|
||||
_declaredKeys = [];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if ([_declaredKeys count] > 0)
|
||||
[aCoder encodeObject:_declaredKeys forKey:CPControllerDeclaredKeysKey];
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aDecoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_editors = [];
|
||||
_declaredKeys = [aDecoder decodeObjectForKey:CPControllerDeclaredKeysKey] || [];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)isEditing
|
||||
{
|
||||
return [_editors count] > 0;
|
||||
}
|
||||
|
||||
- (BOOL)commitEditing
|
||||
{
|
||||
var index = 0,
|
||||
count = _editors.length;
|
||||
|
||||
for (; index < count; ++index)
|
||||
if (![[_editors objectAtIndex:i] commitEditing])
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)discardEditing
|
||||
{
|
||||
[_editors makeObjectsPerformSelector:@selector(discardEditing)];
|
||||
}
|
||||
|
||||
- (void)objectDidBeginEditing:(id)anEditor
|
||||
{
|
||||
[_editors addObject:anEditor];
|
||||
}
|
||||
|
||||
- (void)objectDidEndEditing:(id)anEditor
|
||||
{
|
||||
[_editors removeObject:anEditor];
|
||||
}
|
||||
|
||||
@end
|
||||
+29
-20
@@ -24,7 +24,7 @@
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPCookie
|
||||
CPCookie is the Cappuccino interface to a web browser cookie. You can set the name
|
||||
@@ -33,7 +33,7 @@
|
||||
{
|
||||
CPString _cookieName;
|
||||
CPString _cookieValue;
|
||||
|
||||
|
||||
CPString _expires;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
- (id)initWithName:(CPString)aName
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
_cookieName = aName;
|
||||
_cookieValue = [self _readCookieValue];
|
||||
|
||||
@@ -83,30 +83,39 @@
|
||||
*/
|
||||
- (void)setValue:(CPString)value expires:(CPDate)date domain:(CPString)domain
|
||||
{
|
||||
if(date)
|
||||
var expires = "; expires="+date.toGMTString();
|
||||
else
|
||||
if (date)
|
||||
var expires = "; expires=" + date.toGMTString();
|
||||
else
|
||||
var expires = "";
|
||||
|
||||
if(domain)
|
||||
domain = "; domain="+domain;
|
||||
else
|
||||
|
||||
if (domain)
|
||||
domain = "; domain=" + domain;
|
||||
else
|
||||
domain = "";
|
||||
|
||||
document.cookie = _cookieName+"="+value+expires+"; path=/"+domain;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
document.cookie = _cookieName+"="+value+expires+"; path=/"+domain;
|
||||
#else
|
||||
_cookieValue = value;
|
||||
_expires = expires;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (CPString)_readCookieValue
|
||||
{
|
||||
var nameEQ = _cookieName + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for(var i=0;i < ca.length;i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0)==' ') c = c.substring(1,c.length);
|
||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
|
||||
}
|
||||
return "";
|
||||
#if PLATFORM(DOM)
|
||||
var nameEQ = _cookieName + "=",
|
||||
ca = document.cookie.split(';');
|
||||
|
||||
for (var i = 0; i < ca.length; i++)
|
||||
{
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
||||
}
|
||||
#endif
|
||||
return "";
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Executable
+277
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
Testing browsers:
|
||||
WebKit r50235 (Safari 4.0.3+)
|
||||
FireFox 3.5
|
||||
Opera 10.0.0
|
||||
|
||||
Implemented class methods:
|
||||
Webkit : All
|
||||
IE : All
|
||||
FireFox Win : All
|
||||
FireFox Mac : All except closedHandCursor disappearingItemCursor // Firefox mac does not support url cursors
|
||||
Opera : All except resizeLeftRightCursor resizeUpDownCursor operationNotAllowedCursor dragCopyCursor dragLinkCursor contextualMenuCursor openHandCursor closedHandCursor disappearingItemCursor // Opera does not support url cursors so these won't work with images
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
var currentCursor = nil,
|
||||
cursorStack = [],
|
||||
cursors = {},
|
||||
cursorURLFormat = nil;
|
||||
|
||||
@implementation CPCursor : CPObject
|
||||
{
|
||||
CPString _cssString;
|
||||
BOOL _isSetOnMouseEntered @accessors(readwrite, getter=isSetOnMouseEntered, setter=setOnMouseEntered:);
|
||||
BOOL _isSetOnMouseExited @accessors(readwrite, getter=isSetOnMouseExited, setter=setOnMouseExited:);
|
||||
}
|
||||
|
||||
+ (CPCursor)currentCursor
|
||||
{
|
||||
return currentCursor;
|
||||
}
|
||||
|
||||
- (id)initWithCSSString:(CPString)aString
|
||||
{
|
||||
if (self = [super init])
|
||||
_cssString = aString;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (CPCursor)cursorWithCSSString:(CPString)cssString
|
||||
{
|
||||
var cursor = cursors[cssString];
|
||||
|
||||
if (typeof cursor == 'undefined')
|
||||
{
|
||||
cursor = [[CPCursor alloc] initWithCSSString:cssString];
|
||||
cursors[cssString] = cursor;
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
+ (CPCursor)cursorWithImageNamed:(CPString)imageName
|
||||
{
|
||||
if (!cursorURLFormat)
|
||||
{
|
||||
cursorURLFormat = @"url(" + [[CPBundle bundleForClass:self] resourcePath] + @"/CPCursor/%@.cur)";
|
||||
|
||||
if (CPBrowserIsEngine(CPGeckoBrowserEngine))
|
||||
cursorURLFormat += ", default";
|
||||
}
|
||||
|
||||
var url = [CPString stringWithFormat:cursorURLFormat, imageName];
|
||||
|
||||
return [[CPCursor alloc] initWithCSSString:url];
|
||||
}
|
||||
|
||||
- (CPString)_cssString
|
||||
{
|
||||
return _cssString;
|
||||
}
|
||||
|
||||
+ (CPCursor)arrowCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"default"]; // WebKit | FF | opera | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)crosshairCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"crosshair"]; // WebKit | FF | opera | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)IBeamCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"text"]; // WebKit | FF | opera | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)pointingHandCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"pointer"]; // WebKit | FF | opera | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeDownCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:"s-resize"]; // WebKit | FF | opera
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeUpCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"n-resize"]; // WebKit | FF | opera
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeLeftCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"w-resize"]; // WebKit | FF | opera
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeRightCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"e-resize"]; // WebKit | FF | opera
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeLeftRightCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"col-resize"]; // WebKit | FF | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)resizeUpDownCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"row-resize"]; // WebKit | FF | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)operationNotAllowedCursor
|
||||
{
|
||||
return [CPCursor cursorWithCSSString:@"not-allowed"]; // WebKit | FF | IE
|
||||
}
|
||||
|
||||
+ (CPCursor)dragCopyCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"copy"]; // WebKit | FF
|
||||
}
|
||||
|
||||
+ (CPCursor)dragLinkCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"alias"]; // WebKit | FF
|
||||
}
|
||||
|
||||
+ (CPCursor)contextualMenuCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)];
|
||||
|
||||
return [CPCursor cursorWithCSSString:@"context-menu"]; // WebKit | FF Mac . Not impl FF Win.
|
||||
}
|
||||
|
||||
+ (CPCursor)openHandCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPWebKitBrowserEngine))
|
||||
return [CPCursor cursorWithCSSString:@"-webkit-grab"];
|
||||
else if (CPBrowserIsEngine(CPGeckoBrowserEngine))
|
||||
return [CPCursor cursorWithCSSString:@"-moz-grab"];
|
||||
else if (CPBrowserIsEngine(CPOperaBrowserEngine))
|
||||
return [CPCursor cursorWithCSSString:@"move"];
|
||||
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // WebKit only. move in FFMac|Opera
|
||||
}
|
||||
|
||||
+ (CPCursor)closedHandCursor
|
||||
{
|
||||
if (CPBrowserIsEngine(CPWebKitBrowserEngine))
|
||||
return [CPCursor cursorWithCSSString:@"-webkit-grabbing"];
|
||||
else if (CPBrowserIsEngine(CPGeckoBrowserEngine))
|
||||
return [CPCursor cursorWithCSSString:@"-moz-grabbing"];
|
||||
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // WebKit || FF
|
||||
}
|
||||
|
||||
+ (CPCursor)disappearingItemCursor
|
||||
{
|
||||
return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // None
|
||||
}
|
||||
|
||||
+ (void)hide
|
||||
{
|
||||
[self _setCursorCSS:"none"]; // Not supported in IE
|
||||
}
|
||||
|
||||
+ (void)unhide
|
||||
{
|
||||
[self _setCursorCSS:[currentCursor _cssString]];
|
||||
}
|
||||
|
||||
+ (void)setHiddenUntilMouseMoves:(BOOL)flag
|
||||
{
|
||||
if (flag)
|
||||
[CPCursor hide];
|
||||
else
|
||||
[CPCursor unhide];
|
||||
}
|
||||
|
||||
- (id)initWithImage:(CPImage)image hotSpot:(CPPoint)hotSpot
|
||||
{
|
||||
return [self initWithCSSString:"url(" + [image filename] + "), auto"];
|
||||
}
|
||||
|
||||
|
||||
- (void)mouseEntered:(CPEvent)event
|
||||
{
|
||||
}
|
||||
|
||||
- (void)mouseExited:(CPEvent)event
|
||||
{
|
||||
}
|
||||
|
||||
- (void)set
|
||||
{
|
||||
currentCursor = self;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
[[self class] _setCursorCSS:_cssString];
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
+ (void)_setCursorCSS:(CPString)aString
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var platformWindows = [[CPPlatformWindow visiblePlatformWindows] allObjects];
|
||||
for (var i = 0, count = [platformWindows count]; i < count; i++)
|
||||
platformWindows[i]._DOMBodyElement.style.cursor = aString;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)push
|
||||
{
|
||||
currentCursor = cursorStack.push(self);
|
||||
}
|
||||
|
||||
- (void)pop
|
||||
{
|
||||
[CPCursor pop];
|
||||
}
|
||||
|
||||
+ (void)pop
|
||||
{
|
||||
if (cursorStack.length > 1)
|
||||
{
|
||||
cursorStack.pop();
|
||||
currentCursor = cursorStack[cursorStack.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)coder
|
||||
{
|
||||
if (self = [super init])
|
||||
_cssString = [coder decodeObjectForKey:@"CPCursorNameKey"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)coder
|
||||
{
|
||||
[coder encodeObject:_cssString forKey:@"CPCursorNameKey"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+60
-56
@@ -23,6 +23,7 @@
|
||||
@import <Foundation/CPString.j>
|
||||
@import <Foundation/CPArray.j>
|
||||
|
||||
@import "CPApplication.j"
|
||||
@import "CPResponder.j"
|
||||
@import "CPSavePanel.j"
|
||||
@import "CPViewController.j"
|
||||
@@ -82,7 +83,7 @@ CPDocumentDidFailToSaveNotification = @"CPDocumentDidFailToSaveNotification";
|
||||
|
||||
var CPDocumentUntitledCount = 0;
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPDocument
|
||||
|
||||
@@ -120,7 +121,7 @@ var CPDocumentUntitledCount = 0;
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_windowControllers = [];
|
||||
@@ -131,7 +132,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
[self setNextResponder:CPApp];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -144,10 +145,10 @@ var CPDocumentUntitledCount = 0;
|
||||
- (id)initWithType:(CPString)aType error:({CPError})anError
|
||||
{
|
||||
self = [self init];
|
||||
|
||||
|
||||
if (self)
|
||||
[self setFileType:aType];
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -165,7 +166,7 @@ var CPDocumentUntitledCount = 0;
|
||||
- (id)initWithContentsOfURL:(CPURL)anAbsoluteURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
|
||||
{
|
||||
self = [self init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setFileURL:anAbsoluteURL];
|
||||
@@ -173,7 +174,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
[self readFromURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -190,7 +191,7 @@ var CPDocumentUntitledCount = 0;
|
||||
- (id)initForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
|
||||
{
|
||||
self = [self init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setFileURL:anAbsoluteURL];
|
||||
@@ -198,7 +199,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
[self readFromURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -280,7 +281,11 @@ var CPDocumentUntitledCount = 0;
|
||||
else if (viewController)
|
||||
{
|
||||
var view = [viewController view],
|
||||
theWindow = [[CPWindow alloc] initWithContentRect:[view frame] styleMask:CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask];
|
||||
viewFrame = [view frame];
|
||||
|
||||
viewFrame.origin = CGPointMake(50, 50);
|
||||
|
||||
var theWindow = [[CPWindow alloc] initWithContentRect:viewFrame styleMask:CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask];
|
||||
|
||||
windowController = [[CPWindowController alloc] initWithWindow:theWindow];
|
||||
}
|
||||
@@ -377,14 +382,14 @@ var CPDocumentUntitledCount = 0;
|
||||
{
|
||||
if (_fileURL)
|
||||
return [_fileURL lastPathComponent];
|
||||
|
||||
|
||||
if (!_untitledDocumentIndex)
|
||||
_untitledDocumentIndex = ++CPDocumentUntitledCount;
|
||||
|
||||
if (_untitledDocumentIndex == 1)
|
||||
return @"Untitled";
|
||||
|
||||
return @"Untitled " + _untitledDocumentIndex;
|
||||
if (_untitledDocumentIndex == 1)
|
||||
return @"Untitled";
|
||||
|
||||
return @"Untitled " + _untitledDocumentIndex;
|
||||
}
|
||||
|
||||
- (CPString)viewCibName
|
||||
@@ -453,7 +458,7 @@ var CPDocumentUntitledCount = 0;
|
||||
return;
|
||||
|
||||
_fileURL = aFileURL;
|
||||
|
||||
|
||||
[_windowControllers makeObjectsPerformSelector:@selector(synchronizeWindowTitleWithDocumentName)];
|
||||
}
|
||||
|
||||
@@ -480,14 +485,14 @@ var CPDocumentUntitledCount = 0;
|
||||
else
|
||||
[_writeRequest setHTTPMethod:@"PUT"];
|
||||
|
||||
[_writeRequest setHTTPBody:[data string]];
|
||||
[_writeRequest setHTTPBody:[data rawString]];
|
||||
|
||||
[_writeRequest setValue:@"close" forHTTPHeaderField:@"Connection"];
|
||||
|
||||
if (aSaveOperation == CPSaveOperation)
|
||||
if (aSaveOperation === CPSaveOperation)
|
||||
[_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
|
||||
|
||||
if (aSaveOperation != CPSaveToOperation)
|
||||
if (aSaveOperation !== CPSaveToOperation)
|
||||
[self updateChangeCount:CPChangeCleared];
|
||||
|
||||
// FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors.
|
||||
@@ -505,21 +510,21 @@ var CPDocumentUntitledCount = 0;
|
||||
// If we got this far and it wasn't an HTTP request, then everything is fine.
|
||||
if (![aResponse isKindOfClass:[CPHTTPURLResponse class]])
|
||||
return;
|
||||
|
||||
|
||||
var statusCode = [aResponse statusCode];
|
||||
|
||||
|
||||
// Nothing to do if everything is hunky dory.
|
||||
if (statusCode === 200)
|
||||
return;
|
||||
|
||||
|
||||
var session = aConnection.session;
|
||||
|
||||
|
||||
if (aConnection == _readConnection)
|
||||
{
|
||||
[aConnection cancel];
|
||||
|
||||
|
||||
alert("There was an error retrieving the document.");
|
||||
|
||||
|
||||
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||
}
|
||||
else
|
||||
@@ -528,23 +533,23 @@ var CPDocumentUntitledCount = 0;
|
||||
if (statusCode == 409)
|
||||
{
|
||||
[aConnection cancel];
|
||||
|
||||
|
||||
if (confirm("There already exists a file with that name, would you like to overwrite it?"))
|
||||
{
|
||||
[_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
|
||||
|
||||
|
||||
[aConnection start];
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
if (session.saveOperation != CPSaveToOperation)
|
||||
{
|
||||
_changeCount += session.changeCount;
|
||||
[_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
|
||||
}
|
||||
|
||||
|
||||
_writeRequest = nil;
|
||||
|
||||
|
||||
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||
[self _sendDocumentSavedNotification:NO];
|
||||
}
|
||||
@@ -559,11 +564,11 @@ var CPDocumentUntitledCount = 0;
|
||||
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData
|
||||
{
|
||||
var session = aConnection.session;
|
||||
|
||||
|
||||
// READ
|
||||
if (aConnection == _readConnection)
|
||||
{
|
||||
[self readFromData:[CPData dataWithString:aData] ofType:session.fileType error:nil];
|
||||
[self readFromData:[CPData dataWithRawString:aData] ofType:session.fileType error:nil];
|
||||
|
||||
objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo);
|
||||
}
|
||||
@@ -571,9 +576,9 @@ var CPDocumentUntitledCount = 0;
|
||||
{
|
||||
if (session.saveOperation != CPSaveToOperation)
|
||||
[self setFileURL:session.absoluteURL];
|
||||
|
||||
|
||||
_writeRequest = nil;
|
||||
|
||||
|
||||
objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
|
||||
[self _sendDocumentSavedNotification:YES];
|
||||
}
|
||||
@@ -586,17 +591,17 @@ var CPDocumentUntitledCount = 0;
|
||||
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
|
||||
{
|
||||
var session = aConnection.session;
|
||||
|
||||
|
||||
if (_readConnection == aConnection)
|
||||
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||
|
||||
|
||||
else
|
||||
{
|
||||
{
|
||||
if (session.saveOperation != CPSaveToOperation)
|
||||
{
|
||||
_changeCount += session.changeCount;
|
||||
[_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
|
||||
}
|
||||
}
|
||||
|
||||
_writeRequest = nil;
|
||||
|
||||
@@ -639,9 +644,9 @@ var CPDocumentUntitledCount = 0;
|
||||
else if (aChangeType == CPChangeCleared)
|
||||
_changeCount = 0;
|
||||
/*else if (aChangeType == CPCHangeReadOtherContents)
|
||||
|
||||
else if (aChangeType == CPChangeAutosaved)*/
|
||||
|
||||
|
||||
else if (aChangeType == CPChangeAutosaved)*/
|
||||
|
||||
[_windowControllers makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:[self isDocumentEdited]];
|
||||
}
|
||||
|
||||
@@ -681,9 +686,9 @@ var CPDocumentUntitledCount = 0;
|
||||
{
|
||||
if (_hasUndoManager == aFlag)
|
||||
return;
|
||||
|
||||
|
||||
_hasUndoManager = aFlag;
|
||||
|
||||
|
||||
if (!_hasUndoManager)
|
||||
[self setUndoManager:nil];
|
||||
}
|
||||
@@ -692,7 +697,7 @@ var CPDocumentUntitledCount = 0;
|
||||
- (void)_undoManagerWillCloseGroup:(CPNotification)aNotification
|
||||
{
|
||||
var undoManager = [aNotification object];
|
||||
|
||||
|
||||
if ([undoManager isUndoing] || [undoManager isRedoing])
|
||||
return;
|
||||
|
||||
@@ -719,13 +724,13 @@ var CPDocumentUntitledCount = 0;
|
||||
- (void)setUndoManager:(CPUndoManager)anUndoManager
|
||||
{
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
|
||||
if (_undoManager)
|
||||
{
|
||||
[defaultCenter removeObserver:self
|
||||
name:CPUndoManagerDidUndoChangeNotification
|
||||
object:_undoManager];
|
||||
|
||||
|
||||
[defaultCenter removeObserver:self
|
||||
name:CPUndoManagerDidRedoChangeNotification
|
||||
object:_undoManager];
|
||||
@@ -734,17 +739,17 @@ var CPDocumentUntitledCount = 0;
|
||||
name:CPUndoManagerWillCloseUndoGroupNotification
|
||||
object:_undoManager];
|
||||
}
|
||||
|
||||
|
||||
_undoManager = anUndoManager;
|
||||
|
||||
|
||||
if (_undoManager)
|
||||
{
|
||||
|
||||
|
||||
[defaultCenter addObserver:self
|
||||
selector:@selector(_undoManagerDidUndoChange:)
|
||||
name:CPUndoManagerDidUndoChangeNotification
|
||||
object:_undoManager];
|
||||
|
||||
|
||||
[defaultCenter addObserver:self
|
||||
selector:@selector(_undoManagerDidRedoChange:)
|
||||
name:CPUndoManagerDidRedoChangeNotification
|
||||
@@ -770,7 +775,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
return _undoManager;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Implemented as a delegate of a CPWindow
|
||||
@ignore
|
||||
@@ -799,7 +804,7 @@ var CPDocumentUntitledCount = 0;
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPDocumentWillSaveNotification
|
||||
object:self];
|
||||
|
||||
|
||||
[self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
|
||||
}
|
||||
else
|
||||
@@ -857,7 +862,7 @@ var CPDocumentUntitledCount = 0;
|
||||
[[CPDocumentController sharedDocumentController] removeDocument:self];
|
||||
}
|
||||
|
||||
- (void)shouldCloseWindowController:(CPWindowController)controller delegate:(id)delegate shouldCloseSelector:(SEL)selector contextInfo:(Object)info
|
||||
- (void)shouldCloseWindowController:(CPWindowController)controller delegate:(id)delegate shouldCloseSelector:(SEL)selector contextInfo:(Object)info
|
||||
{
|
||||
if ([controller shouldCloseDocument] || ([_windowControllers count] < 2 && [_windowControllers indexOfObject:controller] !== CPNotFound))
|
||||
[self canCloseDocumentWithDelegate:self shouldCloseSelector:@selector(_document:shouldClose:context:) contextInfo:{delegate:delegate, selector:selector, context:info}];
|
||||
@@ -874,7 +879,7 @@ var CPDocumentUntitledCount = 0;
|
||||
objj_msgSend(context.delegate, context.selector, aDocument, shouldClose, context.context);
|
||||
}
|
||||
|
||||
- (void)canCloseDocumentWithDelegate:(id)aDelegate shouldCloseSelector:(SEL)aSelector contextInfo:(Object)context
|
||||
- (void)canCloseDocumentWithDelegate:(id)aDelegate shouldCloseSelector:(SEL)aSelector contextInfo:(Object)context
|
||||
{
|
||||
if (![self isDocumentEdited])
|
||||
return [aDelegate respondsToSelector:aSelector] && objj_msgSend(aDelegate, aSelector, self, YES, context);
|
||||
@@ -884,8 +889,7 @@ var CPDocumentUntitledCount = 0;
|
||||
[_canCloseAlert setDelegate:self];
|
||||
[_canCloseAlert setAlertStyle:CPWarningAlertStyle];
|
||||
[_canCloseAlert setTitle:@"Unsaved Document"];
|
||||
[_canCloseAlert setMessageText:sprintf(@"Do you want to save the changes you've made to the document \"%@\"?",
|
||||
[self displayName] || [self fileName])];
|
||||
[_canCloseAlert setMessageText:@"Do you want to save the changes you've made to the document \"" + ([self displayName] || [self fileName]) + "\"?"];
|
||||
|
||||
[_canCloseAlert addButtonWithTitle:@"Save"];
|
||||
[_canCloseAlert addButtonWithTitle:@"Cancel"];
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
var CPSharedDocumentController = nil;
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPDocumentController
|
||||
This class is responsible for managing an application's open documents.
|
||||
@@ -41,7 +41,7 @@ var CPSharedDocumentController = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the singleton instance of the application's document controller. If it has not
|
||||
Returns the singleton instance of the application's document controller. If it has not
|
||||
been created yet, it will be created then returned.
|
||||
@return a CPDocumentController
|
||||
*/
|
||||
@@ -49,7 +49,7 @@ var CPSharedDocumentController = nil;
|
||||
{
|
||||
if (!CPSharedDocumentController)
|
||||
[[self alloc] init];
|
||||
|
||||
|
||||
return CPSharedDocumentController;
|
||||
}
|
||||
|
||||
@@ -59,11 +59,11 @@ var CPSharedDocumentController = nil;
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_documents = [[CPArray alloc] init];
|
||||
|
||||
|
||||
if (!CPSharedDocumentController)
|
||||
CPSharedDocumentController = self;
|
||||
|
||||
@@ -85,16 +85,16 @@ var CPSharedDocumentController = nil;
|
||||
{
|
||||
var index = 0,
|
||||
count = [_documents count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var theDocument = _documents[index];
|
||||
|
||||
if ([[theDocument fileURL] isEqual:aURL])
|
||||
return theDocument;
|
||||
|
||||
if ([[theDocument fileURL] isEqual:aURL])
|
||||
return theDocument;
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -105,10 +105,10 @@ var CPSharedDocumentController = nil;
|
||||
- (void)openUntitledDocumentOfType:(CPString)aType display:(BOOL)shouldDisplay
|
||||
{
|
||||
var theDocument = [self makeUntitledDocumentOfType:aType error:nil];
|
||||
|
||||
|
||||
if (theDocument)
|
||||
[self addDocument:theDocument];
|
||||
|
||||
|
||||
if (shouldDisplay)
|
||||
{
|
||||
[theDocument makeWindowControllers];
|
||||
@@ -146,6 +146,8 @@ var CPSharedDocumentController = nil;
|
||||
|
||||
result = [self makeDocumentWithContentsOfURL:anAbsoluteURL ofType:type delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:[CPDictionary dictionaryWithObject:shouldDisplay forKey:@"shouldDisplay"]];
|
||||
|
||||
[self addDocument:result];
|
||||
|
||||
if (result)
|
||||
[self noteNewRecentDocument:result];
|
||||
}
|
||||
@@ -207,9 +209,8 @@ var CPSharedDocumentController = nil;
|
||||
if (!didRead)
|
||||
return;
|
||||
|
||||
[self addDocument:aDocument];
|
||||
[aDocument makeWindowControllers];
|
||||
|
||||
|
||||
if ([aContextInfo objectForKey:@"shouldDisplay"])
|
||||
[aDocument showWindows];
|
||||
}
|
||||
@@ -223,7 +224,7 @@ var CPSharedDocumentController = nil;
|
||||
[self openUntitledDocumentOfType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] display:YES];
|
||||
}
|
||||
|
||||
-(void)openDocument:(id)aSender
|
||||
- (void)openDocument:(id)aSender
|
||||
{
|
||||
var openPanel = [CPOpenPanel openPanel];
|
||||
|
||||
@@ -275,17 +276,17 @@ var CPSharedDocumentController = nil;
|
||||
{
|
||||
var index = 0,
|
||||
count = _documentTypes.length,
|
||||
|
||||
|
||||
extension = [[anAbsoluteURL pathExtension] lowercaseString],
|
||||
starType = nil;
|
||||
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var documentType = _documentTypes[index],
|
||||
extensions = [documentType objectForKey:@"CFBundleTypeExtensions"],
|
||||
extensionIndex = 0,
|
||||
extensionCount = extensions.length;
|
||||
|
||||
|
||||
for (; extensionIndex < extensionCount; ++extensionIndex)
|
||||
{
|
||||
var thisExtension = [extensions[extensionIndex] lowercaseString];
|
||||
@@ -311,11 +312,11 @@ var CPSharedDocumentController = nil;
|
||||
for (;i < count; ++i)
|
||||
{
|
||||
var documentType = _documentTypes[i];
|
||||
|
||||
|
||||
if ([documentType objectForKey:@"CPBundleTypeName"] == aType)
|
||||
return documentType;
|
||||
}
|
||||
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -335,7 +336,7 @@ var CPSharedDocumentController = nil;
|
||||
|
||||
@implementation CPDocumentController (Closing)
|
||||
|
||||
- (void)closeAllDocumentsWithDelegate:(id)aDelegate didCloseAllSelector:(SEL)didCloseSelector contextInfo:(Object)info
|
||||
- (void)closeAllDocumentsWithDelegate:(id)aDelegate didCloseAllSelector:(SEL)didCloseSelector contextInfo:(Object)info
|
||||
{
|
||||
var context = {
|
||||
delegate: aDelegate,
|
||||
@@ -389,13 +390,14 @@ var CPSharedDocumentController = nil;
|
||||
|
||||
- (void)noteNewRecentDocument:(CPDocument)aDocument
|
||||
{
|
||||
[self noteNewRecentDocumentURL:[[aDocument fileURL] absoluteString]];
|
||||
[self noteNewRecentDocumentURL:[aDocument fileURL]];
|
||||
}
|
||||
|
||||
- (void)noteNewRecentDocumentURL:(CPString)aURL
|
||||
- (void)noteNewRecentDocumentURL:(CPURL)aURL
|
||||
{
|
||||
var urlAsString = [aURL isKindOfClass:CPString] ? aURL : [aURL absoluteString];
|
||||
if (typeof window["cpNoteNewRecentDocumentPath"] === 'function')
|
||||
window.cpNoteNewRecentDocumentPath(aURL);
|
||||
window.cpNoteNewRecentDocumentPath(urlAsString);
|
||||
|
||||
[self _updateRecentDocumentsMenu];
|
||||
}
|
||||
|
||||
+125
-50
@@ -20,13 +20,12 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <AppKit/CPView.j>
|
||||
@import <AppKit/CPEvent.j>
|
||||
@import <AppKit/CPPasteboard.j>
|
||||
@import <AppKit/CPImageView.j>
|
||||
|
||||
#import "CoreGraphics/CGGeometry.h"
|
||||
#import "Platform/Platform.h"
|
||||
@import "CPApplication.j"
|
||||
@import "CPEvent.j"
|
||||
@import "CPImageView.j"
|
||||
@import "CPPasteboard.j"
|
||||
@import "CPView.j"
|
||||
@import "CPWindow.j"
|
||||
|
||||
|
||||
CPDragOperationNone = 0,
|
||||
@@ -40,29 +39,10 @@ CPDragOperationEvery = -1;
|
||||
|
||||
#define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window])
|
||||
|
||||
var CPDragServerPreviousEvent = nil,
|
||||
CPDragServerAutoscrollInterval = nil;
|
||||
/*
|
||||
var CPDragServerAutoscroll = function()
|
||||
{
|
||||
[CPDragServerSource autoscroll:CPDragServerPreviousEvent];
|
||||
}
|
||||
var CPDragServerPreviousEvent = nil,
|
||||
CPDragServerPeriodicUpdateInterval = 0.05;
|
||||
|
||||
if (CPDragServerAutoscrollInterval === nil)
|
||||
{
|
||||
if ([CPDragServerSource respondsToSelector:@selector(autoscroll:)])
|
||||
CPDragServerAutoscrollInterval = setInterval(CPDragServerAutoscroll, 100);
|
||||
}
|
||||
|
||||
CPDragServerPreviousEvent = anEvent;
|
||||
|
||||
if (CPDragServerAutoscrollInterval !== nil)
|
||||
clearInterval(CPDragServerAutoscrollInterval);
|
||||
|
||||
CPDragServerAutoscrollInterval = nil;
|
||||
*/
|
||||
|
||||
var CPSharedDragServer = nil;
|
||||
var CPSharedDragServer = nil;
|
||||
|
||||
var CPDragServerSource = nil;
|
||||
var CPDragServerDraggingInfo = nil;
|
||||
@@ -127,7 +107,7 @@ var CPDragServerDraggingInfo = nil;
|
||||
@end
|
||||
|
||||
var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
CPDraggingSource_draggedImage_endAt_operation_ = 1 << 1,
|
||||
CPDraggingSource_draggedImage_endedAt_operation_ = 1 << 1,
|
||||
CPDraggingSource_draggedView_movedTo_ = 1 << 2,
|
||||
CPDraggingSource_draggedView_endedAt_operation_ = 1 << 3;
|
||||
|
||||
@@ -150,6 +130,13 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
|
||||
CGPoint _draggingLocation;
|
||||
id _draggingDestination;
|
||||
BOOL _draggingDestinationWantsPeriodicUpdates;
|
||||
|
||||
CGPoint _startDragLocation;
|
||||
BOOL _shouldSlideBack;
|
||||
unsigned _dragOperation;
|
||||
|
||||
CPTimer _draggingUpdateTimer;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -189,6 +176,11 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)draggingDestination
|
||||
{
|
||||
return _draggingDestination;
|
||||
}
|
||||
|
||||
- (CGPoint)draggingLocation
|
||||
{
|
||||
return _draggingLocation
|
||||
@@ -225,42 +217,108 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
|
||||
- (CPDragOperation)draggingUpdatedInPlatformWindow:(CPPlatformWindow)aPlatformWindow location:(CGPoint)aLocation
|
||||
{
|
||||
var dragOperation = CPDragOperationCopy;
|
||||
[_draggingUpdateTimer invalidate];
|
||||
_draggingUpdateTimer = nil;
|
||||
|
||||
var dragOperation = CPDragOperationCopy,
|
||||
// We have to convert base to bridge since the drag event comes from the source window, not the drag window.
|
||||
var draggingDestination = [aPlatformWindow _dragHitTest:aLocation pasteboard:[CPDragServerDraggingInfo draggingPasteboard]];
|
||||
draggingDestination = [aPlatformWindow _dragHitTest:aLocation pasteboard:[CPDragServerDraggingInfo draggingPasteboard]];
|
||||
|
||||
if (draggingDestination)
|
||||
_draggingLocation = [DRAGGING_WINDOW(draggingDestination) convertPlatformWindowToBase:aLocation];
|
||||
|
||||
if(draggingDestination !== _draggingDestination)
|
||||
if (draggingDestination !== _draggingDestination)
|
||||
{
|
||||
if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingExited:)])
|
||||
if ([_draggingDestination respondsToSelector:@selector(draggingExited:)])
|
||||
[_draggingDestination draggingExited:CPDragServerDraggingInfo];
|
||||
|
||||
_draggingDestination = draggingDestination;
|
||||
|
||||
if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingEntered:)])
|
||||
if ([_draggingDestination respondsToSelector:@selector(wantsPeriodicDraggingUpdates)])
|
||||
_draggingDestinationWantsPeriodicUpdates = [_draggingDestination wantsPeriodicDraggingUpdates];
|
||||
else
|
||||
_draggingDestinationWantsPeriodicUpdates = YES;
|
||||
|
||||
if ([_draggingDestination respondsToSelector:@selector(draggingEntered:)])
|
||||
dragOperation = [_draggingDestination draggingEntered:CPDragServerDraggingInfo];
|
||||
}
|
||||
else if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingUpdated:)])
|
||||
else if ([_draggingDestination respondsToSelector:@selector(draggingUpdated:)])
|
||||
dragOperation = [_draggingDestination draggingUpdated:CPDragServerDraggingInfo];
|
||||
|
||||
if (!_draggingDestination)
|
||||
dragOperation = CPDragOperationNone;
|
||||
else
|
||||
{
|
||||
if (_draggingDestinationWantsPeriodicUpdates)
|
||||
_draggingUpdateTimer = [CPTimer scheduledTimerWithTimeInterval:CPDragServerPeriodicUpdateInterval
|
||||
target:self
|
||||
selector:@selector(_sendPeriodicDraggingUpdate:)
|
||||
userInfo:[CPDictionary dictionaryWithJSObject:{platformWindow:aPlatformWindow, location:aLocation}]
|
||||
repeats:NO];
|
||||
|
||||
var scrollView = [_draggingDestination isKindOfClass:[CPView class]] ? [_draggingDestination enclosingScrollView] : nil;
|
||||
if (scrollView)
|
||||
{
|
||||
var contentView = [scrollView contentView],
|
||||
bounds = [contentView bounds],
|
||||
insetBounds = CGRectInset(bounds, 10, 10),
|
||||
eventLocation = [contentView convertPoint:_draggingLocation fromView:nil],
|
||||
deltaX = 0,
|
||||
deltaY = 0;
|
||||
|
||||
if (!CGRectContainsPoint(insetBounds, eventLocation))
|
||||
{
|
||||
if ([scrollView hasVerticalScroller])
|
||||
{
|
||||
if (eventLocation.y < CGRectGetMinY(insetBounds))
|
||||
deltaY = CGRectGetMinY(insetBounds) - eventLocation.y;
|
||||
else if (eventLocation.y > CGRectGetMaxY(insetBounds))
|
||||
deltaY = CGRectGetMaxY(insetBounds) - eventLocation.y;
|
||||
if (deltaY < -insetBounds.size.height)
|
||||
deltaY = -insetBounds.size.height;
|
||||
if (deltaY > insetBounds.size.height)
|
||||
deltaY = insetBounds.size.height;
|
||||
}
|
||||
|
||||
if ([scrollView hasHorizontalScroller])
|
||||
{
|
||||
if (eventLocation.x < CGRectGetMinX(insetBounds))
|
||||
deltaX = CGRectGetMinX(insetBounds) - eventLocation.x;
|
||||
else if (eventLocation.x > CGRectGetMaxX(insetBounds))
|
||||
deltaX = CGRectGetMaxX(insetBounds) - eventLocation.x;
|
||||
if (deltaX < -insetBounds.size.width)
|
||||
deltaX = -insetBounds.size.width;
|
||||
if (deltaX > insetBounds.size.width)
|
||||
deltaX = insetBounds.size.width;
|
||||
}
|
||||
|
||||
[contentView scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dragOperation;
|
||||
}
|
||||
|
||||
- (void)_sendPeriodicDraggingUpdate:(CPTimer)aTimer
|
||||
{
|
||||
var userInfo = [aTimer userInfo];
|
||||
_dragOperation = [self draggingUpdatedInPlatformWindow:[userInfo objectForKey:@"platformWindow"]
|
||||
location:[userInfo objectForKey:@"location"]];
|
||||
}
|
||||
|
||||
- (void)draggingEndedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation operation:(CPDragOperation)anOperation
|
||||
{
|
||||
[_draggingUpdateTimer invalidate];
|
||||
_draggingUpdateTimer = nil;
|
||||
|
||||
[_draggedView removeFromSuperview];
|
||||
|
||||
if (![CPPlatform supportsDragAndDrop])
|
||||
[_draggedWindow orderOut:self];
|
||||
|
||||
if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_endAt_operation_)
|
||||
if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_endedAt_operation_)
|
||||
[_draggingSource draggedImage:[_draggedView image] endedAt:aLocation operation:anOperation];
|
||||
|
||||
else if (_implementedDraggingSourceMethods & CPDraggingSource_draggedView_endedAt_operation_)
|
||||
[_draggingSource draggedView:_draggedView endedAt:aLocation operation:anOperation];
|
||||
|
||||
@@ -269,8 +327,8 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
|
||||
- (void)performDragOperationInPlatformWindow:(CPPlatformWindow)aPlatformWindow
|
||||
{
|
||||
if (_draggingDestination &&
|
||||
(![_draggingDestination respondsToSelector:@selector(prepareForDragOperation:)] || [_draggingDestination prepareForDragOperation:CPDragServerDraggingInfo]) &&
|
||||
if (_draggingDestination &&
|
||||
(![_draggingDestination respondsToSelector:@selector(prepareForDragOperation:)] || [_draggingDestination prepareForDragOperation:CPDragServerDraggingInfo]) &&
|
||||
(![_draggingDestination respondsToSelector:@selector(performDragOperation:)] || [_draggingDestination performDragOperation:CPDragServerDraggingInfo]) &&
|
||||
[_draggingDestination respondsToSelector:@selector(concludeDragOperation:)])
|
||||
[_draggingDestination concludeDragOperation:CPDragServerDraggingInfo];
|
||||
@@ -296,6 +354,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
_draggingPasteboard = aPasteboard || [CPPasteboard pasteboardWithName:CPDragPboard];
|
||||
_draggingSource = aSourceObject;
|
||||
_draggingDestination = nil;
|
||||
_shouldSlideBack = slideBack;
|
||||
|
||||
// The offset is based on the distance from where we want the view to be initially from where the mouse is initially
|
||||
// Hence the use of mouseDownEvent's location and view's location in global coordinates.
|
||||
@@ -310,7 +369,7 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
_draggingOffset = _CGSizeMake(mouseDownEventLocation.x - viewLocation.x, mouseDownEventLocation.y - viewLocation.y);
|
||||
}
|
||||
else
|
||||
_draggingOffset = _CGSizeMakerZero();
|
||||
_draggingOffset = _CGSizeMakeZero();
|
||||
|
||||
if ([CPPlatform isBrowser])
|
||||
[_draggedWindow setPlatformWindow:[aWindow platformWindow]];
|
||||
@@ -320,7 +379,8 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
var mouseLocation = [CPEvent mouseLocation];
|
||||
|
||||
// Place it where the mouse pointer is.
|
||||
[_draggedWindow setFrameOrigin:_CGPointMake(mouseLocation.x - _draggingOffset.width, mouseLocation.y - _draggingOffset.height)];
|
||||
_startDragLocation = _CGPointMake(mouseLocation.x - _draggingOffset.width, mouseLocation.y - _draggingOffset.height);
|
||||
[_draggedWindow setFrameOrigin:_startDragLocation];
|
||||
[_draggedWindow setFrameSize:[aView frame].size];
|
||||
|
||||
[[_draggedWindow contentView] addSubview:aView];
|
||||
@@ -332,8 +392,8 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedImage:movedTo:)])
|
||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_movedTo_;
|
||||
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedImage:endAt:operation:)])
|
||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_endAt_operation_;
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedImage:endedAt:operation:)])
|
||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_endedAt_operation_;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -385,19 +445,34 @@ var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
[self performDragOperationInPlatformWindow:platformWindow];
|
||||
[self draggingEndedInPlatformWindow:platformWindow globalLocation:platformWindowLocation operation:CPDragOperationNone];
|
||||
// Make sure we do not finalize (cancel) the drag if the last drag update was disallowed
|
||||
if (_dragOperation !== CPDragOperationNone)
|
||||
[self performDragOperationInPlatformWindow:platformWindow];
|
||||
|
||||
[self draggingEndedInPlatformWindow:platformWindow globalLocation:platformWindowLocation operation:_dragOperation];
|
||||
|
||||
// Stop tracking events.
|
||||
return;
|
||||
}
|
||||
|
||||
[self draggingSourceUpdatedWithGlobalLocation:platformWindowLocation];
|
||||
[self draggingUpdatedInPlatformWindow:platformWindow location:platformWindowLocation];
|
||||
else if (type === CPKeyDown)
|
||||
{
|
||||
var characters = [anEvent characters];
|
||||
if (characters === CPEscapeFunctionKey)
|
||||
{
|
||||
_dragOperation = CPDragOperationNone;
|
||||
[self draggingEndedInPlatformWindow:platformWindow globalLocation:CGPointMakeZero() operation:_dragOperation];
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self draggingSourceUpdatedWithGlobalLocation:platformWindowLocation];
|
||||
_dragOperation = [self draggingUpdatedInPlatformWindow:platformWindow location:platformWindowLocation];
|
||||
}
|
||||
|
||||
// If we're not a mouse up, then we're going to want to grab the next event.
|
||||
[CPApp setTarget:self selector:@selector(trackDragging:)
|
||||
forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask
|
||||
forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPKeyDownMask
|
||||
untilDate:nil inMode:0 dequeue:NO];
|
||||
}
|
||||
|
||||
|
||||
+187
-41
@@ -21,8 +21,7 @@
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
@import "CPText.j"
|
||||
|
||||
|
||||
CPLeftMouseDown = 1;
|
||||
@@ -41,7 +40,7 @@ CPAppKitDefined = 13;
|
||||
CPSystemDefined = 14;
|
||||
CPApplicationDefined = 15;
|
||||
CPPeriodic = 16;
|
||||
CPCursorUpdate = 17;
|
||||
CPCursorUpdate = 17;
|
||||
CPScrollWheel = 22;
|
||||
CPOtherMouseDown = 25;
|
||||
CPOtherMouseUp = 26;
|
||||
@@ -52,8 +51,7 @@ CPTouchStart = 28;
|
||||
CPTouchMove = 29;
|
||||
CPTouchEnd = 30;
|
||||
CPTouchCancel = 31;
|
||||
|
||||
|
||||
|
||||
CPAlphaShiftKeyMask = 1 << 16;
|
||||
CPShiftKeyMask = 1 << 17;
|
||||
CPControlKeyMask = 1 << 18;
|
||||
@@ -87,26 +85,103 @@ CPPeriodicMask = 1 << CPPeriodic;
|
||||
CPScrollWheelMask = 1 << CPScrollWheel;
|
||||
CPAnyEventMask = 0xffffffff;
|
||||
|
||||
CPDOMEventDoubleClick = "dblclick",
|
||||
CPDOMEventMouseDown = "mousedown",
|
||||
CPDOMEventMouseUp = "mouseup",
|
||||
CPDOMEventMouseMoved = "mousemove",
|
||||
CPDOMEventMouseDragged = "mousedrag",
|
||||
CPDOMEventKeyUp = "keyup",
|
||||
CPDOMEventKeyDown = "keydown",
|
||||
CPDOMEventKeyPress = "keypress";
|
||||
CPDOMEventCopy = "copy";
|
||||
CPDOMEventPaste = "paste";
|
||||
CPDOMEventScrollWheel = "mousewheel";
|
||||
CPDOMEventTouchStart = "touchstart";
|
||||
CPDOMEventTouchMove = "touchmove";
|
||||
CPDOMEventTouchEnd = "touchend";
|
||||
CPDOMEventTouchCancel = "touchcancel";
|
||||
CPUpArrowFunctionKey = "\uF700";
|
||||
CPDownArrowFunctionKey = "\uF701";
|
||||
CPLeftArrowFunctionKey = "\uF702";
|
||||
CPRightArrowFunctionKey = "\uF703";
|
||||
CPF1FunctionKey = "\uF704";
|
||||
CPF2FunctionKey = "\uF705";
|
||||
CPF3FunctionKey = "\uF706";
|
||||
CPF4FunctionKey = "\uF707";
|
||||
CPF5FunctionKey = "\uF708";
|
||||
CPF6FunctionKey = "\uF709";
|
||||
CPF7FunctionKey = "\uF70A";
|
||||
CPF8FunctionKey = "\uF70B";
|
||||
CPF9FunctionKey = "\uF70C";
|
||||
CPF10FunctionKey = "\uF70D";
|
||||
CPF11FunctionKey = "\uF70E";
|
||||
CPF12FunctionKey = "\uF70F";
|
||||
CPF13FunctionKey = "\uF710";
|
||||
CPF14FunctionKey = "\uF711";
|
||||
CPF15FunctionKey = "\uF712";
|
||||
CPF16FunctionKey = "\uF713";
|
||||
CPF17FunctionKey = "\uF714";
|
||||
CPF18FunctionKey = "\uF715";
|
||||
CPF19FunctionKey = "\uF716";
|
||||
CPF20FunctionKey = "\uF717";
|
||||
CPF21FunctionKey = "\uF718";
|
||||
CPF22FunctionKey = "\uF719";
|
||||
CPF23FunctionKey = "\uF71A";
|
||||
CPF24FunctionKey = "\uF71B";
|
||||
CPF25FunctionKey = "\uF71C";
|
||||
CPF26FunctionKey = "\uF71D";
|
||||
CPF27FunctionKey = "\uF71E";
|
||||
CPF28FunctionKey = "\uF71F";
|
||||
CPF29FunctionKey = "\uF720";
|
||||
CPF30FunctionKey = "\uF721";
|
||||
CPF31FunctionKey = "\uF722";
|
||||
CPF32FunctionKey = "\uF723";
|
||||
CPF33FunctionKey = "\uF724";
|
||||
CPF34FunctionKey = "\uF725";
|
||||
CPF35FunctionKey = "\uF726";
|
||||
CPInsertFunctionKey = "\uF727";
|
||||
CPDeleteFunctionKey = "\uF728";
|
||||
CPHomeFunctionKey = "\uF729";
|
||||
CPBeginFunctionKey = "\uF72A";
|
||||
CPEndFunctionKey = "\uF72B";
|
||||
CPPageUpFunctionKey = "\uF72C";
|
||||
CPPageDownFunctionKey = "\uF72D";
|
||||
CPPrintScreenFunctionKey = "\uF72E";
|
||||
CPScrollLockFunctionKey = "\uF72F";
|
||||
CPPauseFunctionKey = "\uF730";
|
||||
CPSysReqFunctionKey = "\uF731";
|
||||
CPBreakFunctionKey = "\uF732";
|
||||
CPResetFunctionKey = "\uF733";
|
||||
CPStopFunctionKey = "\uF734";
|
||||
CPMenuFunctionKey = "\uF735";
|
||||
CPUserFunctionKey = "\uF736";
|
||||
CPSystemFunctionKey = "\uF737";
|
||||
CPPrintFunctionKey = "\uF738";
|
||||
CPClearLineFunctionKey = "\uF739";
|
||||
CPClearDisplayFunctionKey = "\uF73A";
|
||||
CPInsertLineFunctionKey = "\uF73B";
|
||||
CPDeleteLineFunctionKey = "\uF73C";
|
||||
CPInsertCharFunctionKey = "\uF73D";
|
||||
CPDeleteCharFunctionKey = "\uF73E";
|
||||
CPPrevFunctionKey = "\uF73F";
|
||||
CPNextFunctionKey = "\uF740";
|
||||
CPSelectFunctionKey = "\uF741";
|
||||
CPExecuteFunctionKey = "\uF742";
|
||||
CPUndoFunctionKey = "\uF743";
|
||||
CPRedoFunctionKey = "\uF744";
|
||||
CPFindFunctionKey = "\uF745";
|
||||
CPHelpFunctionKey = "\uF746";
|
||||
CPModeSwitchFunctionKey = "\uF747";
|
||||
CPEscapeFunctionKey = "\u001B";
|
||||
CPSpaceFunctionKey = "\u0020";
|
||||
|
||||
|
||||
CPDOMEventDoubleClick = "dblclick",
|
||||
CPDOMEventMouseDown = "mousedown",
|
||||
CPDOMEventMouseUp = "mouseup",
|
||||
CPDOMEventMouseMoved = "mousemove",
|
||||
CPDOMEventMouseDragged = "mousedrag",
|
||||
CPDOMEventKeyUp = "keyup",
|
||||
CPDOMEventKeyDown = "keydown",
|
||||
CPDOMEventKeyPress = "keypress";
|
||||
CPDOMEventCopy = "copy";
|
||||
CPDOMEventPaste = "paste";
|
||||
CPDOMEventScrollWheel = "mousewheel";
|
||||
CPDOMEventTouchStart = "touchstart";
|
||||
CPDOMEventTouchMove = "touchmove";
|
||||
CPDOMEventTouchEnd = "touchend";
|
||||
CPDOMEventTouchCancel = "touchcancel";
|
||||
|
||||
var _CPEventPeriodicEventPeriod = 0,
|
||||
_CPEventPeriodicEventTimer = nil;
|
||||
_CPEventPeriodicEventTimer = nil,
|
||||
_CPEventUpperCaseRegex = new RegExp("[A-Z]");
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPEvent
|
||||
CPEvent encapsulates the details of a Cappuccino keyboard or mouse event.
|
||||
@@ -128,7 +203,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
BOOL _isARepeat;
|
||||
unsigned _keyCode;
|
||||
DOMEvent _DOMEvent;
|
||||
|
||||
|
||||
float _deltaX;
|
||||
float _deltaY;
|
||||
float _deltaZ;
|
||||
@@ -155,7 +230,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)repeatKey keyCode:(unsigned short)code
|
||||
{
|
||||
return [[self alloc] _initKeyEventWithType:anEventType location:aPoint modifierFlags:modifierFlags
|
||||
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext
|
||||
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext
|
||||
characters:characters charactersIgnoringModifiers:unmodCharacters isARepeat:repeatKey keyCode:code];
|
||||
}
|
||||
|
||||
@@ -173,7 +248,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
@throws CPInternalInconsistencyException if an invalid event type is provided
|
||||
@return the new mouse event
|
||||
*/
|
||||
+ (id)mouseEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
||||
+ (id)mouseEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||
eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure
|
||||
{
|
||||
@@ -204,12 +279,12 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (id)_initMouseEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
||||
- (id)_initMouseEventWithType:(CPEventType)anEventType location:(CPPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||
eventNumber:(int)anEventNumber clickCount:(int)aClickCount pressure:(float)aPressure
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_type = anEventType;
|
||||
@@ -222,7 +297,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
_pressure = aPressure;
|
||||
_window = [CPApp windowWithWindowNumber:aWindowNumber];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -232,7 +307,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
characters:(CPString)characters charactersIgnoringModifiers:(CPString)unmodCharacters isARepeat:(BOOL)isARepeat keyCode:(unsigned short)code
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_type = anEventType;
|
||||
@@ -246,7 +321,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
_keyCode = code;
|
||||
_windowNumber = aWindowNumber;
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -256,7 +331,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
subtype:(short)aSubtype data1:(int)aData1 data2:(int)aData2
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_type = anEventType;
|
||||
@@ -267,7 +342,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
_subtype = aSubtype;
|
||||
_data1 = aData1;
|
||||
_data2 = aData2;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -285,6 +360,17 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _CGPointMakeCopy(_location);
|
||||
}
|
||||
|
||||
- (CGPoint)globalLocation
|
||||
{
|
||||
var theWindow = [self window],
|
||||
location = [self locationInWindow];
|
||||
|
||||
if (theWindow)
|
||||
return [theWindow convertBaseToGlobal:location];
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns event information as a bit mask
|
||||
*/
|
||||
@@ -334,7 +420,10 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
*/
|
||||
- (int)buttonNumber
|
||||
{
|
||||
return _buttonNumber;
|
||||
if (_type === CPRightMouseDown || _type === CPRightMouseUp || _type === CPRightMouseDragged)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -431,23 +520,80 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _deltaZ;
|
||||
}
|
||||
|
||||
- (BOOL)_triggersKeyEquivalent:(CPString)aKeyEquivalent withModifierMask:aKeyEquivalentModifierMask
|
||||
{
|
||||
if (!aKeyEquivalent)
|
||||
return NO;
|
||||
|
||||
if (_CPEventUpperCaseRegex.test(aKeyEquivalent))
|
||||
aKeyEquivalentModifierMask |= CPShiftKeyMask;
|
||||
|
||||
// Windows and Linux don't have command keys, so just switch it to ctrl.
|
||||
if (!CPBrowserIsOperatingSystem(CPMacOperatingSystem) && (aKeyEquivalentModifierMask & CPCommandKeyMask))
|
||||
{
|
||||
aKeyEquivalentModifierMask |= CPControlKeyMask;
|
||||
aKeyEquivalentModifierMask &= ~CPCommandKeyMask;
|
||||
}
|
||||
|
||||
if ((_modifierFlags & (CPShiftKeyMask | CPAlternateKeyMask | CPCommandKeyMask | CPControlKeyMask)) !== aKeyEquivalentModifierMask)
|
||||
return NO;
|
||||
|
||||
// Treat \r and \n as the same key equivalent. See issue #710.
|
||||
if (_characters === CPNewlineCharacter || _characters === CPCarriageReturnCharacter)
|
||||
return CPNewlineCharacter === aKeyEquivalent || CPCarriageReturnCharacter === aKeyEquivalent;
|
||||
|
||||
return [_characters caseInsensitiveCompare:aKeyEquivalent] === CPOrderedSame;
|
||||
}
|
||||
|
||||
- (BOOL)_couldBeKeyEquivalent
|
||||
{
|
||||
// FIXME: More cases? Space?
|
||||
return _type === CPKeyDown &&
|
||||
_modifierFlags & (CPCommandKeyMask | CPControlKeyMask) &&
|
||||
[_characters length] > 0;
|
||||
if (_type !== CPKeyDown)
|
||||
return NO;
|
||||
|
||||
var characterCount = _characters.length;
|
||||
|
||||
if (!characterCount)
|
||||
return NO;
|
||||
|
||||
if (_modifierFlags & (CPCommandKeyMask | CPControlKeyMask))
|
||||
return YES;
|
||||
|
||||
for (var i = 0; i < characterCount; i++)
|
||||
{
|
||||
switch (_characters.charAt(i))
|
||||
{
|
||||
case CPBackspaceCharacter:
|
||||
case CPDeleteCharacter:
|
||||
case CPDeleteFunctionKey:
|
||||
case CPTabCharacter:
|
||||
case CPCarriageReturnCharacter:
|
||||
case CPNewlineCharacter:
|
||||
case CPSpaceFunctionKey:
|
||||
case CPEscapeFunctionKey:
|
||||
case CPPageUpFunctionKey:
|
||||
case CPPageDownFunctionKey:
|
||||
case CPLeftArrowFunctionKey:
|
||||
case CPUpArrowFunctionKey:
|
||||
case CPRightArrowFunctionKey:
|
||||
case CPDownArrowFunctionKey:
|
||||
case CPEndFunctionKey:
|
||||
case CPHomeFunctionKey:
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
// FIXME: More cases?
|
||||
return NO;
|
||||
}
|
||||
|
||||
/*!
|
||||
Generates periodic events every \c aPeriod seconds.
|
||||
Gene rates periodic events every \c aPeriod seconds.
|
||||
@param aDelay the number of seconds before the first event
|
||||
@param aPeriod the length of time in seconds between successive events
|
||||
*/
|
||||
+ (void)startPeriodicEventsAfterDelay:(CPTimeInterval)aDelay withPeriod:(CPTimeInterval)aPeriod
|
||||
{
|
||||
_CPEventPeriodicEventPeriod = aPeriod;
|
||||
|
||||
|
||||
// FIXME: OH TIMERS!!!
|
||||
_CPEventPeriodicEventTimer = window.setTimeout(function() { _CPEventPeriodicEventTimer = window.setInterval(_CPEventFirePeriodEvent, aPeriod * 1000.0); }, aDelay * 1000.0);
|
||||
}
|
||||
@@ -459,9 +605,9 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
{
|
||||
if (_CPEventPeriodicEventTimer === nil)
|
||||
return;
|
||||
|
||||
|
||||
window.clearTimeout(_CPEventPeriodicEventTimer);
|
||||
|
||||
|
||||
_CPEventPeriodicEventTimer = nil;
|
||||
}
|
||||
|
||||
|
||||
+32
-8
@@ -22,7 +22,8 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
/*!
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPFlashMovie
|
||||
|
||||
@@ -30,7 +31,7 @@
|
||||
*/
|
||||
@implementation CPFlashMovie : CPObject
|
||||
{
|
||||
CPString _fileName;
|
||||
CPString _filename;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -38,9 +39,9 @@
|
||||
@param aFilename the swf to load
|
||||
@return the initialized CPFlashMovie
|
||||
*/
|
||||
+ (id)flashMovieWithFile:(CPString)aFileName
|
||||
+ (id)flashMovieWithFile:(CPString)aFilename
|
||||
{
|
||||
return [[self alloc] initWithFile:aFileName];
|
||||
return [[self alloc] initWithFile:aFilename];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -48,14 +49,37 @@
|
||||
@param aFilename the swf to load
|
||||
@return the initialized CPFlashMovie
|
||||
*/
|
||||
- (id)initWithFile:(CPString)aFileName
|
||||
- (id)initWithFile:(CPString)aFilename
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
_fileName = aFileName;
|
||||
|
||||
_filename = aFilename;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPString)filename
|
||||
{
|
||||
return _filename;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPFlashMovieFilenameKey = "CPFlashMovieFilenameKey";
|
||||
|
||||
@implementation CPFlashMovie (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
_filename = [aCoder decodeObjectForKey:CPFlashMovieFilenameKey];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_filename forKey:CPFlashMovieFilenameKey];
|
||||
}
|
||||
|
||||
@end
|
||||
+129
-38
@@ -24,55 +24,58 @@
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
@implementation CPFlashView : CPView
|
||||
{
|
||||
CPFlashMovie _flashMovie;
|
||||
|
||||
DOMElement _DOMEmbedElement;
|
||||
DOMElement _DOMMParamElement;
|
||||
|
||||
CPDictionary _params;
|
||||
CPDictionary _paramElements;
|
||||
#if PLATFORM(DOM)
|
||||
DOMElement _DOMParamElement;
|
||||
DOMElement _DOMObjectElement;
|
||||
DOMElement _DOMInnerObjectElement;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_DOMObjectElement = document.createElement("object");
|
||||
_DOMObjectElement.width = "100%";
|
||||
_DOMObjectElement.height = "100%";
|
||||
_DOMObjectElement.style.top = "0px";
|
||||
_DOMObjectElement.style.left = "0px";
|
||||
|
||||
_DOMParamElement = document.createElement("param");
|
||||
_DOMParamElement.name = "movie";
|
||||
|
||||
_DOMObjectElement.appendChild(_DOMParamElement);
|
||||
|
||||
var param = document.createElement("param");
|
||||
|
||||
param.name = "wmode";
|
||||
param.value = "transparent";
|
||||
|
||||
_DOMObjectElement.appendChild(param);
|
||||
|
||||
_DOMEmbedElement = document.createElement("embed");
|
||||
#if PLATFORM(DOM)
|
||||
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
{
|
||||
_DOMObjectElement = document.createElement(@"object");
|
||||
_DOMObjectElement.width = @"100%";
|
||||
_DOMObjectElement.height = @"100%";
|
||||
_DOMObjectElement.style.top = @"0px";
|
||||
_DOMObjectElement.style.left = @"0px";
|
||||
_DOMObjectElement.type = @"application/x-shockwave-flash";
|
||||
_DOMObjectElement.setAttribute(@"classid", IEFlashCLSID);
|
||||
|
||||
_DOMEmbedElement.type = "application/x-shockwave-flash";
|
||||
_DOMEmbedElement.setAttribute("wmode", "transparent");
|
||||
_DOMEmbedElement.width = "100%";
|
||||
_DOMEmbedElement.height = "100%";
|
||||
_DOMParamElement = document.createElement(@"param");
|
||||
_DOMParamElement.name = @"movie";
|
||||
|
||||
// IE requires this thing to be in the _DOMElement and not the _DOMObjectElement.
|
||||
_DOMElement.appendChild(_DOMEmbedElement);
|
||||
|
||||
_DOMElement.appendChild(_DOMObjectElement);
|
||||
_DOMInnerObjectElement = document.createElement(@"object");
|
||||
_DOMInnerObjectElement.width = @"100%";
|
||||
_DOMInnerObjectElement.height = @"100%";
|
||||
|
||||
_DOMObjectElement.appendChild(_DOMParamElement);
|
||||
_DOMObjectElement.appendChild(_DOMInnerObjectElement);
|
||||
|
||||
_DOMElement.appendChild(_DOMObjectElement);
|
||||
}
|
||||
else
|
||||
[self _rebuildIEObjects];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -80,13 +83,17 @@
|
||||
{
|
||||
if (_flashMovie == aFlashMovie)
|
||||
return;
|
||||
|
||||
|
||||
_flashMovie = aFlashMovie;
|
||||
|
||||
_DOMParamElement.value = aFlashMovie._fileName;
|
||||
|
||||
if (_DOMEmbedElement)
|
||||
_DOMEmbedElement.src = aFlashMovie._fileName;
|
||||
#if PLATFORM(DOM)
|
||||
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
{
|
||||
_DOMParamElement.value = [aFlashMovie filename];
|
||||
_DOMInnerObjectElement.data = [aFlashMovie filename];
|
||||
}
|
||||
else
|
||||
[self _rebuildIEObjects];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (CPFlashMovie)flashMovie
|
||||
@@ -94,6 +101,90 @@
|
||||
return _flashMovie;
|
||||
}
|
||||
|
||||
- (void)setFlashVars:(CPDictionary)aDictionary
|
||||
{
|
||||
var varString = @"",
|
||||
enumerator = [aDictionary keyEnumerator];
|
||||
|
||||
var key;
|
||||
while (key = [enumerator nextObject])
|
||||
varString = [varString stringByAppendingFormat:@"&%@=%@", key, [aDictionary objectForKey:key]];
|
||||
|
||||
if (!_params)
|
||||
_params = [CPDictionary dictionary];
|
||||
|
||||
[_params setObject:varString forKey:@"flashvars"];
|
||||
[self setParameters:_params];
|
||||
}
|
||||
|
||||
- (CPDictionary)flashVars
|
||||
{
|
||||
return [_params objectForKey:@"flashvars"];
|
||||
}
|
||||
|
||||
- (void)setParameters:(CPDictionary)aDictionary
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
if (_paramElements && !CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
{
|
||||
var elements = [_paramElements allValues],
|
||||
count = [elements count];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
_DOMObjectElement.removeChild([elements objectAtIndex:i]);
|
||||
}
|
||||
#endif
|
||||
_params = aDictionary;
|
||||
#if PLATFORM(DOM)
|
||||
if (!CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
{
|
||||
_paramElements = [CPDictionary dictionary];
|
||||
|
||||
var enumerator = [_params keyEnumerator],
|
||||
key;
|
||||
|
||||
while (key = [enumerator nextObject] && _DOMObjectElement)
|
||||
{
|
||||
var param = document.createElement(@"param");
|
||||
param.name = key;
|
||||
param.value = [_params objectForKey:key];
|
||||
|
||||
_DOMObjectElement.appendChild(param);
|
||||
|
||||
[_paramElements setObject:param forKey:key];
|
||||
}
|
||||
}
|
||||
else
|
||||
[self _rebuildIEObjects];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (CPDictionary)parameters
|
||||
{
|
||||
return _params;
|
||||
}
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
- (void)_rebuildIEObjects
|
||||
{
|
||||
_DOMElement.innerHTML = @"";
|
||||
if (![_flashMovie filename])
|
||||
return;
|
||||
|
||||
var paramString = [CPString stringWithFormat:@"<param name='movie' value='%@' />", [_flashMovie filename]],
|
||||
paramEnumerator = [_params keyEnumerator],
|
||||
key;
|
||||
|
||||
while (key = [paramEnumerator nextObject])
|
||||
paramString = [paramString stringByAppendingFormat:@"<param name='%@' value='%@' />", key, [_params objectForKey:key]];
|
||||
|
||||
_DOMObjectElement = document.createElement(@"object");
|
||||
_DOMElement.appendChild(_DOMObjectElement);
|
||||
|
||||
_DOMObjectElement.outerHTML = [CPString stringWithFormat:@"<object classid=%@ width=%@ height=%@>%@</object>", IEFlashCLSID, CGRectGetWidth([self bounds]), CGRectGetHeight([self bounds]), paramString];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
|
||||
+90
-12
@@ -20,12 +20,21 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
var _CPFonts = {};
|
||||
_CPFontSystemFontFace = @"Arial";
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPBundle.j>
|
||||
|
||||
#define _CPCachedFont(aName, aSize, isBold) _CPFonts[(isBold ? @"bold " : @"") + ROUND(aSize) + @"px '" + aName + @"'"]
|
||||
@import "CPView.j"
|
||||
|
||||
/*!
|
||||
|
||||
var _CPFonts = {},
|
||||
_CPFontSystemFontFace = @"Arial, sans-serif",
|
||||
_CPWrapRegExp = new RegExp("\\s*,\\s*", "g");
|
||||
|
||||
|
||||
#define _CPCreateCSSString(aName, aSize, isBold) (isBold ? @"bold " : @"") + ROUND(aSize) + @"px " + ((aName === _CPFontSystemFontFace) ? aName : (@"\"" + aName.replace(_CPWrapRegExp, '", "') + @"\", " + _CPFontSystemFontFace))
|
||||
#define _CPCachedFont(aName, aSize, isBold) _CPFonts[_CPCreateCSSString(aName, aSize, isBold)]
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPFont
|
||||
|
||||
@@ -35,11 +44,22 @@ var _CPFonts = {};
|
||||
{
|
||||
CPString _name;
|
||||
float _size;
|
||||
float _ascender;
|
||||
float _descender;
|
||||
float _lineHeight;
|
||||
BOOL _isBold;
|
||||
|
||||
|
||||
CPString _cssString;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
var systemFont = [[CPBundle bundleForClass:[CPView class]] objectForInfoDictionaryKey:"CPSystemFontFace"];
|
||||
|
||||
if (systemFont)
|
||||
_CPFontSystemFontFace = systemFont;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a font with the specified name and size.
|
||||
@param aName the name of the font
|
||||
@@ -86,25 +106,64 @@ var _CPFonts = {};
|
||||
@ignore
|
||||
*/
|
||||
- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold
|
||||
{
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_name = aName;
|
||||
_size = aSize;
|
||||
_ascender = 0;
|
||||
_descender = 0;
|
||||
_lineHeight = 0;
|
||||
_isBold = isBold;
|
||||
|
||||
_cssString = (_isBold ? @"bold " : @"") + ROUND(aSize) + @"px '" + aName + @"'";
|
||||
|
||||
|
||||
_cssString = _CPCreateCSSString(_name, _size, _isBold);
|
||||
|
||||
_CPFonts[_cssString] = self;
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the font size (in points)
|
||||
Returns the distance of the longest ascender's top y-coordinate from the baseline (in CSS px)
|
||||
*/
|
||||
- (float)ascender
|
||||
{
|
||||
if (!_ascender)
|
||||
[self _getMetrics];
|
||||
|
||||
return _ascender;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the bottom y coordinate (in CSS px), offset from the baseline, of the receiver's longest descender.
|
||||
Thus, if the longest descender extends 2 points below the baseline, descender will return –2.
|
||||
*/
|
||||
- (float)descender
|
||||
{
|
||||
if (!_descender)
|
||||
[self _getMetrics];
|
||||
|
||||
return _descender;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the default line height.
|
||||
|
||||
NOTE: This was moved from NSFont to NSLayoutManager in Cocoa, but since there is no CPLayoutManager, it has been kept here.
|
||||
*/
|
||||
- (float)defaultLineHeightForFont
|
||||
{
|
||||
if (!_lineHeight)
|
||||
[self _getMetrics];
|
||||
|
||||
return _lineHeight;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the font size (in CSS px)
|
||||
*/
|
||||
- (float)size
|
||||
{
|
||||
@@ -127,6 +186,25 @@ var _CPFonts = {};
|
||||
return _name;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)anObject
|
||||
{
|
||||
return [anObject isKindOfClass:[CPFont class]] && [anObject cssString] === [self cssString];
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [CPString stringWithFormat:@"%@ %@ %f pt.", [super description], [self familyName], [self size]];
|
||||
}
|
||||
|
||||
- (void)_getMetrics
|
||||
{
|
||||
var metrics = [CPString metricsOfFont:self];
|
||||
|
||||
_ascender = [metrics objectForKey:@"ascender"];
|
||||
_descender = [metrics objectForKey:@"descender"];
|
||||
_lineHeight = [metrics objectForKey:@"lineHeight"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPFontNameKey = @"CPFontNameKey",
|
||||
|
||||
+40
-34
@@ -22,7 +22,7 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import <AppKit/CPFont.j>
|
||||
@import "CPFont.j"
|
||||
|
||||
|
||||
var CPSharedFontManager = nil,
|
||||
@@ -45,13 +45,13 @@ var CPSharedFontManager = nil,
|
||||
{
|
||||
if (!CPSharedFontManager)
|
||||
CPSharedFontManager = [[CPFontManagerFactory alloc] init];
|
||||
|
||||
|
||||
return CPSharedFontManager;
|
||||
}
|
||||
|
||||
// Changing the Default Font Conversion Classes
|
||||
/*!
|
||||
Sets the class that will be used to create the applcation's
|
||||
Sets the class that will be used to create the application's
|
||||
font manager.
|
||||
*/
|
||||
+ (void)setFontManagerFactory:(Class)aClass
|
||||
@@ -65,7 +65,7 @@ var CPSharedFontManager = nil,
|
||||
- (CPArray)availableFonts
|
||||
{
|
||||
if (!_availableFonts)
|
||||
{
|
||||
{
|
||||
_CPFontDetectSpan = document.createElement("span");
|
||||
_CPFontDetectSpan.fontSize = "24px";
|
||||
_CPFontDetectSpan.appendChild(document.createTextNode("mmmmmmmmmml"));
|
||||
@@ -74,13 +74,14 @@ var CPSharedFontManager = nil,
|
||||
div.style.top = "-1000px";
|
||||
div.appendChild(_CPFontDetectSpan);
|
||||
document.getElementsByTagName("body")[0].appendChild(div);
|
||||
|
||||
_CPFontDetectReferenceFonts = _CPFontDetectPickTwoDifferentFonts(["monospace", "serif", "sans-serif", "cursive"]);
|
||||
|
||||
|
||||
_CPFontDetectReferenceFonts = _CPFontDetectPickTwoDifferentFonts(["monospace", "serif", "sans-serif", "cursive"]);
|
||||
|
||||
_availableFonts = [];
|
||||
for (var i = 0; i < _CPFontDetectAllFonts.length; i++) {
|
||||
for (var i = 0; i < _CPFontDetectAllFonts.length; i++)
|
||||
{
|
||||
var available = _CPFontDetectFontAvailable(_CPFontDetectAllFonts[i]);
|
||||
if (available)
|
||||
if (available)
|
||||
_availableFonts.push(_CPFontDetectAllFonts[i]);
|
||||
}
|
||||
}
|
||||
@@ -101,8 +102,8 @@ var CPSharedFontManager = nil,
|
||||
var _CPFontDetectSpan,
|
||||
_CPFontDetectReferenceFonts,
|
||||
_CPFontDetectAllFonts = [
|
||||
/* "04b_21","A Charming Font","Abadi MT Condensed","Abadi MT Condensed Extra Bold","Abadi MT Condensed Light","Academy Engraved LET","Agency FB","Alba","Alba Matter","Alba Super","Algerian",*/
|
||||
"American Typewriter",
|
||||
/* "04b_21","A Charming Font","Abadi MT Condensed","Abadi MT Condensed Extra Bold","Abadi MT Condensed Light","Academy Engraved LET","Agency FB","Alba","Alba Matter","Alba Super","Algerian",*/
|
||||
"American Typewriter",
|
||||
/* "Andale Mono","Andale Mono IPA","Andy", */
|
||||
"Apple Chancery","Arial","Arial Black","Arial Narrow","Arial Rounded MT Bold","Arial Unicode MS",
|
||||
/* "Avant Garde","Avantgarde","Baby Kruffy","Base 02","Baskerville","Baskerville Old Face","Bauhaus 93","Beesknees ITC","Bell MT","Berlin Sans FB","Berlin Sans FB Demi","Bernard MT Condensed","Bickley Script",*/
|
||||
@@ -122,8 +123,8 @@ var _CPFontDetectSpan,
|
||||
/* "GlooGun","Gloucester MT Extra Condensed","Goudy Old Style","Goudy Stout","Haettenschweiler","Harlow Solid Italic","Harrington",*/
|
||||
"Helvetica","Helvetica Narrow","Helvetica Neue","Herculanum","High Tower Text","Highlight LET","Hoefler Text","Impact","Imprint MT Shadow",
|
||||
/* "Informal Roman","Jenkins v2.0","John Handy LET","Jokerman","Jokerman LET","Jokewood","Juice ITC","Kabel Ult BT","Kartika","Kino MT","Kristen ITC","Kunstler Script","La Bamba LET", */
|
||||
"Lucida","Lucida Bright","Lucida Calligraphy","Lucida Console","Lucida Fax","Lucida Grande","Lucida Handwriting","Lucida Sans","Lucida Sans Typewriter","Lucida Sans Unicode",
|
||||
/* "Luxi Mono","Luxi Sans","Luxi Serif","MARKETPRO","MS Reference Sans Serif","MS Reference Serif","Magneto","Maiandra GD", */
|
||||
"Lucida","Lucida Bright","Lucida Calligraphy","Lucida Console","Lucida Fax","Lucida Grande","Lucida Handwriting","Lucida Sans","Lucida Sans Typewriter","Lucida Sans Unicode",
|
||||
/* "Luxi Mono","Luxi Sans","Luxi Serif","MARKETPRO","MS Reference Sans Serif","MS Reference Serif","Magneto","Maiandra GD", */
|
||||
"Marker Felt",
|
||||
/* "Matisse ITC","Matura MT Script Capitals","Mead Bold","Mekanik LET","Mercurius Script MT Bold", */
|
||||
"Microsoft Sans Serif","Milano LET","Minion Web","MisterEarl BT","Mistral","Monaco","Monotype Corsiva","Monotype.com","New Century Schoolbook","New York","News Gothic MT",
|
||||
@@ -136,39 +137,44 @@ var _CPFontDetectSpan,
|
||||
"Zapfino"];
|
||||
|
||||
// Compare against the reference fonts. Return true if it produces a different size than at least one of them.
|
||||
var _CPFontDetectFontAvailable = function(font) {
|
||||
for (var i = 0; i < _CPFontDetectReferenceFonts.length; i++)
|
||||
if (_CPFontDetectCompareFonts(_CPFontDetectReferenceFonts[i], font))
|
||||
return true;
|
||||
return false;
|
||||
var _CPFontDetectFontAvailable = function(font)
|
||||
{
|
||||
for (var i = 0; i < _CPFontDetectReferenceFonts.length; i++)
|
||||
if (_CPFontDetectCompareFonts(_CPFontDetectReferenceFonts[i], font))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
var _CPFontDetectCache = {};
|
||||
|
||||
// Compares two given fonts. Returns true if they produce different sizes (i.e. fontA didn't fallback to fontB)
|
||||
var _CPFontDetectCompareFonts = function(fontA, fontB) {
|
||||
var _CPFontDetectCompareFonts = function(fontA, fontB)
|
||||
{
|
||||
var a;
|
||||
if (_CPFontDetectCache[fontA]) {
|
||||
if (_CPFontDetectCache[fontA])
|
||||
a = _CPFontDetectCache[fontA];
|
||||
} else {
|
||||
_CPFontDetectSpan.style.fontFamily = '"' + fontA + '"';
|
||||
|
||||
else
|
||||
{
|
||||
_CPFontDetectSpan.style.fontFamily = '"' + fontA + '"';
|
||||
_CPFontDetectCache[fontA] = a = { w: _CPFontDetectSpan.offsetWidth, h: _CPFontDetectSpan.offsetHeight };
|
||||
}
|
||||
|
||||
_CPFontDetectSpan.style.fontFamily= '"' + fontB + '", "' + fontA + '"';
|
||||
var bWidth = _CPFontDetectSpan.offsetWidth;
|
||||
var bHeight = _CPFontDetectSpan.offsetHeight;
|
||||
|
||||
return (a.w != bWidth || a.h != bHeight);
|
||||
|
||||
_CPFontDetectSpan.style.fontFamily = '"' + fontB + '", "' + fontA + '"';
|
||||
var bWidth = _CPFontDetectSpan.offsetWidth,
|
||||
bHeight = _CPFontDetectSpan.offsetHeight;
|
||||
|
||||
return (a.w != bWidth || a.h != bHeight);
|
||||
}
|
||||
|
||||
// Test the candidate fonts pairwise until we find two that are different. Otherwise return the first.
|
||||
var _CPFontDetectPickTwoDifferentFonts = function(candidates) {
|
||||
for (var i = 0; i < candidates.length; i++)
|
||||
for (var j = 0; j < i; j++)
|
||||
if (_CPFontDetectCompareFonts(candidates[i], candidates[j]))
|
||||
return [candidates[i], candidates[j]];
|
||||
return [candidates[0]];
|
||||
var _CPFontDetectPickTwoDifferentFonts = function(candidates)
|
||||
{
|
||||
for (var i = 0; i < candidates.length; i++)
|
||||
for (var j = 0; j < i; j++)
|
||||
if (_CPFontDetectCompareFonts(candidates[i], candidates[j]))
|
||||
return [candidates[i], candidates[j]];
|
||||
return [candidates[0]];
|
||||
}
|
||||
|
||||
[CPFontManager setFontManagerFactory:[CPFontManager class]];
|
||||
|
||||
+32
-18
@@ -70,8 +70,8 @@ function CPPointMake(x, y)
|
||||
*/
|
||||
function CPRectInset(aRect, dX, dY)
|
||||
{
|
||||
return CPRectMake( aRect.origin.x + dX, aRect.origin.y + dY,
|
||||
aRect.size.width - 2 * dX, aRect.size.height - 2*dY);
|
||||
return CPRectMake( aRect.origin.x + dX, aRect.origin.y + dY,
|
||||
aRect.size.width - 2 * dX, aRect.size.height - 2 * dY);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -96,13 +96,13 @@ function CPRectIntegral(aRect)
|
||||
function CPRectIntersection(lhsRect, rhsRect)
|
||||
{
|
||||
var intersection = CPRectMake(
|
||||
Math.max(CPRectGetMinX(lhsRect), CPRectGetMinX(rhsRect)),
|
||||
Math.max(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)),
|
||||
Math.max(CPRectGetMinX(lhsRect), CPRectGetMinX(rhsRect)),
|
||||
Math.max(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)),
|
||||
0, 0);
|
||||
|
||||
|
||||
intersection.size.width = Math.min(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)) - CPRectGetMinX(intersection);
|
||||
intersection.size.height = Math.min(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect)) - CPRectGetMinY(intersection);
|
||||
|
||||
|
||||
return CPRectIsEmpty(intersection) ? CPRectMakeZero() : intersection;
|
||||
}
|
||||
|
||||
@@ -160,13 +160,13 @@ function CPRectStandardize(aRect)
|
||||
standardized.origin.x += width;
|
||||
standardized.size.width = -width;
|
||||
}
|
||||
|
||||
|
||||
if (height < 0.0)
|
||||
{
|
||||
standardized.origin.y += height;
|
||||
standardized.size.height = -height;
|
||||
}
|
||||
|
||||
|
||||
return standardized;
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ function CPRectUnion(lhsRect, rhsRect)
|
||||
minY = Math.min(CPRectGetMinY(lhsRect), CPRectGetMinY(rhsRect)),
|
||||
maxX = Math.max(CPRectGetMaxX(lhsRect), CPRectGetMaxX(rhsRect)),
|
||||
maxY = Math.max(CPRectGetMaxY(lhsRect), CPRectGetMaxY(rhsRect));
|
||||
|
||||
|
||||
return CPRectMake(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
|
||||
@@ -222,8 +222,8 @@ function CPRectContainsPoint(aRect, aPoint)
|
||||
{
|
||||
return aPoint.x >= CPRectGetMinX(aRect) &&
|
||||
aPoint.y >= CPRectGetMinY(aRect) &&
|
||||
aPoint.x < CPRectGetMaxX(aRect) &&
|
||||
aPoint.y < CPRectGetMaxY(aRect);
|
||||
aPoint.x < CPRectGetMaxX(aRect) &&
|
||||
aPoint.y < CPRectGetMaxY(aRect);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -234,9 +234,9 @@ function CPRectContainsPoint(aRect, aPoint)
|
||||
@param possibleInner the CGRect to test if it fits inside \c possibleOuter.
|
||||
@return BOOL \c YES if \c possibleInner fits inside \c possibleOuter.
|
||||
*/
|
||||
function CPRectContainsRect(lhsRect, rhsRect)
|
||||
function CPRectContainsRect(possibleOuter, possibleInner)
|
||||
{
|
||||
return CPRectEqualToRect(CPUnionRect(lhsRect, rhsRect), rhsRect);
|
||||
return CGRectContainsRect(possibleOuter, possibleInner);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -261,7 +261,7 @@ function CPPointEqualToPoint(lhsPoint, rhsPoint)
|
||||
*/
|
||||
function CPRectEqualToRect(lhsRect, rhsRect)
|
||||
{
|
||||
return CPPointEqualToPoint(lhsRect.origin, rhsRect.origin) &&
|
||||
return CPPointEqualToPoint(lhsRect.origin, rhsRect.origin) &&
|
||||
CPSizeEqualToSize(lhsRect.size, rhsRect.size);
|
||||
}
|
||||
|
||||
@@ -380,6 +380,20 @@ function CPRectIsNull(aRect)
|
||||
return aRect.size.width <= 0.0 || aRect.size.height <= 0.0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates two rectangles -- slice and rem -- from inRect, by dividing inRect
|
||||
with a line that's parallel to the side of inRect specified by edge.
|
||||
The size of slice is determined by amount, which specifies the distance from edge.
|
||||
|
||||
slice and rem must not be NULL.
|
||||
|
||||
@group CGRect
|
||||
*/
|
||||
function CPDivideRect(inRect, slice, rem, amount, edge)
|
||||
{
|
||||
CGRectDivide(inRect, slice, rem, amount, edge);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c YES if the two CGSizes are identical.
|
||||
@group CGSize
|
||||
@@ -434,7 +448,7 @@ function CPStringFromRect(aRect)
|
||||
function CPPointFromString(aString)
|
||||
{
|
||||
var comma = aString.indexOf(',');
|
||||
|
||||
|
||||
return { x:parseFloat(aString.substr(1, comma - 1), 10), y:parseFloat(aString.substring(comma + 1, aString.length), 10) };
|
||||
}
|
||||
|
||||
@@ -447,7 +461,7 @@ function CPPointFromString(aString)
|
||||
function CPSizeFromString(aString)
|
||||
{
|
||||
var comma = aString.indexOf(',');
|
||||
|
||||
|
||||
return { width:parseFloat(aString.substr(1, comma - 1), 10), height:parseFloat(aString.substring(comma + 1, aString.length), 10) };
|
||||
}
|
||||
|
||||
@@ -460,7 +474,7 @@ function CPSizeFromString(aString)
|
||||
function CPRectFromString(aString)
|
||||
{
|
||||
var comma = aString.indexOf(',', aString.indexOf(',') + 1);
|
||||
|
||||
|
||||
return { origin:CPPointFromString(aString.substr(1, comma - 1)), size:CPSizeFromString(aString.substring(comma + 2, aString.length)) };
|
||||
}
|
||||
|
||||
@@ -504,6 +518,6 @@ function CPPointMakeZero()
|
||||
return CPPointMake(0, 0, 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@}
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* CPGraphics.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2010, 280 North, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPColor.j"
|
||||
@import "CPGraphicsContext.j"
|
||||
|
||||
|
||||
function CPDrawTiledRects(
|
||||
/* CGRect */ boundsRect,
|
||||
/* CGRect */ clipRect,
|
||||
/* CPRectEdge[] */ sides,
|
||||
/* float[] */ grays)
|
||||
{
|
||||
if (sides.length != grays.length)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and grays (length: " + grays.length + ") must have the same length."];
|
||||
|
||||
var colors = [];
|
||||
|
||||
for (var i = 0; i < grays.length; ++i)
|
||||
colors.push([CPColor colorWithCalibratedWhite:grays[i] alpha:1.0]);
|
||||
|
||||
return CPDrawColorTiledRects(boundsRect, clipRect, sides, colors);
|
||||
}
|
||||
|
||||
function CPDrawColorTiledRects(
|
||||
/* CGRect */ boundsRect,
|
||||
/* CGRect */ clipRect,
|
||||
/* CPRectEdge[] */ sides,
|
||||
/* CPColor[] */ colors)
|
||||
{
|
||||
if (sides.length != colors.length)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and colors (length: " + colors.length + ") must have the same length."];
|
||||
|
||||
var resultRect = _CGRectMakeCopy(boundsRect),
|
||||
slice = _CGRectMakeZero(),
|
||||
remainder = _CGRectMakeZero(),
|
||||
context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextSaveGState(context);
|
||||
CGContextSetLineWidth(context, 1.0);
|
||||
|
||||
for (var sideIndex = 0; sideIndex < sides.length; ++sideIndex)
|
||||
{
|
||||
var side = sides[sideIndex];
|
||||
|
||||
CGRectDivide(resultRect, slice, remainder, 1.0, side);
|
||||
resultRect = remainder;
|
||||
slice = CGRectIntersection(slice, clipRect);
|
||||
|
||||
// Cocoa docs say that only slices that are within the clipRect are actually drawn
|
||||
if (_CGRectIsEmpty(slice))
|
||||
continue;
|
||||
|
||||
var minX, maxX, minY, maxY;
|
||||
|
||||
if (side == CPMinXEdge || side == CPMaxXEdge)
|
||||
{
|
||||
// Make sure we have at least 1 pixel to draw a line
|
||||
if (_CGRectGetWidth(slice) < 1.0)
|
||||
continue;
|
||||
|
||||
minX = _CGRectGetMinX(slice) + 0.5;
|
||||
maxX = minX;
|
||||
minY = _CGRectGetMinY(slice);
|
||||
maxY = _CGRectGetMaxY(slice);
|
||||
}
|
||||
else // CPMinYEdge || CPMaxYEdge
|
||||
{
|
||||
// Make sure we have at least 1 pixel to draw a line
|
||||
if (_CGRectGetHeight(slice) < 1.0)
|
||||
continue;
|
||||
|
||||
minX = _CGRectGetMinX(slice);
|
||||
maxX = _CGRectGetMaxX(slice);
|
||||
minY = _CGRectGetMinY(slice) + 0.5;
|
||||
maxY = minY;
|
||||
}
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextMoveToPoint(context, minX, minY);
|
||||
CGContextAddLineToPoint(context, maxX, maxY);
|
||||
CGContextSetStrokeColor(context, colors[sideIndex]);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
return resultRect;
|
||||
}
|
||||
@@ -20,6 +20,11 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import "CGContext.j"
|
||||
|
||||
|
||||
var CPGraphicsContextCurrent = nil;
|
||||
|
||||
/*!
|
||||
@@ -66,10 +71,10 @@ var CPGraphicsContextCurrent = nil;
|
||||
- (id)initWithGraphicsPort:(CPContext)aGraphicsPort
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
_graphicsPort = aGraphicsPort;
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
+94
-29
@@ -19,7 +19,7 @@
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
@import <Foundation/CPBundle.j>
|
||||
@import <Foundation/CPNotificationCenter.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
@import "CPGeometry.j"
|
||||
|
||||
|
||||
CPImageLoadStatusInitialized = 0;
|
||||
CPImageLoadStatusLoading = 1;
|
||||
CPImageLoadStatusCompleted = 2;
|
||||
@@ -38,18 +39,33 @@ CPImageLoadStatusReadError = 6;
|
||||
|
||||
CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
|
||||
// Image Names
|
||||
CPImageNameColorPanel = @"CPImageNameColorPanel";
|
||||
CPImageNameColorPanelHighlighted = @"CPImageNameColorPanelHighlighted";
|
||||
|
||||
var imagesForNames = { },
|
||||
AppKitImageForNames = { };
|
||||
|
||||
AppKitImageForNames[CPImageNameColorPanel] = CGSizeMake(26.0, 29.0);
|
||||
AppKitImageForNames[CPImageNameColorPanelHighlighted] = CGSizeMake(26.0, 29.0);
|
||||
|
||||
function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
{
|
||||
if (!aBundle)
|
||||
aBundle = [CPBundle mainBundle];
|
||||
|
||||
|
||||
if (aSize)
|
||||
return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename] size:aSize];
|
||||
|
||||
|
||||
return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename]];
|
||||
}
|
||||
|
||||
/*!
|
||||
function CPAppKitImage(aFilename, aSize)
|
||||
{
|
||||
return CPImageInBundle(aFilename, aSize, [CPBundle bundleForClass:[CPView class]]);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPImage
|
||||
|
||||
@@ -57,7 +73,7 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
all image types supported by the browser.
|
||||
|
||||
@par Delegate Methods
|
||||
|
||||
|
||||
@delegate -(void)imageDidLoad:(CPImage)image;
|
||||
Called when the specified image has finished loading.
|
||||
@param image the image that loaded
|
||||
@@ -74,10 +90,11 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
{
|
||||
CGSize _size;
|
||||
CPString _filename;
|
||||
|
||||
CPString _name;
|
||||
|
||||
id _delegate;
|
||||
unsigned _loadStatus;
|
||||
|
||||
|
||||
Image _image;
|
||||
}
|
||||
|
||||
@@ -97,14 +114,14 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
- (id)initByReferencingFile:(CPString)aFilename size:(CGSize)aSize
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_size = CPSizeCreateCopy(aSize);
|
||||
_filename = aFilename;
|
||||
_loadStatus = CPImageLoadStatusInitialized;
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -117,10 +134,10 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
- (id)initWithContentsOfFile:(CPString)aFilename size:(CGSize)aSize
|
||||
{
|
||||
self = [self initByReferencingFile:aFilename size:aSize];
|
||||
|
||||
|
||||
if (self)
|
||||
[self load];
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -133,10 +150,10 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
- (id)initWithContentsOfFile:(CPString)aFilename
|
||||
{
|
||||
self = [self initByReferencingFile:aFilename size:CGSizeMake(-1, -1)];
|
||||
|
||||
|
||||
if (self)
|
||||
[self load];
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -165,6 +182,50 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
return _size;
|
||||
}
|
||||
|
||||
+ (id)imageNamed:(CPString)aName
|
||||
{
|
||||
var image = imagesForNames[aName];
|
||||
|
||||
if (image)
|
||||
return image;
|
||||
|
||||
var imageOrSize = AppKitImageForNames[aName];
|
||||
|
||||
if (!imageOrSize)
|
||||
return nil;
|
||||
|
||||
if (!imageOrSize.isa)
|
||||
{
|
||||
imageOrSize = CPAppKitImage("CPImage/" + aName + ".png", imageOrSize);
|
||||
|
||||
[imageOrSize setName:aName];
|
||||
|
||||
AppKitImageForNames[aName] = imageOrSize;
|
||||
}
|
||||
|
||||
return imageOrSize;
|
||||
}
|
||||
|
||||
- (BOOL)setName:(CPString)aName
|
||||
{
|
||||
if (_name === aName)
|
||||
return YES;
|
||||
|
||||
if (imagesForNames[aName])
|
||||
return NO;
|
||||
|
||||
_name = aName;
|
||||
|
||||
imagesForNames[aName] = self;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CPString)name
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the receiver's delegate.
|
||||
@param the delegate
|
||||
@@ -200,9 +261,10 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
{
|
||||
if (_loadStatus == CPImageLoadStatusLoading || _loadStatus == CPImageLoadStatusCompleted)
|
||||
return;
|
||||
|
||||
|
||||
_loadStatus = CPImageLoadStatusLoading;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_image = new Image();
|
||||
|
||||
var isSynchronous = YES;
|
||||
@@ -219,7 +281,7 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
}
|
||||
[self _derefFromImage];
|
||||
}
|
||||
|
||||
|
||||
_image.onerror = function ()
|
||||
{
|
||||
if (isSynchronous)
|
||||
@@ -231,7 +293,7 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
}
|
||||
[self _derefFromImage];
|
||||
}
|
||||
|
||||
|
||||
_image.onabort = function ()
|
||||
{
|
||||
if (isSynchronous)
|
||||
@@ -243,10 +305,13 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
}
|
||||
[self _derefFromImage];
|
||||
}
|
||||
|
||||
|
||||
_image.src = _filename;
|
||||
|
||||
isSynchronous = NO;
|
||||
|
||||
// onload and friends may fire after this point but BEFORE the end of the run loop,
|
||||
// crazy, I know. So don't set isSynchronous here, rather wait a bit longer.
|
||||
window.setTimeout(function() { isSynchronous = NO; }, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL)isThreePartImage
|
||||
@@ -279,7 +344,7 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPImageDidLoadNotification
|
||||
object:self];
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(imageDidLoad:)])
|
||||
[_delegate imageDidLoad:self];
|
||||
}
|
||||
@@ -288,7 +353,7 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
- (void)_imageDidError
|
||||
{
|
||||
_loadStatus = CPImageLoadStatusReadError;
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(imageDidError:)])
|
||||
[_delegate imageDidError:self];
|
||||
}
|
||||
@@ -297,7 +362,7 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
- (void)_imageDidAbort
|
||||
{
|
||||
_loadStatus = CPImageLoadStatusCancelled;
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(imageDidAbort:)])
|
||||
[_delegate imageDidAbort:self];
|
||||
}
|
||||
@@ -337,7 +402,7 @@ function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
- (id)initWithImageSlices:(CPArray)imageSlices isVertical:(BOOL)isVertical
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_imageSlices = imageSlices;
|
||||
@@ -382,13 +447,13 @@ var CPThreePartImageImageSlicesKey = @"CPThreePartImageImageSlicesKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_imageSlices = [aCoder decodeObjectForKey:CPThreePartImageImageSlicesKey];
|
||||
_isVertical = [aCoder decodeBoolForKey:CPThreePartImageIsVerticalKey];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -409,10 +474,10 @@ var CPThreePartImageImageSlicesKey = @"CPThreePartImageImageSlicesKey",
|
||||
- (id)initWithImageSlices:(CPArray)imageSlices
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
_imageSlices = imageSlices;
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -445,10 +510,10 @@ var CPNinePartImageImageSlicesKey = @"CPNinePartImageImageSlicesKey";
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
_imageSlices = [aCoder decodeObjectForKey:CPNinePartImageImageSlicesKey];
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
+140
-64
@@ -26,31 +26,24 @@
|
||||
@import "CPImage.j"
|
||||
@import "CPShadowView.j"
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
#include "Platform/DOM/CPDOMDisplayServer.h"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPImageScaling
|
||||
*/
|
||||
CPScaleProportionally = 0;
|
||||
/*
|
||||
@global
|
||||
@group CPImageScaling
|
||||
*/
|
||||
CPScaleToFit = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPImageScaling
|
||||
*/
|
||||
CPScaleNone = 2;
|
||||
|
||||
CPImageAlignCenter = 0;
|
||||
CPImageAlignTop = 1;
|
||||
CPImageAlignTopLeft = 2;
|
||||
CPImageAlignTopRight = 3;
|
||||
CPImageAlignLeft = 4;
|
||||
CPImageAlignBottom = 5;
|
||||
CPImageAlignBottomLeft = 6;
|
||||
CPImageAlignBottomRight = 7;
|
||||
CPImageAlignRight = 8;
|
||||
|
||||
var CPImageViewShadowBackgroundColor = nil,
|
||||
CPImageViewEmptyPlaceholderImage = nil;
|
||||
|
||||
var CPImageViewShadowBackgroundColor = nil;
|
||||
|
||||
var LEFT_SHADOW_INSET = 3.0,
|
||||
RIGHT_SHADOW_INSET = 3.0,
|
||||
TOP_SHADOW_INSET = 3.0,
|
||||
@@ -58,7 +51,7 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
VERTICAL_SHADOW_INSET = TOP_SHADOW_INSET + BOTTOM_SHADOW_INSET,
|
||||
HORIZONTAL_SHADOW_INSET = LEFT_SHADOW_INSET + RIGHT_SHADOW_INSET;
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPImageView
|
||||
|
||||
@@ -66,20 +59,28 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
*/
|
||||
@implementation CPImageView : CPControl
|
||||
{
|
||||
DOMElement _DOMImageElement;
|
||||
|
||||
BOOL _hasShadow;
|
||||
CPView _shadowView;
|
||||
DOMElement _DOMImageElement;
|
||||
|
||||
BOOL _isEditable;
|
||||
BOOL _hasShadow;
|
||||
CPView _shadowView;
|
||||
|
||||
CGRect _imageRect;
|
||||
BOOL _isEditable;
|
||||
|
||||
CGRect _imageRect;
|
||||
CPImageAlignment _imageAlignment;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
var bundle = [CPBundle bundleForClass:[CPView class]];
|
||||
|
||||
CPImageViewEmptyPlaceholderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"empty.png"]];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
@@ -95,11 +96,11 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
}
|
||||
|
||||
CPDOMDisplayServerAppendChild(_DOMElement, _DOMImageElement);
|
||||
|
||||
|
||||
_DOMImageElement.style.visibility = "hidden";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -120,29 +121,29 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
- (void)setObjectValue:(CPImage)anImage
|
||||
{
|
||||
var oldImage = [self objectValue];
|
||||
|
||||
|
||||
if (oldImage === anImage)
|
||||
return;
|
||||
|
||||
|
||||
[super setObjectValue:anImage];
|
||||
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
|
||||
if (oldImage)
|
||||
[defaultCenter removeObserver:self name:CPImageDidLoadNotification object:oldImage];
|
||||
|
||||
var newImage = [self objectValue];
|
||||
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMImageElement.src = newImage ? [newImage filename] : "";
|
||||
_DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
|
||||
#endif
|
||||
|
||||
var size = [newImage size];
|
||||
|
||||
|
||||
if (size && size.width === -1 && size.height === -1)
|
||||
{
|
||||
[defaultCenter addObserver:self selector:@selector(imageDidLoad:) name:CPImageDidLoadNotification object:newImage];
|
||||
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMImageElement.width = 0;
|
||||
_DOMImageElement.height = 0;
|
||||
@@ -161,7 +162,7 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
- (void)imageDidLoad:(CPNotification)aNotification
|
||||
{
|
||||
[self hideOrDisplayContents];
|
||||
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
@@ -183,28 +184,52 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
{
|
||||
if (_hasShadow == shouldHaveShadow)
|
||||
return;
|
||||
|
||||
|
||||
_hasShadow = shouldHaveShadow;
|
||||
|
||||
if (_hasShadow)
|
||||
{
|
||||
_shadowView = [[CPShadowView alloc] initWithFrame:[self bounds]];
|
||||
|
||||
|
||||
[self addSubview:_shadowView];
|
||||
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_shadowView removeFromSuperview];
|
||||
|
||||
|
||||
_shadowView = nil;
|
||||
}
|
||||
|
||||
|
||||
[self hideOrDisplayContents];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the type of image alignment that should be used to
|
||||
render the image.
|
||||
@param anImageAlignment the type of scaling to use
|
||||
*/
|
||||
- (void)setImageAlignment:(CPImageAlignment)anImageAlignment
|
||||
{
|
||||
if (_imageAlignment == anImageAlignment)
|
||||
return;
|
||||
|
||||
_imageAlignment = anImageAlignment;
|
||||
|
||||
if (![self image])
|
||||
return;
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (unsigned)imageAlignment
|
||||
{
|
||||
return _imageAlignment;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the type of image scaling that should be used to
|
||||
render the image.
|
||||
@@ -213,18 +238,23 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
- (void)setImageScaling:(CPImageScaling)anImageScaling
|
||||
{
|
||||
[super setImageScaling:anImageScaling];
|
||||
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if ([self currentValueForThemeAttribute:@"image-scaling"] === CPScaleToFit)
|
||||
{
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, 0.0, 0.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (unsigned)imageScaling
|
||||
{
|
||||
return [self currentValueForThemeAttribute:@"image-scaling"];
|
||||
}
|
||||
|
||||
/*!
|
||||
Toggles the display of the image view.
|
||||
*/
|
||||
@@ -284,7 +314,7 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
else
|
||||
{
|
||||
var size = [image size];
|
||||
|
||||
|
||||
if (size.width == -1 && size.height == -1)
|
||||
return;
|
||||
|
||||
@@ -301,7 +331,7 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
{
|
||||
var imageRatio = size.width / size.height,
|
||||
viewRatio = width / height;
|
||||
|
||||
|
||||
if (viewRatio > imageRatio)
|
||||
width = height * imageRatio;
|
||||
else
|
||||
@@ -318,7 +348,7 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
width = size.width;
|
||||
height = size.height;
|
||||
}
|
||||
|
||||
|
||||
if (imageScaling == CPScaleNone)
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
@@ -327,8 +357,46 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
#endif
|
||||
}
|
||||
|
||||
var x = (boundsWidth - width) / 2.0,
|
||||
y = (boundsHeight - height) / 2.0;
|
||||
var x,
|
||||
y;
|
||||
|
||||
switch (_imageAlignment)
|
||||
{
|
||||
case CPImageAlignLeft:
|
||||
case CPImageAlignTopLeft:
|
||||
case CPImageAlignBottomLeft:
|
||||
x = 0.0;
|
||||
break;
|
||||
|
||||
case CPImageAlignRight:
|
||||
case CPImageAlignTopRight:
|
||||
case CPImageAlignBottomRight:
|
||||
x = boundsWidth - width;
|
||||
break;
|
||||
|
||||
default:
|
||||
x = (boundsWidth - width) / 2.0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (_imageAlignment)
|
||||
{
|
||||
case CPImageAlignTop:
|
||||
case CPImageAlignTopLeft:
|
||||
case CPImageAlignTopRight:
|
||||
y = 0.0;
|
||||
break;
|
||||
|
||||
case CPImageAlignBottom:
|
||||
case CPImageAlignBottomLeft:
|
||||
case CPImageAlignBottomRight:
|
||||
y = boundsHeight - height;
|
||||
break;
|
||||
|
||||
default:
|
||||
y = (boundsHeight - height) / 2.0;
|
||||
break;
|
||||
}
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, x, y);
|
||||
@@ -336,7 +404,7 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
}
|
||||
|
||||
_imageRect = _CGRectMake(x, y, width, height);
|
||||
|
||||
|
||||
if (_hasShadow)
|
||||
[_shadowView setFrame:_CGRectMake(x - LEFT_SHADOW_INSET, y - TOP_SHADOW_INSET, width + insetWidth, height + insetHeight)];
|
||||
}
|
||||
@@ -374,20 +442,26 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
return _isEditable;
|
||||
}
|
||||
|
||||
- (void)performDragOperation:(CPDraggingInfo)aSender
|
||||
- (BOOL)performDragOperation:(CPDraggingInfo)aSender
|
||||
{
|
||||
var images = [CPKeyedUnarchiver unarchiveObjectWithData:[[aSender draggingPasteboard] dataForType:CPImagesPboardType]];
|
||||
|
||||
if ([images count])
|
||||
{
|
||||
[self setImage:images[0]];
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||
CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
|
||||
CPImageViewHasShadowKey = @"CPImageViewHasShadowKey",
|
||||
CPImageViewIsEditableKey = @"CPImageViewIsEditableKey";
|
||||
var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||
CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
|
||||
CPImageViewImageAlignmentKey = @"CPImageViewImageAlignmentKey",
|
||||
CPImageViewHasShadowKey = @"CPImageViewHasShadowKey",
|
||||
CPImageViewIsEditableKey = @"CPImageViewIsEditableKey";
|
||||
|
||||
@implementation CPImageView (CPCoding)
|
||||
|
||||
@@ -412,7 +486,7 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||
#endif
|
||||
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
@@ -420,14 +494,15 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||
#endif
|
||||
|
||||
[self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]];
|
||||
|
||||
[self setImageAlignment:[aCoder decodeIntForKey:CPImageViewImageAlignmentKey]];
|
||||
|
||||
if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey] || NO)
|
||||
[self setEditable:YES];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -438,22 +513,23 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
// We do this in order to avoid encoding the _shadowView, which
|
||||
// We do this in order to avoid encoding the _shadowView, which
|
||||
// should just automatically be created programmatically as needed.
|
||||
if (_shadowView)
|
||||
{
|
||||
var actualSubviews = _subviews;
|
||||
|
||||
|
||||
_subviews = [_subviews copy];
|
||||
[_subviews removeObjectIdenticalTo:_shadowView];
|
||||
}
|
||||
|
||||
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
|
||||
if (_shadowView)
|
||||
_subviews = actualSubviews;
|
||||
|
||||
|
||||
[aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey];
|
||||
[aCoder encodeInt:_imageAlignment forKey:CPImageViewImageAlignmentKey];
|
||||
|
||||
if (_isEditable)
|
||||
[aCoder encodeBool:_isEditable forKey:CPImageViewIsEditableKey];
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* CPKeyBinding.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Nicholas Small.
|
||||
* Copyright 2010, 280 North, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import "CPEvent.j"
|
||||
|
||||
|
||||
CPStandardKeyBindings = {
|
||||
@"@.": @"cancelOperation:",
|
||||
|
||||
@"^a": @"moveToBeginningOfParagraph:",
|
||||
@"^$a": @"moveToBeginningOfParagraphAndModifySelection:",
|
||||
@"^b": @"moveBackward:",
|
||||
@"^$b": @"moveBackwardAndModifySelection:",
|
||||
@"^~b": @"moveWordBackward:",
|
||||
@"^~$b": @"moveWordBackwardAndModifySelection:",
|
||||
@"^d": @"deleteForward:",
|
||||
@"^e": @"moveToEndOfParagraph:",
|
||||
@"^$e": @"moveToEndOfParagraphAndModifySelection:",
|
||||
@"^f": @"moveForward:",
|
||||
@"^$f": @"moveForwardAndModifySelection:",
|
||||
@"^~f": @"moveWordForward:",
|
||||
@"^~$f": @"moveWordForwardAndModifySelection:",
|
||||
@"^h": @"deleteBackward:",
|
||||
@"^k": @"deleteToEndOfParagraph:",
|
||||
@"^l": @"centerSelectionInVisibleArea:",
|
||||
@"^n": @"moveDown:",
|
||||
@"^$n": @"moveDownAndModifySelection:",
|
||||
@"^o": [@"insertNewlineIgnoringFieldEditor:", @"moveBackward:"],
|
||||
@"^p": @"moveUp:",
|
||||
@"^$p": @"moveUpAndModifySelection:",
|
||||
@"^t": @"transpose:",
|
||||
@"^v": @"pageDown:",
|
||||
@"^$v": @"pageDownAndModifySelection:",
|
||||
@"^y": @"yank:"
|
||||
};
|
||||
|
||||
CPStandardKeyBindings[CPNewlineCharacter] = @"insertNewline:";
|
||||
CPStandardKeyBindings[CPCarriageReturnCharacter] = @"insertNewline:";
|
||||
CPStandardKeyBindings[CPEnterCharacter] = @"insertNewline:";
|
||||
CPStandardKeyBindings[@"~" + CPNewlineCharacter] = @"insertNewlineIgnoringFieldEditor:";
|
||||
CPStandardKeyBindings[@"~" + CPCarriageReturnCharacter] = @"insertNewlineIgnoringFieldEditor:";
|
||||
CPStandardKeyBindings[@"~" + CPEnterCharacter] = @"insertNewlineIgnoringFieldEditor:";
|
||||
CPStandardKeyBindings[@"^" + CPNewlineCharacter] = @"insertLineBreak:";
|
||||
CPStandardKeyBindings[@"^" + CPCarriageReturnCharacter] = @"insertLineBreak:";
|
||||
CPStandardKeyBindings[@"^" + CPEnterCharacter] = @"insertLineBreak:";
|
||||
|
||||
CPStandardKeyBindings[CPBackspaceCharacter] = @"deleteBackward:";
|
||||
CPStandardKeyBindings[@"~" + CPBackspaceCharacter] = @"deleteWordBackward:";
|
||||
CPStandardKeyBindings[CPDeleteCharacter] = @"deleteBackward:";
|
||||
CPStandardKeyBindings[@"@" + CPDeleteCharacter] = @"deleteToBeginningOfLine:";
|
||||
CPStandardKeyBindings[@"~" + CPDeleteCharacter] = @"deleteWordBackward:";
|
||||
CPStandardKeyBindings[@"^" + CPDeleteCharacter] = @"deleteBackwardByDecomposingPreviousCharacter:";
|
||||
CPStandardKeyBindings[@"^~" + CPDeleteCharacter] = @"deleteWordBackward:";
|
||||
|
||||
CPStandardKeyBindings[CPDeleteFunctionKey] = @"deleteForward:";
|
||||
CPStandardKeyBindings[@"~" + CPDeleteFunctionKey] = @"deleteWordForward:";
|
||||
|
||||
CPStandardKeyBindings[CPTabCharacter] = @"insertTab:";
|
||||
CPStandardKeyBindings[@"~" + CPTabCharacter] = @"insertTabIgnoringFieldEditor:";
|
||||
CPStandardKeyBindings[@"^" + CPTabCharacter] = @"selectNextKeyView:";
|
||||
CPStandardKeyBindings[CPBackTabCharacter] = @"insertBacktab:";
|
||||
CPStandardKeyBindings[@"^" + CPBackTabCharacter] = @"selectPreviousKeyView:";
|
||||
|
||||
CPStandardKeyBindings[CPEscapeFunctionKey] = @"cancelOperation:";
|
||||
CPStandardKeyBindings[@"~" + CPEscapeFunctionKey] = @"complete:";
|
||||
CPStandardKeyBindings[CPF5FunctionKey] = @"complete:";
|
||||
|
||||
CPStandardKeyBindings[CPLeftArrowFunctionKey] = @"moveLeft:";
|
||||
CPStandardKeyBindings[@"~" + CPLeftArrowFunctionKey] = @"moveWordLeft:";
|
||||
CPStandardKeyBindings[@"^" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLine:";
|
||||
CPStandardKeyBindings[@"@" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLine:";
|
||||
CPStandardKeyBindings[@"$" + CPLeftArrowFunctionKey] = @"moveLeftAndModifySelection:";
|
||||
CPStandardKeyBindings[@"$~" + CPLeftArrowFunctionKey] = @"moveWordLeftAndModifySelection:";
|
||||
CPStandardKeyBindings[@"$^" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLineAndModifySelection:";
|
||||
CPStandardKeyBindings[@"$@" + CPLeftArrowFunctionKey] = @"moveToLeftEndOfLineAndModifySelection:";
|
||||
CPStandardKeyBindings[@"@^" + CPLeftArrowFunctionKey] = @"makeBaseWritingDirectionRightToLeft:";
|
||||
CPStandardKeyBindings[@"@^~" + CPLeftArrowFunctionKey] = @"makeTextWritingDirectionRightToLeft:";
|
||||
|
||||
CPStandardKeyBindings[CPRightArrowFunctionKey] = @"moveRight:";
|
||||
CPStandardKeyBindings[@"~" + CPRightArrowFunctionKey] = @"moveWordRight:";
|
||||
CPStandardKeyBindings[@"^" + CPRightArrowFunctionKey] = @"moveToRightEndOfLine:";
|
||||
CPStandardKeyBindings[@"@" + CPRightArrowFunctionKey] = @"moveToRightEndOfLine:";
|
||||
CPStandardKeyBindings[@"$" + CPRightArrowFunctionKey] = @"moveRightAndModifySelection:";
|
||||
CPStandardKeyBindings[@"$~" + CPRightArrowFunctionKey] = @"moveWordRightAndModifySelection:";
|
||||
CPStandardKeyBindings[@"$^" + CPRightArrowFunctionKey] = @"moveToRightEndOfLineAndModifySelection:";
|
||||
CPStandardKeyBindings[@"$@" + CPRightArrowFunctionKey] = @"moveToRightEndOfLineAndModifySelection:";
|
||||
CPStandardKeyBindings[@"@^" + CPRightArrowFunctionKey] = @"makeBaseWritingDirectionLeftToRight:";
|
||||
CPStandardKeyBindings[@"@^~" + CPRightArrowFunctionKey] = @"makeTextWritingDirectionLeftToRight:";
|
||||
|
||||
CPStandardKeyBindings[CPUpArrowFunctionKey] = @"moveUp:";
|
||||
CPStandardKeyBindings[@"~" + CPUpArrowFunctionKey] = [@"moveBackward:", @"moveToBeginningOfParagraph:"];
|
||||
CPStandardKeyBindings[@"^" + CPUpArrowFunctionKey] = @"scrollPageUp:";
|
||||
CPStandardKeyBindings[@"@" + CPUpArrowFunctionKey] = @"moveToBeginningOfDocument:";
|
||||
CPStandardKeyBindings[@"$" + CPUpArrowFunctionKey] = @"moveUpAndModifySelection:";
|
||||
CPStandardKeyBindings[@"$~" + CPUpArrowFunctionKey] = @"moveParagraphBackwardAndModifySelection:";
|
||||
CPStandardKeyBindings[@"$@" + CPUpArrowFunctionKey] = @"moveToBeginningOfDocumentAndModifySelection:";
|
||||
|
||||
CPStandardKeyBindings[CPDownArrowFunctionKey] = @"moveDown:";
|
||||
CPStandardKeyBindings[@"~" + CPDownArrowFunctionKey] = [@"moveForward:", @"moveToEndOfParagraph:"];
|
||||
CPStandardKeyBindings[@"^" + CPDownArrowFunctionKey] = @"scrollPageDown:";
|
||||
CPStandardKeyBindings[@"@" + CPDownArrowFunctionKey] = @"moveToEndOfDocument:";
|
||||
CPStandardKeyBindings[@"$" + CPDownArrowFunctionKey] = @"moveDownAndModifySelection:";
|
||||
CPStandardKeyBindings[@"$~" + CPDownArrowFunctionKey] = @"moveParagraphForwardAndModifySelection:";
|
||||
CPStandardKeyBindings[@"$@" + CPDownArrowFunctionKey] = @"moveToEndOfDocumentAndModifySelection:";
|
||||
CPStandardKeyBindings[@"@^" + CPDownArrowFunctionKey] = @"makeBaseWritingDirectionNatural:";
|
||||
CPStandardKeyBindings[@"@^~" + CPDownArrowFunctionKey] = @"makeTextWritingDirectionNatural:";
|
||||
|
||||
CPStandardKeyBindings[CPHomeFunctionKey] = @"scrollToBeginningOfDocument:";
|
||||
CPStandardKeyBindings[@"$" + CPHomeFunctionKey] = @"moveToBeginningOfDocumentAndModifySelection:";
|
||||
CPStandardKeyBindings[CPEndFunctionKey] = @"scrollToEndOfDocument:";
|
||||
CPStandardKeyBindings[@"$" + CPEndFunctionKey] = @"moveToEndOfDocumentAndModifySelection:";
|
||||
|
||||
CPStandardKeyBindings[CPPageUpFunctionKey] = @"scrollPageUp:";
|
||||
CPStandardKeyBindings[@"~" + CPPageUpFunctionKey] = @"pageUp:";
|
||||
CPStandardKeyBindings[@"$" + CPPageUpFunctionKey] = @"pageUpAndModifySelection:";
|
||||
CPStandardKeyBindings[CPPageDownFunctionKey] = @"scrollPageDown:";
|
||||
CPStandardKeyBindings[@"~" + CPPageDownFunctionKey] = @"pageDown:";
|
||||
CPStandardKeyBindings[@"$" + CPPageDownFunctionKey] = @"pageDownAndModifySelection:";
|
||||
|
||||
var CPKeyBindingCache = {};
|
||||
|
||||
@implementation CPKeyBinding : CPObject
|
||||
{
|
||||
CPString _key;
|
||||
unsigned _modifierFlags;
|
||||
|
||||
CPArray _selectors;
|
||||
|
||||
CPString _cacheName;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if ([self class] !== CPKeyBinding)
|
||||
return;
|
||||
|
||||
[self createKeyBindingsFromJSObject:CPStandardKeyBindings];
|
||||
}
|
||||
|
||||
+ (void)createKeyBindingsFromJSObject:(JSObject)anObject
|
||||
{
|
||||
var binding;
|
||||
for (binding in anObject)
|
||||
[self cacheKeyBinding:[[CPKeyBinding alloc] initWithPhysicalKeyString:binding selectors:anObject[binding]]];
|
||||
}
|
||||
|
||||
+ (void)cacheKeyBinding:(CPKeyBinding)aBinding
|
||||
{
|
||||
if (!aBinding)
|
||||
return;
|
||||
|
||||
CPKeyBindingCache[[aBinding _cacheName]] = aBinding;
|
||||
}
|
||||
|
||||
+ (CPKeyBinding)keyBindingForKey:(CPString)aKey modifierFlags:(unsigned)aFlag
|
||||
{
|
||||
var tempBinding = [[self alloc] initWithKey:aKey modifierFlags:aFlag selectors:nil];
|
||||
return CPKeyBindingCache[[tempBinding _cacheName]];
|
||||
}
|
||||
|
||||
+ (CPArray)selectorsForKey:(CPString)aKey modifierFlags:(unsigned)aFlag
|
||||
{
|
||||
return [[self keyBindingForKey:aKey modifierFlags:aFlag] selectors];
|
||||
}
|
||||
|
||||
- (id)initWithPhysicalKeyString:(CPString)binding selectors:(CPArray)selectors
|
||||
{
|
||||
var components = binding.split(@""),
|
||||
modifierFlags = ([components containsObject:@"$"] ? CPShiftKeyMask : 0) |
|
||||
([components containsObject:@"^"] ? CPControlKeyMask : 0) |
|
||||
([components containsObject:@"~"] ? CPAlternateKeyMask : 0) |
|
||||
([components containsObject:@"@"] ? CPCommandKeyMask : 0);
|
||||
|
||||
if (![selectors isKindOfClass:CPArray])
|
||||
selectors = [selectors];
|
||||
|
||||
return [self initWithKey:[components lastObject] modifierFlags:modifierFlags selectors:selectors];
|
||||
}
|
||||
|
||||
- (id)initWithKey:(CPString)aKey modifierFlags:(unsigned)aFlag selectors:(CPArray)selectors
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_key = aKey;
|
||||
_modifierFlags = aFlag;
|
||||
|
||||
_selectors = selectors;
|
||||
|
||||
// We normalize our key binding string in order to properly cache it.
|
||||
// We want to ensure the modifiers are always in the same order.
|
||||
var cacheName = [];
|
||||
|
||||
if (_modifierFlags & CPCommandKeyMask)
|
||||
cacheName.push(@"@");
|
||||
if (_modifierFlags & CPControlKeyMask)
|
||||
cacheName.push(@"^");
|
||||
if (_modifierFlags & CPAlternateKeyMask)
|
||||
cacheName.push(@"~");
|
||||
if (_modifierFlags & CPShiftKeyMask)
|
||||
cacheName.push(@"$");
|
||||
|
||||
cacheName.push(_key);
|
||||
|
||||
_cacheName = cacheName.join(@"");
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPString)key
|
||||
{
|
||||
return _key;
|
||||
}
|
||||
|
||||
- (unsigned)modifierFlags
|
||||
{
|
||||
return _modifierFlags;
|
||||
}
|
||||
|
||||
- (CPArray)selectors
|
||||
{
|
||||
return _selectors;
|
||||
}
|
||||
|
||||
- (CPString)_cacheName
|
||||
{
|
||||
return _cacheName;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(CPKeyBinding)rhs
|
||||
{
|
||||
return _key === [rhs key] && _modifierFlags === [rhs modifierFlags];
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [CPString stringWithFormat:@"<KeyBinding string: '%@' modifierFlags: 0x%lx selectors: %@>", _key, _modifierFlags, _selectors];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* CPKeyValueBinding.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Ross Boucher 1/13/09
|
||||
* Copyright 280 North, Inc.
|
||||
*
|
||||
* Adapted from GNUStep
|
||||
* Released under the LGPL.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPArray.j>
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPValueTransformer.j>
|
||||
|
||||
|
||||
var exposedBindingsMap = [CPDictionary new],
|
||||
bindingsMap = [CPDictionary new];
|
||||
|
||||
var CPBindingOperationAnd = 0,
|
||||
CPBindingOperationOr = 1;
|
||||
|
||||
@implementation CPBinder : CPObject
|
||||
{
|
||||
CPDictionary _info;
|
||||
id _source;
|
||||
}
|
||||
|
||||
+ (void)exposeBinding:(CPString)aBinding forClass:(Class)aClass
|
||||
{
|
||||
var bindings = [exposedBindingsMap objectForKey:[aClass UID]];
|
||||
|
||||
if (!bindings)
|
||||
{
|
||||
bindings = [];
|
||||
[exposedBindingsMap setObject:bindings forKey:[aClass UID]];
|
||||
}
|
||||
|
||||
bindings.push(aBinding);
|
||||
}
|
||||
|
||||
+ (CPArray)exposedBindingsForClass:(Class)aClass
|
||||
{
|
||||
return [[exposedBindingsMap objectForKey:[aClass UID]] copy];
|
||||
}
|
||||
|
||||
+ (CPBinder)getBinding:(CPString)aBinding forObject:(id)anObject
|
||||
{
|
||||
return [[bindingsMap objectForKey:[anObject UID]] objectForKey:aBinding];
|
||||
}
|
||||
|
||||
+ (CPDictionary)infoForBinding:(CPString)aBinding forObject:(id)anObject
|
||||
{
|
||||
var theBinding = [self getBinding:aBinding forObject:anObject];
|
||||
|
||||
if (theBinding)
|
||||
return theBinding._info;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (CPDictionary)allBindingsForObject:(id)anObject
|
||||
{
|
||||
return [bindingsMap objectForKey:[anObject UID]];
|
||||
}
|
||||
|
||||
+ (void)unbind:(CPString)aBinding forObject:(id)anObject
|
||||
{
|
||||
var bindings = [bindingsMap objectForKey:[anObject UID]];
|
||||
|
||||
if (!bindings)
|
||||
return;
|
||||
|
||||
var theBinding = [bindings objectForKey:aBinding];
|
||||
|
||||
if (!theBinding)
|
||||
return;
|
||||
|
||||
var infoDictionary = theBinding._info,
|
||||
observedObject = [infoDictionary objectForKey:CPObservedObjectKey],
|
||||
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey];
|
||||
|
||||
[observedObject removeObserver:theBinding forKeyPath:keyPath];
|
||||
[bindings removeObjectForKey:aBinding];
|
||||
}
|
||||
|
||||
+ (void)unbindAllForObject:(id)anObject
|
||||
{
|
||||
var bindings = [bindingsMap objectForKey:[anObject UID]];
|
||||
if (!bindings)
|
||||
return;
|
||||
|
||||
var allKeys = [bindings allKeys],
|
||||
count = allKeys.length;
|
||||
|
||||
while (count--)
|
||||
[anObject unbind:[bindings objectForKey:allKeys[count]]];
|
||||
|
||||
[bindingsMap removeObjectForKey:[anObject UID]];
|
||||
}
|
||||
|
||||
- (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_source = aSource;
|
||||
_info = [CPDictionary dictionaryWithObjects:[aDestination, aKeyPath] forKeys:[CPObservedObjectKey, CPObservedKeyPathKey]];
|
||||
|
||||
if (options)
|
||||
[_info setObject:options forKey:CPOptionsKey];
|
||||
|
||||
[aDestination addObserver:self forKeyPath:aKeyPath options:CPKeyValueObservingOptionNew context:aBinding];
|
||||
|
||||
var bindings = [bindingsMap objectForKey:[_source UID]];
|
||||
if (!bindings)
|
||||
{
|
||||
bindings = [CPDictionary new];
|
||||
[bindingsMap setObject:bindings forKey:[_source UID]];
|
||||
}
|
||||
|
||||
[bindings setObject:self forKey:aName];
|
||||
[self setValueFor:aBinding];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)aBinding
|
||||
{
|
||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
||||
options = [_info objectForKey:CPOptionsKey],
|
||||
newValue = [destination valueForKeyPath:keyPath];
|
||||
|
||||
newValue = [self transformValue:newValue withOptions:options];
|
||||
[_source setValue:newValue forKey:aBinding];
|
||||
}
|
||||
|
||||
- (void)reverseSetValueFor:(CPString)aBinding
|
||||
{
|
||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
||||
options = [_info objectForKey:CPOptionsKey],
|
||||
newValue = [_source valueForKeyPath:aBinding];
|
||||
|
||||
newValue = [self reverseTransformValue:newValue withOptions:options];
|
||||
[destination setValue:newValue forKeyPath:keyPath];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
||||
{
|
||||
if (!changes)
|
||||
return;
|
||||
|
||||
[self setValueFor:context];
|
||||
}
|
||||
|
||||
- (id)transformValue:(id)aValue withOptions:(CPDictionary)options
|
||||
{
|
||||
var valueTransformerName,
|
||||
valueTransformer,
|
||||
placeholder;
|
||||
|
||||
var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
|
||||
valueTransformer;
|
||||
|
||||
if (valueTransformerName)
|
||||
{
|
||||
valueTransformer = [CPValueTransformer valueTransformerForName:valueTransformerName];
|
||||
|
||||
if (!valueTransformer)
|
||||
{
|
||||
var valueTransformerClass = CPClassFromString(valueTransformerName);
|
||||
if (valueTransformerClass)
|
||||
{
|
||||
valueTransformer = [[valueTransformerClass alloc] init];
|
||||
[valueTransformerClass setValueTransformer:valueTransformer forName:valueTransformerName];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
valueTransformer = [options objectForKey:CPValueTransformerBindingOption];
|
||||
|
||||
if (valueTransformer)
|
||||
aValue = [valueTransformer transformedValue:aValue];
|
||||
|
||||
|
||||
if (aValue === undefined || aValue === nil || aValue === [CPNull null])
|
||||
aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil;
|
||||
|
||||
return aValue;
|
||||
}
|
||||
|
||||
- (id)reverseTransformValue:(id)aValue withOptions: (CPDictionary)options
|
||||
{
|
||||
var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
|
||||
valueTransformer;
|
||||
|
||||
if (valueTransformerName)
|
||||
valueTransformer = [CPValueTransformer valueTransformerForName:valueTransformerName];
|
||||
else
|
||||
valueTransformer = [options objectForKey:CPValueTransformerBindingOption];
|
||||
|
||||
if (valueTransformer && [[valueTransformer class] allowsReverseTransformation])
|
||||
aValue = [valueTransformer reverseTransformedValue:aValue];
|
||||
|
||||
return aValue;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPObject (KeyValueBindingCreation)
|
||||
|
||||
+ (void)exposeBinding:(CPString)aBinding
|
||||
{
|
||||
[CPBinder exposeBinding:aBinding forClass:[self class]];
|
||||
}
|
||||
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
{
|
||||
return [CPBinder class];
|
||||
}
|
||||
|
||||
- (CPArray)exposedBindings
|
||||
{
|
||||
var exposedBindings = [],
|
||||
theClass = [self class];
|
||||
|
||||
while (theClass)
|
||||
{
|
||||
var temp = [CPBinder exposedBindingsForClass:theClass];
|
||||
|
||||
if (temp)
|
||||
[exposedBindings addObjectsFromArray:temp];
|
||||
|
||||
theClass = [theClass superclass];
|
||||
}
|
||||
|
||||
return exposedBindings;
|
||||
}
|
||||
|
||||
- (Class)valueClassForBinding:(CPString)binding
|
||||
{
|
||||
return [CPString class];
|
||||
}
|
||||
|
||||
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
|
||||
{
|
||||
if (!anObject || !aKeyPath)
|
||||
return CPLog.error("Invalid object or path on "+self+" for "+aBinding);
|
||||
|
||||
//if (![[self exposedBindings] containsObject:aBinding])
|
||||
// CPLog.warn("No binding exposed on "+self+" for "+aBinding);
|
||||
|
||||
var binderClass = [[self class] _binderClassForBinding:aBinding];
|
||||
|
||||
[self unbind:aBinding];
|
||||
[[binderClass alloc] initWithBinding:[self _replacementKeyPathForBinding:aBinding] name:aBinding to:anObject keyPath:aKeyPath options:options from:self];
|
||||
}
|
||||
|
||||
- (CPDictionary)infoForBinding:(CPString)aBinding
|
||||
{
|
||||
return [CPBinder infoForBinding:aBinding forObject:self];
|
||||
}
|
||||
|
||||
- (void)unbind:(CPString)aBinding
|
||||
{
|
||||
var binderClass = [[self class] _binderClassForBinding:aBinding];
|
||||
[binderClass unbind:aBinding forObject:self];
|
||||
}
|
||||
|
||||
- (id)_replacementKeyPathForBinding:(CPString)binding
|
||||
{
|
||||
return binding;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
Provides stub implementations that simply call super for the "objectValue" binding
|
||||
This class should not be necessary but assures backwards compliance with our old way of doing bindings
|
||||
Every class with a value binding should implement a subclass to handle it's specific value binding logic
|
||||
*/
|
||||
@implementation _CPValueBinder : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)theBinding
|
||||
{
|
||||
[super setValueFor:@"objectValue"];
|
||||
}
|
||||
|
||||
- (void)reverseSetValueFor:(CPString)theBinding
|
||||
{
|
||||
[super reverseSetValueFor:@"objectValue"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPKeyValueOrBinding : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)aBinding
|
||||
{
|
||||
var bindings = [bindingsMap valueForKey:[_source UID]];
|
||||
|
||||
if (!bindings)
|
||||
return;
|
||||
|
||||
[_source setValue:resolveMultipleValues(aBinding, bindings, CPBindingOperationOr) forKey:aBinding];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
|
||||
{
|
||||
[self setValueFor:context];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPKeyValueAndBinding : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)aBinding
|
||||
{
|
||||
var bindings = [bindingsMap objectForKey:[_source UID]];
|
||||
|
||||
if (!bindings)
|
||||
return;
|
||||
|
||||
[_source setValue:resolveMultipleValues(aBinding, bindings, CPBindingOperationAnd) forKey:aBinding];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObejct change:(CPDictionary)changes context:(id)context
|
||||
{
|
||||
[self setValueFor:context];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var resolveMultipleValues = function resolveMultipleValues(/*CPString*/key, /*CPDictionary*/bindings, /*GSBindingOperationKind*/operation)
|
||||
{
|
||||
var bindingName = key,
|
||||
theBinding,
|
||||
count = 1;
|
||||
|
||||
while (theBinding = [bindings objectForKey:bindingName])
|
||||
{
|
||||
var infoDictionary = theBinding._info,
|
||||
object = [infoDictionary objectForKey:CPObservedObjectKey],
|
||||
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey],
|
||||
options = [infoDictionary objectForKey:CPOptionsKey];
|
||||
|
||||
var value = [theBinding transformValue:[object valueForKeyPath:keyPath] withOptions:options];
|
||||
|
||||
if (value == operation)
|
||||
return operation;
|
||||
|
||||
bindingName = [CPString stringWithFormat:@"%@%i", key, ++count];
|
||||
}
|
||||
|
||||
return !operation;
|
||||
}
|
||||
|
||||
var invokeAction = function invokeAction(/*CPString*/targetKey, /*CPString*/argumentKey, /*CPDictionary*/bindings)
|
||||
{
|
||||
var theBinding = [bindings objectForKey:targetKey],
|
||||
infoDictionary = theBinding._info,
|
||||
|
||||
object = [infoDictionary objectForKey:CPObservedObjectKey],
|
||||
keyPath = [infoDictionary objectForKey:CPObservedKeyPathKey],
|
||||
options = [infoDictionary objectForKey:CPOptionsKey],
|
||||
|
||||
target = [object valueForKeyPath:keyPath],
|
||||
selector = [options objectForKey:CPSelectorNameBindingOption];
|
||||
|
||||
if (!target || !selector)
|
||||
return;
|
||||
|
||||
var invocation = [CPInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]];
|
||||
[invocation setSelector:selector];
|
||||
|
||||
var bindingName = argumentKey,
|
||||
count = 1;
|
||||
|
||||
while (theBinding = [bindings objectForKey:bindingName])
|
||||
{
|
||||
infoDictionary = theBinding._info;
|
||||
|
||||
keyPath = [infoDictionary objectForKey:CPObserverKeyPathKey];
|
||||
object = [[infoDictionary objectForKey:CPObservedObjectKey] valueForKeyPath:keyPath];
|
||||
|
||||
if (object)
|
||||
[invocation setArgument:object atIndex:++count];
|
||||
|
||||
bindingName = [CPString stringWithFormat:@"%@%i", argumentKey, count];
|
||||
}
|
||||
|
||||
[invocation invoke];
|
||||
}
|
||||
|
||||
// Keys in options dictionary
|
||||
|
||||
// Keys in dictionary returned by infoForBinding
|
||||
CPObservedObjectKey = @"CPObservedObjectKey";
|
||||
CPObservedKeyPathKey = @"CPObservedKeyPathKey";
|
||||
CPOptionsKey = @"CPOptionsKey";
|
||||
|
||||
// special markers
|
||||
CPMultipleValuesMarker = @"CPMultipleValuesMarker";
|
||||
CPNoSelectionMarker = @"CPNoSelectionMarker";
|
||||
CPNotApplicableMarker = @"CPNotApplicableMarker";
|
||||
|
||||
// Binding name constants
|
||||
CPAlignmentBinding = @"CPAlignmentBinding";
|
||||
CPEditableBinding = @"CPEditableBinding";
|
||||
CPEnabledBinding = @"CPEnabledBinding";
|
||||
CPFontBinding = @"CPFontBinding";
|
||||
CPHiddenBinding = @"CPHiddenBinding";
|
||||
CPSelectedIndexBinding = @"CPSelectedIndexBinding";
|
||||
CPTextColorBinding = @"CPTextColorBinding";
|
||||
CPToolTipBinding = @"CPToolTipBinding";
|
||||
CPValueBinding = @"value";
|
||||
|
||||
//Binding options constants
|
||||
CPAllowsEditingMultipleValuesSelectionBindingOption = @"CPAllowsEditingMultipleValuesSelection";
|
||||
CPAllowsNullArgumentBindingOption = @"CPAllowsNullArgument";
|
||||
CPConditionallySetsEditableBindingOption = @"CPConditionallySetsEditable";
|
||||
CPConditionallySetsEnabledBindingOption = @"CPConditionallySetsEnabled";
|
||||
CPConditionallySetsHiddenBindingOption = @"CPConditionallySetsHidden";
|
||||
CPContinuouslyUpdatesValueBindingOption = @"CPContinuouslyUpdatesValue";
|
||||
CPCreatesSortDescriptorBindingOption = @"CPCreatesSortDescriptor";
|
||||
CPDeletesObjectsOnRemoveBindingsOption = @"CPDeletesObjectsOnRemove";
|
||||
CPDisplayNameBindingOption = @"CPDisplayName";
|
||||
CPDisplayPatternBindingOption = @"CPDisplayPattern";
|
||||
CPHandlesContentAsCompoundValueBindingOption = @"CPHandlesContentAsCompoundValue";
|
||||
CPInsertsNullPlaceholderBindingOption = @"CPInsertsNullPlaceholder";
|
||||
CPInvokesSeparatelyWithArrayObjectsBindingOption = @"CPInvokesSeparatelyWithArrayObjects";
|
||||
CPMultipleValuesPlaceholderBindingOption = @"CPMultipleValuesPlaceholder";
|
||||
CPNoSelectionPlaceholderBindingOption = @"CPNoSelectionPlaceholder";
|
||||
CPNotApplicablePlaceholderBindingOption = @"CPNotApplicablePlaceholder";
|
||||
CPNullPlaceholderBindingOption = @"CPNullPlaceholder";
|
||||
CPPredicateFormatBindingOption = @"CPPredicateFormat";
|
||||
CPRaisesForNotApplicableKeysBindingOption = @"CPRaisesForNotApplicableKeys";
|
||||
CPSelectorNameBindingOption = @"CPSelectorName";
|
||||
CPSelectsAllWhenSettingContentBindingOption = @"CPSelectsAllWhenSettingContent";
|
||||
CPValidatesImmediatelyBindingOption = @"CPValidatesImmediately";
|
||||
CPValueTransformerNameBindingOption = @"CPValueTransformerName";
|
||||
CPValueTransformerBindingOption = @"CPValueTransformer";
|
||||
|
||||
CPIsControllerMarker = function(/*id*/anObject)
|
||||
{
|
||||
return anObject === CPMultipleValuesMarker || anObject === CPNoSelectionMarker || anObject === CPNotApplicableMarker;
|
||||
}
|
||||
-2014
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,553 @@
|
||||
@import "CPPanel.j"
|
||||
@import "_CPMenuWindow.j"
|
||||
|
||||
|
||||
var MENUBAR_HEIGHT = 28.0,
|
||||
MENUBAR_MARGIN = 10.0,
|
||||
MENUBAR_LEFT_MARGIN = 10.0,
|
||||
MENUBAR_RIGHT_MARGIN = 10.0;
|
||||
|
||||
var _CPMenuBarWindowBackgroundColor = nil,
|
||||
_CPMenuBarWindowFont = nil;
|
||||
|
||||
@implementation _CPMenuBarWindow : CPPanel
|
||||
{
|
||||
CPMenu _menu;
|
||||
CPView _highlightView;
|
||||
CPArray _menuItemViews;
|
||||
|
||||
CPMenuItem _trackingMenuItem;
|
||||
|
||||
CPImageView _iconImageView;
|
||||
CPTextField _titleField;
|
||||
|
||||
CPColor _textColor;
|
||||
CPColor _titleColor;
|
||||
|
||||
CPColor _textShadowColor;
|
||||
CPColor _titleShadowColor;
|
||||
|
||||
CPColor _highlightColor;
|
||||
CPColor _highlightTextColor;
|
||||
CPColor _highlightTextShadowColor;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPMenuBarWindow class])
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPMenuBarWindowFont = [CPFont boldSystemFontOfSize:12.0];
|
||||
}
|
||||
|
||||
+ (CPFont)font
|
||||
{
|
||||
return _CPMenuBarWindowFont;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
// This only shows up in browser land, so don't bother calculating metrics in desktop.
|
||||
var contentRect = [[CPPlatformWindow primaryPlatformWindow] contentBounds];
|
||||
|
||||
contentRect.size.height = MENUBAR_HEIGHT;
|
||||
|
||||
self = [super initWithContentRect:contentRect styleMask:CPBorderlessWindowMask];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setLevel:CPMainMenuWindowLevel];
|
||||
[self setAutoresizingMask:CPWindowWidthSizable];
|
||||
|
||||
var contentView = [self contentView];
|
||||
|
||||
[contentView setAutoresizesSubviews:NO];
|
||||
|
||||
[self setBecomesKeyOnlyIfNeeded:YES];
|
||||
|
||||
//
|
||||
_iconImageView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 16.0, 16.0)];
|
||||
|
||||
[contentView addSubview:_iconImageView];
|
||||
|
||||
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_titleField setFont:[CPFont boldSystemFontOfSize:13.0]];
|
||||
[_titleField setAlignment:CPCenterTextAlignment];
|
||||
[_titleField setTextShadowOffset:CGSizeMake(0, 1)];
|
||||
|
||||
[contentView addSubview:_titleField];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var bundleName = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPBundleName"];
|
||||
|
||||
if (![bundleName length])
|
||||
document.title = aTitle;
|
||||
else if ([aTitle length])
|
||||
document.title = aTitle + @" - " + bundleName;
|
||||
else
|
||||
document.title = bundleName;
|
||||
#endif
|
||||
|
||||
[_titleField setStringValue:aTitle];
|
||||
[_titleField sizeToFit];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)setIconImage:(CPImage)anImage
|
||||
{
|
||||
[_iconImageView setImage:anImage];
|
||||
[_iconImageView setHidden:anImage == nil];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)setIconImageAlphaValue:(float)anAlphaValue
|
||||
{
|
||||
[_iconImageView setAlphaValue:anAlphaValue];
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
if (!aColor)
|
||||
{
|
||||
if (!_CPMenuBarWindowBackgroundColor)
|
||||
_CPMenuBarWindowBackgroundColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[_CPMenuBarWindow class]] pathForResource:@"_CPMenuBarWindow/_CPMenuBarWindowBackground.png"] size:CGSizeMake(1.0, 28.0)]];
|
||||
|
||||
[[self contentView] setBackgroundColor:_CPMenuBarWindowBackgroundColor];
|
||||
}
|
||||
else
|
||||
[[self contentView] setBackgroundColor:aColor];
|
||||
}
|
||||
|
||||
- (void)setTextColor:(CPColor)aColor
|
||||
{
|
||||
if (_textColor == aColor)
|
||||
return;
|
||||
|
||||
_textColor = aColor;
|
||||
|
||||
[_menuItemViews makeObjectsPerformSelector:@selector(setTextColor:) withObject:_textColor];
|
||||
}
|
||||
|
||||
- (void)setTitleColor:(CPColor)aColor
|
||||
{
|
||||
if (_titleColor == aColor)
|
||||
return;
|
||||
|
||||
_titleColor = aColor;
|
||||
|
||||
[_titleField setTextColor:aColor ? aColor : [CPColor blackColor]];
|
||||
}
|
||||
|
||||
- (void)setTextShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_textShadowColor == aColor)
|
||||
return;
|
||||
|
||||
_textShadowColor = aColor;
|
||||
|
||||
[_menuItemViews makeObjectsPerformSelector:@selector(setTextShadowColor:) withObject:_textShadowColor];
|
||||
}
|
||||
|
||||
- (void)setTitleShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_titleShadowColor == aColor)
|
||||
return;
|
||||
|
||||
_titleShadowColor = aColor;
|
||||
|
||||
[_titleField setTextShadowColor:aColor ? aColor : [CPColor whiteColor]];
|
||||
}
|
||||
|
||||
- (void)setHighlightColor:(CPColor)aColor
|
||||
{
|
||||
if (_highlightColor == aColor)
|
||||
return;
|
||||
|
||||
_highlightColor = aColor;
|
||||
}
|
||||
|
||||
- (void)setHighlightTextColor:(CPColor)aColor
|
||||
{
|
||||
if (_highlightTextColor == aColor)
|
||||
return;
|
||||
|
||||
_highlightTextColor = aColor;
|
||||
|
||||
// [_menuItemViews makeObjectsPerformSelector:@selector(setActivateColor:) withObject:_highlightTextColor];
|
||||
}
|
||||
|
||||
- (void)setHighlightTextShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_highlightTextShadowColor == aColor)
|
||||
return;
|
||||
|
||||
_highlightTextShadowColor = aColor;
|
||||
|
||||
// [_menuItemViews makeObjectsPerformSelector:@selector(setActivateShadowColor:) withObject:_highlightTextShadowColor];
|
||||
}
|
||||
|
||||
- (void)setMenu:(CPMenu)aMenu
|
||||
{
|
||||
if (_menu == aMenu)
|
||||
return;
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
if (_menu)
|
||||
{
|
||||
[defaultCenter
|
||||
removeObserver:self
|
||||
name:CPMenuDidAddItemNotification
|
||||
object:_menu];
|
||||
|
||||
[defaultCenter
|
||||
removeObserver:self
|
||||
name:CPMenuDidChangeItemNotification
|
||||
object:_menu];
|
||||
|
||||
[defaultCenter
|
||||
removeObserver:self
|
||||
name:CPMenuDidRemoveItemNotification
|
||||
object:_menu];
|
||||
|
||||
var items = [_menu itemArray],
|
||||
count = items.length;
|
||||
|
||||
while (count--)
|
||||
[[items[count] _menuItemView] removeFromSuperview];
|
||||
}
|
||||
|
||||
_menu = aMenu;
|
||||
|
||||
if (_menu)
|
||||
{
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(menuDidAddItem:)
|
||||
name:CPMenuDidAddItemNotification
|
||||
object:_menu];
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(menuDidChangeItem:)
|
||||
name:CPMenuDidChangeItemNotification
|
||||
object:_menu];
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(menuDidRemoveItem:)
|
||||
name:CPMenuDidRemoveItemNotification
|
||||
object:_menu];
|
||||
}
|
||||
|
||||
_menuItemViews = [];
|
||||
|
||||
var contentView = [self contentView],
|
||||
items = [_menu itemArray],
|
||||
count = items.length;
|
||||
|
||||
for (index = 0; index < count; ++index)
|
||||
{
|
||||
var item = items[index],
|
||||
menuItemView = [item _menuItemView];
|
||||
|
||||
_menuItemViews.push(menuItemView);
|
||||
|
||||
[menuItemView setTextColor:_textColor];
|
||||
[menuItemView setHidden:[item isHidden]];
|
||||
|
||||
[menuItemView synchronizeWithMenuItem];
|
||||
|
||||
[contentView addSubview:menuItemView];
|
||||
}
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)menuDidChangeItem:(CPNotification)aNotification
|
||||
{
|
||||
var menuItem = [_menu itemAtIndex:[[aNotification userInfo] objectForKey:@"CPMenuItemIndex"]],
|
||||
menuItemView = [menuItem _menuItemView];
|
||||
|
||||
[menuItemView setHidden:[menuItem isHidden]];
|
||||
[menuItemView synchronizeWithMenuItem];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)menuDidAddItem:(CPNotification)aNotification
|
||||
{
|
||||
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"],
|
||||
menuItem = [_menu itemAtIndex:index],
|
||||
menuItemView = [menuItem _menuItemView];
|
||||
|
||||
[_menuItemViews insertObject:menuItemView atIndex:index];
|
||||
|
||||
[menuItemView setTextColor:_textColor];
|
||||
[menuItemView setHidden:[menuItem isHidden]];
|
||||
|
||||
[menuItemView synchronizeWithMenuItem];
|
||||
|
||||
[[self contentView] addSubview:menuItemView];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)menuDidRemoveItem:(CPNotification)aNotification
|
||||
{
|
||||
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"],
|
||||
menuItemView = [_menuItemViews objectAtIndex:index];
|
||||
|
||||
[_menuItemViews removeObjectAtIndex:index];
|
||||
|
||||
[menuItemView removeFromSuperview];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
var constraintRect = CGRectInset([[self platformWindow] visibleFrame], 5.0, 0.0);
|
||||
|
||||
constraintRect.size.height -= 5.0;
|
||||
|
||||
[[_CPMenuManager sharedMenuManager]
|
||||
beginTracking:anEvent
|
||||
menuContainer:self
|
||||
constraintRect:constraintRect
|
||||
callback:function(aMenuContainer, aMenu)
|
||||
{
|
||||
[aMenu _performActionOfHighlightedItemChain];
|
||||
[aMenu _highlightItemAtIndex:CPNotFound];
|
||||
}];
|
||||
}
|
||||
|
||||
- (CPFont)font
|
||||
{
|
||||
[CPFont systemFontOfSize:12.0];
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
{
|
||||
var items = [_menu itemArray],
|
||||
index = 0,
|
||||
count = items.length,
|
||||
|
||||
x = MENUBAR_LEFT_MARGIN,
|
||||
y = 0.0,
|
||||
isLeftAligned = YES;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var item = items[index];
|
||||
|
||||
if ([item isSeparatorItem])
|
||||
{
|
||||
x = CGRectGetWidth([self frame]) - MENUBAR_RIGHT_MARGIN;
|
||||
isLeftAligned = NO;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([item isHidden])
|
||||
continue;
|
||||
|
||||
var menuItemView = [item _menuItemView],
|
||||
frame = [menuItemView frame];
|
||||
|
||||
if (isLeftAligned)
|
||||
{
|
||||
[menuItemView setFrame:CGRectMake(x, 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)];
|
||||
|
||||
x += CGRectGetWidth([menuItemView frame]);
|
||||
}
|
||||
else
|
||||
{
|
||||
[menuItemView setFrame:CGRectMake(x - CGRectGetWidth(frame), 0.0, CGRectGetWidth(frame), MENUBAR_HEIGHT)];
|
||||
|
||||
x = CGRectGetMinX([menuItemView frame]);
|
||||
}
|
||||
}
|
||||
|
||||
var bounds = [[self contentView] bounds],
|
||||
titleFrame = [_titleField frame];
|
||||
|
||||
if ([_iconImageView isHidden])
|
||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(titleFrame)) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
||||
else
|
||||
{
|
||||
var iconFrame = [_iconImageView frame],
|
||||
iconWidth = CGRectGetWidth(iconFrame),
|
||||
totalWidth = iconWidth + CGRectGetWidth(titleFrame);
|
||||
|
||||
[_iconImageView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(iconFrame)) / 2.0)];
|
||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0 + iconWidth, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)aRect display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
||||
{
|
||||
var size = [self frame].size;
|
||||
|
||||
[super setFrame:aRect display:shouldDisplay animate:shouldAnimate];
|
||||
|
||||
if (!_CGSizeEqualToSize(size, aRect.size))
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuBarWindow (CPMenuContainer)
|
||||
|
||||
- (BOOL)isMenuBar
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CGRect)globalFrame
|
||||
{
|
||||
return [self frame];
|
||||
}
|
||||
|
||||
- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation
|
||||
{
|
||||
return _CPMenuManagerScrollingStateNone;
|
||||
}
|
||||
|
||||
- (CPInteger)itemIndexAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var items = [_menu itemArray],
|
||||
index = items.length;
|
||||
|
||||
while (index--)
|
||||
{
|
||||
var item = items[index];
|
||||
|
||||
if ([item isHidden] || [item isSeparatorItem])
|
||||
continue;
|
||||
|
||||
if (CGRectContainsPoint([self rectForItemAtIndex:index], aPoint))
|
||||
return index;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (CGRect)rectForItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
var menuItem = [_menu itemAtIndex:anIndex === CPNotFound ? 0 : anIndex];
|
||||
|
||||
return [[menuItem _menuItemView] frame];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuBarView : _CPMenuView
|
||||
{
|
||||
}
|
||||
|
||||
- (CGRect)rectForItemAtIndex:(int)anIndex
|
||||
{
|
||||
return [_menuItemViews[anIndex === CPNotFound ? 0 : anIndex] frame];
|
||||
}
|
||||
|
||||
- (int)itemIndexAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var bounds = [self bounds];
|
||||
|
||||
if (!CGRectContainsPoint(bounds, aPoint))
|
||||
return CPNotFound;
|
||||
|
||||
var x = aPoint.x,
|
||||
low = 0,
|
||||
high = _visibleMenuItemInfos.length - 1;
|
||||
|
||||
while (low <= high)
|
||||
{
|
||||
var middle = FLOOR(low + (high - low) / 2),
|
||||
info = _visibleMenuItemInfos[middle],
|
||||
frame = [info.view frame];
|
||||
|
||||
if (x < CGRectGetMinX(frame))
|
||||
high = middle - 1;
|
||||
|
||||
else if (x > CGRectGetMaxX(frame))
|
||||
low = middle + 1;
|
||||
|
||||
else
|
||||
return info.index;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
{
|
||||
var items = [_menu itemArray],
|
||||
index = 0,
|
||||
count = items.length,
|
||||
|
||||
x = MENUBAR_LEFT_MARGIN,
|
||||
y = 0.0,
|
||||
isLeftAligned = YES;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var item = items[index];
|
||||
|
||||
if ([item isSeparatorItem])
|
||||
{
|
||||
x = CGRectGetWidth([self frame]) - MENUBAR_RIGHT_MARGIN;
|
||||
isLeftAligned = NO;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([item isHidden])
|
||||
continue;
|
||||
|
||||
var menuItemView = [item _menuItemView],
|
||||
frame = [menuItemView frame];
|
||||
|
||||
if (isLeftAligned)
|
||||
{
|
||||
[menuItemView setFrameOrigin:CGPointMake(x, (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)];
|
||||
|
||||
x += CGRectGetWidth([menuItemView frame]) + MENUBAR_MARGIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
[menuItemView setFrameOrigin:CGPointMake(x - CGRectGetWidth(frame), (MENUBAR_HEIGHT - 1.0 - CGRectGetHeight(frame)) / 2.0)];
|
||||
|
||||
x = CGRectGetMinX([menuItemView frame]) - MENUBAR_MARGIN;
|
||||
}
|
||||
}
|
||||
|
||||
var bounds = [[self contentView] bounds],
|
||||
titleFrame = [_titleField frame];
|
||||
|
||||
if ([_iconImageView isHidden])
|
||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(titleFrame)) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
||||
else
|
||||
{
|
||||
var iconFrame = [_iconImageView frame],
|
||||
iconWidth = CGRectGetWidth(iconFrame),
|
||||
totalWidth = iconWidth + CGRectGetWidth(titleFrame);
|
||||
|
||||
[_iconImageView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0, (CGRectGetHeight(bounds) - CGRectGetHeight(iconFrame)) / 2.0)];
|
||||
[_titleField setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - totalWidth) / 2.0 + iconWidth, (CGRectGetHeight(bounds) - CGRectGetHeight(titleFrame)) / 2.0)];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,367 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
_CPMenuManagerScrollingStateUp = -1,
|
||||
_CPMenuManagerScrollingStateDown = 1,
|
||||
_CPMenuManagerScrollingStateNone = 0;
|
||||
|
||||
var STICKY_TIME_INTERVAL = 500;
|
||||
|
||||
var SharedMenuManager = nil;
|
||||
|
||||
@implementation _CPMenuManager: CPObject
|
||||
{
|
||||
CPTimeInterval _startTime;
|
||||
int _scrollingState;
|
||||
CGPoint _lastGlobalLocation;
|
||||
|
||||
CPView _lastMouseOverMenuView;
|
||||
|
||||
CGRect _constraintRect;
|
||||
CPMutableArray _menuContainerStack;
|
||||
|
||||
Function _trackingCallback;
|
||||
}
|
||||
|
||||
+ (_CPMenuManager)sharedMenuManager
|
||||
{
|
||||
if (!SharedMenuManager)
|
||||
SharedMenuManager = [[_CPMenuManager alloc] init];
|
||||
|
||||
return SharedMenuManager;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (SharedMenuManager)
|
||||
return SharedMenuManager;
|
||||
|
||||
return [super init];
|
||||
}
|
||||
|
||||
- (id)trackingMenuContainer
|
||||
{
|
||||
return _menuContainerStack[0];
|
||||
}
|
||||
|
||||
- (CPMenu)trackingMenu
|
||||
{
|
||||
return [[self trackingMenuContainer] menu];
|
||||
}
|
||||
|
||||
- (void)beginTracking:(CPEvent)anEvent
|
||||
menuContainer:(id)aMenuContainer
|
||||
constraintRect:(CGRect)aRect
|
||||
callback:(Function)aCallback
|
||||
{
|
||||
var menu = [aMenuContainer menu];
|
||||
|
||||
CPApp._activeMenu = menu;
|
||||
|
||||
_startTime = [anEvent timestamp];//new Date();
|
||||
_scrollingState = _CPMenuManagerScrollingStateNone;
|
||||
|
||||
_constraintRect = aRect;
|
||||
_menuContainerStack = [aMenuContainer];
|
||||
_trackingCallback = aCallback;
|
||||
|
||||
if (menu === [CPApp mainMenu])
|
||||
{
|
||||
var globalLocation = [anEvent globalLocation];
|
||||
|
||||
// Find which menu window the mouse is currently on top of
|
||||
var menuLocation = [aMenuContainer convertGlobalToBase:globalLocation];
|
||||
|
||||
// Find out the item the mouse is currently on top of
|
||||
var activeItemIndex = [aMenuContainer itemIndexAtPoint:menuLocation],
|
||||
activeItem = activeItemIndex !== CPNotFound ? [menu itemAtIndex:activeItemIndex] : nil;
|
||||
|
||||
_menuBarButtonItemIndex = activeItemIndex;
|
||||
_menuBarButtonMenuContainer = aMenuContainer;
|
||||
|
||||
if ([activeItem _isMenuBarButton])
|
||||
return [self trackMenuBarButtonEvent:anEvent];
|
||||
}
|
||||
|
||||
[self trackEvent:anEvent];
|
||||
}
|
||||
|
||||
- (void)trackEvent:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type],
|
||||
trackingMenu = [self trackingMenu];
|
||||
|
||||
// Close Menu Event.
|
||||
if (type === CPAppKitDefined)
|
||||
return [self completeTracking];
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES];
|
||||
|
||||
// Periodic events don't have a valid location.
|
||||
var globalLocation = type === CPPeriodic ? _lastGlobalLocation : [anEvent globalLocation];
|
||||
|
||||
// Remember this for the next periodic event.
|
||||
_lastGlobalLocation = globalLocation;
|
||||
|
||||
// Find which menu window the mouse is currently on top of
|
||||
var activeMenuContainer = [self menuContainerForPoint:globalLocation],
|
||||
activeMenu = [activeMenuContainer menu],
|
||||
menuLocation = [activeMenuContainer convertGlobalToBase:globalLocation];
|
||||
|
||||
// Find out the item the mouse is currently on top of
|
||||
var activeItemIndex = activeMenuContainer ? [activeMenuContainer itemIndexAtPoint:menuLocation] : CPNotFound,
|
||||
activeItem = activeItemIndex !== CPNotFound ? [activeMenu itemAtIndex:activeItemIndex] : nil;
|
||||
|
||||
// If the item isn't enabled its as if we clicked on nothing.
|
||||
if (![activeItem isEnabled] || [activeItem _isMenuBarButton])
|
||||
{
|
||||
activeItemIndex = CPNotFound;
|
||||
activeItem = nil;
|
||||
}
|
||||
|
||||
var mouseOverMenuView = [activeItem view];
|
||||
|
||||
if (type === CPPeriodic)
|
||||
{
|
||||
if (_scrollingState === _CPMenuManagerScrollingStateUp)
|
||||
[activeMenuContainer scrollUp];
|
||||
|
||||
else if (_scrollingState === _CPMenuManagerScrollingStateDown)
|
||||
[activeMenuContainer scrollDown];
|
||||
}
|
||||
|
||||
// If we're over a custom menu view...
|
||||
if (mouseOverMenuView)
|
||||
{
|
||||
if (!_lastMouseOverMenuView)
|
||||
[activeMenu _highlightItemAtIndex:CPNotFound];
|
||||
|
||||
if (_lastMouseOverMenuView != mouseOverMenuView)
|
||||
{
|
||||
[mouseOverMenuView mouseExited:anEvent];
|
||||
// FIXME: Possibly multiple of these?
|
||||
[_lastMouseOverMenuView mouseEntered:anEvent];
|
||||
|
||||
_lastMouseOverMenuView = mouseOverMenuView;
|
||||
}
|
||||
|
||||
var menuContainerWindow = activeMenuContainer;
|
||||
|
||||
if (![menuContainerWindow isKindOfClass:[CPWindow class]])
|
||||
menuContainerWindow = [menuContainerWindow window];
|
||||
|
||||
[menuContainerWindow
|
||||
sendEvent:[CPEvent mouseEventWithType:type location:menuLocation modifierFlags:[anEvent modifierFlags]
|
||||
timestamp:[anEvent timestamp]
|
||||
windowNumber:menuContainerWindow
|
||||
context:nil
|
||||
eventNumber:0
|
||||
clickCount:[anEvent clickCount]
|
||||
pressure:[anEvent pressure]]];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_lastMouseOverMenuView)
|
||||
{
|
||||
[_lastMouseOverMenuView mouseExited:anEvent];
|
||||
_lastMouseOverMenuView = nil;
|
||||
}
|
||||
|
||||
[activeMenu _highlightItemAtIndex:activeItemIndex];
|
||||
|
||||
if (type === CPMouseMoved || type === CPLeftMouseDragged || type === CPLeftMouseDown)
|
||||
{
|
||||
var oldScrollingState = _scrollingState;
|
||||
|
||||
_scrollingState = [activeMenuContainer scrollingStateForPoint:globalLocation];
|
||||
|
||||
if (_scrollingState !== oldScrollingState)
|
||||
{
|
||||
if (_scrollingState === _CPMenuManagerScrollingStateNone)
|
||||
[CPEvent stopPeriodicEvents];
|
||||
|
||||
else if (oldScrollingState === _CPMenuManagerScrollingStateNone)
|
||||
[CPEvent startPeriodicEventsAfterDelay:0.0 withPeriod:0.04];
|
||||
}
|
||||
}
|
||||
else if (type === CPLeftMouseUp && ([anEvent timestamp] - _startTime > STICKY_TIME_INTERVAL))
|
||||
[trackingMenu cancelTracking];
|
||||
}
|
||||
|
||||
// If the item has a submenu, show it.
|
||||
if ([activeItem hasSubmenu])// && [activeItem action] === @selector(submenuAction:))
|
||||
{
|
||||
var activeItemRect = [activeMenuContainer rectForItemAtIndex:activeItemIndex];
|
||||
|
||||
if ([activeMenuContainer isMenuBar])
|
||||
var newMenuOrigin = CGPointMake(CGRectGetMinX(activeItemRect), CGRectGetMaxY(activeItemRect));
|
||||
else
|
||||
var newMenuOrigin = CGPointMake(CGRectGetMaxX(activeItemRect), CGRectGetMinY(activeItemRect));
|
||||
|
||||
newMenuOrigin = [activeMenuContainer convertBaseToGlobal:newMenuOrigin];
|
||||
|
||||
[self showMenu:[activeItem submenu] fromMenu:[activeItem menu] atPoint:newMenuOrigin];
|
||||
}
|
||||
|
||||
// This handles both the case where we've moved away from the menu, and where
|
||||
// we've moved to an item without a submenu.
|
||||
else
|
||||
[self showMenu:nil fromMenu:activeMenu atPoint:CGPointMakeZero()];
|
||||
}
|
||||
|
||||
- (void)trackMenuBarButtonEvent:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type];
|
||||
|
||||
if (type === CPAppKitDefined)
|
||||
return [self completeTracking];
|
||||
|
||||
var globalLocation = [anEvent globalLocation];
|
||||
|
||||
// Find which menu window the mouse is currently on top of
|
||||
var menu = [self trackingMenu],
|
||||
trackingMenuContainer = [self trackingMenuContainer],
|
||||
menuLocation = [trackingMenuContainer convertGlobalToBase:globalLocation];
|
||||
|
||||
if ([trackingMenuContainer itemIndexAtPoint:menuLocation] === _menuBarButtonItemIndex)
|
||||
[menu _highlightItemAtIndex:_menuBarButtonItemIndex];
|
||||
|
||||
else
|
||||
[menu _highlightItemAtIndex:CPNotFound];
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackMenuBarButtonEvent:) forNextEventMatchingMask:CPPeriodicMask | CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPAppKitDefinedMask untilDate:nil inMode:nil dequeue:YES];
|
||||
|
||||
if (type === CPLeftMouseUp)
|
||||
[menu cancelTracking];
|
||||
}
|
||||
|
||||
- (void)completeTracking
|
||||
{
|
||||
var trackingMenu = [self trackingMenu];
|
||||
|
||||
// Stop all periodic events at this point.
|
||||
[CPEvent stopPeriodicEvents];
|
||||
|
||||
// Get the highlighted item from the original menu.
|
||||
var highlightedItem = [trackingMenu highlightedItem];
|
||||
|
||||
// Hide all submenus.
|
||||
[self showMenu:nil fromMenu:trackingMenu atPoint:nil];
|
||||
|
||||
var delegate = [trackingMenu delegate];
|
||||
|
||||
if ([delegate respondsToSelector:@selector(menuDidClose:)])
|
||||
[delegate menuDidClose:trackingMenu];
|
||||
|
||||
if (_trackingCallback)
|
||||
_trackingCallback([self trackingMenuContainer], trackingMenu);
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPMenuDidEndTrackingNotification
|
||||
object:trackingMenu];
|
||||
|
||||
CPApp._activeMenu = nil;
|
||||
}
|
||||
|
||||
- (id)menuContainerForPoint:(float)aGlobalLocation
|
||||
{
|
||||
var count = [_menuContainerStack count],
|
||||
firstMenuContainer = _menuContainerStack[0];
|
||||
|
||||
// Trivial case.
|
||||
if (count === 1)
|
||||
return firstMenuContainer;
|
||||
|
||||
var firstMenuWindowIndex = 0,
|
||||
lastMenuWindowIndex = count - 1;
|
||||
|
||||
// If the first menu container is a bar, then it has to fall strictly within its bounds.
|
||||
if ([firstMenuContainer isMenuBar])
|
||||
{
|
||||
if (CGRectContainsPoint([firstMenuContainer globalFrame], aGlobalLocation))
|
||||
return firstMenuContainer;
|
||||
|
||||
firstMenuWindowIndex = 1;
|
||||
}
|
||||
|
||||
var index = count,
|
||||
x = aGlobalLocation.x,
|
||||
closerDeltaX = Infinity,
|
||||
closerMenuContainer = nil;
|
||||
|
||||
while (index-- > firstMenuWindowIndex)
|
||||
{
|
||||
var menuContainer = _menuContainerStack[index],
|
||||
menuContainerFrame = [menuContainer globalFrame],
|
||||
menuContainerMinX = _CGRectGetMinX(menuContainerFrame),
|
||||
menuContainerMaxX = _CGRectGetMaxX(menuContainerFrame);
|
||||
|
||||
// If within the x bounds of this menu container, return it.
|
||||
if (x < menuContainerMaxX && x >= menuContainerMinX)
|
||||
return menuContainer;
|
||||
|
||||
// If this is either the first or last menu *window*, check to see how close we are to it.
|
||||
if (index === firstMenuWindowIndex || index === lastMenuWindowIndex)
|
||||
{
|
||||
var deltaX = ABS(x < menuContainerMinX ? menuContainerMinX - x : menuContainerMaxX - x);
|
||||
|
||||
if (deltaX < closerDeltaX)
|
||||
{
|
||||
closerMenuContainer = menuContainer;
|
||||
closerDeltaX = deltaX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closerMenuContainer;
|
||||
}
|
||||
|
||||
- (void)showMenu:(CPMenu)newMenu fromMenu:(CPMenu)baseMenu atPoint:(CGPoint)aGlobalLocation
|
||||
{
|
||||
var count = _menuContainerStack.length,
|
||||
index = count;
|
||||
|
||||
// Hide all menus up to the base menu...
|
||||
while (index--)
|
||||
{
|
||||
var menuContainer = _menuContainerStack[index],
|
||||
menu = [menuContainer menu];
|
||||
|
||||
// If we reach the base menu, or this menu is already being shown, break.
|
||||
if (menu === baseMenu)
|
||||
break;
|
||||
|
||||
// If this menu is already being shown, unhighlight and return.
|
||||
if (menu === newMenu)//&& [menu supermenu] === baseMenu)
|
||||
return [newMenu _highlightItemAtIndex:CPNotFound];
|
||||
|
||||
[menuContainer orderOut:self];
|
||||
[menuContainer setMenu:nil];
|
||||
|
||||
[_CPMenuWindow poolMenuWindow:menuContainer];
|
||||
[_menuContainerStack removeObjectAtIndex:index];
|
||||
}
|
||||
|
||||
if (!newMenu)
|
||||
return;
|
||||
|
||||
// Unhighlight any previously highlighted item.
|
||||
[newMenu _highlightItemAtIndex:CPNotFound];
|
||||
|
||||
var menuWindow = [_CPMenuWindow menuWindowWithMenu:newMenu font:[_menuContainerStack[0] font]];
|
||||
|
||||
[_menuContainerStack addObject:menuWindow];
|
||||
|
||||
[menuWindow setConstraintRect:_constraintRect];
|
||||
|
||||
// If our parent menu is a menu bar...
|
||||
if (baseMenu === [self trackingMenu] && [[self trackingMenuContainer] isMenuBar])
|
||||
[menuWindow setBackgroundStyle:_CPMenuWindowMenuBarBackgroundStyle];
|
||||
else
|
||||
[menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
|
||||
|
||||
[menuWindow setFrameOrigin:aGlobalLocation];
|
||||
[menuWindow orderFront:self];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,547 @@
|
||||
@import "CPWindow.j"
|
||||
|
||||
|
||||
var _CPMenuWindowPool = [],
|
||||
_CPMenuWindowPoolCapacity = 5,
|
||||
|
||||
_CPMenuWindowBackgroundColors = [];
|
||||
|
||||
_CPMenuWindowMenuBarBackgroundStyle = 0;
|
||||
_CPMenuWindowPopUpBackgroundStyle = 1;
|
||||
_CPMenuWindowAttachedMenuBackgroundStyle = 2;
|
||||
|
||||
var STICKY_TIME_INTERVAL = 500,
|
||||
|
||||
TOP_MARGIN = 5.0,
|
||||
LEFT_MARGIN = 1.0,
|
||||
RIGHT_MARGIN = 1.0,
|
||||
BOTTOM_MARGIN = 5.0,
|
||||
|
||||
SCROLL_INDICATOR_HEIGHT = 16.0;
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
@implementation _CPMenuWindow : CPWindow
|
||||
{
|
||||
_CPMenuView _menuView;
|
||||
CPClipView _menuClipView;
|
||||
|
||||
CPImageView _moreAboveView;
|
||||
CPImageView _moreBelowView;
|
||||
|
||||
CGRect _unconstrainedFrame;
|
||||
CGRect _constraintRect;
|
||||
}
|
||||
|
||||
+ (id)menuWindowWithMenu:(CPMenu)aMenu font:(CPFont)aFont
|
||||
{
|
||||
var menuWindow = nil;
|
||||
|
||||
if (_CPMenuWindowPool.length)
|
||||
{
|
||||
menuWindow = _CPMenuWindowPool.pop();
|
||||
|
||||
// Do this so that coordinates will be accurate.
|
||||
[menuWindow setFrameOrigin:CGPointMakeZero()];
|
||||
}
|
||||
else
|
||||
menuWindow = [[_CPMenuWindow alloc] init];
|
||||
|
||||
[menuWindow setFont:aFont];
|
||||
[menuWindow setMenu:aMenu];
|
||||
[menuWindow setMinWidth:[aMenu minimumWidth]];
|
||||
|
||||
return menuWindow;
|
||||
}
|
||||
|
||||
+ (void)poolMenuWindow:(_CPMenuWindow)aMenuWindow
|
||||
{
|
||||
if (!aMenuWindow || _CPMenuWindowPool.length >= _CPMenuWindowPoolCapacity)
|
||||
return;
|
||||
|
||||
_CPMenuWindowPool.push(aMenuWindow);
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPMenuWindow class])
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPMenuWindowMoreAboveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowMoreAbove.png"] size:CGSizeMake(38.0, 18.0)];
|
||||
_CPMenuWindowMoreBelowImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowMoreBelow.png"] size:CGSizeMake(38.0, 18.0)];
|
||||
}
|
||||
|
||||
- (id)initWithContentRect:(CGRect)aRect styleMask:(unsigned)aStyleMask
|
||||
{
|
||||
_constraintRect = _CGRectMakeZero();
|
||||
_unconstrainedFrame = _CGRectMakeZero();
|
||||
|
||||
self = [super initWithContentRect:aRect styleMask:CPBorderlessWindowMask];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setLevel:CPPopUpMenuWindowLevel];
|
||||
[self setHasShadow:YES];
|
||||
[self setShadowStyle:CPMenuWindowShadowStyle];
|
||||
[self setAcceptsMouseMovedEvents:YES];
|
||||
|
||||
var contentView = [self contentView];
|
||||
|
||||
_menuView = [[_CPMenuView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
_menuClipView = [[CPClipView alloc] initWithFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, 0.0, 0.0)];
|
||||
[_menuClipView setDocumentView:_menuView];
|
||||
|
||||
[contentView addSubview:_menuClipView];
|
||||
|
||||
_moreAboveView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_moreAboveView setImage:_CPMenuWindowMoreAboveImage];
|
||||
[_moreAboveView setFrameSize:[_CPMenuWindowMoreAboveImage size]];
|
||||
|
||||
[contentView addSubview:_moreAboveView];
|
||||
|
||||
_moreBelowView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_moreBelowView setImage:_CPMenuWindowMoreBelowImage];
|
||||
[_moreBelowView setFrameSize:[_CPMenuWindowMoreBelowImage size]];
|
||||
|
||||
[contentView addSubview:_moreBelowView];
|
||||
|
||||
[self setShadowStyle:CPWindowShadowStyleMenu];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (float)_standardLeftMargin
|
||||
{
|
||||
return LEFT_MARGIN;
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
[_menuView setFont:aFont];
|
||||
}
|
||||
|
||||
- (CPFont)font
|
||||
{
|
||||
return [_menuView font];
|
||||
}
|
||||
|
||||
+ (CPColor)backgroundColorForBackgroundStyle:(_CPMenuWindowBackgroundStyle)aBackgroundStyle
|
||||
{
|
||||
var color = _CPMenuWindowBackgroundColors[aBackgroundStyle];
|
||||
|
||||
if (!color)
|
||||
{
|
||||
var bundle = [CPBundle bundleForClass:[self class]];
|
||||
|
||||
if (aBackgroundStyle == _CPMenuWindowPopUpBackgroundStyle)
|
||||
color = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded0.png"] size:CGSizeMake(4.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow1.png"] size:CGSizeMake(1.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded2.png"] size:CGSizeMake(4.0, 4.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 1.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded6.png"] size:CGSizeMake(4.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow7.png"] size:CGSizeMake(1.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded8.png"] size:CGSizeMake(4.0, 4.0)]
|
||||
]]];
|
||||
|
||||
else if (aBackgroundStyle == _CPMenuWindowMenuBarBackgroundStyle)
|
||||
color = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 0.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 0.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 0.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow3.png"] size:CGSizeMake(4.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow4.png"] size:CGSizeMake(1.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow5.png"] size:CGSizeMake(4.0, 1.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded6.png"] size:CGSizeMake(4.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindow7.png"] size:CGSizeMake(1.0, 4.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuWindow/_CPMenuWindowRounded8.png"] size:CGSizeMake(4.0, 4.0)]
|
||||
]]];
|
||||
|
||||
_CPMenuWindowBackgroundColors[aBackgroundStyle] = color;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
- (void)setBackgroundStyle:(_CPMenuWindowBackgroundStyle)aBackgroundStyle
|
||||
{
|
||||
[self setBackgroundColor:[[self class] backgroundColorForBackgroundStyle:aBackgroundStyle]];
|
||||
}
|
||||
|
||||
- (void)setMenu:(CPMenu)aMenu
|
||||
{
|
||||
[aMenu _setMenuWindow:self];
|
||||
[_menuView setMenu:aMenu];
|
||||
|
||||
var menuViewSize = [_menuView frame].size;
|
||||
|
||||
[self setFrameSize:CGSizeMake(LEFT_MARGIN + menuViewSize.width + RIGHT_MARGIN, TOP_MARGIN + menuViewSize.height + BOTTOM_MARGIN)];
|
||||
|
||||
[_menuView scrollPoint:CGPointMake(0.0, 0.0)];
|
||||
[_menuClipView setFrame:CGRectMake(LEFT_MARGIN, TOP_MARGIN, menuViewSize.width, menuViewSize.height)];
|
||||
}
|
||||
|
||||
- (void)setMinWidth:(float)aWidth
|
||||
{
|
||||
var size = [self unconstrainedFrame].size;
|
||||
|
||||
[self setFrameSize:CGSizeMake(MAX(size.width, aWidth), size.height)];
|
||||
}
|
||||
|
||||
- (CPMenu)menu
|
||||
{
|
||||
return [_menuView menu];
|
||||
}
|
||||
|
||||
- (void)orderFront:(id)aSender
|
||||
{
|
||||
[self setFrame:_unconstrainedFrame];
|
||||
|
||||
[super orderFront:aSender];
|
||||
}
|
||||
|
||||
- (void)setConstraintRect:(CGRect)aRect
|
||||
{
|
||||
_constraintRect = aRect;
|
||||
|
||||
[self setFrame:_unconstrainedFrame];
|
||||
}
|
||||
|
||||
- (CGRect)unconstrainedFrame
|
||||
{
|
||||
return _CGRectMakeCopy(_unconstrainedFrame);
|
||||
}
|
||||
|
||||
// We need this because if not this will call setFrame: with -frame instead of -unconstrainedFrame, turning
|
||||
// the constrained frame into the unconstrained frame.
|
||||
- (void)setFrameOrigin:(CGPoint)aPoint
|
||||
{
|
||||
[super setFrame:_CGRectMake(aPoint.x, aPoint.y, _CGRectGetWidth(_unconstrainedFrame), _CGRectGetHeight(_unconstrainedFrame))];
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
[super setFrame:_CGRectMake(_CGRectGetMinX(_unconstrainedFrame), _CGRectGetMinY(_unconstrainedFrame), aSize.width, aSize.height)];
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)aFrame display:(BOOL)shouldDisplay animate:(BOOL)shouldAnimate
|
||||
{
|
||||
// FIXME: There are integral window issues with platform windows.
|
||||
// FIXME: This gets called far too often.
|
||||
_unconstrainedFrame = _CGRectMakeCopy(aFrame);
|
||||
|
||||
var constrainedFrame = CGRectIntersection(_unconstrainedFrame, _constraintRect);
|
||||
|
||||
// We don't want to simply intersect the visible frame and the unconstrained frame.
|
||||
// We should be allowing as much of the width to fit as possible (pushing back and forward).
|
||||
constrainedFrame.origin.x = CGRectGetMinX(_unconstrainedFrame);
|
||||
constrainedFrame.size.width = CGRectGetWidth(_unconstrainedFrame);
|
||||
|
||||
if (CGRectGetWidth(constrainedFrame) > CGRectGetWidth(_constraintRect))
|
||||
constrainedFrame.size.width = CGRectGetWidth(_constraintRect);
|
||||
|
||||
if (CGRectGetMaxX(constrainedFrame) > CGRectGetMaxX(_constraintRect))
|
||||
constrainedFrame.origin.x -= CGRectGetMaxX(constrainedFrame) - CGRectGetMaxX(_constraintRect);
|
||||
|
||||
if (CGRectGetMinX(constrainedFrame) < CGRectGetMinX(_constraintRect))
|
||||
constrainedFrame.origin.x = CGRectGetMinX(_constraintRect);
|
||||
|
||||
[super setFrame:constrainedFrame display:shouldDisplay animate:shouldAnimate];
|
||||
|
||||
// This needs to happen before changing the frame.
|
||||
var menuViewOrigin = CGPointMake(CGRectGetMinX(aFrame) + LEFT_MARGIN, CGRectGetMinY(aFrame) + TOP_MARGIN),
|
||||
moreAbove = menuViewOrigin.y < CGRectGetMinY(constrainedFrame) + TOP_MARGIN,
|
||||
moreBelow = menuViewOrigin.y + CGRectGetHeight([_menuView frame]) > CGRectGetMaxY(constrainedFrame) - BOTTOM_MARGIN,
|
||||
|
||||
topMargin = TOP_MARGIN,
|
||||
bottomMargin = BOTTOM_MARGIN,
|
||||
|
||||
contentView = [self contentView],
|
||||
bounds = [contentView bounds];
|
||||
|
||||
if (moreAbove)
|
||||
{
|
||||
topMargin += SCROLL_INDICATOR_HEIGHT;
|
||||
|
||||
var frame = [_moreAboveView frame];
|
||||
|
||||
[_moreAboveView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth(frame)) / 2.0, (TOP_MARGIN + SCROLL_INDICATOR_HEIGHT - CGRectGetHeight(frame)) / 2.0)];
|
||||
}
|
||||
|
||||
[_moreAboveView setHidden:!moreAbove];
|
||||
|
||||
if (moreBelow)
|
||||
{
|
||||
bottomMargin += SCROLL_INDICATOR_HEIGHT;
|
||||
|
||||
[_moreBelowView setFrameOrigin:CGPointMake((CGRectGetWidth(bounds) - CGRectGetWidth([_moreBelowView frame])) / 2.0, CGRectGetHeight(bounds) - SCROLL_INDICATOR_HEIGHT - BOTTOM_MARGIN)];
|
||||
}
|
||||
|
||||
[_moreBelowView setHidden:!moreBelow];
|
||||
|
||||
var clipFrame = CGRectMake(LEFT_MARGIN, topMargin, CGRectGetWidth(constrainedFrame) - LEFT_MARGIN - RIGHT_MARGIN, CGRectGetHeight(constrainedFrame) - topMargin - bottomMargin)
|
||||
|
||||
[_menuClipView setFrame:clipFrame];
|
||||
[_menuView setFrameSize:CGSizeMake(CGRectGetWidth(clipFrame), CGRectGetHeight([_menuView frame]))];
|
||||
|
||||
[_menuView scrollPoint:CGPointMake(0.0, [self convertBaseToGlobal:clipFrame.origin].y - menuViewOrigin.y)];
|
||||
}
|
||||
|
||||
- (BOOL)hasMinimumNumberOfVisibleItems
|
||||
{
|
||||
var visibleRect = [_menuView visibleRect];
|
||||
|
||||
// Clearly if the entire view isn't visible the minimum won't be visible.
|
||||
if (CGRectIsEmpty(visibleRect))
|
||||
return NO;
|
||||
|
||||
var numberOfUnhiddenItems = [_menuView numberOfUnhiddenItems],
|
||||
minimumNumberOfVisibleItems = MIN(numberOfUnhiddenItems, 3),
|
||||
count = 0,
|
||||
index = [_menuView itemIndexAtPoint:[_menuView convertPoint:[_menuClipView frame].origin fromView:nil]];
|
||||
|
||||
for (; index < numberOfUnhiddenItems && count < minimumNumberOfVisibleItems; ++index)
|
||||
{
|
||||
var itemRect = [_menuView rectForUnhiddenItemAtIndex:index],
|
||||
visibleItemRect = CGRectIntersection(visibleRect, itemRect);
|
||||
|
||||
// As soon as we get to the first unhidden item that is no longer visible, stop.
|
||||
if (CGRectIsEmpty(visibleItemRect))
|
||||
break;
|
||||
|
||||
// If the item is *completely* visible, count it.
|
||||
if (CGRectEqualToRect(visibleItemRect, itemRect))
|
||||
++count;
|
||||
}
|
||||
|
||||
return count >= minimumNumberOfVisibleItems;
|
||||
}
|
||||
|
||||
- (BOOL)canScrollUp
|
||||
{
|
||||
return ![_moreAboveView isHidden];
|
||||
}
|
||||
|
||||
- (BOOL)canScrollDown
|
||||
{
|
||||
return ![_moreBelowView isHidden];
|
||||
}
|
||||
|
||||
- (BOOL)canScroll
|
||||
{
|
||||
return [self canScrollUp] || [self canScrollDown];
|
||||
}
|
||||
|
||||
- (void)scrollUp
|
||||
{
|
||||
if (CGRectGetMinY(_unconstrainedFrame) >= CGRectGetMinY(_constraintRect))
|
||||
return;
|
||||
|
||||
_unconstrainedFrame.origin.y += 10;
|
||||
|
||||
[self setFrame:_unconstrainedFrame];
|
||||
}
|
||||
|
||||
- (void)scrollDown
|
||||
{
|
||||
if (CGRectGetMaxY(_unconstrainedFrame) <= CGRectGetHeight(_constraintRect))
|
||||
return;
|
||||
|
||||
_unconstrainedFrame.origin.y -= 10;
|
||||
|
||||
[self setFrame:_unconstrainedFrame];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuWindow (CPMenuContainer)
|
||||
|
||||
- (CGRect)globalFrame
|
||||
{
|
||||
return [self frame];
|
||||
}
|
||||
|
||||
- (BOOL)isMenuBar
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (_CPManagerScrollingState)scrollingStateForPoint:(CGPoint)aGlobalLocation
|
||||
{
|
||||
var frame = [self frame];
|
||||
|
||||
if (![self canScroll])
|
||||
return _CPMenuManagerScrollingStateNone;
|
||||
|
||||
// If we're at or above of the top scroll indicator...
|
||||
if (aGlobalLocation.y < CGRectGetMinY(frame) + TOP_MARGIN + SCROLL_INDICATOR_HEIGHT)
|
||||
return _CPMenuManagerScrollingStateUp;
|
||||
|
||||
// If we're at or below the bottom scroll indicator...
|
||||
if (aGlobalLocation.y > CGRectGetMaxY(frame) - BOTTOM_MARGIN - SCROLL_INDICATOR_HEIGHT)
|
||||
return _CPMenuManagerScrollingStateDown;
|
||||
|
||||
return _CPMenuManagerScrollingStateNone;
|
||||
}
|
||||
|
||||
- (float)deltaYForItemAtIndex:(int)anIndex
|
||||
{
|
||||
return TOP_MARGIN + CGRectGetMinY([_menuView rectForItemAtIndex:anIndex]);
|
||||
}
|
||||
|
||||
- (CGPoint)rectForItemAtIndex:(int)anIndex
|
||||
{
|
||||
return [_menuView convertRect:[_menuView rectForItemAtIndex:anIndex] toView:nil];
|
||||
}
|
||||
|
||||
- (int)itemIndexAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
// Don't return indexes of items that aren't visible.
|
||||
if (!CGRectContainsPoint([_menuClipView bounds], [_menuClipView convertPoint:aPoint fromView:nil]))
|
||||
return NO;
|
||||
|
||||
return [_menuView itemIndexAtPoint:[_menuView convertPoint:aPoint fromView:nil]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
|
||||
@implementation _CPMenuView : CPView
|
||||
{
|
||||
CPArray _menuItemViews;
|
||||
CPArray _visibleMenuItemInfos;
|
||||
|
||||
CPFont _font @accessors(property=font);
|
||||
}
|
||||
|
||||
- (unsigned)numberOfUnhiddenItems
|
||||
{
|
||||
return _visibleMenuItemInfos.length;
|
||||
}
|
||||
|
||||
- (CGRect)rectForUnhiddenItemAtIndex:(int)anIndex
|
||||
{
|
||||
return [self rectForItemAtIndex:_visibleMenuItemInfos[anIndex].index];
|
||||
}
|
||||
|
||||
- (CGRect)rectForItemAtIndex:(int)anIndex
|
||||
{
|
||||
return [_menuItemViews[anIndex === CPNotFound ? 0 : anIndex] frame];
|
||||
}
|
||||
|
||||
- (int)itemIndexAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var x = aPoint.x,
|
||||
bounds = [self bounds];
|
||||
|
||||
if (x < CGRectGetMinX(bounds) || x > CGRectGetMaxX(bounds))
|
||||
return CPNotFound;
|
||||
|
||||
var y = aPoint.y,
|
||||
low = 0,
|
||||
high = _visibleMenuItemInfos.length - 1;
|
||||
|
||||
while (low <= high)
|
||||
{
|
||||
var middle = FLOOR(low + (high - low) / 2),
|
||||
info = _visibleMenuItemInfos[middle]
|
||||
frame = [info.view frame];
|
||||
|
||||
if (y < CGRectGetMinY(frame))
|
||||
high = middle - 1;
|
||||
|
||||
else if (y > CGRectGetMaxY(frame))
|
||||
low = middle + 1;
|
||||
|
||||
else
|
||||
return info.index;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
{
|
||||
[_menuItemViews makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||||
|
||||
_menuItemViews = [];
|
||||
_visibleMenuItemInfos = [];
|
||||
|
||||
var menu = [self menu];
|
||||
|
||||
if (!menu)
|
||||
return;
|
||||
|
||||
var items = [menu itemArray],
|
||||
index = 0,
|
||||
count = [items count],
|
||||
maxWidth = 0,
|
||||
y = 0,
|
||||
showsStateColumn = [menu showsStateColumn];
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var item = items[index],
|
||||
view = [item _menuItemView];
|
||||
|
||||
_menuItemViews.push(view);
|
||||
|
||||
if ([item isHidden])
|
||||
continue;
|
||||
|
||||
_visibleMenuItemInfos.push({ view:view, index:index });
|
||||
|
||||
[view setFont:_font];
|
||||
[view setShowsStateColumn:showsStateColumn];
|
||||
[view synchronizeWithMenuItem];
|
||||
|
||||
[view setFrameOrigin:CGPointMake(0.0, y)];
|
||||
|
||||
[self addSubview:view];
|
||||
|
||||
var size = [view minSize],
|
||||
width = size.width;
|
||||
|
||||
if (maxWidth < width)
|
||||
maxWidth = width;
|
||||
|
||||
y += size.height;
|
||||
}
|
||||
|
||||
for (index = 0; index < count; ++index)
|
||||
{
|
||||
var view = _menuItemViews[index];
|
||||
|
||||
[view setFrameSize:CGSizeMake(maxWidth, CGRectGetHeight([view frame]))];
|
||||
}
|
||||
|
||||
[self setAutoresizesSubviews:NO];
|
||||
[self setFrameSize:CGSizeMake(maxWidth, y)];
|
||||
[self setAutoresizesSubviews:YES];
|
||||
}
|
||||
|
||||
- (void)setMenu:(CPMenu)aMenu
|
||||
{
|
||||
[super setMenu:aMenu];
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -24,11 +24,13 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import <AppKit/CPImage.j>
|
||||
@import <AppKit/CPMenu.j>
|
||||
@import <AppKit/CPView.j>
|
||||
@import "CPImage.j"
|
||||
@import "CPMenu.j"
|
||||
@import "CPView.j"
|
||||
@import "_CPMenuItemView.j"
|
||||
|
||||
/*!
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPMenuItem
|
||||
|
||||
@@ -42,39 +44,39 @@
|
||||
|
||||
CPString _title;
|
||||
//CPAttributedString _attributedTitle;
|
||||
|
||||
|
||||
CPFont _font;
|
||||
|
||||
|
||||
id _target;
|
||||
SEL _action;
|
||||
|
||||
|
||||
BOOL _isEnabled;
|
||||
BOOL _isHidden;
|
||||
|
||||
|
||||
int _tag;
|
||||
int _state;
|
||||
|
||||
|
||||
CPImage _image;
|
||||
CPImage _alternateImage;
|
||||
CPImage _onStateImage;
|
||||
CPImage _offStateImage;
|
||||
CPImage _mixedStateImage;
|
||||
|
||||
|
||||
CPMenu _submenu;
|
||||
CPMenu _menu;
|
||||
|
||||
|
||||
CPString _keyEquivalent;
|
||||
unsigned _keyEquivalentModifierMask;
|
||||
|
||||
|
||||
int _mnemonicLocation;
|
||||
|
||||
|
||||
BOOL _isAlternate;
|
||||
int _indentationLevel;
|
||||
|
||||
|
||||
CPString _toolTip;
|
||||
id _representedObject;
|
||||
CPView _view;
|
||||
|
||||
|
||||
_CPMenuItemView _menuItemView;
|
||||
}
|
||||
|
||||
@@ -93,25 +95,27 @@
|
||||
- (id)initWithTitle:(CPString)aTitle action:(SEL)anAction keyEquivalent:(CPString)aKeyEquivalent
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_isSeparator = NO;
|
||||
|
||||
_title = aTitle;
|
||||
_action = anAction;
|
||||
|
||||
|
||||
_isEnabled = YES;
|
||||
|
||||
|
||||
_tag = 0;
|
||||
_state = CPOffState;
|
||||
|
||||
|
||||
_keyEquivalent = aKeyEquivalent || @"";
|
||||
_keyEquivalentModifierMask = CPPlatformActionKeyMask;
|
||||
|
||||
|
||||
_indentationLevel = 0;
|
||||
|
||||
_mnemonicLocation = CPNotFound;
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -149,7 +153,7 @@
|
||||
{
|
||||
if (_isHidden == isHidden)
|
||||
return;
|
||||
|
||||
|
||||
_isHidden = isHidden;
|
||||
|
||||
[_menu itemChanged:self];
|
||||
@@ -170,9 +174,9 @@
|
||||
{
|
||||
if (_isHidden)
|
||||
return YES;
|
||||
|
||||
|
||||
var supermenu = [_menu supermenu];
|
||||
|
||||
|
||||
if ([[supermenu itemAtIndex:[supermenu indexOfItemWithSubmenu:_menu]] isHiddenOrHasHiddenAncestor])
|
||||
return YES;
|
||||
|
||||
@@ -225,11 +229,11 @@
|
||||
|
||||
if (_title == aTitle)
|
||||
return;
|
||||
|
||||
|
||||
_title = aTitle;
|
||||
|
||||
|
||||
[_menuItemView setDirty];
|
||||
|
||||
|
||||
[_menu itemChanged:self];
|
||||
}
|
||||
|
||||
@@ -257,11 +261,11 @@
|
||||
{
|
||||
if (_font == aFont)
|
||||
return;
|
||||
|
||||
|
||||
_font = aFont;
|
||||
|
||||
[_menu itemChanged:self];
|
||||
|
||||
|
||||
[_menuItemView setDirty];
|
||||
}
|
||||
|
||||
@@ -313,9 +317,9 @@ CPOffState
|
||||
{
|
||||
if (_state == aState)
|
||||
return;
|
||||
|
||||
|
||||
_state = aState;
|
||||
|
||||
|
||||
[_menu itemChanged:self];
|
||||
|
||||
[_menuItemView setDirty];
|
||||
@@ -343,11 +347,11 @@ CPOffState
|
||||
{
|
||||
if (_image == anImage)
|
||||
return;
|
||||
|
||||
|
||||
_image = anImage;
|
||||
|
||||
[_menuItemView setDirty];
|
||||
|
||||
|
||||
[_menu itemChanged:self];
|
||||
}
|
||||
|
||||
@@ -385,7 +389,7 @@ CPOffState
|
||||
{
|
||||
if (_onStateImage == anImage)
|
||||
return;
|
||||
|
||||
|
||||
_onStateImage = anImage;
|
||||
[_menu itemChanged:self];
|
||||
}
|
||||
@@ -406,7 +410,7 @@ CPOffState
|
||||
{
|
||||
if (_offStateImage == anImage)
|
||||
return;
|
||||
|
||||
|
||||
_offStateImage = anImage;
|
||||
[_menu itemChanged:self];
|
||||
}
|
||||
@@ -427,7 +431,7 @@ CPOffState
|
||||
{
|
||||
if (_mixedStateImage == anImage)
|
||||
return;
|
||||
|
||||
|
||||
_mixedStateImage = anImage;
|
||||
[_menu itemChanged:self];
|
||||
}
|
||||
@@ -448,20 +452,32 @@ CPOffState
|
||||
*/
|
||||
- (void)setSubmenu:(CPMenu)aMenu
|
||||
{
|
||||
var supermenu = [_submenu supermenu];
|
||||
|
||||
if (supermenu == self)
|
||||
if (_submenu === aMenu)
|
||||
return;
|
||||
|
||||
|
||||
var supermenu = [_submenu supermenu];
|
||||
|
||||
if (supermenu)
|
||||
return alert("bad");
|
||||
|
||||
[_submenu setSupermenu:_menu];
|
||||
|
||||
[CPException raise:CPInvalidArgumentException
|
||||
reason: @"Can't add submenu \"" + [aMenu title] + "\" to item \"" + [self title] + "\", because it is already submenu of \"" + [[aMenu supermenu] title] + "\""];
|
||||
|
||||
_submenu = aMenu;
|
||||
|
||||
|
||||
if (_submenu)
|
||||
{
|
||||
[_submenu setSupermenu:_menu];
|
||||
|
||||
[self setTarget:_menu];
|
||||
[self setAction:@selector(submenuAction:)];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setTarget:nil];
|
||||
[self setAction:NULL];
|
||||
}
|
||||
|
||||
[_menuItemView setDirty];
|
||||
|
||||
|
||||
[_menu itemChanged:self];
|
||||
}
|
||||
|
||||
@@ -570,6 +586,44 @@ CPControlKeyMask
|
||||
return _keyEquivalentModifierMask;
|
||||
}
|
||||
|
||||
- (CPString)keyEquivalentStringRepresentation
|
||||
{
|
||||
if (![_keyEquivalent length])
|
||||
return @"";
|
||||
|
||||
var string = _keyEquivalent.toUpperCase(),
|
||||
needsShift = _keyEquivalentModifierMask & CPShiftKeyMask ||
|
||||
(string === _keyEquivalent && _keyEquivalent.toLowerCase() !== _keyEquivalent.toUpperCase());
|
||||
|
||||
if (CPBrowserIsOperatingSystem(CPMacOperatingSystem))
|
||||
{
|
||||
if (_keyEquivalentModifierMask & CPCommandKeyMask)
|
||||
string = "⌘" + string;
|
||||
|
||||
if (needsShift)
|
||||
string = "⇧" + string;
|
||||
|
||||
if (_keyEquivalentModifierMask & CPAlternateKeyMask)
|
||||
string = "⌥" + string;
|
||||
|
||||
if (_keyEquivalentModifierMask & CPControlKeyMask)
|
||||
string = "^" + string;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (needsShift)
|
||||
string = "Shift-" + string;
|
||||
|
||||
if (_keyEquivalentModifierMask & CPAlternateKeyMask)
|
||||
string = "Alt-" + string;
|
||||
|
||||
if (_keyEquivalentModifierMask & CPControlKeyMask || _keyEquivalentModifierMask & CPCommandKeyMask)
|
||||
string = "Ctrl-" + string;
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
// Managing Mnemonics
|
||||
/*!
|
||||
Sets the index of the mnemonic character in the title. The character
|
||||
@@ -596,14 +650,14 @@ CPControlKeyMask
|
||||
- (void)setTitleWithMnemonicLocation:(CPString)aTitle
|
||||
{
|
||||
var location = [aTitle rangeOfString:@"&"].location;
|
||||
|
||||
|
||||
if (location == CPNotFound)
|
||||
[self setTitle:aTitle];
|
||||
else
|
||||
{
|
||||
[self setTitle:[aTitle substringToIndex:location] + [aTitle substringFromIndex:location + 1]];
|
||||
[self setMnemonicLocation:location];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -643,8 +697,8 @@ CPControlKeyMask
|
||||
- (void)setIndentationLevel:(unsigned)aLevel
|
||||
{
|
||||
if (aLevel < 0)
|
||||
[CPException raise:CPInvalidArgumentException reason:"setIndentationLevel: argument must be greater than 0."];
|
||||
|
||||
[CPException raise:CPInvalidArgumentException reason:"setIndentationLevel: argument must be greater than or equal to 0."];
|
||||
|
||||
_indentationLevel = MIN(15, aLevel);
|
||||
}
|
||||
|
||||
@@ -703,11 +757,11 @@ CPControlKeyMask
|
||||
{
|
||||
if (_view === aView)
|
||||
return;
|
||||
|
||||
|
||||
_view = aView;
|
||||
|
||||
|
||||
[_menuItemView setDirty];
|
||||
|
||||
|
||||
[_menu itemChanged:self];
|
||||
}
|
||||
|
||||
@@ -729,7 +783,48 @@ CPControlKeyMask
|
||||
return [[self menu] highlightedItem] == self;
|
||||
}
|
||||
|
||||
//
|
||||
#pragma mark CPObject Overrides
|
||||
|
||||
/*!
|
||||
Returns a copy of the item. The copy does not belong If the item has a submenu, it is NOT copied.
|
||||
*/
|
||||
- (id)copy
|
||||
{
|
||||
var item = [[CPMenuItem alloc] init];
|
||||
|
||||
// No point in going through accessors and doing lots of unnecessary state checking/updating
|
||||
item._isSeparator = _isSeparator;
|
||||
|
||||
[item setTitle:_title];
|
||||
[item setFont:_font];
|
||||
[item setTarget:_target];
|
||||
[item setAction:_action];
|
||||
[item setEnabled:_isEnabled];
|
||||
[item setHidden:_isHidden]
|
||||
[item setTag:_tag];
|
||||
[item setState:_state];
|
||||
[item setImage:_image];
|
||||
[item setAlternateImage:_alternateImage];
|
||||
[item setOnStateImage:_onStateImage];
|
||||
[item setOffStateImage:_offStateImage];
|
||||
[item setMixedStateImage:_mixedStateImage];
|
||||
[item setKeyEquivalent:_keyEquivalent];
|
||||
[item setKeyEquivalentModifierMask:_keyEquivalentModifierMask];
|
||||
[item setMnemonicLocation:_mnemonicLocation];
|
||||
[item setAlternate:_isAlternate];
|
||||
[item setIndentationLevel:_indentationLevel];
|
||||
[item setToolTip:_toolTip];
|
||||
[item setRepresentedObject:_representedObject];
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
- (id)mutableCopy
|
||||
{
|
||||
return [self copy];
|
||||
}
|
||||
|
||||
#pragma mark Internal
|
||||
|
||||
/*
|
||||
@ignore
|
||||
@@ -738,10 +833,20 @@ CPControlKeyMask
|
||||
{
|
||||
if (!_menuItemView)
|
||||
_menuItemView = [[_CPMenuItemView alloc] initWithFrame:CGRectMakeZero() forMenuItem:self];
|
||||
|
||||
|
||||
return _menuItemView;
|
||||
}
|
||||
|
||||
- (BOOL)_isSelectable
|
||||
{
|
||||
return ![self submenu] || [self action] !== @selector(submenuAction:) || [self target] !== [self menu];
|
||||
}
|
||||
|
||||
- (BOOL)_isMenuBarButton
|
||||
{
|
||||
return ![self submenu] && [self menu] === [CPApp mainMenu];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
@@ -765,6 +870,8 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
CPMenuItemKeyEquivalentKey = @"CPMenuItemKeyEquivalentKey",
|
||||
CPMenuItemKeyEquivalentModifierMaskKey = @"CPMenuItemKeyEquivalentModifierMaskKey",
|
||||
|
||||
CPMenuItemIndentationLevelKey = @"CPMenuItemIndentationLevelKey",
|
||||
|
||||
CPMenuItemRepresentedObjectKey = @"CPMenuItemRepresentedObjectKey",
|
||||
CPMenuItemViewKey = @"CPMenuItemViewKey";
|
||||
|
||||
@@ -780,15 +887,15 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_isSeparator = [aCoder containsValueForKey:CPMenuItemIsSeparatorKey] && [aCoder decodeBoolForKey:CPMenuItemIsSeparatorKey];
|
||||
|
||||
_title = [aCoder decodeObjectForKey:CPMenuItemTitleKey];
|
||||
|
||||
|
||||
// _font;
|
||||
|
||||
|
||||
_target = [aCoder decodeObjectForKey:CPMenuItemTargetKey];
|
||||
_action = [aCoder decodeObjectForKey:CPMenuItemActionKey];
|
||||
|
||||
@@ -796,7 +903,6 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
_isHidden = DEFAULT_VALUE(CPMenuItemIsHiddenKey, NO);
|
||||
_tag = DEFAULT_VALUE(CPMenuItemTagKey, 0);
|
||||
_state = DEFAULT_VALUE(CPMenuItemStateKey, CPOffState);
|
||||
// int _state;
|
||||
|
||||
_image = DEFAULT_VALUE(CPMenuItemImageKey, nil);
|
||||
_alternateImage = DEFAULT_VALUE(CPMenuItemAlternateImageKey, nil);
|
||||
@@ -804,8 +910,9 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
// CPImage _offStateImage;
|
||||
// CPImage _mixedStateImage;
|
||||
|
||||
_submenu = DEFAULT_VALUE(CPMenuItemSubmenuKey, nil);
|
||||
// This order matters because setSubmenu: needs _menu to be around.
|
||||
_menu = DEFAULT_VALUE(CPMenuItemMenuKey, nil);
|
||||
[self setSubmenu:DEFAULT_VALUE(CPMenuItemSubmenuKey, nil)];
|
||||
|
||||
_keyEquivalent = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentKey] || @"";
|
||||
_keyEquivalentModifierMask = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentModifierMaskKey] || 0;
|
||||
@@ -813,14 +920,16 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
// int _mnemonicLocation;
|
||||
|
||||
// BOOL _isAlternate;
|
||||
// int _indentationLevel;
|
||||
|
||||
// Default is 0.
|
||||
[self setIndentationLevel:[aCoder decodeIntForKey:CPMenuItemIndentationLevelKey] || 0];
|
||||
|
||||
// CPString _toolTip;
|
||||
|
||||
_representedObject = DEFAULT_VALUE(CPMenuItemRepresentedObjectKey, nil);
|
||||
_view = DEFAULT_VALUE(CPMenuItemViewKey, nil);
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -834,11 +943,11 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
[aCoder encodeBool:_isSeparator forKey:CPMenuItemIsSeparatorKey];
|
||||
|
||||
[aCoder encodeObject:_title forKey:CPMenuItemTitleKey];
|
||||
|
||||
|
||||
[aCoder encodeObject:_target forKey:CPMenuItemTargetKey];
|
||||
[aCoder encodeObject:_action forKey:CPMenuItemActionKey];
|
||||
|
||||
ENCODE_IFNOT(CPMenuItemIsEnabledKey, _isEnabled, YES);
|
||||
ENCODE_IFNOT(CPMenuItemIsEnabledKey, _isEnabled, YES);
|
||||
ENCODE_IFNOT(CPMenuItemIsHiddenKey, _isHidden, NO);
|
||||
|
||||
ENCODE_IFNOT(CPMenuItemTagKey, _tag, 0);
|
||||
@@ -846,7 +955,7 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
|
||||
ENCODE_IFNOT(CPMenuItemImageKey, _image, nil);
|
||||
ENCODE_IFNOT(CPMenuItemAlternateImageKey, _alternateImage, nil);
|
||||
|
||||
|
||||
ENCODE_IFNOT(CPMenuItemSubmenuKey, _submenu, nil);
|
||||
ENCODE_IFNOT(CPMenuItemMenuKey, _menu, nil);
|
||||
|
||||
@@ -856,423 +965,11 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey",
|
||||
if (_keyEquivalentModifierMask)
|
||||
[aCoder encodeObject:_keyEquivalentModifierMask forKey:CPMenuItemKeyEquivalentModifierMaskKey];
|
||||
|
||||
if (_indentationLevel > 0)
|
||||
[aCoder encodeInt:_indentationLevel forKey:CPMenuItemIndentationLevelKey];
|
||||
|
||||
ENCODE_IFNOT(CPMenuItemRepresentedObjectKey, _representedObject, nil);
|
||||
ENCODE_IFNOT(CPMenuItemViewKey, _view, nil);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var LEFT_MARGIN = 3.0,
|
||||
RIGHT_MARGIN = 16.0,
|
||||
STATE_COLUMN_WIDTH = 14.0,
|
||||
INDENTATION_WIDTH = 17.0,
|
||||
VERTICAL_MARGIN = 4.0;
|
||||
|
||||
var _CPMenuItemSelectionColor = nil,
|
||||
_CPMenuItemTextShadowColor = nil,
|
||||
|
||||
_CPMenuItemDefaultStateImages = [],
|
||||
_CPMenuItemDefaultStateHighlightedImages = [];
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
@implementation _CPMenuItemView : CPView
|
||||
{
|
||||
CPMenuItem _menuItem;
|
||||
|
||||
CPFont _font;
|
||||
CPColor _textColor;
|
||||
CPColor _textShadowColor;
|
||||
CPColor _activateColor;
|
||||
CPColor _activateShadowColor;
|
||||
|
||||
CGSize _minSize;
|
||||
BOOL _isDirty;
|
||||
BOOL _showsStateColumn;
|
||||
BOOL _belongsToMenuBar;
|
||||
|
||||
CPImageView _stateView;
|
||||
_CPImageAndTextView _imageAndTextView;
|
||||
CPView _submenuView;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPMenuItemView class])
|
||||
return;
|
||||
|
||||
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
||||
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0]
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
||||
}
|
||||
|
||||
+ (float)leftMargin
|
||||
{
|
||||
return LEFT_MARGIN + STATE_COLUMN_WIDTH;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_menuItem = aMenuItem;
|
||||
_showsStateColumn = YES;
|
||||
_isDirty = YES;
|
||||
|
||||
[self setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[self synchronizeWithMenuItem];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGSize)minSize
|
||||
{
|
||||
return _minSize;
|
||||
}
|
||||
|
||||
- (void)setDirty
|
||||
{
|
||||
_isDirty = YES;
|
||||
}
|
||||
|
||||
- (void)synchronizeWithMenuItem
|
||||
{
|
||||
if (!_isDirty)
|
||||
return;
|
||||
|
||||
_isDirty = NO;
|
||||
|
||||
var view = [_menuItem view];
|
||||
|
||||
if ([_menuItem isSeparatorItem])
|
||||
{
|
||||
var line = [[CPView alloc] initWithFrame:CGRectMake(0.0, 5.0, 10.0, 1.0)];
|
||||
|
||||
view = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 10.0)];
|
||||
|
||||
[view setAutoresizingMask:CPViewWidthSizable];
|
||||
[line setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[line setBackgroundColor:[CPColor lightGrayColor]];
|
||||
|
||||
[view addSubview:line];
|
||||
}
|
||||
|
||||
if (view)
|
||||
{
|
||||
[_imageAndTextView removeFromSuperview];
|
||||
_imageAndTextView = nil;
|
||||
|
||||
[_stateView removeFromSuperview];
|
||||
_stateView = nil;
|
||||
|
||||
[_submenuView removeFromSuperview];
|
||||
_submenuView = nil;
|
||||
|
||||
_minSize = [view frame].size;
|
||||
|
||||
[self setFrameSize:_minSize];
|
||||
|
||||
[self addSubview:view];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// State Column
|
||||
var x = _belongsToMenuBar ? 0.0 : (LEFT_MARGIN + [_menuItem indentationLevel] * INDENTATION_WIDTH);
|
||||
|
||||
if (_showsStateColumn)
|
||||
{
|
||||
if (!_stateView)
|
||||
{
|
||||
_stateView = [[CPImageView alloc] initWithFrame:CGRectMake(x, (CGRectGetHeight([self frame]) - STATE_COLUMN_WIDTH) / 2.0, STATE_COLUMN_WIDTH, STATE_COLUMN_WIDTH)];
|
||||
|
||||
[_stateView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin];
|
||||
|
||||
[self addSubview:_stateView];
|
||||
}
|
||||
|
||||
var state = [_menuItem state];
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case CPOffState:
|
||||
case CPOnState:
|
||||
case CPMixedState: [_stateView setImage:_CPMenuItemDefaultStateImages[state]];
|
||||
break;
|
||||
|
||||
default: [_stateView setImage:nil];
|
||||
}
|
||||
|
||||
x += STATE_COLUMN_WIDTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
[_stateView removeFromSuperview];
|
||||
|
||||
_stateView = nil;
|
||||
}
|
||||
|
||||
// Image and Title
|
||||
|
||||
if (!_imageAndTextView)
|
||||
{
|
||||
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
||||
|
||||
[_imageAndTextView setImagePosition:CPImageLeft];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
|
||||
[self addSubview:_imageAndTextView];
|
||||
}
|
||||
|
||||
var font = [_menuItem font];
|
||||
|
||||
if (!font)
|
||||
font = _font;
|
||||
|
||||
[_imageAndTextView setFont:font];
|
||||
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
[_imageAndTextView setText:[_menuItem title]];
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0, 1)];
|
||||
[_imageAndTextView setFrameOrigin:CGPointMake(x, VERTICAL_MARGIN)];
|
||||
[_imageAndTextView sizeToFit];
|
||||
|
||||
var frame = [_imageAndTextView frame];
|
||||
|
||||
// frame.size.height += 1.0;
|
||||
// [_imageAndTextView setFrame:frame];
|
||||
|
||||
frame.size.height += 2 * VERTICAL_MARGIN;
|
||||
|
||||
x += CGRectGetWidth(frame);
|
||||
|
||||
// Submenu Arrow
|
||||
if ([_menuItem hasSubmenu])
|
||||
{
|
||||
x += 3.0;
|
||||
|
||||
if (!_submenuView)
|
||||
{
|
||||
_submenuView = [[_CPMenuItemArrowView alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 10.0)];
|
||||
|
||||
[self addSubview:_submenuView];
|
||||
}
|
||||
|
||||
[_submenuView setHidden:NO];
|
||||
[_submenuView setColor:_belongsToMenuBar ? [self textColor] : nil];
|
||||
[_submenuView setFrameOrigin:CGPointMake(x, (CGRectGetHeight(frame) - 10.0) / 2.0)];
|
||||
|
||||
x += 10.0;
|
||||
}
|
||||
else
|
||||
[_submenuView setHidden:YES];
|
||||
|
||||
_minSize = CGSizeMake(x + (_belongsToMenuBar ? 0.0 : RIGHT_MARGIN) + 3.0, CGRectGetHeight(frame));
|
||||
|
||||
[self setFrameSize:_minSize];
|
||||
}
|
||||
|
||||
- (CGFloat)overlapOffsetWidth
|
||||
{
|
||||
return LEFT_MARGIN + ([[_menuItem menu] showsStateColumn] ? STATE_COLUMN_WIDTH : 0.0);
|
||||
}
|
||||
|
||||
- (void)setShowsStateColumn:(BOOL)shouldShowStateColumn
|
||||
{
|
||||
_showsStateColumn = shouldShowStateColumn;
|
||||
}
|
||||
|
||||
- (void)setBelongsToMenuBar:(BOOL)shouldBelongToMenuBar
|
||||
{
|
||||
_belongsToMenuBar = shouldBelongToMenuBar;
|
||||
}
|
||||
|
||||
- (void)highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
// ASSERT(![_menuItem view]);
|
||||
|
||||
if (_belongsToMenuBar)
|
||||
[_imageAndTextView setImage:shouldHighlight ? [_menuItem alternateImage] : [_menuItem image]];
|
||||
|
||||
else if ([_menuItem isEnabled])
|
||||
{
|
||||
if (shouldHighlight)
|
||||
{
|
||||
[self setBackgroundColor:_CPMenuItemSelectionColor];
|
||||
|
||||
[_imageAndTextView setTextColor:[CPColor whiteColor]];
|
||||
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setBackgroundColor:nil];
|
||||
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
}
|
||||
|
||||
var state = [_menuItem state];
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case CPOffState:
|
||||
case CPOnState:
|
||||
case CPMixedState: [_stateView setImage:shouldHighlight ? _CPMenuItemDefaultStateHighlightedImages[state] : _CPMenuItemDefaultStateImages[state]];
|
||||
break;
|
||||
|
||||
default: [_stateView setImage:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)activate:(BOOL)shouldActivate
|
||||
{
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
|
||||
if (shouldActivate)
|
||||
{
|
||||
[_imageAndTextView setTextColor:[self activateColor] || [CPColor whiteColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self activateShadowColor] || [CPColor blackColor]];
|
||||
[_submenuView setColor:[self activateColor] || [CPColor whiteColor]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
[_submenuView setColor:[self textColor]];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)eventOnSubmenu:(CPEvent)anEvent
|
||||
{
|
||||
if (![_menuItem hasSubmenu])
|
||||
return NO;
|
||||
|
||||
return CGRectContainsPoint([_submenuView frame], [self convertPoint:[anEvent locationInWindow] fromView:nil]);
|
||||
}
|
||||
|
||||
- (BOOL)isHidden
|
||||
{
|
||||
return [_menuItem isHidden];
|
||||
}
|
||||
|
||||
- (CPMenuItem)menuItem
|
||||
{
|
||||
return _menuItem;
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
if (_font == aFont)
|
||||
return;
|
||||
|
||||
_font = aFont;
|
||||
|
||||
[self setDirty];
|
||||
}
|
||||
|
||||
- (void)setTextColor:(CPColor)aColor
|
||||
{
|
||||
if (_textColor == aColor)
|
||||
return;
|
||||
|
||||
_textColor = aColor;
|
||||
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_submenuView setColor:[self textColor]];
|
||||
}
|
||||
|
||||
- (CPColor)textColor
|
||||
{
|
||||
return [_menuItem isEnabled] ? (_textColor ? _textColor : [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0]) : [CPColor lightGrayColor];
|
||||
}
|
||||
|
||||
- (void)setTextShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_textShadowColor == aColor)
|
||||
return;
|
||||
|
||||
_textShadowColor = aColor;
|
||||
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
//[_submenuView setColor:[self textColor]];
|
||||
}
|
||||
|
||||
- (CPColor)textShadowColor
|
||||
{
|
||||
return [_menuItem isEnabled] ? (_textShadowColor ? _textShadowColor : [CPColor colorWithWhite:1.0 alpha:0.8]) : [CPColor colorWithWhite:0.8 alpha:0.8];
|
||||
}
|
||||
|
||||
- (void)setActivateColor:(CPColor)aColor
|
||||
{
|
||||
_activateColor = aColor;
|
||||
}
|
||||
|
||||
- (CPColor)activateColor
|
||||
{
|
||||
return _activateColor;
|
||||
}
|
||||
|
||||
- (void)setActivateShadowColor:(CPColor)aColor
|
||||
{
|
||||
_activateShadowColor = aColor;
|
||||
}
|
||||
|
||||
- (CPColor)activateShadowColor
|
||||
{
|
||||
return _activateShadowColor;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuItemArrowView : CPView
|
||||
{
|
||||
CPColor _color;
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
if (_color == aColor)
|
||||
return;
|
||||
|
||||
_color = aColor;
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextMoveToPoint(context, 1.0, 4.0);
|
||||
CGContextAddLineToPoint(context, 9.0, 4.0);
|
||||
CGContextAddLineToPoint(context, 5.0, 8.0);
|
||||
CGContextAddLineToPoint(context, 1.0, 4.0);
|
||||
|
||||
CGContextClosePath(context);
|
||||
|
||||
CGContextSetFillColor(context, _color);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,239 @@
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
var HORIZONTAL_MARGIN = 8.0,
|
||||
SUBMENU_INDICATOR_MARGIN = 3.0,
|
||||
VERTICAL_MARGIN = 4.0;
|
||||
|
||||
var SelectionColor = nil,
|
||||
SUBMENU_INDICATOR_COLOR = nil,
|
||||
_CPMenuItemSelectionColor = nil,
|
||||
_CPMenuItemTextShadowColor = nil,
|
||||
|
||||
_CPMenuItemDefaultStateImages = [],
|
||||
_CPMenuItemDefaultStateHighlightedImages = [];
|
||||
|
||||
@implementation _CPMenuItemMenuBarView : CPView
|
||||
{
|
||||
CPMenuItem _menuItem @accessors(property=menuItem);
|
||||
|
||||
CPFont _font;
|
||||
CPColor _textColor;
|
||||
CPColor _textShadowColor;
|
||||
|
||||
BOOL _isDirty;
|
||||
|
||||
_CPImageAndTextView _imageAndTextView;
|
||||
CPView _submenuIndicatorView;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self !== [_CPMenuItemMenuBarView class])
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
SelectionColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"_CPMenuBarWindow/_CPMenuBarWindowBackgroundSelected.png"] size:CGSizeMake(1.0, 28.0)]];
|
||||
|
||||
SUBMENU_INDICATOR_COLOR = [CPColor grayColor];
|
||||
|
||||
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
||||
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
||||
}
|
||||
|
||||
+ (id)view
|
||||
{
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(HORIZONTAL_MARGIN, 0.0, 0.0, 0.0)];
|
||||
|
||||
[_imageAndTextView setImagePosition:CPImageLeft];
|
||||
[_imageAndTextView setImageOffset:3.0];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
[_imageAndTextView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin];
|
||||
|
||||
[self addSubview:_imageAndTextView];
|
||||
|
||||
_submenuIndicatorView = [[_CPMenuItemMenuBarSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 9.0, 6.0)];
|
||||
|
||||
[_submenuIndicatorView setAutoresizingMask:CPViewMinYMargin | CPViewMaxYMargin];
|
||||
|
||||
[self addSubview:_submenuIndicatorView];
|
||||
|
||||
[self setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPColor)textColor
|
||||
{
|
||||
if (![_menuItem isEnabled])
|
||||
return [CPColor lightGrayColor];
|
||||
|
||||
return _textColor || [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0];
|
||||
}
|
||||
|
||||
- (CPColor)textShadowColor
|
||||
{
|
||||
if (![_menuItem isEnabled])
|
||||
return [CPColor colorWithWhite:0.8 alpha:0.8];
|
||||
|
||||
return _textShadowColor || [CPColor colorWithWhite:1.0 alpha:0.8];
|
||||
}
|
||||
|
||||
- (void)update
|
||||
{
|
||||
var x = HORIZONTAL_MARGIN,
|
||||
height = 0.0;
|
||||
|
||||
[_imageAndTextView setFont:[_menuItem font] || [_CPMenuBarWindow font]];
|
||||
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
[_imageAndTextView setText:[_menuItem title]];
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
[_imageAndTextView sizeToFit];
|
||||
|
||||
var imageAndTextViewFrame = [_imageAndTextView frame];
|
||||
|
||||
imageAndTextViewFrame.origin.x = x;
|
||||
x += CGRectGetWidth(imageAndTextViewFrame);
|
||||
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame));
|
||||
|
||||
var hasSubmenuIndicator = [_menuItem hasSubmenu] && [_menuItem action];
|
||||
|
||||
if (hasSubmenuIndicator)
|
||||
{
|
||||
[_submenuIndicatorView setHidden:NO];
|
||||
[_submenuIndicatorView setColor:[self textColor]];
|
||||
[_submenuIndicatorView setShadowColor:[self textShadowColor]];
|
||||
|
||||
var submenuViewFrame = [_submenuIndicatorView frame];
|
||||
|
||||
submenuViewFrame.origin.x = x + SUBMENU_INDICATOR_MARGIN;
|
||||
|
||||
x = CGRectGetMaxX(submenuViewFrame);
|
||||
height = MAX(height, CGRectGetHeight(submenuViewFrame));
|
||||
}
|
||||
else
|
||||
[_submenuIndicatorView setHidden:YES];
|
||||
|
||||
height += 2.0 * VERTICAL_MARGIN;
|
||||
|
||||
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
|
||||
[_imageAndTextView setFrame:imageAndTextViewFrame];
|
||||
|
||||
if (hasSubmenuIndicator)
|
||||
{
|
||||
submenuViewFrame.origin.y = FLOOR((height - CGRectGetHeight(submenuViewFrame)) / 2.0) + 1.0;
|
||||
[_submenuIndicatorView setFrame:submenuViewFrame];
|
||||
}
|
||||
|
||||
_minSize = CGSizeMake(x + HORIZONTAL_MARGIN, height);
|
||||
|
||||
[self setAutoresizesSubviews:NO];
|
||||
[self setFrameSize:_minSize];
|
||||
[self setAutoresizesSubviews:YES];
|
||||
}
|
||||
|
||||
- (void)highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
// FIXME: This should probably be even throw.
|
||||
if (![_menuItem isEnabled])
|
||||
shouldHighlight = NO;
|
||||
|
||||
if (shouldHighlight)
|
||||
{
|
||||
if (![_menuItem _isMenuBarButton])
|
||||
[self setBackgroundColor:SelectionColor];
|
||||
|
||||
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
|
||||
[_imageAndTextView setTextColor:[CPColor whiteColor]];
|
||||
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
|
||||
|
||||
[_submenuIndicatorView setColor:[CPColor whiteColor]];
|
||||
[_submenuIndicatorView setShadowColor:[CPColor colorWithWhite:0.1 alpha:0.7]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setBackgroundColor:nil];
|
||||
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
|
||||
[_submenuIndicatorView setColor:[self textColor]];
|
||||
[_submenuIndicatorView setShadowColor:[self textShadowColor]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuItemMenuBarSubmenuIndicatorView : CPView
|
||||
{
|
||||
CPColor _color;
|
||||
CPColor _shadowColor;
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
if (_color === aColor)
|
||||
return;
|
||||
|
||||
_color = aColor;
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_shadowColor === aColor)
|
||||
return;
|
||||
|
||||
_shadowColor = aColor;
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
bounds = [self bounds];
|
||||
|
||||
bounds.size.height -= 1.0;
|
||||
bounds.size.width -= 2.0;
|
||||
bounds.origin.x += 1.0;
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), CGRectGetMinY(bounds));
|
||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), CGRectGetMinY(bounds));
|
||||
CGContextAddLineToPoint(context, CGRectGetMidX(bounds), CGRectGetMaxY(bounds));
|
||||
|
||||
CGContextClosePath(context);
|
||||
|
||||
CGContextSetShadowWithColor(context, CGSizeMake(0.0, 1.0), 1.1, _shadowColor || [CPColor whiteColor]);
|
||||
CGContextSetFillColor(context, _color || [CPColor blackColor]);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
@implementation _CPMenuItemSeparatorView : CPView
|
||||
{
|
||||
}
|
||||
|
||||
+ (id)view
|
||||
{
|
||||
return [[self alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 10.0)];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
[self setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
bounds = [self bounds];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), FLOOR(CGRectGetMidY(bounds)) - 0.5);
|
||||
|
||||
CGContextSetStrokeColor(context, [CPColor lightGrayColor]);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,306 @@
|
||||
@import "CPControl.j"
|
||||
|
||||
|
||||
var LEFT_MARGIN = 3.0,
|
||||
RIGHT_MARGIN = 14.0 + 3.0,
|
||||
STATE_COLUMN_WIDTH = 14.0,
|
||||
INDENTATION_WIDTH = 17.0,
|
||||
VERTICAL_MARGIN = 4.0,
|
||||
|
||||
RIGHT_COLUMNS_MARGIN = 30.0,
|
||||
KEY_EQUIVALENT_MARGIN = 10.0;
|
||||
|
||||
var SUBMENU_INDICATOR_COLOR = nil,
|
||||
_CPMenuItemSelectionColor = nil,
|
||||
_CPMenuItemTextShadowColor = nil,
|
||||
|
||||
_CPMenuItemDefaultStateImages = [],
|
||||
_CPMenuItemDefaultStateHighlightedImages = [];
|
||||
|
||||
@implementation _CPMenuItemStandardView : CPView
|
||||
{
|
||||
CPMenuItem _menuItem @accessors(property=menuItem);
|
||||
|
||||
CPFont _font;
|
||||
CPColor _textColor;
|
||||
CPColor _textShadowColor;
|
||||
|
||||
CGSize _minSize @accessors(readonly, property=minSize);
|
||||
BOOL _isDirty;
|
||||
|
||||
CPImageView _stateView;
|
||||
_CPImageAndTextView _imageAndTextView;
|
||||
_CPImageAndTextView _keyEquivalentView;
|
||||
CPView _submenuIndicatorView;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self !== [_CPMenuItemStandardView class])
|
||||
return;
|
||||
|
||||
SUBMENU_INDICATOR_COLOR = [CPColor grayColor];
|
||||
|
||||
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
||||
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0];
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
||||
}
|
||||
|
||||
+ (id)view
|
||||
{
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
+ (float)_standardLeftMargin
|
||||
{
|
||||
return LEFT_MARGIN + STATE_COLUMN_WIDTH;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_stateView = [[CPImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
||||
|
||||
[_stateView setImageScaling:CPScaleNone];
|
||||
|
||||
[self addSubview:_stateView];
|
||||
|
||||
_imageAndTextView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
||||
|
||||
[_imageAndTextView setImagePosition:CPImageLeft];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
|
||||
[self addSubview:_imageAndTextView];
|
||||
|
||||
_keyEquivalentView = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0)];
|
||||
|
||||
[_keyEquivalentView setImagePosition:CPNoImage];
|
||||
[_keyEquivalentView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
[_keyEquivalentView setAutoresizingMask:CPViewMinXMargin];
|
||||
|
||||
[self addSubview:_keyEquivalentView];
|
||||
|
||||
_submenuIndicatorView = [[_CPMenuItemSubmenuIndicatorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 8.0, 10.0)];
|
||||
|
||||
[_submenuIndicatorView setColor:SUBMENU_INDICATOR_COLOR];
|
||||
[_submenuIndicatorView setAutoresizingMask:CPViewMinXMargin];
|
||||
|
||||
[self addSubview:_submenuIndicatorView];
|
||||
|
||||
[self setAutoresizingMask:CPViewWidthSizable];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPColor)textColor
|
||||
{
|
||||
if (![_menuItem isEnabled])
|
||||
return [CPColor lightGrayColor];
|
||||
|
||||
return _textColor || [CPColor colorWithCalibratedRed:70.0 / 255.0 green:69.0 / 255.0 blue:69.0 / 255.0 alpha:1.0];
|
||||
}
|
||||
|
||||
- (CPColor)textShadowColor
|
||||
{
|
||||
if (![_menuItem isEnabled])
|
||||
return nil;
|
||||
|
||||
return _textShadowColor || [CPColor colorWithWhite:1.0 alpha:0.8];
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
_font = aFont;
|
||||
}
|
||||
|
||||
- (void)update
|
||||
{
|
||||
var x = LEFT_MARGIN + [_menuItem indentationLevel] * INDENTATION_WIDTH,
|
||||
height = 0.0,
|
||||
hasStateColumn = [[_menuItem menu] showsStateColumn];
|
||||
|
||||
if (hasStateColumn)
|
||||
{
|
||||
[_stateView setHidden:NO];
|
||||
[_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
|
||||
|
||||
x += STATE_COLUMN_WIDTH;
|
||||
}
|
||||
else
|
||||
[_stateView setHidden:YES];
|
||||
|
||||
[_imageAndTextView setFont:[_menuItem font] || _font];
|
||||
[_imageAndTextView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
[_imageAndTextView setText:[_menuItem title]];
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
[_imageAndTextView setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
[_imageAndTextView sizeToFit];
|
||||
|
||||
var imageAndTextViewFrame = [_imageAndTextView frame];
|
||||
|
||||
imageAndTextViewFrame.origin.x = x;
|
||||
x += CGRectGetWidth(imageAndTextViewFrame);
|
||||
height = MAX(height, CGRectGetHeight(imageAndTextViewFrame));
|
||||
|
||||
var hasKeyEquivalent = !![_menuItem keyEquivalent],
|
||||
hasSubmenu = [_menuItem hasSubmenu];
|
||||
|
||||
if (hasKeyEquivalent || hasSubmenu)
|
||||
x += RIGHT_COLUMNS_MARGIN;
|
||||
|
||||
if (hasKeyEquivalent)
|
||||
{
|
||||
[_keyEquivalentView setFont:[_menuItem font] || _font];
|
||||
[_keyEquivalentView setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
[_keyEquivalentView setImage:[_menuItem image]];
|
||||
[_keyEquivalentView setText:[_menuItem keyEquivalentStringRepresentation]];
|
||||
[_keyEquivalentView setTextColor:[self textColor]];
|
||||
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
|
||||
[_keyEquivalentView setTextShadowOffset:CGSizeMake(0, 1)];
|
||||
[_keyEquivalentView setFrameOrigin:CGPointMake(x, VERTICAL_MARGIN)];
|
||||
[_keyEquivalentView sizeToFit];
|
||||
|
||||
var keyEquivalentViewFrame = [_keyEquivalentView frame];
|
||||
|
||||
keyEquivalentViewFrame.origin.x = x;
|
||||
x += CGRectGetWidth(keyEquivalentViewFrame);
|
||||
height = MAX(height, CGRectGetHeight(keyEquivalentViewFrame));
|
||||
|
||||
if (hasSubmenu)
|
||||
x += RIGHT_COLUMNS_MARGIN;
|
||||
}
|
||||
else
|
||||
[_keyEquivalentView setHidden:YES];
|
||||
|
||||
if (hasSubmenu)
|
||||
{
|
||||
[_submenuIndicatorView setHidden:NO];
|
||||
|
||||
var submenuViewFrame = [_submenuIndicatorView frame];
|
||||
|
||||
submenuViewFrame.origin.x = x;
|
||||
|
||||
x += CGRectGetWidth(submenuViewFrame);
|
||||
height = MAX(height, CGRectGetHeight(submenuViewFrame));
|
||||
}
|
||||
else
|
||||
[_submenuIndicatorView setHidden:YES];
|
||||
|
||||
height += 2.0 * VERTICAL_MARGIN;
|
||||
|
||||
imageAndTextViewFrame.origin.y = FLOOR((height - CGRectGetHeight(imageAndTextViewFrame)) / 2.0);
|
||||
[_imageAndTextView setFrame:imageAndTextViewFrame];
|
||||
|
||||
if (hasStateColumn)
|
||||
[_stateView setFrameSize:CGSizeMake(STATE_COLUMN_WIDTH, height)];
|
||||
|
||||
if (hasKeyEquivalent)
|
||||
{
|
||||
keyEquivalentViewFrame.origin.y = FLOOR((height - CGRectGetHeight(keyEquivalentViewFrame)) / 2.0);
|
||||
[_keyEquivalentView setFrame:keyEquivalentViewFrame];
|
||||
}
|
||||
|
||||
if (hasSubmenu)
|
||||
{
|
||||
submenuViewFrame.origin.y = FLOOR((height - CGRectGetHeight(submenuViewFrame)) / 2.0);
|
||||
[_submenuIndicatorView setFrame:submenuViewFrame];
|
||||
}
|
||||
|
||||
_minSize = CGSizeMake(x + RIGHT_MARGIN, height);
|
||||
|
||||
[self setAutoresizesSubviews:NO];
|
||||
[self setFrameSize:_minSize];
|
||||
[self setAutoresizesSubviews:YES];
|
||||
}
|
||||
|
||||
- (void)highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
// FIXME: This should probably be even throw.
|
||||
if (![_menuItem isEnabled])
|
||||
return;
|
||||
|
||||
if (shouldHighlight)
|
||||
{
|
||||
[self setBackgroundColor:_CPMenuItemSelectionColor];
|
||||
|
||||
[_imageAndTextView setImage:[_menuItem alternateImage] || [_menuItem image]];
|
||||
[_imageAndTextView setTextColor:[CPColor whiteColor]];
|
||||
[_keyEquivalentView setTextColor:[CPColor whiteColor]];
|
||||
[_submenuIndicatorView setColor:[CPColor whiteColor]];
|
||||
|
||||
[_imageAndTextView setTextShadowColor:_CPMenuItemTextShadowColor];
|
||||
[_keyEquivalentView setTextShadowColor:_CPMenuItemTextShadowColor];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setBackgroundColor:nil];
|
||||
|
||||
[_imageAndTextView setImage:[_menuItem image]];
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_keyEquivalentView setTextColor:[self textColor]];
|
||||
[_submenuIndicatorView setColor:SUBMENU_INDICATOR_COLOR];
|
||||
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
[_keyEquivalentView setTextShadowColor:[self textShadowColor]];
|
||||
}
|
||||
|
||||
if ([[_menuItem menu] showsStateColumn])
|
||||
{
|
||||
if (shouldHighlight)
|
||||
[_stateView setImage:_CPMenuItemDefaultStateHighlightedImages[[_menuItem state]] || nil];
|
||||
else
|
||||
[_stateView setImage:_CPMenuItemDefaultStateImages[[_menuItem state]] || nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuItemSubmenuIndicatorView : CPView
|
||||
{
|
||||
CPColor _color;
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
if (_color === aColor)
|
||||
return;
|
||||
|
||||
_color = aColor;
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
bounds = [self bounds];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextMoveToPoint(context, CGRectGetMinX(bounds), CGRectGetMinY(bounds));
|
||||
CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), CGRectGetMidY(bounds));
|
||||
CGContextAddLineToPoint(context, CGRectGetMinX(bounds), CGRectGetMaxY(bounds));
|
||||
|
||||
CGContextClosePath(context);
|
||||
|
||||
CGContextSetFillColor(context, _color);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,257 @@
|
||||
|
||||
@import <AppKit/CPControl.j>
|
||||
|
||||
@import "_CPMenuItemSeparatorView.j"
|
||||
@import "_CPMenuItemStandardView.j"
|
||||
@import "_CPMenuItemMenuBarView.j"
|
||||
|
||||
|
||||
var LEFT_MARGIN = 3.0,
|
||||
RIGHT_MARGIN = 16.0,
|
||||
STATE_COLUMN_WIDTH = 14.0,
|
||||
INDENTATION_WIDTH = 17.0,
|
||||
VERTICAL_MARGIN = 4.0;
|
||||
|
||||
var _CPMenuItemSelectionColor = nil,
|
||||
_CPMenuItemTextShadowColor = nil,
|
||||
|
||||
_CPMenuItemDefaultStateImages = [],
|
||||
_CPMenuItemDefaultStateHighlightedImages = [];
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
@implementation _CPMenuItemView : CPView
|
||||
{
|
||||
CPMenuItem _menuItem;
|
||||
CPView _view;
|
||||
|
||||
CPFont _font;
|
||||
CPColor _textColor;
|
||||
CPColor _textShadowColor;
|
||||
|
||||
CGSize _minSize;
|
||||
BOOL _isDirty;
|
||||
BOOL _showsStateColumn;
|
||||
|
||||
CPImageView _stateView;
|
||||
_CPImageAndTextView _imageAndTextView;
|
||||
CPView _submenuView;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPMenuItemView class])
|
||||
return;
|
||||
|
||||
_CPMenuItemSelectionColor = [CPColor colorWithCalibratedRed:95.0 / 255.0 green:131.0 / 255.0 blue:185.0 / 255.0 alpha:1.0];
|
||||
_CPMenuItemTextShadowColor = [CPColor colorWithCalibratedRed:26.0 / 255.0 green: 73.0 / 255.0 blue:109.0 / 255.0 alpha:1.0];
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOffState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOffState] = nil;
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnState.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPOnState] = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPMenuItem/CPMenuItemOnStateHighlighted.png"] size:CGSizeMake(14.0, 14.0)];
|
||||
|
||||
_CPMenuItemDefaultStateImages[CPMixedState] = nil;
|
||||
_CPMenuItemDefaultStateHighlightedImages[CPMixedState] = nil;
|
||||
}
|
||||
|
||||
+ (float)leftMargin
|
||||
{
|
||||
return LEFT_MARGIN + STATE_COLUMN_WIDTH;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame forMenuItem:(CPMenuItem)aMenuItem
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_menuItem = aMenuItem;
|
||||
_showsStateColumn = YES;
|
||||
_isDirty = YES;
|
||||
|
||||
[self setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[self synchronizeWithMenuItem];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGSize)minSize
|
||||
{
|
||||
return _minSize;
|
||||
}
|
||||
|
||||
- (void)setDirty
|
||||
{
|
||||
_isDirty = YES;
|
||||
}
|
||||
|
||||
- (void)synchronizeWithMenuItem
|
||||
{
|
||||
var menuItemView = [_menuItem view];
|
||||
|
||||
if ([_menuItem isSeparatorItem])
|
||||
{
|
||||
if (![_view isKindOfClass:[_CPMenuItemSeparatorView class]])
|
||||
{
|
||||
[_view removeFromSuperview];
|
||||
_view = [_CPMenuItemSeparatorView view];
|
||||
}
|
||||
}
|
||||
else if (menuItemView)
|
||||
{
|
||||
if (_view !== menuItemView)
|
||||
{
|
||||
[_view removeFromSuperview];
|
||||
_view = menuItemView;
|
||||
}
|
||||
}
|
||||
|
||||
else if ([_menuItem menu] == [CPApp mainMenu])
|
||||
{
|
||||
if (![_view isKindOfClass:[_CPMenuItemMenuBarView class]])
|
||||
{
|
||||
[_view removeFromSuperview];
|
||||
_view = [_CPMenuItemMenuBarView view];
|
||||
}
|
||||
|
||||
[_view setMenuItem:_menuItem];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (![_view isKindOfClass:[_CPMenuItemStandardView class]])
|
||||
{
|
||||
[_view removeFromSuperview];
|
||||
_view = [_CPMenuItemStandardView view];
|
||||
}
|
||||
|
||||
[_view setMenuItem:_menuItem];
|
||||
}
|
||||
|
||||
if ([_view superview] !== self)
|
||||
[self addSubview:_view];
|
||||
|
||||
if ([_view respondsToSelector:@selector(update)])
|
||||
[_view update];
|
||||
|
||||
_minSize = [_view frame].size;
|
||||
[self setAutoresizesSubviews:NO];
|
||||
[self setFrameSize:_minSize];
|
||||
[self setAutoresizesSubviews:YES];
|
||||
}
|
||||
|
||||
- (void)setShowsStateColumn:(BOOL)shouldShowStateColumn
|
||||
{
|
||||
_showsStateColumn = shouldShowStateColumn;
|
||||
}
|
||||
|
||||
- (void)highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(highlight:)])
|
||||
[_view highlight:shouldHighlight];
|
||||
}
|
||||
|
||||
- (BOOL)eventOnSubmenu:(CPEvent)anEvent
|
||||
{
|
||||
if (![_menuItem hasSubmenu])
|
||||
return NO;
|
||||
|
||||
return CGRectContainsPoint([_submenuView frame], [self convertPoint:[anEvent locationInWindow] fromView:nil]);
|
||||
}
|
||||
|
||||
- (BOOL)isHidden
|
||||
{
|
||||
return [_menuItem isHidden];
|
||||
}
|
||||
|
||||
- (CPMenuItem)menuItem
|
||||
{
|
||||
return _menuItem;
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
if (_font === aFont)
|
||||
return;
|
||||
|
||||
_font = aFont;
|
||||
|
||||
if ([_view respondsToSelector:@selector(setFont:)])
|
||||
[_view setFont:aFont];
|
||||
|
||||
[self setDirty];
|
||||
}
|
||||
|
||||
- (void)setTextColor:(CPColor)aColor
|
||||
{
|
||||
if (_textColor == aColor)
|
||||
return;
|
||||
|
||||
_textColor = aColor;
|
||||
|
||||
[_imageAndTextView setTextColor:[self textColor]];
|
||||
[_submenuView setColor:[self textColor]];
|
||||
}
|
||||
|
||||
- (CPColor)textColor
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)setTextShadowColor:(CPColor)aColor
|
||||
{
|
||||
if (_textShadowColor == aColor)
|
||||
return;
|
||||
|
||||
_textShadowColor = aColor;
|
||||
|
||||
[_imageAndTextView setTextShadowColor:[self textShadowColor]];
|
||||
//[_submenuView setColor:[self textColor]];
|
||||
}
|
||||
|
||||
- (CPColor)textShadowColor
|
||||
{
|
||||
return [_menuItem isEnabled] ? (_textShadowColor ? _textShadowColor : [CPColor colorWithWhite:1.0 alpha:0.8]) : [CPColor colorWithWhite:0.8 alpha:0.8];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPMenuItemArrowView : CPView
|
||||
{
|
||||
CPColor _color;
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)aColor
|
||||
{
|
||||
if (_color == aColor)
|
||||
return;
|
||||
|
||||
_color = aColor;
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextMoveToPoint(context, 1.0, 4.0);
|
||||
CGContextAddLineToPoint(context, 9.0, 4.0);
|
||||
CGContextAddLineToPoint(context, 5.0, 8.0);
|
||||
CGContextAddLineToPoint(context, 1.0, 4.0);
|
||||
|
||||
CGContextClosePath(context);
|
||||
|
||||
CGContextSetFillColor(context, _color);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,652 @@
|
||||
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPCountedSet.j>
|
||||
|
||||
@import "CPController.j"
|
||||
|
||||
|
||||
@implementation CPObjectController : CPController
|
||||
{
|
||||
id _contentObject;
|
||||
id _selection;
|
||||
|
||||
Class _objectClass;
|
||||
CPString _objectClassName;
|
||||
|
||||
BOOL _isEditable;
|
||||
BOOL _automaticallyPreparesContent;
|
||||
|
||||
CPCountedSet _observedKeys;
|
||||
}
|
||||
|
||||
+ (id)initialize
|
||||
{
|
||||
[self exposeBinding:@"editable"];
|
||||
[self exposeBinding:@"contentObject"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingContentObject
|
||||
{
|
||||
return [CPSet setWithObjects:"content"];
|
||||
}
|
||||
|
||||
+ (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey
|
||||
{
|
||||
if (aKey === @"contentObject")
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingCanAdd
|
||||
{
|
||||
return [CPSet setWithObject:"editable"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingCanInsert
|
||||
{
|
||||
return [CPSet setWithObject:"editable"];
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingCanRemove
|
||||
{
|
||||
return [CPSet setWithObjects:"editable", "selection"];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithContent:nil];
|
||||
}
|
||||
|
||||
- (id)initWithContent:(id)aContent
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
[self setContent:aContent];
|
||||
[self setEditable:YES];
|
||||
[self setObjectClass:[CPMutableDictionary class]];
|
||||
|
||||
_observedKeys = [[CPCountedSet alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)content
|
||||
{
|
||||
return _contentObject;
|
||||
}
|
||||
|
||||
- (void)setContent:(id)aContent
|
||||
{
|
||||
[self willChangeValueForKey:@"contentObject"];
|
||||
[self _selectionWillChange];
|
||||
|
||||
_contentObject = aContent;
|
||||
|
||||
[self didChangeValueForKey:@"contentObject"];
|
||||
[self _selectionDidChange];
|
||||
}
|
||||
|
||||
- (void)_setContentObject:(id)aContent
|
||||
{
|
||||
[self setContent:aContent];
|
||||
}
|
||||
|
||||
- (id)_contentObject
|
||||
{
|
||||
return [self content];
|
||||
}
|
||||
|
||||
- (void)setAutomaticallyPreparesContent:(BOOL)shouldAutomaticallyPrepareContent
|
||||
{
|
||||
_automaticallyPreparesContent = shouldAutomaticallyPrepareContent;
|
||||
}
|
||||
|
||||
- (BOOL)automaticallyPreparesContent
|
||||
{
|
||||
return _automaticallyPreparesContent;
|
||||
}
|
||||
|
||||
- (void)prepareContent
|
||||
{
|
||||
[self setContent:[self newObject]];
|
||||
}
|
||||
|
||||
- (void)setObjectClass:(Class)aClass
|
||||
{
|
||||
_objectClass = aClass;
|
||||
}
|
||||
|
||||
- (Class)objectClass
|
||||
{
|
||||
return _objectClass;
|
||||
}
|
||||
|
||||
- (id)_defaultNewObject
|
||||
{
|
||||
return [[[self objectClass] alloc] init];
|
||||
}
|
||||
|
||||
- (id)newObject
|
||||
{
|
||||
return [self _defaultNewObject];
|
||||
}
|
||||
|
||||
- (void)addObject:(id)anObject
|
||||
{
|
||||
[self setContent:anObject];
|
||||
|
||||
var binderClass = [[self class] _binderClassForBinding:@"contentObject"];
|
||||
[[binderClass getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
|
||||
}
|
||||
|
||||
- (void)removeObject:(id)anObject
|
||||
{
|
||||
if ([self content] === anObject)
|
||||
[self setContent:nil];
|
||||
|
||||
var binderClass = [[self class] _binderClassForBinding:@"contentObject"];
|
||||
[[binderClass getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
|
||||
}
|
||||
|
||||
- (void)add:(id)aSender
|
||||
{
|
||||
// FIXME: This should happen on the next run loop?
|
||||
[self addObject:[self newObject]];
|
||||
}
|
||||
|
||||
- (BOOL)canAdd
|
||||
{
|
||||
return [self isEditable];
|
||||
}
|
||||
|
||||
- (void)remove:(id)aSender
|
||||
{
|
||||
// FIXME: This should happen on the next run loop?
|
||||
[self removeObject:[self content]];
|
||||
}
|
||||
|
||||
- (BOOL)canRemove
|
||||
{
|
||||
return [self isEditable] && [[self selectedObjects] count];
|
||||
}
|
||||
|
||||
- (void)setEditable:(BOOL)shouldBeEditable
|
||||
{
|
||||
_isEditable = shouldBeEditable;
|
||||
}
|
||||
|
||||
- (BOOL)isEditable
|
||||
{
|
||||
return _isEditable;
|
||||
}
|
||||
|
||||
- (CPArray)selectedObjects
|
||||
{
|
||||
return [[_CPObservableArray alloc] initWithArray:[_contentObject]];
|
||||
}
|
||||
|
||||
- (id)selection
|
||||
{
|
||||
return _selection;
|
||||
}
|
||||
|
||||
- (void)_selectionWillChange
|
||||
{
|
||||
[_selection controllerWillChange];
|
||||
[self willChangeValueForKey:@"selection"];
|
||||
}
|
||||
|
||||
- (void)_selectionDidChange
|
||||
{
|
||||
if (_selection === undefined || _selection === nil)
|
||||
_selection = [[CPControllerSelectionProxy alloc] initWithController:self];
|
||||
|
||||
[_selection controllerDidChange];
|
||||
[self didChangeValueForKey:@"selection"];
|
||||
}
|
||||
|
||||
- (id)observedKeys
|
||||
{
|
||||
return _observedKeys;
|
||||
}
|
||||
|
||||
- (void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
|
||||
{
|
||||
[_observedKeys addObject:aKeyPath];
|
||||
[super addObserver:anObserver forKeyPath:aKeyPath options:options context:context];
|
||||
}
|
||||
|
||||
- (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
[_observedKeys removeObject:aKeyPath];
|
||||
[super removeObserver:anObserver forKeyPath:aKeyPath];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPObjectControllerContentKey = @"CPObjectControllerContentKey",
|
||||
CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
|
||||
CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
|
||||
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey";
|
||||
|
||||
@implementation CPObjectController (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey],
|
||||
objectClass = CPClassFromString(objectClassName);
|
||||
|
||||
[self setObjectClass:objectClass || [CPMutableDictionary class]];
|
||||
[self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]];
|
||||
[self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey] || NO];
|
||||
[self setContent:[aCoder decodeObjectForKey:CPObjectControllerContentKey]];
|
||||
|
||||
_observedKeys = [[CPCountedSet alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:[self content] forKey:CPObjectControllerContentKey];
|
||||
|
||||
if (_objectClass)
|
||||
[aCoder encodeObject:CPStringFromClass(_objectClass) forKey:CPObjectControllerObjectClassNameKey];
|
||||
else if (_objectClassName)
|
||||
[aCoder encodeObject:_objectClassName forKey:CPObjectControllerObjectClassNameKey];
|
||||
|
||||
[aCoder encodeBool:[self isEditable] forKey:CPObjectControllerIsEditableKey];
|
||||
[aCoder encodeBool:[self automaticallyPreparesContent] forKey:CPObjectControllerAutomaticallyPreparesContentKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPObservationProxy : CPObject
|
||||
{
|
||||
id _keyPath;
|
||||
id _observer;
|
||||
id _object;
|
||||
|
||||
BOOL _notifyObject;
|
||||
|
||||
id _context;
|
||||
int _options;
|
||||
}
|
||||
|
||||
- (id)initWithKeyPath:(id)aKeyPath observer:(id)anObserver object:(id)anObject
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_keyPath = aKeyPath;
|
||||
_observer = anObserver;
|
||||
_object = anObject;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)observer
|
||||
{
|
||||
return _observer;
|
||||
}
|
||||
|
||||
- (id)keyPath
|
||||
{
|
||||
return _keyPath;
|
||||
}
|
||||
|
||||
- (id)context
|
||||
{
|
||||
return _context;
|
||||
}
|
||||
|
||||
- (int)options
|
||||
{
|
||||
return _options;
|
||||
}
|
||||
|
||||
- (void)setNotifyObject:(BOOL)notify
|
||||
{
|
||||
_notifyObject = notify;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)anObject
|
||||
{
|
||||
if ([anObject class] === [self class])
|
||||
{
|
||||
if (anObject._observer === _observer && [anObject._keyPath isEqual:_keyPath] && [anObject._object isEqual:_object])
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)change context:(id)context
|
||||
{
|
||||
if (_notifyObject)
|
||||
[_object observeValueForKeyPath:aKeyPath ofObject:_object change:change context:context];
|
||||
|
||||
[_observer observeValueForKeyPath:aKeyPath ofObject:_object change:change context:context];
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [super description] + [CPString stringWithFormat:@"observation proxy for %@ on key path %@", _observer, _keyPath];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// FIXME: This should subclass CPMutableArray not _CPJavaScriptArray
|
||||
@implementation _CPObservableArray : _CPJavaScriptArray
|
||||
{
|
||||
CPArray _observationProxies;
|
||||
}
|
||||
|
||||
+ (id)alloc
|
||||
{
|
||||
var a = [];
|
||||
a.isa = self;
|
||||
|
||||
var ivars = class_copyIvarList(self),
|
||||
count = ivars.length;
|
||||
|
||||
while (count--)
|
||||
a[ivar_getName(ivars[count])] = nil;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return "<_CPObservableArray: "+[super description]+" >";
|
||||
}
|
||||
|
||||
- (id)initWithArray:(CPArray)anArray
|
||||
{
|
||||
self = [super initWithArray:anArray];
|
||||
|
||||
self.isa = [_CPObservableArray class];
|
||||
self._observationProxies = [];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
|
||||
{
|
||||
if (aKeyPath.indexOf("@") === 0)
|
||||
{
|
||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self];
|
||||
|
||||
proxy._options = options;
|
||||
proxy._context = context;
|
||||
|
||||
[_observationProxies addObject:proxy];
|
||||
|
||||
var dotIndex = aKeyPath.indexOf("."),
|
||||
remaining = aKeyPath.substring(dotIndex + 1),
|
||||
indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
||||
|
||||
[self addObserver:proxy toObjectsAtIndexes:indexes forKeyPath:remaining options:options context:context];
|
||||
}
|
||||
else
|
||||
{
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
||||
[self addObserver:anObserver toObjectsAtIndexes:indexes forKeyPath:aKeyPath options:options context:context];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
if (aKeyPath.indexOf("@") === 0)
|
||||
{
|
||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self],
|
||||
index = [_observationProxies indexOfObject:proxy];
|
||||
|
||||
proxy = [_observationProxies objectAtIndex:index];
|
||||
|
||||
var dotIndex = aKeyPath.indexOf("."),
|
||||
remaining = aKeyPath.substring(dotIndex + 1),
|
||||
indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
||||
|
||||
[self removeObserver:proxy fromObjectsAtIndexes:indexes forKeyPath:remaining];
|
||||
}
|
||||
else
|
||||
{
|
||||
var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
|
||||
[self removeObserver:anObserver fromObjectsAtIndexes:indexes forKeyPath:aKeyPath];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)insertObject:(id)anObject atIndex:(unsigned)anIndex
|
||||
{
|
||||
for (var i = 0, count = [_observationProxies count]; i < count; i++)
|
||||
{
|
||||
var proxy = [_observationProxies objectAtIndex:i],
|
||||
keyPath = [proxy keyPath],
|
||||
operator = keyPath.indexOf(".") === 0;
|
||||
|
||||
if (operator)
|
||||
[self willChangeValueForKey:keyPath];
|
||||
|
||||
[anObject addObserver:proxy forKeyPath:keyPath options:[proxy options] context:[proxy context]];
|
||||
|
||||
if (operator)
|
||||
[self didChangeValueForKey:keyPath];
|
||||
}
|
||||
|
||||
[super insertObject:anObject atIndex:anIndex];
|
||||
}
|
||||
|
||||
- (void)removeObjectAtIndex:(unsigned)anIndex
|
||||
{
|
||||
for (var i = 0, count = [_observationProxies count]; i < count; i++)
|
||||
{
|
||||
var proxy = [_observationProxies objectAtIndex:i],
|
||||
keyPath = [proxy keyPath],
|
||||
operator = keyPath.indexOf(".") === 0;
|
||||
|
||||
if (operator)
|
||||
[self willChangeValueForKey:keyPath];
|
||||
|
||||
[anObject removeObserver:proxy forKeyPath:keyPath];
|
||||
|
||||
if (operator)
|
||||
[self didChangeValueForKey:keyPath];
|
||||
}
|
||||
|
||||
[super removeObjectAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (_CPObservableArray)objectsAtIndexes:(CPIndexSet)theIndexes
|
||||
{
|
||||
return [_CPObservableArray arrayWithArray:[super objectsAtIndexes:theIndexes]];
|
||||
}
|
||||
|
||||
- (void)addObject:(id)anObject
|
||||
{
|
||||
[self insertObject:anObject atIndex:[self count]];
|
||||
}
|
||||
|
||||
- (void)removeLastObject
|
||||
{
|
||||
[self removeObjectAtIndex:[self count]];
|
||||
}
|
||||
|
||||
- (void)replaceObjectAtIndex:(unsigned)anIndex withObject:(id)anObject
|
||||
{
|
||||
var currentObject = [self objectAtIndex:anIndex];
|
||||
|
||||
for (var i = 0, count = [_observationProxies count]; i < count; i++)
|
||||
{
|
||||
var proxy = [_observationProxies objectAtIndex:i],
|
||||
keyPath = [proxy keyPath],
|
||||
operator = keyPath.indexOf(".") === 0;
|
||||
|
||||
if (operator)
|
||||
[self willChangeValueForKey:keyPath];
|
||||
|
||||
[currentObject removeObserver:proxy forKeyPath:keyPath];
|
||||
[anObject addObserver:proxy forKeyPath:keyPath options:[proxy options] context:[proxy context]];
|
||||
|
||||
if (operator)
|
||||
[self didChangeValueForKey:keyPath];
|
||||
}
|
||||
|
||||
[super replaceObjectAtIndex:anIndex withObject:anObject];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPControllerSelectionProxy : CPObject
|
||||
{
|
||||
id _controller;
|
||||
id _keys;
|
||||
|
||||
CPDictionary _cachedValues;
|
||||
CPArray _observationProxies;
|
||||
|
||||
Object _observedObjectsByKeyPath;
|
||||
}
|
||||
|
||||
- (id)initWithController:(id)aController
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_cachedValues = [CPDictionary dictionary];
|
||||
_observationProxies = [CPArray array];
|
||||
_controller = aController;
|
||||
_observedObjectsByKeyPath = {};
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)_controllerMarkerForValues:(CPArray)theValues
|
||||
{
|
||||
var count = [theValues count];
|
||||
|
||||
if (!count)
|
||||
value = CPNoSelectionMarker;
|
||||
else if (count === 1)
|
||||
value = [theValues objectAtIndex:0];
|
||||
else
|
||||
{
|
||||
if ([_controller alwaysUsesMultipleValuesMarker])
|
||||
value = CPMultipleValuesMarker;
|
||||
else
|
||||
{
|
||||
value = [theValues objectAtIndex:0];
|
||||
|
||||
for (var i = 0, count= [theValues count]; i < count && value != CPMultipleValuesMarker; i++)
|
||||
{
|
||||
if (![value isEqual:[theValues objectAtIndex:i]])
|
||||
value = CPMultipleValuesMarker;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
- (id)valueForKeyPath:(CPString)theKeyPath
|
||||
{
|
||||
var values = [[_controller selectedObjects] valueForKeyPath:theKeyPath];
|
||||
value = [self _controllerMarkerForValues:values];
|
||||
|
||||
[_cachedValues setObject:value forKey:theKeyPath];
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
- (id)valueForKey:(CPString)theKeyPath
|
||||
{
|
||||
return [self valueForKeyPath:theKeyPath];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)theValue forKeyPath:(CPString)theKeyPath
|
||||
{
|
||||
[[_controller selectedObjects] setValue:theValue forKeyPath:theKeyPath];
|
||||
[_cachedValues removeObjectForKey:theKeyPath];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)theValue forKey:(CPString)theKeyPath
|
||||
{
|
||||
[self setValue:theKeyPath forKeyPath:theKeyPath];
|
||||
}
|
||||
|
||||
- (unsigned)count
|
||||
{
|
||||
return [_cachedValues count];
|
||||
}
|
||||
|
||||
- (id)keyEnumerator
|
||||
{
|
||||
return [_cachedValues keyEnumerator];
|
||||
}
|
||||
|
||||
- (void)controllerWillChange
|
||||
{
|
||||
_keys = [_cachedValues allKeys];
|
||||
|
||||
if (!_keys)
|
||||
return;
|
||||
|
||||
for (var i = 0, count = _keys.length; i < count; i++)
|
||||
[self willChangeValueForKey:_keys[i]];
|
||||
|
||||
[_cachedValues removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)controllerDidChange
|
||||
{
|
||||
[_cachedValues removeAllObjects];
|
||||
|
||||
if (!_keys)
|
||||
return;
|
||||
|
||||
for (var i = 0, count = _keys.length; i < count; i++)
|
||||
[self didChangeValueForKey:_keys[i]];
|
||||
|
||||
_keys = nil;
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)change context:(id)context
|
||||
{
|
||||
[_cachedValues removeObjectForKey:aKeyPath];
|
||||
}
|
||||
|
||||
- (void)addObserver:(id)anObject forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
|
||||
{
|
||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObject object:self];
|
||||
|
||||
[proxy setNotifyObject:YES];
|
||||
[_observationProxies addObject:proxy];
|
||||
|
||||
// We keep are reference to the observed objects
|
||||
// because the removeObserver: will be called after the selection changes
|
||||
var observedObjects = [_controller selectedObjects];
|
||||
_observedObjectsByKeyPath[aKeyPath] = observedObjects;
|
||||
[observedObjects addObserver:proxy forKeyPath:aKeyPath options:options context:context];
|
||||
}
|
||||
|
||||
- (void)removeObserver:(id)anObject forKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObject object:self],
|
||||
index = [_observationProxies indexOfObject:proxy];
|
||||
|
||||
var observedObjects = _observedObjectsByKeyPath[aKeyPath];
|
||||
[observedObjects removeObserver:[_observationProxies objectAtIndex:index] forKeyPath:aKeyPath];
|
||||
|
||||
[_observationProxies removeObjectAtIndex:index];
|
||||
|
||||
_observedObjects = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
@import <AppKit/CPPanel.j>
|
||||
@import "CPPanel.j"
|
||||
|
||||
|
||||
@implementation CPOpenPanel : CPPanel
|
||||
|
||||
+1144
-187
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -51,7 +51,7 @@ CPCancelButton = 0;
|
||||
@global
|
||||
@class CPWindow
|
||||
*/
|
||||
CPDocModalWindowMask = 1 << 6;
|
||||
CPDocModalWindowMask = 1 << 6;
|
||||
|
||||
@implementation CPPanel : CPWindow
|
||||
{
|
||||
|
||||
+110
-40
@@ -25,8 +25,6 @@
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPPropertyListSerialization.j>
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
|
||||
|
||||
CPGeneralPboard = @"CPGeneralPboard";
|
||||
CPFontPboard = @"CPFontPboard";
|
||||
@@ -43,26 +41,31 @@ CPURLPboardType = @"CPURLPboardType";
|
||||
CPImagesPboardType = @"CPImagesPboardType";
|
||||
CPVideosPboardType = @"CPVideosPboardType";
|
||||
|
||||
UTF8PboardType = @"public.utf8-plain-text";
|
||||
|
||||
// Deprecated
|
||||
CPImagePboardType = @"CPImagePboardType";
|
||||
|
||||
|
||||
var CPPasteboards = nil;
|
||||
var CPPasteboards = nil,
|
||||
supportsNativePasteboard = NO;
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPPasteboard
|
||||
|
||||
CPPasteBoard is the object responsible for cut/copy/paste and drag&drop operations.
|
||||
CPPasteBoard is the object responsible for cut/copy/paste and drag&drop operations.
|
||||
*/
|
||||
@implementation CPPasteboard : CPObject
|
||||
{
|
||||
CPArray _types;
|
||||
CPDictionary _owners;
|
||||
CPDictionary _provided;
|
||||
|
||||
|
||||
unsigned _changeCount;
|
||||
CPString _stateUID;
|
||||
|
||||
WebScriptObject _nativePasteboard;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -74,8 +77,11 @@ var CPPasteboards = nil;
|
||||
return;
|
||||
|
||||
[self setVersion:1.0];
|
||||
|
||||
|
||||
CPPasteboards = [CPDictionary dictionary];
|
||||
|
||||
if (typeof window.cpPasteboardWithName !== "undefined")
|
||||
supportsNativePasteboard = YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -94,13 +100,13 @@ var CPPasteboards = nil;
|
||||
+ (id)pasteboardWithName:(CPString)aName
|
||||
{
|
||||
var pasteboard = [CPPasteboards objectForKey:aName];
|
||||
|
||||
|
||||
if (pasteboard)
|
||||
return pasteboard;
|
||||
|
||||
|
||||
pasteboard = [[CPPasteboard alloc] _initWithName:aName];
|
||||
[CPPasteboards setObject:pasteboard forKey:aName];
|
||||
|
||||
|
||||
return pasteboard;
|
||||
}
|
||||
|
||||
@@ -108,18 +114,24 @@ var CPPasteboards = nil;
|
||||
- (id)_initWithName:(CPString)aName
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_name = aName;
|
||||
_types = [];
|
||||
|
||||
|
||||
_owners = [CPDictionary dictionary];
|
||||
_provided = [CPDictionary dictionary];
|
||||
|
||||
|
||||
_changeCount = 0;
|
||||
|
||||
if (supportsNativePasteboard)
|
||||
{
|
||||
_nativePasteboard = window.cpPasteboardWithName(aName);
|
||||
[self _synchronizePasteboard];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -133,20 +145,29 @@ var CPPasteboards = nil;
|
||||
{
|
||||
var i = 0,
|
||||
count = types.length;
|
||||
|
||||
|
||||
for (; i < count; ++i)
|
||||
{
|
||||
var type = types[i];
|
||||
|
||||
if(![_owners objectForKey:type])
|
||||
|
||||
if (![_owners objectForKey:type])
|
||||
{
|
||||
[_types addObject:type];
|
||||
[_provided removeObjectForKey:type];
|
||||
}
|
||||
|
||||
|
||||
[_owners setObject:anOwner forKey:type];
|
||||
}
|
||||
|
||||
|
||||
if (_nativePasteboard)
|
||||
{
|
||||
var nativeTypes = [types copy];
|
||||
if ([types containsObject:CPStringPboardType])
|
||||
nativeTypes.push(UTF8PboardType);
|
||||
|
||||
_nativePasteboard.addTypes_(nativeTypes);
|
||||
}
|
||||
|
||||
return ++_changeCount;
|
||||
}
|
||||
|
||||
@@ -157,6 +178,12 @@ var CPPasteboards = nil;
|
||||
@return the pasteboard's change count
|
||||
*/
|
||||
- (unsigned)declareTypes:(CPArray)types owner:(id)anOwner
|
||||
{
|
||||
[self _declareTypes:types owner:anOwner updateNativePasteboard:YES];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (unsigned)_declareTypes:(CPArray)types owner:(id)anOwner updateNativePasteboard:(BOOL)shouldUpdate
|
||||
{
|
||||
[_types setArray:types];
|
||||
|
||||
@@ -164,10 +191,19 @@ var CPPasteboards = nil;
|
||||
_provided = [CPDictionary dictionary];
|
||||
|
||||
var count = _types.length;
|
||||
|
||||
|
||||
while (count--)
|
||||
[_owners setObject:anOwner forKey:_types[count]];
|
||||
|
||||
|
||||
if (_nativePasteboard && shouldUpdate)
|
||||
{
|
||||
var nativeTypes = [types copy];
|
||||
if ([types containsObject:CPStringPboardType])
|
||||
nativeTypes.push(UTF8PboardType);
|
||||
|
||||
_nativePasteboard.declareTypes_(nativeTypes);
|
||||
_changeCount = _nativePasteboard.changeCount();
|
||||
}
|
||||
return ++_changeCount;
|
||||
}
|
||||
|
||||
@@ -180,7 +216,10 @@ var CPPasteboards = nil;
|
||||
- (BOOL)setData:(CPData)aData forType:(CPString)aType
|
||||
{
|
||||
[_provided setObject:aData forKey:aType];
|
||||
|
||||
|
||||
if (aType === CPStringPboardType)
|
||||
[self setData:aData forType:UTF8PboardType];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -192,7 +231,7 @@ var CPPasteboards = nil;
|
||||
*/
|
||||
- (BOOL)setPropertyList:(id)aPropertyList forType:(CPString)aType
|
||||
{
|
||||
return [self setData:[CPPropertyListSerialization dataFromPropertyList:aPropertyList format:CPPropertyListXMLFormat_v1_0 errorDescription:nil] forType:aType];
|
||||
return [self setData:[CPPropertyListSerialization dataFromPropertyList:aPropertyList format:CPPropertyList280NorthFormat_v1_0] forType:aType];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -203,7 +242,7 @@ var CPPasteboards = nil;
|
||||
*/
|
||||
- (void)setString:(CPString)aString forType:(CPString)aType
|
||||
{
|
||||
return [self setPropertyList:aString forType:aType];
|
||||
[self setPropertyList:aString forType:aType];
|
||||
}
|
||||
|
||||
// Determining Types
|
||||
@@ -223,6 +262,7 @@ var CPPasteboards = nil;
|
||||
*/
|
||||
- (CPArray)types
|
||||
{
|
||||
[self _synchronizePasteboard];
|
||||
return _types;
|
||||
}
|
||||
|
||||
@@ -243,24 +283,54 @@ var CPPasteboards = nil;
|
||||
- (CPData)dataForType:(CPString)aType
|
||||
{
|
||||
var data = [_provided objectForKey:aType];
|
||||
|
||||
|
||||
if (data)
|
||||
return data;
|
||||
|
||||
|
||||
var owner = [_owners objectForKey:aType];
|
||||
|
||||
|
||||
if (owner)
|
||||
{
|
||||
[owner pasteboard:self provideDataForType:aType];
|
||||
|
||||
++_changeCount;
|
||||
|
||||
return [_provided objectForKey:aType];
|
||||
}
|
||||
|
||||
|
||||
if (aType === CPStringPboardType)
|
||||
return [self dataForType:UTF8PboardType];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)_synchronizePasteboard
|
||||
{
|
||||
if (_nativePasteboard && _nativePasteboard.changeCount() > _changeCount)
|
||||
{
|
||||
var nativeTypes = [_nativePasteboard.types() copy];
|
||||
if ([nativeTypes containsObject:UTF8PboardType])
|
||||
nativeTypes.push(CPStringPboardType);
|
||||
|
||||
[self _declareTypes:nativeTypes owner:self updateNativePasteboard:NO];
|
||||
|
||||
_changeCount = _nativePasteboard.changeCount();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @ignore
|
||||
method provided for integration with native pasteboard
|
||||
*/
|
||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||
{
|
||||
if (aType === CPStringPboardType)
|
||||
{
|
||||
var string = _nativePasteboard.stringForType_(UTF8PboardType);
|
||||
|
||||
[self setString:string forType:CPStringPboardType];
|
||||
[self setString:string forType:UTF8PboardType];
|
||||
}
|
||||
else
|
||||
[self setString:_nativePasteboard.stringForType_(aType) forType:aType];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the property list for the specified data type
|
||||
@param aType the requested data type
|
||||
@@ -269,10 +339,10 @@ var CPPasteboards = nil;
|
||||
- (id)propertyListForType:(CPString)aType
|
||||
{
|
||||
var data = [self dataForType:aType];
|
||||
|
||||
|
||||
if (data)
|
||||
return [CPPropertyListSerialization propertyListFromData:data format:CPPropertyListXMLFormat_v1_0 errorDescription:nil];
|
||||
|
||||
return [CPPropertyListSerialization propertyListFromData:data format:CPPropertyList280NorthFormat_v1_0];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -290,9 +360,9 @@ var CPPasteboards = nil;
|
||||
- (CPString)_generateStateUID
|
||||
{
|
||||
var bits = 32;
|
||||
|
||||
|
||||
_stateUID = @"";
|
||||
|
||||
|
||||
while (bits--)
|
||||
_stateUID += FLOOR(RAND() * 16.0).toString(16).toUpperCase();
|
||||
|
||||
@@ -343,7 +413,7 @@ var DOMDataTransferPasteboard = nil;
|
||||
if (type === CPStringPboardType)
|
||||
_dataTransfer.setData(type, [aPasteboard stringForType:type]);
|
||||
else
|
||||
_dataTransfer.setData(type, [[aPasteboard dataForType:type] string]);
|
||||
_dataTransfer.setData(type, [[aPasteboard dataForType:type] rawString]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,9 +427,9 @@ var DOMDataTransferPasteboard = nil;
|
||||
var dataString = _dataTransfer.getData(aType);
|
||||
|
||||
if (aType === CPStringPboardType)
|
||||
return [CPData dataFromPropertyList:dataString format:kCFPropertyList280NorthFormat_v1_0 errorDescription:0];
|
||||
return [CPData dataFromPropertyList:dataString format:kCFPropertyList280NorthFormat_v1_0];
|
||||
|
||||
return [CPData dataWithString:dataString];
|
||||
return [CPData dataWithRawString:dataString];
|
||||
}
|
||||
|
||||
- (id)propertyListForType:(CPString)aType
|
||||
@@ -367,7 +437,7 @@ var DOMDataTransferPasteboard = nil;
|
||||
if (aType === CPStringPboardType)
|
||||
return _dataTransfer.getData(aType);
|
||||
|
||||
return [CPPropertyListSerialization propertyListFromData:[self dataForType:aType] format:CPPropertyListUnknownFormat errorDescription:nil];
|
||||
return [CPPropertyListSerialization propertyListFromData:[self dataForType:aType] format:CPPropertyListUnknownFormat];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+129
-81
@@ -25,14 +25,12 @@
|
||||
@import "CPMenu.j"
|
||||
@import "CPMenuItem.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
var VISIBLE_MARGIN = 7.0;
|
||||
|
||||
CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPPopUpButton
|
||||
|
||||
@@ -42,15 +40,20 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
{
|
||||
int _selectedIndex;
|
||||
CPRectEdge _preferredEdge;
|
||||
|
||||
|
||||
CPMenu _menu;
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return "popup-button";
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingSelectedTag
|
||||
{
|
||||
return [CPSet setWithObject:@"selectedIndex"];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the pop-up button to the specified size.
|
||||
@param aFrame the size for the button
|
||||
@@ -60,21 +63,21 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
- (id)initWithFrame:(CGRect)aFrame pullsDown:(BOOL)shouldPullDown
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_selectedIndex = CPNotFound;
|
||||
_preferredEdge = CPMaxYEdge;
|
||||
|
||||
|
||||
[self setValue:CPImageLeft forThemeAttribute:@"image-position"];
|
||||
[self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
|
||||
[self setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"];
|
||||
|
||||
|
||||
[self setMenu:[[CPMenu alloc] initWithTitle:@""]];
|
||||
|
||||
[self setPullsDown:shouldPullDown];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -145,7 +148,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
{
|
||||
var index = 0,
|
||||
count = [titles count];
|
||||
|
||||
|
||||
for (; index < count; ++index)
|
||||
[self addItemWithTitle:titles[index]];
|
||||
}
|
||||
@@ -159,11 +162,11 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
{
|
||||
var items = [self itemArray],
|
||||
count = [items count];
|
||||
|
||||
|
||||
while (count--)
|
||||
if ([items[count] title] == aTitle)
|
||||
[self removeItemAtIndex:count];
|
||||
|
||||
|
||||
[_menu insertItemWithTitle:aTitle action:NULL keyEquivalent:nil atIndex:anIndex];
|
||||
}
|
||||
|
||||
@@ -173,7 +176,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
- (void)removeAllItems
|
||||
{
|
||||
var count = [_menu numberOfItems];
|
||||
|
||||
|
||||
while (count--)
|
||||
[_menu removeItemAtIndex:0];
|
||||
}
|
||||
@@ -206,7 +209,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
{
|
||||
if (_selectedIndex < 0 || _selectedIndex > [self numberOfItems] - 1)
|
||||
return nil;
|
||||
|
||||
|
||||
return [_menu itemAtIndex:_selectedIndex];
|
||||
}
|
||||
|
||||
@@ -235,6 +238,22 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
return _selectedIndex;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (int)selectedTag
|
||||
{
|
||||
return [[self selectedItem] tag];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_setSelectedTag:(int)aTag
|
||||
{
|
||||
[self selectItemWithTag:aTag];
|
||||
}
|
||||
|
||||
// Setting the Current Selection
|
||||
/*!
|
||||
Selects the specified menu item.
|
||||
@@ -253,16 +272,20 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
{
|
||||
if (_selectedIndex == anIndex)
|
||||
return;
|
||||
|
||||
|
||||
[self willChangeValueForKey:@"selectedIndex"];
|
||||
|
||||
if (_selectedIndex >= 0 && ![self pullsDown])
|
||||
[[self selectedItem] setState:CPOffState];
|
||||
|
||||
|
||||
_selectedIndex = anIndex;
|
||||
|
||||
if (_selectedIndex >= 0 && ![self pullsDown])
|
||||
[[self selectedItem] setState:CPOnState];
|
||||
|
||||
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
|
||||
[self didChangeValueForKey:@"selectedIndex"];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -310,7 +333,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
return;
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
|
||||
if (_menu)
|
||||
{
|
||||
[defaultCenter
|
||||
@@ -330,7 +353,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
}
|
||||
|
||||
_menu = aMenu;
|
||||
|
||||
|
||||
if (_menu)
|
||||
{
|
||||
[defaultCenter
|
||||
@@ -338,20 +361,20 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
selector:@selector(menuDidAddItem:)
|
||||
name:CPMenuDidAddItemNotification
|
||||
object:_menu];
|
||||
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(menuDidChangeItem:)
|
||||
name:CPMenuDidChangeItemNotification
|
||||
object:_menu];
|
||||
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(menuDidRemoveItem:)
|
||||
name:CPMenuDidRemoveItemNotification
|
||||
object:_menu];
|
||||
}
|
||||
|
||||
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
}
|
||||
|
||||
@@ -396,12 +419,13 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
{
|
||||
var titles = [],
|
||||
items = [self itemArray],
|
||||
|
||||
index = 0,
|
||||
count = [items count];
|
||||
|
||||
|
||||
for (; index < count; ++index)
|
||||
items.push([items[index] title]);
|
||||
titles.push([items[index] title]);
|
||||
|
||||
return titles;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -519,14 +543,14 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
else
|
||||
{
|
||||
var index = [self indexOfItemWithTitle:aTitle];
|
||||
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
[self addItemWithTitle:aTitle];
|
||||
|
||||
|
||||
index = [self numberOfItems] - 1;
|
||||
}
|
||||
|
||||
|
||||
[self selectItemAtIndex:index];
|
||||
}
|
||||
}
|
||||
@@ -554,7 +578,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
if ([self pullsDown])
|
||||
{
|
||||
var items = [_menu itemArray];
|
||||
|
||||
|
||||
if ([items count] > 0)
|
||||
item = items[0];
|
||||
}
|
||||
@@ -573,22 +597,22 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
- (void)menuDidAddItem:(CPNotification)aNotification
|
||||
{
|
||||
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"];
|
||||
|
||||
|
||||
if (_selectedIndex < 0)
|
||||
[self selectItemAtIndex:0];
|
||||
|
||||
|
||||
else if (index == _selectedIndex)
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
|
||||
|
||||
else if (index < _selectedIndex)
|
||||
++_selectedIndex;
|
||||
|
||||
|
||||
if (index == 0 && [self pullsDown])
|
||||
{
|
||||
var items = [_menu itemArray];
|
||||
|
||||
|
||||
[items[0] setHidden:YES];
|
||||
|
||||
|
||||
if (items.length > 0)
|
||||
[items[1] setHidden:NO];
|
||||
}
|
||||
@@ -613,10 +637,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
|
||||
if ([self pullsDown] && index != 0)
|
||||
return;
|
||||
|
||||
|
||||
if (![self pullsDown] && index != _selectedIndex)
|
||||
return;
|
||||
|
||||
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
}
|
||||
|
||||
@@ -627,7 +651,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
- (void)menuDidRemoveItem:(CPNotification)aNotification
|
||||
{
|
||||
var numberOfItems = [self numberOfItems];
|
||||
|
||||
|
||||
if (numberOfItems <= _selectedIndex && numberOfItems > 0)
|
||||
[self selectItemAtIndex:numberOfItems - 1];
|
||||
else
|
||||
@@ -638,21 +662,52 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
{
|
||||
if (![self isEnabled] || ![self numberOfItems])
|
||||
return;
|
||||
|
||||
|
||||
[self highlight:YES];
|
||||
|
||||
var menu = [self menu],
|
||||
theWindow = [self window],
|
||||
menuWindow = [_CPMenuWindow menuWindowWithMenu:menu font:[self font]];
|
||||
|
||||
[menuWindow setDelegate:self];
|
||||
[menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
|
||||
|
||||
// Pull Down Menus show up directly below their buttons.
|
||||
bounds = [self bounds],
|
||||
minimumWidth = CGRectGetWidth(bounds);
|
||||
|
||||
// FIXME: setFont: should set the font on the menu.
|
||||
[menu setFont:[self font]];
|
||||
|
||||
if ([self pullsDown])
|
||||
var menuOrigin = [theWindow convertBaseToGlobal:[self convertPoint:CGPointMake(0.0, CGRectGetMaxY([self bounds])) toView:nil]];
|
||||
|
||||
// Pop Up Menus attempt to show up "on top" of the selected item.
|
||||
{
|
||||
var positionedItem = nil,
|
||||
location = CGPointMake(0.0, CGRectGetMaxY(bounds));
|
||||
}
|
||||
else
|
||||
{
|
||||
var contentRect = [self contentRectForBounds:bounds],
|
||||
positionedItem = [self selectedItem],
|
||||
standardLeftMargin = [_CPMenuWindow _standardLeftMargin] + [_CPMenuItemStandardView _standardLeftMargin],
|
||||
location = CGPointMake(CGRectGetMinX(contentRect) - standardLeftMargin, 0.0);
|
||||
|
||||
minimumWidth += standardLeftMargin;
|
||||
|
||||
// To ensure the selected item is highlighted correctly, unset the highlighted item
|
||||
[menu _highlightItemAtIndex:CPNotFound];
|
||||
}
|
||||
|
||||
[menu setMinimumWidth:minimumWidth];
|
||||
|
||||
[menu
|
||||
_popUpMenuPositioningItem:positionedItem
|
||||
atLocation:location
|
||||
topY:CGRectGetMinY(bounds)
|
||||
bottomY:CGRectGetMaxY(bounds)
|
||||
inView:self
|
||||
callback:function(aMenu)
|
||||
{
|
||||
[self highlight:NO];
|
||||
|
||||
var highlightedItem = [aMenu highlightedItem];
|
||||
|
||||
if ([highlightedItem _isSelectable])
|
||||
[self selectItem:highlightedItem];
|
||||
}];
|
||||
/*
|
||||
else
|
||||
{
|
||||
// This is confusing, I KNOW, so let me explain it to you.
|
||||
@@ -660,43 +715,19 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
// 1. So calculate where our content is, then calculate where the menu item is.
|
||||
// 2. Move LEFT by whatever indentation we have (offsetWidths, aka, window margin, item margin, etc).
|
||||
// 3. MOVE UP by the difference in sizes of the content and menu item, this will only work if the content is vertically centered.
|
||||
var contentRect = [self convertRect:[self contentRectForBounds:[self bounds]] toView:nil],
|
||||
var contentRect = [self convertRect:[self contentRectForBounds:bounds] toView:nil],
|
||||
menuOrigin = [theWindow convertBaseToGlobal:contentRect.origin],
|
||||
menuItemRect = [menuWindow rectForItemAtIndex:_selectedIndex];
|
||||
|
||||
|
||||
menuOrigin.x -= CGRectGetMinX(menuItemRect) + [menuWindow overlapOffsetWidth] + [[[menu itemAtIndex:_selectedIndex] _menuItemView] overlapOffsetWidth];
|
||||
menuOrigin.y -= CGRectGetMinY(menuItemRect) + (CGRectGetHeight(menuItemRect) - CGRectGetHeight(contentRect)) / 2.0;
|
||||
}
|
||||
|
||||
[menuWindow setFrameOrigin:menuOrigin];
|
||||
|
||||
var menuMaxX = CGRectGetMaxX([menuWindow frame]),
|
||||
buttonMaxX = [theWindow convertBaseToGlobal:CGPointMake(CGRectGetMaxX([self convertRect:[self bounds] toView:nil]), 0.0)].x;
|
||||
|
||||
if (menuMaxX < buttonMaxX)
|
||||
[menuWindow setMinWidth:CGRectGetWidth([menuWindow frame]) + buttonMaxX - menuMaxX - ([self pullsDown] ? 0.0 : VISIBLE_MARGIN)];
|
||||
|
||||
[menuWindow orderFront:self];
|
||||
[menuWindow beginTrackingWithEvent:anEvent sessionDelegate:self didEndSelector:@selector(menuWindowDidFinishTracking:highlightedItem:)];
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (void)menuWindowDidFinishTracking:(_CPMenuWindow)aMenuWindow highlightedItem:(CPMenuItem)aMenuItem
|
||||
- (void)rightMouseDown:(CPEvent)anEvent
|
||||
{
|
||||
[_CPMenuWindow poolMenuWindow:aMenuWindow];
|
||||
|
||||
[self highlight:NO];
|
||||
|
||||
var index = [_menu indexOfItem:aMenuItem];
|
||||
|
||||
if (index == CPNotFound)
|
||||
return;
|
||||
|
||||
[self selectItemAtIndex:index];
|
||||
|
||||
[CPApp sendAction:[aMenuItem action] to:[aMenuItem target] from:aMenuItem];
|
||||
// Disable standard CPView behaviour which incorrectly displays the menu as a 'context menu'.
|
||||
}
|
||||
|
||||
- (void)_popUpItemAction:(id)aSender
|
||||
@@ -704,6 +735,23 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
}
|
||||
|
||||
- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
|
||||
{
|
||||
var count = objects.length,
|
||||
value = [objects[0] valueForKeyPath:aKeyPath];
|
||||
|
||||
[self selectItemWithTag:value];
|
||||
[self setEnabled:YES];
|
||||
|
||||
while (count-- > 1)
|
||||
{
|
||||
if (value !== [objects[count] valueForKeyPath:aKeyPath])
|
||||
{
|
||||
[[self selectedItem] setState:CPOffState];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
||||
@@ -721,7 +769,7 @@ var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
// Nothing is currently selected
|
||||
@@ -730,7 +778,7 @@ var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
||||
[self setMenu:[aCoder decodeObjectForKey:CPPopUpButtonMenuKey]];
|
||||
[self selectItemAtIndex:[aCoder decodeObjectForKey:CPPopUpButtonSelectedIndexKey]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -742,7 +790,7 @@ var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
|
||||
[aCoder encodeObject:_menu forKey:CPPopUpButtonMenuKey];
|
||||
[aCoder encodeInt:_selectedIndex forKey:CPPopUpButtonSelectedIndexKey];
|
||||
}
|
||||
|
||||
@@ -20,11 +20,9 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <AppKit/CGGeometry.j>
|
||||
@import <AppKit/CPImageView.j>
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
@import "CGGeometry.j"
|
||||
@import "CPImageView.j"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
/*
|
||||
@@ -49,7 +47,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
CPProgressIndicatorStyleIdentifiers = nil,
|
||||
CPProgressIndicatorStyleSizes = nil;
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPProgressIndicator
|
||||
|
||||
@@ -61,19 +59,19 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
{
|
||||
double _minValue;
|
||||
double _maxValue;
|
||||
|
||||
|
||||
double _doubleValue;
|
||||
|
||||
|
||||
CPControlSize _controlSize;
|
||||
|
||||
|
||||
BOOL _isIndeterminate;
|
||||
CPProgressIndicatorStyle _style;
|
||||
|
||||
|
||||
BOOL _isAnimating;
|
||||
|
||||
|
||||
BOOL _isDisplayedWhenStoppedSet;
|
||||
BOOL _isDisplayedWhenStopped;
|
||||
|
||||
|
||||
CPView _barView;
|
||||
}
|
||||
|
||||
@@ -84,11 +82,11 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
{
|
||||
if (self != [CPProgressIndicator class])
|
||||
return;
|
||||
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
|
||||
CPProgressIndicatorSpinningStyleColors = [];
|
||||
|
||||
|
||||
CPProgressIndicatorSpinningStyleColors[CPMiniControlSize] = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:
|
||||
[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(64.0, 64.0)]];
|
||||
CPProgressIndicatorSpinningStyleColors[CPSmallControlSize] = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:
|
||||
@@ -97,28 +95,25 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
[bundle pathForResource:@"CPProgressIndicator/CPProgressIndicatorSpinningStyleRegular.gif"] size:CGSizeMake(64.0, 64.0)]];
|
||||
|
||||
CPProgressIndicatorBezelBorderViewPool = [];
|
||||
|
||||
|
||||
var start = CPProgressIndicatorBarStyle,
|
||||
end = CPProgressIndicatorHUDBarStyle;
|
||||
|
||||
|
||||
for (; start <= end; ++start)
|
||||
{
|
||||
CPProgressIndicatorBezelBorderViewPool[start] = [];
|
||||
|
||||
CPProgressIndicatorBezelBorderViewPool[start][CPMiniControlSize] = [];
|
||||
CPProgressIndicatorBezelBorderViewPool[start][CPSmallControlSize] = [];
|
||||
CPProgressIndicatorBezelBorderViewPool[start][CPRegularControlSize] = [];
|
||||
}
|
||||
|
||||
|
||||
CPProgressIndicatorClassName = [self className];
|
||||
|
||||
|
||||
CPProgressIndicatorStyleIdentifiers = [];
|
||||
|
||||
|
||||
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] = @"Bar";
|
||||
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorSpinningStyle] = @"Spinny";
|
||||
CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle] = @"HUDBar";
|
||||
|
||||
|
||||
var regularIdentifier = _CPControlIdentifierForControlSize(CPRegularControlSize),
|
||||
smallIdentifier = _CPControlIdentifierForControlSize(CPSmallControlSize),
|
||||
miniIdentifier = _CPControlIdentifierForControlSize(CPMiniControlSize);
|
||||
@@ -126,52 +121,42 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
CPProgressIndicatorStyleSizes = [];
|
||||
|
||||
// Bar Sttyle
|
||||
var prefix = CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle];
|
||||
var prefixes = [
|
||||
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle],
|
||||
CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle],
|
||||
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle],
|
||||
CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle]
|
||||
];
|
||||
|
||||
CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||
CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||
|
||||
prefix = CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle];
|
||||
|
||||
CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||
CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||
|
||||
// HUD Bar Style
|
||||
prefix = CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle];
|
||||
|
||||
CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||
CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = [_CGSizeMake(3.0, 15.0), _CGSizeMake(1.0, 15.0), _CGSizeMake(3.0, 15.0)];
|
||||
|
||||
prefix = CPProgressIndicatorClassName + @"Bar" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorHUDBarStyle];
|
||||
|
||||
CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||
CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = _CGSizeMake(1.0, 9.0);
|
||||
for (var i = 0, count = prefixes.length; i < count; i++)
|
||||
{
|
||||
var prefix = prefixes[i];
|
||||
CPProgressIndicatorStyleSizes[prefix + regularIdentifier] = [_CGSizeMake(3.0, 16.0), _CGSizeMake(1.0, 16.0), _CGSizeMake(3.0, 16.0)];
|
||||
CPProgressIndicatorStyleSizes[prefix + smallIdentifier] = [_CGSizeMake(3.0, 16.0), _CGSizeMake(1.0, 16.0), _CGSizeMake(3.0, 16.0)];
|
||||
CPProgressIndicatorStyleSizes[prefix + miniIdentifier] = [_CGSizeMake(3.0, 16.0), _CGSizeMake(1.0, 16.0), _CGSizeMake(3.0, 16.0)];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_minValue = 0.0;
|
||||
_maxValue = 100.0;
|
||||
|
||||
|
||||
_doubleValue = 0.0;
|
||||
|
||||
|
||||
_style = CPProgressIndicatorBarStyle;
|
||||
_isDisplayedWhenStoppedSet = NO;
|
||||
|
||||
|
||||
_controlSize = CPRegularControlSize;
|
||||
|
||||
|
||||
[self updateBackgroundColor];
|
||||
[self drawBar];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -189,7 +174,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
- (void)startAnimation:(id)aSender
|
||||
{
|
||||
_isAnimating = YES;
|
||||
|
||||
|
||||
[self _hideOrDisplay];
|
||||
}
|
||||
|
||||
@@ -228,7 +213,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
- (void)setDoubleValue:(double)aValue
|
||||
{
|
||||
_doubleValue = MIN(MAX(aValue, _minValue), _maxValue);
|
||||
|
||||
|
||||
[self drawBar];
|
||||
}
|
||||
|
||||
@@ -283,7 +268,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
{
|
||||
if (_controlSize == aControlSize)
|
||||
return;
|
||||
|
||||
|
||||
_controlSize = aControlSize;
|
||||
|
||||
[self updateBackgroundColor];
|
||||
@@ -335,7 +320,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
{
|
||||
if (_isIndeterminate == isIndeterminate)
|
||||
return;
|
||||
|
||||
|
||||
_isIndeterminate = isIndeterminate;
|
||||
|
||||
[self updateBackgroundColor];
|
||||
@@ -357,9 +342,9 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
{
|
||||
if (_style == aStyle)
|
||||
return;
|
||||
|
||||
|
||||
_style = aStyle;
|
||||
|
||||
|
||||
[self updateBackgroundColor];
|
||||
}
|
||||
|
||||
@@ -372,8 +357,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
[self setFrameSize:[[CPProgressIndicatorSpinningStyleColors[_controlSize] patternImage] size]];
|
||||
else
|
||||
[self setFrameSize:CGSizeMake(CGRectGetWidth([self frame]), CPProgressIndicatorStyleSizes[
|
||||
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] +
|
||||
_CPControlIdentifierForControlSize(_controlSize)][0].height)];
|
||||
CPProgressIndicatorClassName + @"BezelBorder" + CPProgressIndicatorStyleIdentifiers[CPProgressIndicatorBarStyle] + _CPControlIdentifierForControlSize(_controlSize)][0].height)];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -385,11 +369,11 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
{
|
||||
if (_isDisplayedWhenStoppedSet && _isDisplayedWhenStopped == isDisplayedWhenStopped)
|
||||
return;
|
||||
|
||||
|
||||
_isDisplayedWhenStoppedSet = YES;
|
||||
|
||||
|
||||
_isDisplayedWhenStopped = isDisplayedWhenStopped;
|
||||
|
||||
|
||||
[self _hideOrDisplay];
|
||||
}
|
||||
|
||||
@@ -400,10 +384,10 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
{
|
||||
if (_isDisplayedWhenStoppedSet)
|
||||
return _isDisplayedWhenStopped;
|
||||
|
||||
|
||||
if (_style == CPProgressIndicatorBarStyle || _style == CPProgressIndicatorHUDBarStyle)
|
||||
return YES;
|
||||
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -416,7 +400,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
|
||||
[self drawBar];
|
||||
}
|
||||
|
||||
@@ -425,24 +409,28 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
{
|
||||
if (_style == CPProgressIndicatorSpinningStyle)
|
||||
return;
|
||||
|
||||
|
||||
if (!_barView)
|
||||
{
|
||||
_barView = [[CPView alloc] initWithFrame:CGRectMake(2.0, 2.0, 0.0, 9.0)];
|
||||
|
||||
[_barView setBackgroundColor:[CPColor redColor]];
|
||||
|
||||
_barView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 16.0)];
|
||||
[self addSubview:_barView];
|
||||
}
|
||||
|
||||
[_barView setBackgroundColor:_CPControlColorWithPatternImage(
|
||||
|
||||
[_barView setBackgroundColor:_CPControlThreePartImagePattern(
|
||||
NO,
|
||||
CPProgressIndicatorStyleSizes,
|
||||
CPProgressIndicatorClassName,
|
||||
@"Bar",
|
||||
CPProgressIndicatorStyleIdentifiers[_style],
|
||||
_CPControlIdentifierForControlSize(_controlSize))];
|
||||
|
||||
[_barView setFrameSize:CGSizeMake(CGRectGetWidth([self bounds]) * (_doubleValue - _minValue) / (_maxValue - _minValue) - 4.0, 9.0)];
|
||||
|
||||
var width = CGRectGetWidth([self bounds]),
|
||||
barWidth = width * ((_doubleValue - _minValue) / (_maxValue - _minValue));
|
||||
|
||||
if (barWidth > 0.0 && barWidth < 4.0)
|
||||
barWidth = 4.0;
|
||||
|
||||
[_barView setFrameSize:CGSizeMake(barWidth, 16.0)];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
@@ -453,9 +441,9 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
if (_style == CPProgressIndicatorSpinningStyle)
|
||||
{
|
||||
[_barView removeFromSuperview];
|
||||
|
||||
|
||||
_barView = nil;
|
||||
|
||||
|
||||
[self setBackgroundColor:CPProgressIndicatorSpinningStyleColors[_controlSize]];
|
||||
}
|
||||
else
|
||||
@@ -467,7 +455,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
@"BezelBorder",
|
||||
CPProgressIndicatorStyleIdentifiers[_style],
|
||||
_CPControlIdentifierForControlSize(_controlSize))];
|
||||
|
||||
|
||||
[self drawBar];
|
||||
}
|
||||
}
|
||||
|
||||
+24
-15
@@ -25,28 +25,29 @@
|
||||
|
||||
@import "CPButton.j"
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
|
||||
|
||||
from this mailing list thread:
|
||||
http://groups.google.com/group/objectivej/browse_thread/thread/7c41cbd9cbee9ea3
|
||||
|
||||
http://groups.google.com/group/objectivej/browse_thread/thread/7c41cbd9cbee9ea3
|
||||
|
||||
-----------------------------------
|
||||
|
||||
|
||||
Creating a checkbox is easy enough:
|
||||
|
||||
|
||||
checkbox = [[CPCheckBox alloc] initWithFrame:aFrame];
|
||||
|
||||
|
||||
That's basically all there is to it. Radio buttons are very similar,
|
||||
the key difference is the introduction of a new class CPRadioGroup,
|
||||
which defines which radio buttons are part of the same group:
|
||||
|
||||
|
||||
[myRadioButton setRadioGroup:aRadioGroup];
|
||||
|
||||
|
||||
Every radio button receives a unique radio group by default (so if you
|
||||
do nothing further, they will all behave independently), but you can
|
||||
use an existing radio button's group with other buttons as so:
|
||||
|
||||
|
||||
button1 = [[CPRadio alloc] initWithFrame:aFrame];
|
||||
...
|
||||
button2 = [[CPRadio alloc] initWithFrame:aFrame radioGroup:[button1
|
||||
@@ -55,13 +56,16 @@
|
||||
button3 = [[CPRadio alloc] initWithFrame:aFrame radioGroup:[button1
|
||||
radioGroup]];
|
||||
...etc...
|
||||
|
||||
|
||||
Here, all the radio buttons will act "together". [[button1 radioGroup]
|
||||
allRadios] returns every button that's part of this group, and
|
||||
[[button1 radioGroup] selectedRadio] returns the currently selected
|
||||
option.
|
||||
|
||||
*/
|
||||
|
||||
CPRadioImageOffset = 4.0;
|
||||
|
||||
@implementation CPRadio : CPButton
|
||||
{
|
||||
CPRadioGroup _radioGroup;
|
||||
@@ -86,7 +90,7 @@
|
||||
return button;
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"radio";
|
||||
}
|
||||
@@ -95,7 +99,7 @@
|
||||
- (id)initWithFrame:(CGRect)aFrame radioGroup:(CPRadioGroup)aRadioGroup
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setRadioGroup:aRadioGroup];
|
||||
@@ -109,8 +113,8 @@
|
||||
|
||||
[self setBordered:YES];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -141,7 +145,7 @@
|
||||
- (void)setObjectValue:(id)aValue
|
||||
{
|
||||
[super setObjectValue:aValue];
|
||||
|
||||
|
||||
if ([self state] === CPOnState)
|
||||
[_radioGroup _setSelectedRadio:self];
|
||||
}
|
||||
@@ -175,6 +179,9 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
||||
{
|
||||
CPSet _radios;
|
||||
CPRadio _selectedRadio;
|
||||
|
||||
id _target @accessors(property=target);
|
||||
SEL _action @accessors(property=action);
|
||||
}
|
||||
|
||||
- (id)init
|
||||
@@ -213,6 +220,8 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
||||
|
||||
[_selectedRadio setState:CPOffState];
|
||||
_selectedRadio = aRadio;
|
||||
|
||||
[CPApp sendAction:_action to:_target from:self];
|
||||
}
|
||||
|
||||
- (CPRadio)selectedRadio
|
||||
|
||||
+65
-46
@@ -23,19 +23,23 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
CPDeleteKeyCode = 8;
|
||||
CPTabKeyCode = 9;
|
||||
CPReturnKeyCode = 13;
|
||||
CPEscapeKeyCode = 27;
|
||||
CPLeftArrowKeyCode = 37;
|
||||
CPUpArrowKeyCode = 38;
|
||||
CPRightArrowKeyCode = 39;
|
||||
CPDownArrowKeyCode = 40;
|
||||
CPDeleteKeyCode = 8;
|
||||
CPTabKeyCode = 9;
|
||||
CPReturnKeyCode = 13;
|
||||
CPEscapeKeyCode = 27;
|
||||
CPSpaceKeyCode = 32;
|
||||
CPPageUpKeyCode = 33;
|
||||
CPPageDownKeyCode = 34;
|
||||
CPLeftArrowKeyCode = 37;
|
||||
CPUpArrowKeyCode = 38;
|
||||
CPRightArrowKeyCode = 39;
|
||||
CPDownArrowKeyCode = 40;
|
||||
CPDeleteForwardKeyCode = 46;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPResponder
|
||||
|
||||
|
||||
Subclasses of CPResonder can be part of the responder chain.
|
||||
*/
|
||||
@implementation CPResponder : CPObject
|
||||
@@ -101,38 +105,24 @@ CPDownArrowKeyCode = 40;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var event = events[index];
|
||||
var event = events[index],
|
||||
modifierFlags = [event modifierFlags],
|
||||
character = [event charactersIgnoringModifiers],
|
||||
selectorNames = [CPKeyBinding selectorsForKey:character modifierFlags:modifierFlags];
|
||||
|
||||
switch([event keyCode])
|
||||
if (selectorNames)
|
||||
{
|
||||
case CPLeftArrowKeyCode: [self doCommandBySelector:@selector(moveLeft:)];
|
||||
break;
|
||||
case CPRightArrowKeyCode: [self doCommandBySelector:@selector(moveRight:)];
|
||||
break;
|
||||
case CPUpArrowKeyCode: [self doCommandBySelector:@selector(moveUp:)];
|
||||
break;
|
||||
case CPDownArrowKeyCode: [self doCommandBySelector:@selector(moveDown:)];
|
||||
break;
|
||||
case CPDeleteKeyCode: [self doCommandBySelector:@selector(deleteBackward:)];
|
||||
break;
|
||||
case CPReturnKeyCode:
|
||||
case 3: [self doCommandBySelector:@selector(insertLineBreak:)];
|
||||
break;
|
||||
|
||||
case CPEscapeKeyCode: [self doCommandBySelector:@selector(cancel:)];
|
||||
break;
|
||||
for (var s = 0, scount = selectorNames.length; s < scount; s++)
|
||||
{
|
||||
var selector = selectorNames[s];
|
||||
if (!selector)
|
||||
continue;
|
||||
|
||||
case CPTabKeyCode: var shift = [event modifierFlags] & CPShiftKeyMask;
|
||||
|
||||
if (!shift)
|
||||
[self doCommandBySelector:@selector(insertTab:)];
|
||||
else
|
||||
[self doCommandBySelector:@selector(insertBackTab:)];
|
||||
|
||||
break;
|
||||
|
||||
default: [self insertText:[event characters]];
|
||||
[self doCommandBySelector:CPSelectorFromString(selector)];
|
||||
}
|
||||
}
|
||||
else if (!(modifierFlags & (CPCommandKeyMask | CPControlKeyMask)) && [self respondsToSelector:@selector(insertText:)])
|
||||
[self insertText:[event characters]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +135,15 @@ CPDownArrowKeyCode = 40;
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*!
|
||||
Notifies the receiver that the user has clicked the right mouse down in its area.
|
||||
@param anEvent contains information about the right click
|
||||
*/
|
||||
- (void)rightMouseDown:(CPEvent)anEvent
|
||||
{
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*!
|
||||
Notifies the receiver that the user has initiated a drag
|
||||
over it. A drag is a mouse movement while the left button is down.
|
||||
@@ -164,6 +163,15 @@ CPDownArrowKeyCode = 40;
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*!
|
||||
Notifies the receiver that the user has released the right mouse button.
|
||||
@param anEvent contains information about the release
|
||||
*/
|
||||
- (void)rightMouseUp:(CPEvent)anEvent
|
||||
{
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*!
|
||||
Notifies the receiver that the user has moved the mouse (with no buttons down).
|
||||
@param anEvent contains information about the movement
|
||||
@@ -214,6 +222,15 @@ CPDownArrowKeyCode = 40;
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*!
|
||||
Notifies the receiver that the user has pressed or released a modifier key (Shift, Control, and so on).
|
||||
@param anEvent information about the key press
|
||||
*/
|
||||
- (void)flagsChanged:(CPEvent)anEvent
|
||||
{
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*
|
||||
FIXME This description is bad.
|
||||
Based on \c anEvent, the receiver should simulate the event.
|
||||
@@ -244,10 +261,6 @@ CPDownArrowKeyCode = 40;
|
||||
[[self nextResponder] insertNewline:aSender];
|
||||
}
|
||||
|
||||
- (void)cancel:(id)sender
|
||||
{
|
||||
}
|
||||
|
||||
- (void)insertTab:(id)sender
|
||||
{
|
||||
}
|
||||
@@ -287,10 +300,10 @@ CPDownArrowKeyCode = 40;
|
||||
*/
|
||||
- (BOOL)tryToPerform:(SEL)aSelector with:(id)anObject
|
||||
{
|
||||
if([self respondsToSelector:aSelector])
|
||||
if ([self respondsToSelector:aSelector])
|
||||
{
|
||||
[self performSelector:aSelector withObject:anObject];
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -329,7 +342,8 @@ CPDownArrowKeyCode = 40;
|
||||
|
||||
@end
|
||||
|
||||
var CPResponderNextResponderKey = @"CPResponderNextResponderKey";
|
||||
var CPResponderNextResponderKey = @"CPResponderNextResponderKey",
|
||||
CPResponderMenuKey = @"CPResponderMenuKey";
|
||||
|
||||
@implementation CPResponder (CPCoding)
|
||||
|
||||
@@ -341,10 +355,13 @@ var CPResponderNextResponderKey = @"CPResponderNextResponderKey";
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_nextResponder = [aCoder decodeObjectForKey:CPResponderNextResponderKey];
|
||||
|
||||
_menu = [aCoder decodeObjectForKey:CPResponderMenuKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -357,6 +374,8 @@ var CPResponderNextResponderKey = @"CPResponderNextResponderKey";
|
||||
// This will come out nil on the other side with decodeObjectForKey:
|
||||
if (_nextResponder !== nil)
|
||||
[aCoder encodeConditionalObject:_nextResponder forKey:CPResponderNextResponderKey];
|
||||
|
||||
[aCoder encodeObject:_menu forKey:CPResponderMenuKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
@import <AppKit/CPPanel.j>
|
||||
@import "CPPanel.j"
|
||||
|
||||
|
||||
@implementation CPSavePanel : CPPanel
|
||||
|
||||
+4
-2
@@ -2,15 +2,17 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
#import "CoreGraphics/CGGeometry.h"
|
||||
|
||||
@implementation CPScreen : CPObject
|
||||
{
|
||||
}
|
||||
|
||||
- (CGRect)visibleFrame
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
return _CGRectMake(window.screen.availLeft, window.screen.availTop, window.screen.availWidth, window.screen.availHeight);
|
||||
#else
|
||||
return _CGRectMakeZero();
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
+456
-121
@@ -20,14 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPView.j"
|
||||
@import "CPBox.j"
|
||||
@import "CPClipView.j"
|
||||
@import "CPScroller.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPScrollView
|
||||
|
||||
@@ -37,23 +36,39 @@
|
||||
*/
|
||||
@implementation CPScrollView : CPView
|
||||
{
|
||||
CPClipView _contentView;
|
||||
CPClipView _headerClipView;
|
||||
CPView _cornerView;
|
||||
CPClipView _contentView;
|
||||
CPClipView _headerClipView;
|
||||
CPView _cornerView;
|
||||
CPView _bottomCornerView;
|
||||
|
||||
BOOL _hasVerticalScroller;
|
||||
BOOL _hasHorizontalScroller;
|
||||
BOOL _autohidesScrollers;
|
||||
|
||||
CPScroller _verticalScroller;
|
||||
CPScroller _horizontalScroller;
|
||||
|
||||
int _recursionCount;
|
||||
|
||||
float _verticalLineScroll;
|
||||
float _verticalPageScroll;
|
||||
float _horizontalLineScroll;
|
||||
float _horizontalPageScroll;
|
||||
BOOL _hasVerticalScroller;
|
||||
BOOL _hasHorizontalScroller;
|
||||
BOOL _autohidesScrollers;
|
||||
|
||||
CPScroller _verticalScroller;
|
||||
CPScroller _horizontalScroller;
|
||||
|
||||
int _recursionCount;
|
||||
|
||||
float _verticalLineScroll;
|
||||
float _verticalPageScroll;
|
||||
float _horizontalLineScroll;
|
||||
float _horizontalPageScroll;
|
||||
|
||||
CPBorderType _borderType;
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"scrollview"
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithJSObject:{
|
||||
@"bottom-corner-color": [CPColor whiteColor],
|
||||
@"border-color": [CPColor blackColor]
|
||||
}];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -68,14 +83,18 @@
|
||||
_horizontalLineScroll = 10.0;
|
||||
_horizontalPageScroll = 10.0;
|
||||
|
||||
_contentView = [[CPClipView alloc] initWithFrame:[self bounds]];
|
||||
_borderType = CPNoBorder;
|
||||
|
||||
_contentView = [[CPClipView alloc] initWithFrame:[self _insetBounds]];
|
||||
|
||||
[self addSubview:_contentView];
|
||||
|
||||
_headerClipView = [[CPClipView alloc] init];
|
||||
|
||||
[self addSubview:_headerClipView];
|
||||
|
||||
_bottomCornerView = [[CPView alloc] init];
|
||||
[self addSubview:_bottomCornerView];
|
||||
|
||||
[self setHasVerticalScroller:YES];
|
||||
[self setHasHorizontalScroller:YES];
|
||||
}
|
||||
@@ -83,11 +102,70 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
// Calculating Layout
|
||||
|
||||
+ (CGSize)contentSizeForFrameSize:(CGSize)frameSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(CPBorderType)borderType
|
||||
{
|
||||
var bounds = [self _insetBounds:_CGRectMake(0.0, 0.0, frameSize.width, frameSize.height) borderType:borderType],
|
||||
scrollerWidth = [CPScroller scrollerWidth];
|
||||
|
||||
if (hFlag)
|
||||
bounds.size.height -= scrollerWidth;
|
||||
|
||||
if (vFlag)
|
||||
bounds.size.width -= scrollerWidth;
|
||||
|
||||
return bounds.size;
|
||||
}
|
||||
|
||||
+ (CGSize)frameSizeForContentSize:(CGSize)contentSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(CPBorderType)borderType
|
||||
{
|
||||
var bounds = [self _insetBounds:_CGRectMake(0.0, 0.0, contentSize.width, contentSize.height) borderType:borderType],
|
||||
widthInset = contentSize.width - bounds.size.width,
|
||||
heightInset = contentSize.height - bounds.size.height,
|
||||
frameSize = _CGSizeMake(contentSize.width + widthInset, contentSize.height + heightInset),
|
||||
scrollerWidth = [CPScroller scrollerWidth];
|
||||
|
||||
if (hFlag)
|
||||
frameSize.height -= scrollerWidth;
|
||||
|
||||
if (vFlag)
|
||||
frameSize.width -= scrollerWidth;
|
||||
|
||||
return frameSize;
|
||||
}
|
||||
|
||||
+ (CGRect)_insetBounds:(CGRect)bounds borderType:(CPBorderType)borderType
|
||||
{
|
||||
switch (borderType)
|
||||
{
|
||||
case CPLineBorder:
|
||||
case CPBezelBorder:
|
||||
return _CGRectInset(bounds, 1.0, 1.0);
|
||||
|
||||
case CPGrooveBorder:
|
||||
bounds = _CGRectInset(bounds, 2.0, 2.0);
|
||||
++bounds.origin.y;
|
||||
--bounds.size.height;
|
||||
|
||||
return bounds;
|
||||
|
||||
case CPNoBorder:
|
||||
default:
|
||||
return bounds;
|
||||
}
|
||||
}
|
||||
|
||||
- (CGRect)_insetBounds
|
||||
{
|
||||
return [[self class] _insetBounds:[self bounds] borderType:_borderType];
|
||||
}
|
||||
|
||||
// Determining component sizes
|
||||
/*!
|
||||
Returns the size of the scroll view's content view.
|
||||
*/
|
||||
- (CGRect)contentSize
|
||||
- (CGSize)contentSize
|
||||
{
|
||||
return [_contentView frame].size;
|
||||
}
|
||||
@@ -106,7 +184,7 @@
|
||||
*/
|
||||
- (void)setContentView:(CPClipView)aContentView
|
||||
{
|
||||
if (_contentView !== aContentView || !aContentView)
|
||||
if (_contentView === aContentView || !aContentView)
|
||||
return;
|
||||
|
||||
var documentView = [aContentView documentView];
|
||||
@@ -153,16 +231,16 @@
|
||||
*/
|
||||
- (void)reflectScrolledClipView:(CPClipView)aClipView
|
||||
{
|
||||
if(_contentView !== aClipView)
|
||||
if (_contentView !== aClipView)
|
||||
return;
|
||||
|
||||
if (_recursionCount > 5)
|
||||
return;
|
||||
|
||||
|
||||
++_recursionCount;
|
||||
|
||||
var documentView = [self documentView];
|
||||
|
||||
|
||||
if (!documentView)
|
||||
{
|
||||
if (_autohidesScrollers)
|
||||
@@ -176,7 +254,7 @@
|
||||
// [_horizontalScroller setEnabled:NO];
|
||||
}
|
||||
|
||||
[_contentView setFrame:[self bounds]];
|
||||
[_contentView setFrame:[self _insetBounds]];
|
||||
[_headerClipView setFrame:_CGRectMakeZero()];
|
||||
|
||||
--_recursionCount;
|
||||
@@ -185,7 +263,7 @@
|
||||
}
|
||||
|
||||
var documentFrame = [documentView frame], // the size of the whole document
|
||||
contentFrame = [self bounds], // assume it takes up the entire size of the scrollview (no scrollers)
|
||||
contentFrame = [self _insetBounds], // assume it takes up the entire size of the scrollview (no scrollers)
|
||||
headerClipViewFrame = [self _headerClipViewFrame],
|
||||
headerClipViewHeight = _CGRectGetHeight(headerClipViewFrame);
|
||||
|
||||
@@ -235,11 +313,10 @@
|
||||
|
||||
if (shouldShowVerticalScroller)
|
||||
{
|
||||
var verticalScrollerY = MAX(_CGRectGetHeight([self _cornerViewFrame]), headerClipViewHeight),
|
||||
verticalScrollerHeight = _CGRectGetHeight([self bounds]) - verticalScrollerY;
|
||||
var verticalScrollerY =
|
||||
MAX(_CGRectGetMinY(contentFrame), MAX(_CGRectGetMaxY([self _cornerViewFrame]), _CGRectGetMaxY(headerClipViewFrame)));
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
verticalScrollerHeight -= horizontalScrollerHeight;
|
||||
var verticalScrollerHeight = _CGRectGetMaxY(contentFrame) - verticalScrollerY;
|
||||
|
||||
[_verticalScroller setFloatValue:(difference.height <= 0.0) ? 0.0 : scrollPoint.y / difference.height];
|
||||
[_verticalScroller setKnobProportion:_CGRectGetHeight(contentFrame) / _CGRectGetHeight(documentFrame)];
|
||||
@@ -250,12 +327,12 @@
|
||||
[_verticalScroller setFloatValue:0.0];
|
||||
[_verticalScroller setKnobProportion:1.0];
|
||||
}
|
||||
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
{
|
||||
[_horizontalScroller setFloatValue:(difference.width <= 0.0) ? 0.0 : scrollPoint.x / difference.width];
|
||||
[_horizontalScroller setKnobProportion:_CGRectGetWidth(contentFrame) / _CGRectGetWidth(documentFrame)];
|
||||
[_horizontalScroller setFrame:_CGRectMake(0.0, _CGRectGetMaxY(contentFrame), _CGRectGetWidth(contentFrame), horizontalScrollerHeight)];
|
||||
[_horizontalScroller setFrame:_CGRectMake(_CGRectGetMinX(contentFrame), _CGRectGetMaxY(contentFrame), _CGRectGetWidth(contentFrame), horizontalScrollerHeight)];
|
||||
}
|
||||
else if (wasShowingHorizontalScroller)
|
||||
{
|
||||
@@ -267,9 +344,41 @@
|
||||
[_headerClipView setFrame:headerClipViewFrame];
|
||||
[_cornerView setFrame:[self _cornerViewFrame]];
|
||||
|
||||
[[self bottomCornerView] setFrame:[self _bottomCornerViewFrame]];
|
||||
[[self bottomCornerView] setBackgroundColor:[self currentValueForThemeAttribute:@"bottom-corner-color"]];
|
||||
|
||||
--_recursionCount;
|
||||
}
|
||||
|
||||
// Managing Graphics Attributes
|
||||
|
||||
/*!
|
||||
Sets the type of border to be drawn around the view.
|
||||
Valid types are:
|
||||
CPNoBorder
|
||||
CPLineBorder
|
||||
CPBezelBorder
|
||||
CPGrooveBorder
|
||||
*/
|
||||
- (void)setBorderType:(CPBorderType)borderType
|
||||
{
|
||||
if (_borderType == borderType)
|
||||
return;
|
||||
|
||||
_borderType = borderType;
|
||||
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the border type drawn around the view.
|
||||
*/
|
||||
- (CPBorderType)borderType
|
||||
{
|
||||
return _borderType;
|
||||
}
|
||||
|
||||
// Managing Scrollers
|
||||
/*!
|
||||
Sets the scroll view's horizontal scroller.
|
||||
@@ -279,13 +388,13 @@
|
||||
{
|
||||
if (_horizontalScroller === aScroller)
|
||||
return;
|
||||
|
||||
|
||||
[_horizontalScroller removeFromSuperview];
|
||||
[_horizontalScroller setTarget:nil];
|
||||
[_horizontalScroller setAction:nil];
|
||||
|
||||
|
||||
_horizontalScroller = aScroller;
|
||||
|
||||
|
||||
[_horizontalScroller setTarget:self];
|
||||
[_horizontalScroller setAction:@selector(_horizontalScrollerDidScroll:)];
|
||||
|
||||
@@ -315,7 +424,12 @@
|
||||
_hasHorizontalScroller = shouldHaveHorizontalScroller;
|
||||
|
||||
if (_hasHorizontalScroller && !_horizontalScroller)
|
||||
[self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, _CGRectGetWidth([self bounds]), [CPScroller scrollerWidth])]];
|
||||
{
|
||||
var bounds = [self _insetBounds];
|
||||
|
||||
[self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, MAX(_CGRectGetWidth(bounds), [CPScroller scrollerWidth] + 1), [CPScroller scrollerWidth])]];
|
||||
[[self horizontalScroller] setFrameSize:CGSizeMake(_CGRectGetWidth(bounds), [CPScroller scrollerWidth])];
|
||||
}
|
||||
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
@@ -336,13 +450,13 @@
|
||||
{
|
||||
if (_verticalScroller === aScroller)
|
||||
return;
|
||||
|
||||
|
||||
[_verticalScroller removeFromSuperview];
|
||||
[_verticalScroller setTarget:nil];
|
||||
[_verticalScroller setAction:nil];
|
||||
|
||||
|
||||
_verticalScroller = aScroller;
|
||||
|
||||
|
||||
[_verticalScroller setTarget:self];
|
||||
[_verticalScroller setAction:@selector(_verticalScrollerDidScroll:)];
|
||||
|
||||
@@ -373,7 +487,12 @@
|
||||
_hasVerticalScroller = shouldHaveVerticalScroller;
|
||||
|
||||
if (_hasVerticalScroller && !_verticalScroller)
|
||||
[self setVerticalScroller:[[CPScroller alloc] initWithFrame:_CGRectMake(0.0, 0.0, [CPScroller scrollerWidth], _CGRectGetHeight([self bounds]))]];
|
||||
{
|
||||
var bounds = [self _insetBounds];
|
||||
|
||||
[self setVerticalScroller:[[CPScroller alloc] initWithFrame:_CGRectMake(0.0, 0.0, [CPScroller scrollerWidth], MAX(_CGRectGetHeight(bounds), [CPScroller scrollerWidth] + 1))]];
|
||||
[[self verticalScroller] setFrameSize:CGSizeMake([CPScroller scrollerWidth], _CGRectGetHeight(bounds))];
|
||||
}
|
||||
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
@@ -397,7 +516,7 @@
|
||||
return;
|
||||
|
||||
_autohidesScrollers = autohidesScrollers;
|
||||
|
||||
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
@@ -439,9 +558,7 @@
|
||||
|
||||
- (CPView)_headerView
|
||||
{
|
||||
var headerClipViewSubviews = [_headerClipView subviews];
|
||||
|
||||
return [headerClipViewSubviews count] ? headerClipViewSubviews[0] : nil;
|
||||
return [_headerClipView documentView];
|
||||
}
|
||||
|
||||
- (CGRect)_cornerViewFrame
|
||||
@@ -449,11 +566,11 @@
|
||||
if (!_cornerView)
|
||||
return _CGRectMakeZero();
|
||||
|
||||
var bounds = [self bounds],
|
||||
var bounds = [self _insetBounds],
|
||||
frame = [_cornerView frame];
|
||||
|
||||
frame.origin.x = _CGRectGetMaxX(bounds) - _CGRectGetWidth(frame);
|
||||
frame.origin.y = 0;
|
||||
frame.origin.y = _CGRectGetMinY(bounds);
|
||||
|
||||
return frame;
|
||||
}
|
||||
@@ -465,7 +582,7 @@
|
||||
if (!headerView)
|
||||
return _CGRectMakeZero();
|
||||
|
||||
var frame = [self bounds];
|
||||
var frame = [self _insetBounds];
|
||||
|
||||
frame.size.height = _CGRectGetHeight([headerView frame]);
|
||||
frame.size.width -= _CGRectGetWidth([self _cornerViewFrame]);
|
||||
@@ -473,33 +590,69 @@
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (CGRect)_bottomCornerViewFrame
|
||||
{
|
||||
if ([[self horizontalScroller] isHidden] || [[self verticalScroller] isHidden])
|
||||
return CGRectMakeZero();
|
||||
|
||||
var verticalFrame = [[self verticalScroller] frame],
|
||||
bottomCornerFrame = CGRectMakeZero();
|
||||
|
||||
bottomCornerFrame.origin.x = CGRectGetMinX(verticalFrame);
|
||||
bottomCornerFrame.origin.y = CGRectGetMaxY(verticalFrame);
|
||||
bottomCornerFrame.size.width = [CPScroller scrollerWidth];
|
||||
bottomCornerFrame.size.height = [CPScroller scrollerWidth];
|
||||
|
||||
return bottomCornerFrame;
|
||||
}
|
||||
|
||||
- (void)setBottomCornerView:(CPView)aBottomCornerView
|
||||
{
|
||||
if (_bottomCornerView === aBottomCornerView)
|
||||
return;
|
||||
|
||||
[_bottomCornerView removeFromSuperview];
|
||||
|
||||
[aBottomCornerView setFrame:[self _bottomCornerViewFrame]];
|
||||
[self addSubview:aBottomCornerView];
|
||||
|
||||
_bottomCornerView = aBottomCornerView;
|
||||
|
||||
[self _updateCornerAndHeaderView];
|
||||
}
|
||||
|
||||
- (CPView)bottomCornerView
|
||||
{
|
||||
return _bottomCornerView;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_verticalScrollerDidScroll:(CPScroller)aScroller
|
||||
{
|
||||
var value = [aScroller floatValue],
|
||||
documentFrame = [[_contentView documentView] frame];
|
||||
var value = [aScroller floatValue],
|
||||
documentFrame = [[_contentView documentView] frame],
|
||||
contentBounds = [_contentView bounds];
|
||||
|
||||
switch ([_verticalScroller hitPart])
|
||||
{
|
||||
case CPScrollerDecrementLine: contentBounds.origin.y -= _verticalLineScroll;
|
||||
break;
|
||||
|
||||
|
||||
case CPScrollerIncrementLine: contentBounds.origin.y += _verticalLineScroll;
|
||||
break;
|
||||
|
||||
|
||||
case CPScrollerDecrementPage: contentBounds.origin.y -= _CGRectGetHeight(contentBounds) - _verticalPageScroll;
|
||||
break;
|
||||
|
||||
|
||||
case CPScrollerIncrementPage: contentBounds.origin.y += _CGRectGetHeight(contentBounds) - _verticalPageScroll;
|
||||
break;
|
||||
|
||||
|
||||
case CPScrollerKnobSlot:
|
||||
case CPScrollerKnob:
|
||||
// We want integral bounds!
|
||||
default: contentBounds.origin.y = ROUND(value * (_CGRectGetHeight(documentFrame) - _CGRectGetHeight(contentBounds)));
|
||||
}
|
||||
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
}
|
||||
|
||||
@@ -509,21 +662,21 @@
|
||||
var value = [aScroller floatValue],
|
||||
documentFrame = [[self documentView] frame],
|
||||
contentBounds = [_contentView bounds];
|
||||
|
||||
|
||||
switch ([_horizontalScroller hitPart])
|
||||
{
|
||||
case CPScrollerDecrementLine: contentBounds.origin.x -= _horizontalLineScroll;
|
||||
break;
|
||||
|
||||
|
||||
case CPScrollerIncrementLine: contentBounds.origin.x += _horizontalLineScroll;
|
||||
break;
|
||||
|
||||
|
||||
case CPScrollerDecrementPage: contentBounds.origin.x -= _CGRectGetWidth(contentBounds) - _horizontalPageScroll;
|
||||
break;
|
||||
|
||||
|
||||
case CPScrollerIncrementPage: contentBounds.origin.x += _CGRectGetWidth(contentBounds) - _horizontalPageScroll;
|
||||
break;
|
||||
|
||||
|
||||
case CPScrollerKnobSlot:
|
||||
case CPScrollerKnob:
|
||||
// We want integral bounds!
|
||||
@@ -548,7 +701,7 @@
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
-(void)resizeSubviewsWithOldSize:(CGSize)aSize
|
||||
- (void)resizeSubviewsWithOldSize:(CGSize)aSize
|
||||
{
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
@@ -658,74 +811,238 @@
|
||||
return _verticalPageScroll;
|
||||
}
|
||||
|
||||
// CPView Overrides
|
||||
|
||||
- (void)drawRect:(CPRect)aRect
|
||||
{
|
||||
[super drawRect:aRect];
|
||||
|
||||
if (_borderType == CPNoBorder)
|
||||
return;
|
||||
|
||||
var strokeRect = [self bounds],
|
||||
context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextSetLineWidth(context, 1);
|
||||
|
||||
switch (_borderType)
|
||||
{
|
||||
case CPLineBorder:
|
||||
CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:@"border-color"]);
|
||||
CGContextStrokeRect(context, _CGRectInset(strokeRect, 0.5, 0.5));
|
||||
break;
|
||||
|
||||
case CPBezelBorder:
|
||||
[self _drawGrayBezelInContext:context bounds:strokeRect];
|
||||
break;
|
||||
|
||||
case CPGrooveBorder:
|
||||
[self _drawGrooveInContext:context bounds:strokeRect];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_drawGrayBezelInContext:(CGContext)context bounds:(CGRect)aRect
|
||||
{
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetStrokeColor(context, [CPColor colorWithWhite:142.0 / 255.0 alpha:1.0]);
|
||||
|
||||
var y = _CGRectGetMinY(aRect) + 0.5;
|
||||
|
||||
CGContextMoveToPoint(context, _CGRectGetMinX(aRect), y);
|
||||
CGContextAddLineToPoint(context, _CGRectGetMinX(aRect) + 1.0, y);
|
||||
CGContextStrokePath(context);
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetStrokeColor(context, [CPColor colorWithWhite:192.0 / 255.0 alpha:1.0]);
|
||||
CGContextMoveToPoint(context, _CGRectGetMinX(aRect) + 1.0, y);
|
||||
CGContextAddLineToPoint(context, _CGRectGetMaxX(aRect) - 1.0, y);
|
||||
CGContextStrokePath(context);
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetStrokeColor(context, [CPColor colorWithWhite:142.0 / 255.0 alpha:1.0]);
|
||||
CGContextMoveToPoint(context, _CGRectGetMaxX(aRect) - 1.0, y);
|
||||
CGContextAddLineToPoint(context, _CGRectGetMaxX(aRect), y);
|
||||
CGContextStrokePath(context);
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetStrokeColor(context, [CPColor colorWithWhite:190.0 / 255.0 alpha:1.0]);
|
||||
|
||||
var x = _CGRectGetMaxX(aRect) - 0.5;
|
||||
|
||||
CGContextMoveToPoint(context, x, _CGRectGetMinY(aRect) + 1.0);
|
||||
CGContextAddLineToPoint(context, x, _CGRectGetMaxY(aRect));
|
||||
|
||||
CGContextMoveToPoint(context, x - 0.5, _CGRectGetMaxY(aRect) - 0.5);
|
||||
CGContextAddLineToPoint(context, _CGRectGetMinX(aRect), _CGRectGetMaxY(aRect) - 0.5);
|
||||
|
||||
x = _CGRectGetMinX(aRect) + 0.5;
|
||||
|
||||
CGContextMoveToPoint(context, x, _CGRectGetMaxY(aRect));
|
||||
CGContextAddLineToPoint(context, x, _CGRectGetMinY(aRect) + 1.0);
|
||||
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
- (void)_drawGrooveInContext:(CGContext)context bounds:(CGRect)aRect
|
||||
{
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetStrokeColor(context, [CPColor colorWithWhite:159.0 / 255.0 alpha:1.0]);
|
||||
|
||||
var y = _CGRectGetMinY(aRect) + 0.5;
|
||||
|
||||
CGContextMoveToPoint(context, _CGRectGetMinX(aRect), y);
|
||||
CGContextAddLineToPoint(context, _CGRectGetMaxX(aRect), y);
|
||||
|
||||
var x = _CGRectGetMaxX(aRect) - 1.5;
|
||||
|
||||
CGContextMoveToPoint(context, x, _CGRectGetMinY(aRect) + 2.0);
|
||||
CGContextAddLineToPoint(context, x, _CGRectGetMaxY(aRect) - 1.0);
|
||||
|
||||
y = _CGRectGetMaxY(aRect) - 1.5;
|
||||
|
||||
CGContextMoveToPoint(context, _CGRectGetMaxX(aRect) - 1.0, y);
|
||||
CGContextAddLineToPoint(context, _CGRectGetMinX(aRect) + 2.0, y);
|
||||
|
||||
x = _CGRectGetMinX(aRect) + 0.5;
|
||||
|
||||
CGContextMoveToPoint(context, x, _CGRectGetMaxY(aRect));
|
||||
CGContextAddLineToPoint(context, x, _CGRectGetMinY(aRect));
|
||||
|
||||
CGContextStrokePath(context);
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetStrokeColor(context, [CPColor whiteColor]);
|
||||
|
||||
var rect = _CGRectOffset(aRect, 1.0, 1.0);
|
||||
|
||||
rect.size.width -= 1.0;
|
||||
rect.size.height -= 1.0;
|
||||
CGContextStrokeRect(context, _CGRectInset(rect, 0.5, 0.5));
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetStrokeColor(context, [CPColor colorWithWhite:192.0 / 255.0 alpha:1.0]);
|
||||
|
||||
y = _CGRectGetMinY(aRect) + 2.5;
|
||||
|
||||
CGContextMoveToPoint(context, _CGRectGetMinX(aRect) + 2.0, y);
|
||||
CGContextAddLineToPoint(context, _CGRectGetMaxX(aRect) - 2.0, y);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
|
||||
|
||||
// CPResponder Overrides
|
||||
|
||||
/*!
|
||||
Handles a scroll wheel event from the user.
|
||||
@param anEvent the scroll wheel event
|
||||
*/
|
||||
- (void)scrollWheel:(CPEvent)anEvent
|
||||
{
|
||||
var documentFrame = [[self documentView] frame],
|
||||
contentBounds = [_contentView bounds];
|
||||
|
||||
// We want integral bounds!
|
||||
contentBounds.origin.x = ROUND(contentBounds.origin.x + [anEvent deltaX] * _horizontalLineScroll);
|
||||
contentBounds.origin.y = ROUND(contentBounds.origin.y + [anEvent deltaY] * _verticalLineScroll);
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0.0)];
|
||||
[self _respondToScrollWheelEventWithDeltaX:[anEvent deltaX] deltaY:[anEvent deltaY]];
|
||||
}
|
||||
|
||||
- (void)keyDown:(CPEvent)anEvent
|
||||
- (void)_respondToScrollWheelEventWithDeltaX:(float)deltaX deltaY:(float)deltaY
|
||||
{
|
||||
var keyCode = [anEvent keyCode],
|
||||
var documentFrame = [[self documentView] frame],
|
||||
contentBounds = [_contentView bounds],
|
||||
contentFrame = [_contentView frame],
|
||||
enclosingScrollView = [self enclosingScrollView];
|
||||
|
||||
// We want integral bounds!
|
||||
contentBounds.origin.x = ROUND(contentBounds.origin.x + deltaX);
|
||||
contentBounds.origin.y = ROUND(contentBounds.origin.y + deltaY);
|
||||
|
||||
var constrainedOrigin = [_contentView constrainScrollPoint:CGPointCreateCopy(contentBounds.origin)],
|
||||
extraX = contentBounds.origin.x - constrainedOrigin.x,
|
||||
extraY = contentBounds.origin.y - constrainedOrigin.y;
|
||||
|
||||
[_contentView scrollToPoint:constrainedOrigin];
|
||||
[_headerClipView scrollToPoint:CGPointMake(constrainedOrigin.x, 0.0)];
|
||||
|
||||
if (extraX || extraY)
|
||||
[enclosingScrollView _respondToScrollWheelEventWithDeltaX:extraX deltaY:extraY];
|
||||
}
|
||||
|
||||
- (void)scrollPageUp:(id)sender
|
||||
{
|
||||
var contentBounds = [_contentView bounds];
|
||||
[self moveByOffset:CGSizeMake(0.0, -(_CGRectGetHeight(contentBounds) - _verticalPageScroll))];
|
||||
}
|
||||
|
||||
- (void)scrollPageDown:(id)sender
|
||||
{
|
||||
var contentBounds = [_contentView bounds];
|
||||
[self moveByOffset:CGSizeMake(0.0, _CGRectGetHeight(contentBounds) - _verticalPageScroll)];
|
||||
}
|
||||
|
||||
- (void)scrollToBeginningOfDocument:(id)sender
|
||||
{
|
||||
[_contentView scrollToPoint:_CGPointMakeZero()];
|
||||
[_headerClipView scrollToPoint:_CGPointMakeZero()];
|
||||
}
|
||||
|
||||
- (void)scrollToEndOfDocument:(id)sender
|
||||
{
|
||||
var contentBounds = [_contentView bounds],
|
||||
documentFrame = [[self documentView] frame],
|
||||
scrollPoint = _CGPointMake(0.0, _CGRectGetHeight(documentFrame) - _CGRectGetHeight(contentBounds));
|
||||
|
||||
[_contentView scrollToPoint:scrollPoint];
|
||||
[_headerClipView scrollToPoint:_CGPointMakeZero()];
|
||||
}
|
||||
|
||||
- (void)moveLeft:(id)sender
|
||||
{
|
||||
[self moveByOffset:CGSizeMake(-_horizontalLineScroll, 0.0)];
|
||||
}
|
||||
|
||||
- (void)moveRight:(id)sender
|
||||
{
|
||||
[self moveByOffset:CGSizeMake(_horizontalLineScroll, 0.0)];
|
||||
}
|
||||
|
||||
- (void)moveUp:(id)sender
|
||||
{
|
||||
[self moveByOffset:CGSizeMake(0.0, -_verticalLineScroll)];
|
||||
}
|
||||
|
||||
- (void)moveDown:(id)sender
|
||||
{
|
||||
[self moveByOffset:CGSizeMake(0.0, _verticalLineScroll)];
|
||||
}
|
||||
|
||||
- (void)moveByOffset:(CGSize)aSize
|
||||
{
|
||||
var documentFrame = [[self documentView] frame],
|
||||
contentBounds = [_contentView bounds];
|
||||
|
||||
switch (keyCode)
|
||||
{
|
||||
case 33: /*pageup*/
|
||||
contentBounds.origin.y -= _CGRectGetHeight(contentBounds) - _verticalPageScroll;
|
||||
break;
|
||||
|
||||
case 34: /*pagedown*/
|
||||
contentBounds.origin.y += _CGRectGetHeight(contentBounds) - _verticalPageScroll;
|
||||
break;
|
||||
|
||||
case 38: /*up arrow*/
|
||||
contentBounds.origin.y -= _verticalLineScroll;
|
||||
break;
|
||||
|
||||
case 40: /*down arrow*/
|
||||
contentBounds.origin.y += _verticalLineScroll;
|
||||
break;
|
||||
|
||||
case 37: /*left arrow*/
|
||||
contentBounds.origin.x -= _horizontalLineScroll;
|
||||
break;
|
||||
|
||||
case 49: /*right arrow*/
|
||||
contentBounds.origin.x += _horizontalLineScroll;
|
||||
break;
|
||||
|
||||
default: return [super keyDown:anEvent];
|
||||
}
|
||||
contentBounds.origin.x += aSize.width;
|
||||
contentBounds.origin.y += aSize.height;
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin, 0)];
|
||||
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPScrollViewContentViewKey = "CPScrollViewContentView",
|
||||
CPScrollViewVLineScrollKey = "CPScrollViewVLineScroll",
|
||||
CPScrollViewHLineScrollKey = "CPScrollViewHLineScroll",
|
||||
CPScrollViewVPageScrollKey = "CPScrollViewVPageScroll",
|
||||
CPScrollViewHPageScrollKey = "CPScrollViewHPageScroll",
|
||||
CPScrollViewHasVScrollerKey = "CPScrollViewHasVScroller",
|
||||
CPScrollViewHasHScrollerKey = "CPScrollViewHasHScroller",
|
||||
CPScrollViewVScrollerKey = "CPScrollViewVScroller",
|
||||
CPScrollViewHScrollerKey = "CPScrollViewHScroller",
|
||||
CPScrollViewAutohidesScrollerKey = "CPScrollViewAutohidesScroller";
|
||||
var CPScrollViewContentViewKey = @"CPScrollViewContentView",
|
||||
CPScrollViewHeaderClipViewKey = @"CPScrollViewHeaderClipViewKey",
|
||||
CPScrollViewVLineScrollKey = @"CPScrollViewVLineScroll",
|
||||
CPScrollViewHLineScrollKey = @"CPScrollViewHLineScroll",
|
||||
CPScrollViewVPageScrollKey = @"CPScrollViewVPageScroll",
|
||||
CPScrollViewHPageScrollKey = @"CPScrollViewHPageScroll",
|
||||
CPScrollViewHasVScrollerKey = @"CPScrollViewHasVScroller",
|
||||
CPScrollViewHasHScrollerKey = @"CPScrollViewHasHScroller",
|
||||
CPScrollViewVScrollerKey = @"CPScrollViewVScroller",
|
||||
CPScrollViewHScrollerKey = @"CPScrollViewHScroller",
|
||||
CPScrollViewAutohidesScrollerKey = @"CPScrollViewAutohidesScroller",
|
||||
CPScrollViewCornerViewKey = @"CPScrollViewCornerViewKey",
|
||||
CPScrollViewBottomCornerViewKey = @"CPScrollViewBottomCornerViewKey",
|
||||
CPScrollViewBorderTypeKey = @"CPScrollViewBorderTypeKey";
|
||||
|
||||
@implementation CPScrollView (CPCoding)
|
||||
|
||||
@@ -738,40 +1055,58 @@ var CPScrollViewContentViewKey = "CPScrollViewContentView",
|
||||
|
||||
_horizontalLineScroll = [aCoder decodeFloatForKey:CPScrollViewHLineScrollKey];
|
||||
_horizontalPageScroll = [aCoder decodeFloatForKey:CPScrollViewHPageScrollKey];
|
||||
|
||||
|
||||
_contentView = [aCoder decodeObjectForKey:CPScrollViewContentViewKey];
|
||||
|
||||
_headerClipView = [aCoder decodeObjectForKey:CPScrollViewHeaderClipViewKey];
|
||||
|
||||
if (!_headerClipView)
|
||||
{
|
||||
_headerClipView = [[CPClipView alloc] init];
|
||||
[self addSubview:_headerClipView];
|
||||
}
|
||||
|
||||
_verticalScroller = [aCoder decodeObjectForKey:CPScrollViewVScrollerKey];
|
||||
_horizontalScroller = [aCoder decodeObjectForKey:CPScrollViewHScrollerKey];
|
||||
|
||||
_hasVerticalScroller = [aCoder decodeBoolForKey:CPScrollViewHasVScrollerKey];
|
||||
_hasHorizontalScroller = [aCoder decodeBoolForKey:CPScrollViewHasHScrollerKey];
|
||||
_autohidesScrollers = [aCoder decodeBoolForKey:CPScrollViewAutohidesScrollerKey];
|
||||
|
||||
|
||||
_borderType = [aCoder decodeIntForKey:CPScrollViewBorderTypeKey];
|
||||
|
||||
_cornerView = [aCoder decodeObjectForKey:CPScrollViewCornerViewKey];
|
||||
_bottomCornerView = [aCoder decodeObjectForKey:CPScrollViewBottomCornerViewKey];
|
||||
|
||||
// Do to the anything goes nature of decoding, our subviews may not exist yet, so layout at the end of the run loop when we're sure everything is in a correct state.
|
||||
[[CPRunLoop currentRunLoop] performSelector:@selector(reflectScrolledClipView:) target:self argument:_contentView order:0 modes:[CPDefaultRunLoopMode]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
|
||||
[aCoder encodeObject:_contentView forKey:CPScrollViewContentViewKey];
|
||||
|
||||
[aCoder encodeObject:_headerClipView forKey:CPScrollViewHeaderClipViewKey];
|
||||
|
||||
[aCoder encodeObject:_verticalScroller forKey:CPScrollViewVScrollerKey];
|
||||
[aCoder encodeObject:_horizontalScroller forKey:CPScrollViewHScrollerKey];
|
||||
|
||||
|
||||
[aCoder encodeFloat:_verticalLineScroll forKey:CPScrollViewVLineScrollKey];
|
||||
[aCoder encodeFloat:_verticalPageScroll forKey:CPScrollViewVPageScrollKey];
|
||||
[aCoder encodeFloat:_horizontalLineScroll forKey:CPScrollViewHLineScrollKey];
|
||||
[aCoder encodeFloat:_horizontalPageScroll forKey:CPScrollViewHPageScrollKey];
|
||||
|
||||
|
||||
[aCoder encodeBool:_hasVerticalScroller forKey:CPScrollViewHasVScrollerKey];
|
||||
[aCoder encodeBool:_hasHorizontalScroller forKey:CPScrollViewHasHScrollerKey];
|
||||
[aCoder encodeBool:_autohidesScrollers forKey:CPScrollViewAutohidesScrollerKey];
|
||||
|
||||
[aCoder encodeObject:_cornerView forKey:CPScrollViewCornerViewKey];
|
||||
[aCoder encodeObject:_bottomCornerView forKey:CPScrollViewBottomCornerViewKey];
|
||||
|
||||
[aCoder encodeInt:_borderType forKey:CPScrollViewBorderTypeKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+112
-81
@@ -22,8 +22,6 @@
|
||||
|
||||
@import "CPControl.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
// CPScroller Constants
|
||||
CPScrollerNoPart = 0;
|
||||
@@ -41,9 +39,9 @@ CPNoScrollerParts = 0;
|
||||
CPOnlyScrollerArrows = 1;
|
||||
CPAllScrollerParts = 2;
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPScroller
|
||||
@class CPScroller
|
||||
*/
|
||||
|
||||
var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
|
||||
@@ -72,24 +70,25 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
CGPoint _trackingStartPoint;
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return "scroller";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[ nil, nil, nil, nil,
|
||||
_CGSizeMakeZero(), _CGSizeMakeZero(), _CGInsetMakeZero(), _CGInsetMakeZero(), _CGSizeMakeZero()]
|
||||
forKeys:[ @"knob-slot-color",
|
||||
@"decrement-line-color",
|
||||
@"increment-line-color",
|
||||
@"knob-color",
|
||||
@"decrement-line-size",
|
||||
@"increment-line-size",
|
||||
@"track-inset",
|
||||
@"knob-inset",
|
||||
@"minimum-knob-length"]];
|
||||
return [CPDictionary dictionaryWithJSObject:{
|
||||
@"scroller-width": 15.0,
|
||||
@"knob-slot-color": [CPColor lightGrayColor],
|
||||
@"decrement-line-color": [CPNull null],
|
||||
@"increment-line-color": [CPNull null],
|
||||
@"knob-color": [CPColor grayColor],
|
||||
@"decrement-line-size":_CGSizeMakeZero(),
|
||||
@"increment-line-size":_CGSizeMakeZero(),
|
||||
@"track-inset":_CGInsetMakeZero(),
|
||||
@"knob-inset": _CGInsetMakeZero(),
|
||||
@"minimum-knob-length":21.0
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +97,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_controlSize = CPRegularControlSize;
|
||||
@@ -106,7 +105,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
|
||||
[self setFloatValue:0.0];
|
||||
[self setKnobProportion:1.0];
|
||||
|
||||
|
||||
_hitPart = CPScrollerNoPart;
|
||||
|
||||
[self _calculateIsVertical];
|
||||
@@ -121,7 +120,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
*/
|
||||
+ (float)scrollerWidth
|
||||
{
|
||||
return 15.0;//[self scrollerWidthForControlSize:CPRegularControlSize];
|
||||
return [[[CPScroller alloc] init] currentValueForThemeAttribute:@"scroller-width"];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -130,7 +129,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
*/
|
||||
+ (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
|
||||
{
|
||||
return 15.0;//_CPScrollerWidths[aControlSize];
|
||||
return [self scrollerWidth];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -180,10 +179,10 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
- (id)currentValueForThemeAttribute:(CPString)anAttributeName
|
||||
{
|
||||
var themeState = _themeState;
|
||||
|
||||
|
||||
if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
|
||||
themeState &= ~CPThemeStateHighlighted;
|
||||
|
||||
|
||||
return [self valueForThemeAttribute:anAttributeName inState:themeState];
|
||||
}
|
||||
|
||||
@@ -205,25 +204,25 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
- (CPScrollerPart)testPart:(CGPoint)aPoint
|
||||
{
|
||||
aPoint = [self convertPoint:aPoint fromView:nil];
|
||||
|
||||
// The ordering of these tests is important. We check the knob and
|
||||
|
||||
// The ordering of these tests is important. We check the knob and
|
||||
// page rects first since they may overlap with the arrows.
|
||||
|
||||
|
||||
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
|
||||
return CPScrollerKnob;
|
||||
|
||||
|
||||
if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementPage], aPoint))
|
||||
return CPScrollerDecrementPage;
|
||||
|
||||
|
||||
if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementPage], aPoint))
|
||||
return CPScrollerIncrementPage;
|
||||
|
||||
|
||||
if (CGRectContainsPoint([self rectForPart:CPScrollerDecrementLine], aPoint))
|
||||
return CPScrollerDecrementLine;
|
||||
|
||||
|
||||
if (CGRectContainsPoint([self rectForPart:CPScrollerIncrementLine], aPoint))
|
||||
return CPScrollerIncrementLine;
|
||||
|
||||
|
||||
if (CGRectContainsPoint([self rectForPart:CPScrollerKnobSlot], aPoint))
|
||||
return CPScrollerKnobSlot;
|
||||
|
||||
@@ -241,16 +240,16 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
if (_knobProportion === 1.0)
|
||||
{
|
||||
_usableParts = CPNoScrollerParts;
|
||||
|
||||
_partRects[CPScrollerDecrementPage] = _CGRectMakeZero();
|
||||
_partRects[CPScrollerKnob] = _CGRectMakeZero();
|
||||
_partRects[CPScrollerIncrementPage] = _CGRectMakeZero();
|
||||
_partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
|
||||
_partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
|
||||
|
||||
_partRects[CPScrollerDecrementPage] = CGRectMakeZero();
|
||||
_partRects[CPScrollerKnob] = CGRectMakeZero();
|
||||
_partRects[CPScrollerIncrementPage] = CGRectMakeZero();
|
||||
_partRects[CPScrollerDecrementLine] = CGRectMakeZero();
|
||||
_partRects[CPScrollerIncrementLine] = CGRectMakeZero();
|
||||
|
||||
// In this case, the slot is the entirety of the scroller.
|
||||
_partRects[CPScrollerKnobSlot] = _CGRectMakeCopy(bounds);
|
||||
|
||||
_partRects[CPScrollerKnobSlot] = CGRectMakeCopy(bounds);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -260,8 +259,8 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
var knobInset = [self currentValueForThemeAttribute:@"knob-inset"],
|
||||
trackInset = [self currentValueForThemeAttribute:@"track-inset"],
|
||||
width = _CGRectGetWidth(bounds),
|
||||
height = _CGRectGetHeight(bounds);
|
||||
|
||||
height = _CGRectGetHeight(bounds);
|
||||
|
||||
if ([self isVertical])
|
||||
{
|
||||
var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
|
||||
@@ -279,15 +278,24 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
_partRects[CPScrollerIncrementPage] = _CGRectMake(0.0, knobLocation + knobHeight, width, height - (knobLocation + knobHeight) - effectiveIncrementLineHeight);
|
||||
_partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, effectiveDecrementLineHeight, width - trackInset.left - trackInset.right, slotHeight);
|
||||
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
|
||||
_partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height);
|
||||
}
|
||||
_partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - incrementLineSize.height, incrementLineSize.width, incrementLineSize.height);
|
||||
|
||||
if (height < knobHeight + decrementLineSize.height + incrementLineSize.height + trackInset.top + trackInset.bottom)
|
||||
_partRects[CPScrollerKnob] = _CGRectMakeZero();
|
||||
|
||||
if (height < decrementLineSize.height + incrementLineSize.height - 2)
|
||||
{
|
||||
_partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
|
||||
_partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
|
||||
_partRects[CPScrollerKnobSlot] = _CGRectMake(trackInset.left, 0, width - trackInset.left - trackInset.right, height);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
|
||||
incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
|
||||
effectiveDecrementLineWidth = decrementLineSize.width + trackInset.left,
|
||||
effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right;
|
||||
effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right,
|
||||
slotWidth = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
|
||||
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
||||
knobWidth = MAX(minimumKnobLength, (slotWidth * _knobProportion)),
|
||||
@@ -300,6 +308,16 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
_partRects[CPScrollerKnobSlot] = _CGRectMake(effectiveDecrementLineWidth, trackInset.top, slotWidth, height - trackInset.top - trackInset.bottom);
|
||||
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, decrementLineSize.width, decrementLineSize.height);
|
||||
_partRects[CPScrollerIncrementLine] = _CGRectMake(width - incrementLineSize.width, 0.0, incrementLineSize.width, incrementLineSize.height);
|
||||
|
||||
if (width < knobWidth + decrementLineSize.width + incrementLineSize.width + trackInset.left + trackInset.right)
|
||||
_partRects[CPScrollerKnob] = _CGRectMakeZero();
|
||||
|
||||
if (width < decrementLineSize.width + incrementLineSize.width - 2)
|
||||
{
|
||||
_partRects[CPScrollerIncrementLine] = _CGRectMakeZero();
|
||||
_partRects[CPScrollerDecrementLine] = _CGRectMakeZero();
|
||||
_partRects[CPScrollerKnobSlot] = _CGRectMake(0.0, 0.0, width, slotHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,9 +357,9 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
- (CPView)createViewForPart:(CPScrollerPart)aPart
|
||||
{
|
||||
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
|
||||
[view setHitTests:NO];
|
||||
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -355,7 +373,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
[view setHitTests:NO];
|
||||
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -369,12 +387,12 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var part = PARTS_ARRANGEMENT[index];
|
||||
|
||||
|
||||
if (index === 0)
|
||||
view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowBelow relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index + 1]];
|
||||
else
|
||||
view = [self layoutEphemeralSubviewNamed:part positioned:CPWindowAbove relativeToEphemeralSubviewNamed:PARTS_ARRANGEMENT[index - 1]];
|
||||
|
||||
|
||||
if (view)
|
||||
[view setBackgroundColor:[self currentValueForThemeAttribute:NAMES_FOR_PARTS[part] + "-color"]];
|
||||
}
|
||||
@@ -384,7 +402,7 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
Caches images for the scroll arrow and knob.
|
||||
*/
|
||||
- (void)drawParts
|
||||
{
|
||||
{
|
||||
[self drawKnobSlot];
|
||||
[self drawKnob];
|
||||
[self drawArrow:CPScrollerDecrementArrow highlight:NO];
|
||||
@@ -407,37 +425,37 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
- (void)trackKnob:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type];
|
||||
|
||||
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
_hitPart = CPScrollerNoPart;
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (type === CPLeftMouseDown)
|
||||
{
|
||||
_trackingFloatValue = [self floatValue];
|
||||
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
}
|
||||
|
||||
|
||||
else if (type === CPLeftMouseDragged)
|
||||
{
|
||||
var knobRect = [self rectForPart:CPScrollerKnob],
|
||||
knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
|
||||
remainder = ![self isVertical] ? (_CGRectGetWidth(knobSlotRect) - _CGRectGetWidth(knobRect)) : (_CGRectGetHeight(knobSlotRect) - _CGRectGetHeight(knobRect));
|
||||
|
||||
|
||||
if (remainder <= 0)
|
||||
[self setFloatValue:0.0];
|
||||
else
|
||||
{
|
||||
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
delta = ![self isVertical] ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
|
||||
|
||||
[self setFloatValue:_trackingFloatValue + delta / remainder];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackKnob:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
@@ -455,26 +473,26 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
{
|
||||
[self highlight:NO];
|
||||
[CPEvent stopPeriodicEvents];
|
||||
|
||||
|
||||
_hitPart = CPScrollerNoPart;
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (type === CPLeftMouseDown)
|
||||
{
|
||||
_trackingPart = [self hitPart];
|
||||
|
||||
|
||||
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
if ([anEvent modifierFlags] & CPAlternateKeyMask)
|
||||
{
|
||||
if (_trackingPart == CPScrollerDecrementLine)
|
||||
_hitPart = CPScrollerDecrementPage;
|
||||
|
||||
|
||||
else if (_trackingPart == CPScrollerIncrementLine)
|
||||
_hitPart = CPScrollerIncrementPage;
|
||||
|
||||
|
||||
else if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
|
||||
{
|
||||
var knobRect = [self rectForPart:CPScrollerKnob],
|
||||
@@ -483,42 +501,42 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
remainder = (![self isVertical] ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth;
|
||||
|
||||
[self setFloatValue:((![self isVertical] ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
|
||||
|
||||
|
||||
_hitPart = CPScrollerKnob;
|
||||
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
|
||||
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
|
||||
// Now just track the knob.
|
||||
return [self trackKnob:anEvent];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[self highlight:YES];
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
|
||||
|
||||
[CPEvent startPeriodicEventsAfterDelay:0.5 withPeriod:0.04];
|
||||
}
|
||||
|
||||
|
||||
else if (type === CPLeftMouseDragged)
|
||||
{
|
||||
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
|
||||
if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
|
||||
{
|
||||
var hitPart = [self testPart:[anEvent locationInWindow]];
|
||||
|
||||
|
||||
if (hitPart == CPScrollerDecrementPage || hitPart == CPScrollerIncrementPage)
|
||||
{
|
||||
_trackingPart = hitPart;
|
||||
_hitPart = hitPart;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[self highlight:CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint)];
|
||||
}
|
||||
else if (type == CPPeriodic && CGRectContainsPoint([self rectForPart:_trackingPart], _trackingStartPoint))
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackScrollButtons:) forNextEventMatchingMask:CPPeriodicMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
|
||||
}
|
||||
@@ -550,15 +568,15 @@ NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
|
||||
_hitPart = [self testPart:[anEvent locationInWindow]];
|
||||
|
||||
|
||||
switch (_hitPart)
|
||||
{
|
||||
case CPScrollerKnob: return [self trackKnob:anEvent];
|
||||
|
||||
case CPScrollerDecrementLine:
|
||||
case CPScrollerIncrementLine:
|
||||
|
||||
case CPScrollerDecrementLine:
|
||||
case CPScrollerIncrementLine:
|
||||
case CPScrollerDecrementPage:
|
||||
case CPScrollerIncrementPage: return [self trackScrollButtons:anEvent];
|
||||
}
|
||||
@@ -584,19 +602,32 @@ var CPScrollerControlSizeKey = "CPScrollerControlSize",
|
||||
_knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
|
||||
|
||||
_partRects = [];
|
||||
|
||||
|
||||
_hitPart = CPScrollerNoPart;
|
||||
|
||||
[self _calculateIsVertical];
|
||||
|
||||
// Adjust the size of the scroller if the size from cib
|
||||
// isn't equal to the scrollerWidth
|
||||
var frame = [self frame],
|
||||
scrollerWidth = [CPScroller scrollerWidth];
|
||||
|
||||
if ([self isVertical] && CGRectGetWidth(frame) !== scrollerWidth)
|
||||
frame.size.width = scrollerWidth;
|
||||
|
||||
if (![self isVertical] && CGRectGetHeight(frame) !== scrollerWidth)
|
||||
frame.size.height = scrollerWidth;
|
||||
|
||||
[self setFrame:frame];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
|
||||
[aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
|
||||
[aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
|
||||
}
|
||||
|
||||
+381
-291
@@ -20,42 +20,33 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPButton.j"
|
||||
@import "CPTextField.j"
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
|
||||
/*!
|
||||
@global
|
||||
@group Menu tags
|
||||
*/
|
||||
CPSearchFieldRecentsTitleMenuItemTag = 1000;
|
||||
/*!
|
||||
@global
|
||||
@group Menu tags
|
||||
*/
|
||||
CPSearchFieldRecentsMenuItemTag = 1001;
|
||||
/*!
|
||||
@global
|
||||
@group Menu tags
|
||||
*/
|
||||
CPSearchFieldClearRecentsMenuItemTag = 1002;
|
||||
/*!
|
||||
@global
|
||||
@group Menu tags
|
||||
*/
|
||||
CPSearchFieldNoRecentsMenuItemTag = 1003;
|
||||
CPSearchFieldRecentsTitleMenuItemTag = 1000;
|
||||
CPSearchFieldRecentsMenuItemTag = 1001;
|
||||
CPSearchFieldClearRecentsMenuItemTag = 1002;
|
||||
CPSearchFieldNoRecentsMenuItemTag = 1003;
|
||||
|
||||
var CPSearchFieldSearchImage = nil,
|
||||
CPSearchFieldFindImage = nil,
|
||||
CPSearchFieldCancelImage = nil,
|
||||
CPSearchFieldCancelPressedImage = nil;
|
||||
|
||||
/*!
|
||||
var SEARCH_BUTTON_DEFAULT_WIDTH = 25.0,
|
||||
CANCEL_BUTTON_DEFAULT_WIDTH = 22.0,
|
||||
BUTTON_DEFAULT_HEIGHT = 22.0;
|
||||
|
||||
var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification";
|
||||
|
||||
var RECENT_SEARCH_PREFIX = @" ";
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPSearchField
|
||||
The CPSearchField class defines the programmatic interface for text fields that are optimized for text-based searches. A CPSearchField object directly inherits from the CPTextField class. The search field implemented by these classes presents a standard user interface for searches, including a search button, a cancel button, and a pop-up icon menu for listing recent search strings and custom search categories.
|
||||
|
||||
When the user types and then pauses, the text field's action message is sent to its target. You can query the text field's string value for the current text to search for. Do not rely on the sender of the action to be an CPMenu object because the menu may change. If you need to change the menu, modify the search menu template and call the setSearchMenuTemplate: method to update.
|
||||
When the user types and then pauses, the text field's action message is sent to its target. You can query the text field's string value for the current text to search for. Do not rely on the sender of the action to be an CPMenu object because the menu may change. If you need to change the menu, modify the search menu template and call the setSearchMenuTemplate: method to update.
|
||||
*/
|
||||
@implementation CPSearchField : CPTextField
|
||||
{
|
||||
@@ -66,80 +57,72 @@ var CPSearchFieldSearchImage = nil,
|
||||
|
||||
CPString _recentsAutosaveName;
|
||||
CPArray _recentSearches;
|
||||
|
||||
|
||||
int _maximumRecents;
|
||||
BOOL _sendsWholeSearchString;
|
||||
BOOL _sendsSearchStringImmediately;
|
||||
BOOL _canResignFirstResponder;
|
||||
CPTimer _partialStringTimer;
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"searchfield"
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [CPSearchField class])
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
CPSearchFieldSearchImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldSearch.png"]];
|
||||
CPSearchFieldFindImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldFind.png"]];
|
||||
CPSearchFieldCancelImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancel.png"]];
|
||||
CPSearchFieldCancelPressedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancelPressed.png"]];
|
||||
CPSearchFieldSearchImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldSearch.png"] size:_CGSizeMake(SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
|
||||
CPSearchFieldFindImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldFind.png"] size:_CGSizeMake(SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
|
||||
CPSearchFieldCancelImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancel.png"] size:_CGSizeMake(CANCEL_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
|
||||
CPSearchFieldCancelPressedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSearchField/CPSearchFieldCancelPressed.png"] size:_CGSizeMake(CANCEL_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT)];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)frame
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self != nil)
|
||||
{
|
||||
_recentSearches = [CPArray array];
|
||||
_maximumRecents = 10;
|
||||
_sendsWholeSearchString = NO;
|
||||
_sendsSearchStringImmediately = NO;
|
||||
|
||||
[self setBezeled:YES];
|
||||
[self setBezelStyle:CPTextFieldRoundedBezel];
|
||||
[self setBordered:YES];
|
||||
[self setEditable:YES];
|
||||
[self setDelegate:self];
|
||||
|
||||
_cancelButton = [[CPButton alloc] initWithFrame:CPMakeRect(frame.size.width - 27,(frame.size.height-22)/2,22,22)];
|
||||
[self resetCancelButton];
|
||||
|
||||
|
||||
[_cancelButton setHidden:YES];
|
||||
[_cancelButton setAutoresizingMask:CPViewMinXMargin];
|
||||
[self addSubview:_cancelButton];
|
||||
|
||||
_searchButton = [[CPButton alloc] initWithFrame:CPMakeRect(5,(frame.size.height-25)/2,25,25)];
|
||||
[_searchButton setBezelStyle:CPRegularSquareBezelStyle];
|
||||
[_searchButton setAutoresizingMask:CPViewMaxXMargin]
|
||||
[_searchButton setBordered:NO];
|
||||
[_searchButton setImageScaling:CPScaleToFit];
|
||||
if (self = [super initWithFrame:frame])
|
||||
{
|
||||
_maximumRecents = 10;
|
||||
_sendsWholeSearchString = NO;
|
||||
_sendsSearchStringImmediately = NO;
|
||||
_recentsAutosaveName = nil;
|
||||
|
||||
[self _init];
|
||||
#if PLATFORM(DOM)
|
||||
_cancelButton._DOMElement.style.cursor = "default";
|
||||
_searchButton._DOMElement.style.cursor = "default";
|
||||
_cancelButton._DOMElement.style.cursor = "default";
|
||||
_searchButton._DOMElement.style.cursor = "default";
|
||||
#endif
|
||||
|
||||
[self setSearchMenuTemplate:[self _searchMenuTemplate]];
|
||||
[self addSubview:_searchButton];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copy
|
||||
- (void)_init
|
||||
{
|
||||
var copy = [super copy];
|
||||
|
||||
[copy setCancelButton:[_cancelButton copy]];
|
||||
[copy setSearchButton:[_searchButton copy]];
|
||||
[copy setrecentsAutosaveName:[_recentsAutosaveName copy]];
|
||||
[copy setSendsWholeSearchString:[_sendsWholeSearchString copy]];
|
||||
[copy setSendsSearchStringImmediately:[_sendsSearchStringImmediately copy]];
|
||||
[copy setMaximumRecents:_maximumRecents];
|
||||
[copy setSearchMenutemplate:[_searchMenuTemplate copy]];
|
||||
|
||||
return copy;
|
||||
_recentSearches = [CPArray array];
|
||||
|
||||
[self setBezeled:YES];
|
||||
[self setBezelStyle:CPTextFieldRoundedBezel];
|
||||
[self setBordered:YES];
|
||||
[self setEditable:YES];
|
||||
[self setDelegate:self];
|
||||
[self setContinuous:YES];
|
||||
|
||||
var bounds = [self bounds],
|
||||
cancelButton = [[CPButton alloc] initWithFrame:[self cancelButtonRectForBounds:bounds]],
|
||||
searchButton = [[CPButton alloc] initWithFrame:[self searchButtonRectForBounds:bounds]];
|
||||
|
||||
[self setCancelButton:cancelButton];
|
||||
[self resetCancelButton];
|
||||
|
||||
[self setSearchButton:searchButton];
|
||||
[self resetSearchButton];
|
||||
|
||||
_canResignFirstResponder = YES;
|
||||
}
|
||||
|
||||
// Managing Buttons
|
||||
@@ -148,8 +131,16 @@ var CPSearchFieldSearchImage = nil,
|
||||
@param button The search button.
|
||||
*/
|
||||
- (void)setSearchButton:(CPButton)button
|
||||
{
|
||||
_searchButton = button;
|
||||
{
|
||||
if (button != _searchButton)
|
||||
{
|
||||
[_searchButton removeFromSuperview];
|
||||
_searchButton = button;
|
||||
|
||||
[_searchButton setFrame:[self searchButtonRectForBounds:[self bounds]]];
|
||||
[_searchButton setAutoresizingMask:CPViewMaxXMargin];
|
||||
[self addSubview:_searchButton];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -167,27 +158,13 @@ var CPSearchFieldSearchImage = nil,
|
||||
*/
|
||||
- (void)resetSearchButton
|
||||
{
|
||||
var searchButtonImage,
|
||||
action,
|
||||
target,
|
||||
button = [self searchButton];
|
||||
|
||||
if (_searchMenuTemplate == nil)
|
||||
{
|
||||
searchButtonImage = CPSearchFieldSearchImage;
|
||||
action = [self action];
|
||||
target = [self target];
|
||||
}
|
||||
else
|
||||
{
|
||||
searchButtonImage = CPSearchFieldFindImage;
|
||||
action = @selector(_showMenu:);
|
||||
target = self;
|
||||
}
|
||||
|
||||
var button = [self searchButton],
|
||||
searchButtonImage = (_searchMenuTemplate === nil) ? CPSearchFieldSearchImage : CPSearchFieldFindImage;
|
||||
|
||||
[button setBordered:NO];
|
||||
[button setImageScaling:CPScaleToFit];
|
||||
[button setImage:searchButtonImage];
|
||||
[button setTarget:target];
|
||||
[button setAction:action];
|
||||
[button setAutoresizingMask:CPViewMaxXMargin];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -196,7 +173,18 @@ var CPSearchFieldSearchImage = nil,
|
||||
*/
|
||||
- (void)setCancelButton:(CPButton)button
|
||||
{
|
||||
_cancelButton = button;
|
||||
if (button != _cancelButton)
|
||||
{
|
||||
[_cancelButton removeFromSuperview];
|
||||
_cancelButton = button;
|
||||
|
||||
[_cancelButton setFrame:[self cancelButtonRectForBounds:[self bounds]]];
|
||||
[_cancelButton setAutoresizingMask:CPViewMinXMargin];
|
||||
[_cancelButton setTarget:self];
|
||||
[_cancelButton setAction:@selector(_searchFieldCancel:)];
|
||||
[self _updateCancelButtonVisibility];
|
||||
[self addSubview:_cancelButton];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -215,11 +203,11 @@ var CPSearchFieldSearchImage = nil,
|
||||
- (void)resetCancelButton
|
||||
{
|
||||
var button = [self cancelButton];
|
||||
[button setBezelStyle:CPRegularSquareBezelStyle];
|
||||
[button setBordered:NO];
|
||||
[button setImageScaling:CPScaleToFit];
|
||||
[button setImage:CPSearchFieldCancelImage];
|
||||
[button setAlternateImage:CPSearchFieldCancelPressedImage];
|
||||
[button setAutoresizingMask:CPViewMinXMargin];
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(_searchFieldCancel:)];
|
||||
}
|
||||
@@ -231,23 +219,25 @@ var CPSearchFieldSearchImage = nil,
|
||||
@return The updated bounding rectangle to use for the search text field. The default value is the value passed into the rect parameter.
|
||||
Subclasses can override this method to return a new bounding rectangle for the text-field object. You might use this method to provide a custom layout for the search field control.
|
||||
*/
|
||||
- (CPRect)searchTextRectForBounds:(CPRect)rect
|
||||
- (CGRect)searchTextRectForBounds:(CGRect)rect
|
||||
{
|
||||
var leftOffset = 0, width = rect.size.width;
|
||||
|
||||
var leftOffset = 0,
|
||||
width = _CGRectGetWidth(rect),
|
||||
bounds = [self bounds];
|
||||
|
||||
if (_searchButton)
|
||||
{
|
||||
var searchRect = [_searchButton frame];
|
||||
leftOffset = searchRect.origin.x + searchRect.size.width;
|
||||
var searchBounds = [self searchButtonRectForBounds:bounds];
|
||||
leftOffset = _CGRectGetMaxX(searchBounds) + 2;
|
||||
}
|
||||
|
||||
|
||||
if (_cancelButton)
|
||||
{
|
||||
var cancelRect = [_cancelButton frame];
|
||||
width = cancelRect.origin.x - leftOffset;
|
||||
var cancelRect = [self cancelButtonRectForBounds:bounds];
|
||||
width = _CGRectGetMinX(cancelRect) - leftOffset;
|
||||
}
|
||||
|
||||
return CPMakeRect(leftOffset,rect.origin.y,width,rect.size.height);
|
||||
|
||||
return _CGRectMake(leftOffset, _CGRectGetMinY(rect), width, _CGRectGetHeight(rect));
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -255,9 +245,9 @@ var CPSearchFieldSearchImage = nil,
|
||||
@param rect The current bounding rectangle for the search button.
|
||||
Subclasses can override this method to return a new bounding rectangle for the search button. You might use this method to provide a custom layout for the search field control.
|
||||
*/
|
||||
- (CPRect)searchButtonRectForBounds:(CPRect)rect // fix
|
||||
- (CGRect)searchButtonRectForBounds:(CGRect)rect
|
||||
{
|
||||
return [_searchButton frame];
|
||||
return _CGRectMake(5, (_CGRectGetHeight(rect) - BUTTON_DEFAULT_HEIGHT) / 2, SEARCH_BUTTON_DEFAULT_WIDTH, BUTTON_DEFAULT_HEIGHT);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -265,9 +255,9 @@ var CPSearchFieldSearchImage = nil,
|
||||
@param rect The updated bounding rectangle to use for the cancel button. The default value is the value passed into the rect parameter.
|
||||
Subclasses can override this method to return a new bounding rectangle for the cancel button. You might use this method to provide a custom layout for the search field control.
|
||||
*/
|
||||
- (CPRect)cancelButtonRectForBounds:(CPRect)rect
|
||||
- (CGRect)cancelButtonRectForBounds:(CGRect)rect
|
||||
{
|
||||
return [_cancelButton frame];
|
||||
return _CGRectMake(_CGRectGetWidth(rect) - CANCEL_BUTTON_DEFAULT_WIDTH - 5, (_CGRectGetHeight(rect) - CANCEL_BUTTON_DEFAULT_WIDTH) / 2, BUTTON_DEFAULT_HEIGHT, BUTTON_DEFAULT_HEIGHT);
|
||||
}
|
||||
|
||||
// Managing Menu Templates
|
||||
@@ -285,11 +275,12 @@ var CPSearchFieldSearchImage = nil,
|
||||
@param menu The menu template to use.
|
||||
The receiver looks for the tag constants described in ŇMenu tagsÓ to determine how to populate the menu with items related to recent searches. See ŇConfiguring a Search MenuÓ for a sample of how you might set up the search menu template.
|
||||
*/
|
||||
- (void)setSearchMenuTemplate:(CPMenu)menu
|
||||
- (void)setSearchMenuTemplate:(CPMenu)aMenu
|
||||
{
|
||||
_searchMenuTemplate = menu;
|
||||
|
||||
_searchMenuTemplate = aMenu;
|
||||
|
||||
[self resetSearchButton];
|
||||
[self _loadRecentSearchList];
|
||||
[self _updateSearchMenu];
|
||||
}
|
||||
|
||||
@@ -299,8 +290,8 @@ var CPSearchFieldSearchImage = nil,
|
||||
@return \c YES if the action message is sent all at once when the user clicks the search button or presses return; otherwise, NO if the search string is sent after each keystroke. The default value is NO.
|
||||
*/
|
||||
- (BOOL)sendsWholeSearchString
|
||||
{
|
||||
return _sendsWholeSearchString;
|
||||
{
|
||||
return _sendsWholeSearchString;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -317,8 +308,8 @@ var CPSearchFieldSearchImage = nil,
|
||||
@return \c YES if the text field sends its action immediately upon notification of any changes to the search field; otherwise, NO.
|
||||
*/
|
||||
- (BOOL)sendsSearchStringImmediately
|
||||
{
|
||||
return _sendsSearchStringImmediately;
|
||||
{
|
||||
return _sendsSearchStringImmediately;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -336,7 +327,7 @@ var CPSearchFieldSearchImage = nil,
|
||||
@return The maximum number of search strings that can appear in the menu. This value is between 0 and 254.
|
||||
*/
|
||||
- (int)maximumRecents
|
||||
{
|
||||
{
|
||||
return _maximumRecents;
|
||||
}
|
||||
|
||||
@@ -350,13 +341,13 @@ var CPSearchFieldSearchImage = nil,
|
||||
max = 254;
|
||||
else if (max < 0)
|
||||
max = 10;
|
||||
|
||||
|
||||
_maximumRecents = max;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the list of recent search strings for the control.
|
||||
@return An array of \c CPString objects, each of which contains a search string either displayed in the search menu or from a recent autosave archive. If there have been no recent searches and no prior searches saved under an autosave name, this array may be empty.
|
||||
@return An array of \c CPString objects, each of which contains a search string either displayed in the search menu or from a recent autosave archive. If there have been no recent searches and no prior searches saved under an autosave name, this array may be empty.
|
||||
*/
|
||||
- (CPArray)recentSearches
|
||||
{
|
||||
@@ -370,10 +361,10 @@ var CPSearchFieldSearchImage = nil,
|
||||
*/
|
||||
- (void)setRecentSearches:(CPArray)searches
|
||||
{
|
||||
var max = MIN([self maximumRecents],[searches count]);
|
||||
var searches = [searches subarrayWithRange:CPMakeRange(0,max)];
|
||||
var max = MIN([self maximumRecents], [searches count]),
|
||||
searches = [searches subarrayWithRange:CPMakeRange(0, max)];
|
||||
|
||||
_recentSearches = searches;
|
||||
|
||||
[self _autosaveRecentSearchList];
|
||||
}
|
||||
|
||||
@@ -383,7 +374,7 @@ var CPSearchFieldSearchImage = nil,
|
||||
*/
|
||||
- (CPString)recentsAutosaveName
|
||||
{
|
||||
return _recentsAutosaveName;
|
||||
return _recentsAutosaveName;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -392,25 +383,26 @@ var CPSearchFieldSearchImage = nil,
|
||||
*/
|
||||
- (void)setRecentsAutosaveName:(CPString)name
|
||||
{
|
||||
if (_recentsAutosaveName != nil)
|
||||
[self _deregisterForAutosaveNotification];
|
||||
|
||||
_recentsAutosaveName = name;
|
||||
|
||||
if(name != nil)
|
||||
|
||||
if (_recentsAutosaveName != nil)
|
||||
[self _registerForAutosaveNotification];
|
||||
else
|
||||
[self _deregisterForAutosaveNotification];
|
||||
}
|
||||
|
||||
// Private methods and subclassing
|
||||
|
||||
- (CPRect)contentRectForBounds:(CPRect)bounds
|
||||
- (CGRect)contentRectForBounds:(CGRect)bounds
|
||||
{
|
||||
var superbounds = [super contentRectForBounds:bounds];
|
||||
var superbounds = [super contentRectForBounds:bounds];
|
||||
return [self searchTextRectForBounds:superbounds];
|
||||
}
|
||||
|
||||
+ (double)_keyboardDelayForPartialSearchString:(CPString)string
|
||||
{
|
||||
return (6 - MIN([string length],4))/10;
|
||||
return (6 - MIN([string length], 4)) / 10;
|
||||
}
|
||||
|
||||
- (CPMenu)menu
|
||||
@@ -425,177 +417,284 @@ var CPSearchFieldSearchImage = nil,
|
||||
|
||||
- (void)_updateCancelButtonVisibility
|
||||
{
|
||||
[_cancelButton setHidden:([[self stringValue] length] == 0)];
|
||||
[_cancelButton setHidden:([[self stringValue] length] === 0)];
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(CPNotification)aNotification
|
||||
{
|
||||
if(!_sendsWholeSearchString)
|
||||
if (![self sendsWholeSearchString])
|
||||
{
|
||||
if(_sendsSearchStringImmediately)
|
||||
if ([self sendsSearchStringImmediately])
|
||||
[self _sendPartialString];
|
||||
else
|
||||
{
|
||||
[_partialStringTimer invalidate];
|
||||
var timeInterval = [CPSearchField _keyboardDelayForPartialSearchString:[self stringValue]];
|
||||
|
||||
_partialStringTimer = [CPTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(_sendPartialString) userInfo:nil repeats:NO];
|
||||
|
||||
_partialStringTimer = [CPTimer scheduledTimerWithTimeInterval:timeInterval
|
||||
target:self
|
||||
selector:@selector(_sendPartialString)
|
||||
userInfo:nil
|
||||
repeats:NO];
|
||||
}
|
||||
}
|
||||
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)_sendAction:(id)sender
|
||||
{
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
}
|
||||
|
||||
- (void)sendAction:(SEL)anAction to:(id)anObject
|
||||
{
|
||||
[super sendAction:anAction to:anObject];
|
||||
|
||||
[_partialStringTimer invalidate];
|
||||
|
||||
var current_value = [self objectValue];
|
||||
if(current_value != nil && current_value != "" && ![_recentSearches containsObject:current_value])
|
||||
{
|
||||
[self _addStringToRecentSearches:current_value];
|
||||
[self _updateSearchMenu];
|
||||
}
|
||||
|
||||
[self _addStringToRecentSearches:[self stringValue]];
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)_addStringToRecentSearches:(CPString)string
|
||||
{
|
||||
var newSearches = [CPMutableArray arrayWithArray:_recentSearches];
|
||||
[newSearches addObject:string];
|
||||
[self setRecentSearches:newSearches];
|
||||
if (string === nil || string === @"" || [_recentSearches containsObject:string])
|
||||
return;
|
||||
|
||||
var searches = [CPMutableArray arrayWithArray:_recentSearches];
|
||||
[searches addObject:string];
|
||||
[self setRecentSearches:searches];
|
||||
[self _updateSearchMenu];
|
||||
}
|
||||
|
||||
- (BOOL)trackMouse:(CPEvent)event
|
||||
- (CPView)hitTest:(CGPoint)aPoint
|
||||
{
|
||||
var rect;
|
||||
var point;
|
||||
var location = [event locationInWindow];
|
||||
|
||||
point = [self convertPoint:location fromView:nil];
|
||||
|
||||
rect = [self searchButtonRectForBounds:[self frame]];
|
||||
|
||||
if (CPRectContainsPoint(rect,point))
|
||||
{
|
||||
return [[self searchButton] trackMouse:event];
|
||||
}
|
||||
|
||||
rect = [self cancelButtonRectForBounds:[self frame]];
|
||||
if (CPRectContainsPoint(rect,point))
|
||||
{
|
||||
return [[self cancelButton] trackMouse:event];
|
||||
}
|
||||
|
||||
return [super trackMouse:event];
|
||||
// Make sure a hit anywhere within the search field returns the search field itself
|
||||
if (_CGRectContainsPoint([self frame], aPoint))
|
||||
return self;
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPMenu)_searchMenuTemplate
|
||||
- (BOOL)resignFirstResponder
|
||||
{
|
||||
var template, item;
|
||||
|
||||
template = [[CPMenu alloc] init];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent searches" action:NULL keyEquivalent:@""];
|
||||
return _canResignFirstResponder && [super resignFirstResponder];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
var location = [anEvent locationInWindow],
|
||||
point = [self convertPoint:location fromView:nil];
|
||||
|
||||
if (_CGRectContainsPoint([self searchButtonRectForBounds:[self bounds]], point))
|
||||
{
|
||||
if (_searchMenuTemplate == nil)
|
||||
[self _sendAction:self];
|
||||
else
|
||||
[self _showMenu];
|
||||
}
|
||||
else if (_CGRectContainsPoint([self cancelButtonRectForBounds:[self bounds]], point))
|
||||
[_cancelButton mouseDown:anEvent];
|
||||
else
|
||||
[super mouseDown:anEvent];
|
||||
}
|
||||
|
||||
/*!
|
||||
Provides the common case items for a recent searches menu. If there are not recent searches,
|
||||
displays a single disabled item:
|
||||
|
||||
No Recent Searches
|
||||
|
||||
If there are 1 more recent searches, it displays:
|
||||
|
||||
Recent Searches
|
||||
recent search 1
|
||||
recent search 2
|
||||
etc.
|
||||
---------------------
|
||||
Clear Recent Searches
|
||||
|
||||
If you wish to add items before or after the template, you can. If you put items
|
||||
before, a separator will automatically be placed before the default template item.
|
||||
If you add items after the default template, it is your responsibility to add a separator.
|
||||
|
||||
To add a custom item:
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"google"
|
||||
action:@selector(google:)
|
||||
keyEquivalent:@""];
|
||||
[item setTag:700];
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
Be sure that your custom items do not use tags in the range 1000-1003 inclusive.
|
||||
If you wish to maintain state in custom menu items that you add, you will need to maintain
|
||||
the item state yourself, then in the action method of the custom items, modify the items
|
||||
in the search menu template and send [searchField setSearchMenuTemplate:template] to update the menu.
|
||||
*/
|
||||
- (CPMenu)defaultSearchMenuTemplate
|
||||
{
|
||||
var template = [[CPMenu alloc] init],
|
||||
item;
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent Searches"
|
||||
action:nil
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldRecentsTitleMenuItemTag];
|
||||
[item setEnabled:NO];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent search item" action:@selector(_searchFieldSearch:) keyEquivalent:@""];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent search item"
|
||||
action:@selector(_searchFieldSearch:)
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldRecentsMenuItemTag];
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Clear recent searches" action:@selector(_searchFieldClearRecents:) keyEquivalent:@""];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Clear Recent Searches"
|
||||
action:@selector(_searchFieldClearRecents:)
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldClearRecentsMenuItemTag];
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"No recent searches" action:NULL keyEquivalent:@""];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"No Recent Searches"
|
||||
action:nil
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldNoRecentsMenuItemTag];
|
||||
[item setEnabled:NO];
|
||||
[template addItem:item];
|
||||
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
- (void)_updateSearchMenu
|
||||
{
|
||||
if(_searchMenuTemplate == nil)
|
||||
if (_searchMenuTemplate === nil)
|
||||
return;
|
||||
|
||||
var i, menu = [[CPMenu alloc] init];
|
||||
var countOfRecents = [_recentSearches count];
|
||||
|
||||
for (i = 0; i < [_searchMenuTemplate numberOfItems]; i++)
|
||||
|
||||
var menu = [[CPMenu alloc] init],
|
||||
countOfRecents = [_recentSearches count],
|
||||
numberOfItems = [_searchMenuTemplate numberOfItems];
|
||||
|
||||
for (var i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
var item = [_searchMenuTemplate itemAtIndex:i];
|
||||
var tag = [item tag];
|
||||
|
||||
if(tag == CPSearchFieldClearRecentsMenuItemTag && countOfRecents != 0)
|
||||
var item = [[_searchMenuTemplate itemAtIndex:i] copy];
|
||||
|
||||
switch ([item tag])
|
||||
{
|
||||
var separator = [CPMenuItem separatorItem];
|
||||
[menu addItem:separator];
|
||||
}
|
||||
|
||||
if (!(tag == CPSearchFieldRecentsTitleMenuItemTag && countOfRecents == 0) &&
|
||||
!(tag == CPSearchFieldClearRecentsMenuItemTag && countOfRecents == 0) &&
|
||||
!(tag == CPSearchFieldNoRecentsMenuItemTag && countOfRecents != 0) &&
|
||||
!(tag == CPSearchFieldRecentsMenuItemTag))
|
||||
{
|
||||
var templateItem = [[CPMenuItem alloc] initWithTitle:[item title] action:[item action] keyEquivalent:[item keyEquivalent]];
|
||||
[templateItem setTarget:[item target]];
|
||||
[templateItem setEnabled:[item isEnabled]];
|
||||
[templateItem setTag:[item tag]];
|
||||
[menu addItem:templateItem];
|
||||
}
|
||||
else if (tag == CPSearchFieldRecentsMenuItemTag)
|
||||
{
|
||||
var j;
|
||||
for (j = 0; j < countOfRecents; j++)
|
||||
case CPSearchFieldRecentsTitleMenuItemTag:
|
||||
if (countOfRecents === 0)
|
||||
continue;
|
||||
|
||||
if ([menu numberOfItems] > 0)
|
||||
[self _addSeparatorToMenu:menu];
|
||||
break;
|
||||
|
||||
case CPSearchFieldRecentsMenuItemTag:
|
||||
{
|
||||
var rencentItem = [[CPMenuItem alloc] initWithTitle:[_recentSearches objectAtIndex:j] action:[item action] keyEquivalent:[item keyEquivalent]];
|
||||
[rencentItem setTarget:[item target]];
|
||||
[menu addItem:rencentItem];
|
||||
var itemAction = @selector(_searchFieldSearch:);
|
||||
|
||||
for (var recentIndex = 0; recentIndex < countOfRecents; ++recentIndex)
|
||||
{
|
||||
// RECENT_SEARCH_PREFIX is a hack until CPMenuItem -setIndentationLevel works
|
||||
var recentItem = [[CPMenuItem alloc] initWithTitle:RECENT_SEARCH_PREFIX + [_recentSearches objectAtIndex:recentIndex]
|
||||
action:itemAction
|
||||
keyEquivalent:[item keyEquivalent]];
|
||||
[item setTarget:self];
|
||||
[menu addItem:recentItem];
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case CPSearchFieldClearRecentsMenuItemTag:
|
||||
if (countOfRecents === 0)
|
||||
continue;
|
||||
|
||||
if ([menu numberOfItems] > 0)
|
||||
[self _addSeparatorToMenu:menu];
|
||||
|
||||
[item setAction:@selector(_searchFieldClearRecents:)];
|
||||
[item setTarget:self];
|
||||
break;
|
||||
|
||||
case CPSearchFieldNoRecentsMenuItemTag:
|
||||
if (countOfRecents !== 0)
|
||||
continue;
|
||||
|
||||
if ([menu numberOfItems] > 0)
|
||||
[self _addSeparatorToMenu:menu];
|
||||
break;
|
||||
}
|
||||
|
||||
[item setEnabled:([item isEnabled] && [item action] != nil && [item target] != nil)];
|
||||
[menu addItem:item];
|
||||
}
|
||||
|
||||
[menu setDelegate:self];
|
||||
|
||||
_searchMenu = menu;
|
||||
}
|
||||
|
||||
|
||||
- (void)_showMenu:(id)sender
|
||||
- (void)_addSeparatorToMenu:(CPMenu)aMenu
|
||||
{
|
||||
if(_searchMenu == nil || ![self isEnabled])
|
||||
var separator = [CPMenuItem separatorItem];
|
||||
[separator setEnabled:NO];
|
||||
[aMenu addItem:separator];
|
||||
}
|
||||
|
||||
- (void)menuWillOpen:(CPMenu)menu
|
||||
{
|
||||
_canResignFirstResponder = NO;
|
||||
}
|
||||
|
||||
- (void)menuDidClose:(CPMenu)menu
|
||||
{
|
||||
_canResignFirstResponder = YES;
|
||||
|
||||
[self becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (void)_showMenu
|
||||
{
|
||||
if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![self isEnabled])
|
||||
return;
|
||||
|
||||
[super selectText:nil];
|
||||
|
||||
var origin = CPMakePoint([self frame].origin.x, [self frame].origin.y + [self frame].size.height);
|
||||
var anEvent = [CPEvent keyEventWithType:CPRightMouseDown location:origin modifierFlags:0 timestamp:[CPDate date] windowNumber:1 context:[[CPGraphicsContext currentContext] graphicsPort] characters:"" charactersIgnoringModifiers:"" isARepeat:NO keyCode:0];
|
||||
|
||||
[CPMenu popUpContextMenu:_searchMenu withEvent:anEvent forView:sender];
|
||||
|
||||
var aFrame = [[self superview] convertRect:[self frame] toView:nil],
|
||||
location = CPMakePoint(aFrame.origin.x + 10, aFrame.origin.y + aFrame.size.height - 4);
|
||||
|
||||
var anEvent = [CPEvent mouseEventWithType:CPRightMouseDown location:location modifierFlags:0 timestamp:[[CPApp currentEvent] timestamp] windowNumber:[[self window] windowNumber] context:nil eventNumber:1 clickCount:1 pressure:0];
|
||||
|
||||
[self selectAll:nil];
|
||||
[CPMenu popUpContextMenu:_searchMenu withEvent:anEvent forView:self];
|
||||
}
|
||||
|
||||
- (void)_sendPartialString
|
||||
{
|
||||
[[self target] performSelector:[self action] withObject:self];
|
||||
[super sendAction:[self action] to:[self target]];
|
||||
[_partialStringTimer invalidate];
|
||||
}
|
||||
|
||||
- (void)_searchFieldCancel:(id)sender
|
||||
{
|
||||
[self setObjectValue:nil];
|
||||
{
|
||||
[self setObjectValue:@""];
|
||||
[self _sendPartialString];
|
||||
[self _updateCancelButtonVisibility];
|
||||
[sender setHidden:YES];
|
||||
}
|
||||
|
||||
- (void)_searchFieldSearch:(id)sender
|
||||
{
|
||||
[self setObjectValue:[sender title]];
|
||||
var searchString = [[sender title] substringFromIndex:[RECENT_SEARCH_PREFIX length]];
|
||||
|
||||
if ([sender tag] != CPSearchFieldRecentsMenuItemTag)
|
||||
[self _addStringToRecentSearches:searchString];
|
||||
|
||||
[self setObjectValue:searchString];
|
||||
[self _sendPartialString];
|
||||
[self selectAll:nil];
|
||||
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
@@ -603,104 +702,95 @@ var CPSearchFieldSearchImage = nil,
|
||||
{
|
||||
[self setRecentSearches:[CPArray array]];
|
||||
[self _updateSearchMenu];
|
||||
[self setStringValue:@""];
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)_registerForAutosaveNotification
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_updateAutosavedRecents:) name:@"CPAutosavedRecentsChangedNotification" object:nil];
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_updateAutosavedRecents:) name:CPAutosavedRecentsChangedNotification object:_recentsAutosaveName];
|
||||
}
|
||||
|
||||
- (void)_deregisterForAutosaveNotification
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:self name:@"CPAutosavedRecentsChangedNotification" object:nil];
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:self name:CPAutosavedRecentsChangedNotification object:_recentsAutosaveName];
|
||||
}
|
||||
|
||||
- (void)_autosaveRecentSearchList
|
||||
{
|
||||
if (_recentsAutosaveName != nil)
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPAutosavedRecentsChangedNotification object:_recentsAutosaveName];
|
||||
}
|
||||
|
||||
- (void)_updateAutosavedRecents:(id)notification
|
||||
{
|
||||
var name = [notification object];
|
||||
var list = [self recentSearches];
|
||||
|
||||
[[CPUserDefaults standardUserDefaults] setObject:list forKey:name];
|
||||
|
||||
}
|
||||
|
||||
- (void)_autosaveRecentSearchList
|
||||
{
|
||||
if(_recentsAutosaveName != nil)
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:@"CPAutosavedRecentsChangedNotification" object:_recentsAutosaveName];
|
||||
[[CPUserDefaults standardUserDefaults] setObject:_recentSearches forKey:name];
|
||||
}
|
||||
|
||||
- (void)_loadRecentSearchList
|
||||
{
|
||||
var list,
|
||||
name = [self recentsAutosaveName];
|
||||
|
||||
list = [[CPUserDefaults standardUserDefaults] objectForKey:name];
|
||||
_recentSearches = list;
|
||||
}
|
||||
var name = [self recentsAutosaveName];
|
||||
if (name === nil)
|
||||
return;
|
||||
|
||||
/*
|
||||
- (BOOL)trackMouse:(CPEvent)theEvent inRect:(CPRect)cellFrame ofView:(CPView)aTextView untilMouseUp:(BOOL)flag
|
||||
{
|
||||
}
|
||||
var list = [[CPUserDefaults standardUserDefaults] objectForKey:name];
|
||||
|
||||
- (BOOL)_trimRecentSearchList
|
||||
{
|
||||
if (list !== nil)
|
||||
_recentSearches = list;
|
||||
}
|
||||
|
||||
- (void)_trackButton:(CPButton)button forEvent:(CPEvent)event inRect:(CPRect)rect ofView:(id)view
|
||||
{
|
||||
}
|
||||
|
||||
- (id)_selectOrEdit:(CPRect)rect inView:(id)view target:(id)target editor:(id)editor event:(id)event start:(int)start end:(int)end
|
||||
{
|
||||
}
|
||||
|
||||
- (void)resetCursorRect:(CPRect)rect inView:(id)view
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
|
||||
var CPSearchButtonKey = @"CPSearchButtonKey",
|
||||
CPCancelButtonKey = @"CPCancelButtonKey",
|
||||
CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
|
||||
var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
|
||||
CPSendsWholeSearchStringKey = @"CPSendsWholeSearchStringKey",
|
||||
CPSendsSearchStringImmediatelyKey = @"CPSendsSearchStringImmediatelyKey",
|
||||
CPMaximumRecentsKey = @"CPMaximumRecentsKey",
|
||||
CPSearchMenuTemplateKey = @"CPSearchMenuTemplateKey";
|
||||
CPSearchMenuTemplateKey = @"CPSearchMenuTemplateKey";
|
||||
|
||||
@implementation CPSearchField (CPCoding)
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)coder
|
||||
{
|
||||
[_searchButton removeFromSuperview];
|
||||
[_cancelButton removeFromSuperview];
|
||||
|
||||
[super encodeWithCoder:coder];
|
||||
|
||||
[coder encodeObject:_searchButton forKey:CPSearchButtonKey];
|
||||
[coder encodeObject:_cancelButton forKey:CPCancelButtonKey];
|
||||
[coder encodeObject:_recentsAutosaveName forKey:CPRecentsAutosaveNameKey];
|
||||
|
||||
if (_searchButton)
|
||||
[self addSubview:_searchButton];
|
||||
if (_cancelButton)
|
||||
[self addSubview:_cancelButton];
|
||||
|
||||
[coder encodeBool:_sendsWholeSearchString forKey:CPSendsWholeSearchStringKey];
|
||||
[coder encodeBool:_sendsSearchStringImmediately forKey:CPSendsSearchStringImmediatelyKey];
|
||||
[coder encodeInt:_maximumRecents forKey:CPMaximumRecentsKey];
|
||||
[coder encodeObject:_searchMenuTemplate forKey:CPSearchMenuTemplateKey];
|
||||
|
||||
if (_recentsAutosaveName)
|
||||
[coder encodeObject:_recentsAutosaveName forKey:CPRecentsAutosaveNameKey];
|
||||
|
||||
if (_searchMenuTemplate)
|
||||
[coder encodeObject:_searchMenuTemplate forKey:CPSearchMenuTemplateKey];
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)coder
|
||||
{
|
||||
self = [super initWithCoder:coder];
|
||||
|
||||
_searchButton = [coder decodeObjectForKey:CPSearchButtonKey];
|
||||
_cancelButton = [coder decodeObjectForKey:CPCancelButtonKey];
|
||||
_recentsAutosaveName = [coder decodeObjectForKey:CPRecentsAutosaveNameKey];
|
||||
_sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
|
||||
_sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
|
||||
_maximumRecents = [coder decodeIntForKey:CPMaximumRecentsKey];
|
||||
[self setSearchMenuTemplate:[coder decodeObjectForKey:CPSearchMenuTemplateKey]];
|
||||
[self resetCancelButton];
|
||||
[self setDelegate:self];
|
||||
if (self = [super initWithCoder:coder])
|
||||
{
|
||||
[self setRecentsAutosaveName:[coder decodeObjectForKey:CPRecentsAutosaveNameKey]];
|
||||
_sendsWholeSearchString = [coder decodeBoolForKey:CPSendsWholeSearchStringKey];
|
||||
_sendsSearchStringImmediately = [coder decodeBoolForKey:CPSendsSearchStringImmediatelyKey];
|
||||
_maximumRecents = [coder decodeIntForKey:CPMaximumRecentsKey];
|
||||
|
||||
var template = [coder decodeObjectForKey:CPSearchMenuTemplateKey];
|
||||
|
||||
if (template)
|
||||
[self setSearchMenuTemplate:template];
|
||||
|
||||
[self _init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
@import "CPTextField.j"
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
|
||||
+183
-166
@@ -24,25 +24,12 @@
|
||||
|
||||
@import "CPControl.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPSegmentSwitchTracking
|
||||
*/
|
||||
CPSegmentSwitchTrackingSelectOne = 0;
|
||||
/*
|
||||
@global
|
||||
@group CPSegmentSwitchTracking
|
||||
*/
|
||||
CPSegmentSwitchTrackingSelectAny = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPSegmentSwitchTracking
|
||||
*/
|
||||
CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPSegmentedControl
|
||||
|
||||
@@ -61,14 +48,14 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
BOOL _trackingHighlighted;
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return "segmented-control";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[CPCenterTextAlignment, CPCenterVerticalTextAlignment, CPImageLeft, CPScaleNone, _CGInsetMakeZero(), _CGInsetMakeZero(), nil, nil, nil, nil, 1.0, 24.0]
|
||||
return [CPDictionary dictionaryWithObjects:[CPCenterTextAlignment, CPCenterVerticalTextAlignment, CPImageLeft, CPScaleNone, _CGInsetMakeZero(), _CGInsetMakeZero(), [CPNull null], [CPNull null], [CPNull null], [CPNull null], 1.0, 24.0]
|
||||
forKeys:[@"alignment", @"vertical-alignment", @"image-position", @"image-scaling", @"bezel-inset", @"content-inset", @"left-segment-bezel-color", @"right-segment-bezel-color", @"center-segment-bezel-color", @"divider-bezel-color", @"divider-thickness", @"default-height"]];
|
||||
}
|
||||
|
||||
@@ -76,16 +63,16 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
_segments = [];
|
||||
_themeStates = [];
|
||||
|
||||
|
||||
self = [super initWithFrame:aRect];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_selectedSegment = -1;
|
||||
|
||||
|
||||
_trackingMode = CPSegmentSwitchTrackingSelectOne;
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -94,7 +81,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (int)selectedTag
|
||||
{
|
||||
return _segments[_selectedSegment].tag;
|
||||
return [_segments[_selectedSegment] tag];
|
||||
}
|
||||
|
||||
// Specifying the number of segments
|
||||
@@ -106,11 +93,13 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
if (_segments.length == aCount)
|
||||
return;
|
||||
|
||||
var height = CGRectGetHeight([self bounds]);
|
||||
|
||||
|
||||
var height = CGRectGetHeight([self bounds]),
|
||||
dividersBefore = MAX(0, _segments.length - 1),
|
||||
dividersAfter = MAX(0, aCount - 1);
|
||||
|
||||
if (_segments.length < aCount)
|
||||
{
|
||||
{
|
||||
for (var index = _segments.length; index < aCount; ++index)
|
||||
{
|
||||
_segments[index] = [[_CPSegmentItem alloc] init];
|
||||
@@ -126,6 +115,14 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
if (_selectedSegment >= _segments.length)
|
||||
_selectedSegment = -1;
|
||||
|
||||
// Make space for/remove space used by dividers.
|
||||
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
|
||||
delta = thickness * (dividersAfter - dividersBefore),
|
||||
frame = [self frame];
|
||||
|
||||
if (delta)
|
||||
[self setFrameSize:CGSizeMake(frame.size.width + delta, frame.size.height)];
|
||||
|
||||
[self tileWithChangedSegment:0];
|
||||
}
|
||||
|
||||
@@ -163,15 +160,15 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
- (BOOL)selectSegmentWithTag:(int)aTag
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
|
||||
for (; index < _segments.length; ++index)
|
||||
if (_segments[index].tag == aTag)
|
||||
{
|
||||
[self setSelectedSegment:index];
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -186,28 +183,28 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
if (_trackingMode == aTrackingMode)
|
||||
return;
|
||||
|
||||
|
||||
_trackingMode = aTrackingMode;
|
||||
|
||||
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectOne)
|
||||
{
|
||||
var index = 0,
|
||||
selected = NO;
|
||||
|
||||
|
||||
for (; index < _segments.length; ++index)
|
||||
if (_segments[index].selected)
|
||||
if ([_segments[index] selected])
|
||||
if (selected)
|
||||
[self setSelected:NO forSegment:index];
|
||||
else
|
||||
selected = YES;
|
||||
}
|
||||
|
||||
|
||||
else if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
|
||||
for (; index < _segments.length; ++index)
|
||||
if (_segments[index].selected)
|
||||
if ([_segments[index] selected])
|
||||
[self setSelected:NO forSegment:index];
|
||||
}
|
||||
}
|
||||
@@ -229,8 +226,8 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
|
||||
{
|
||||
_segments[aSegment].width = aWidth;
|
||||
|
||||
[_segments[aSegment] setWidth:aWidth];
|
||||
|
||||
[self tileWithChangedSegment:aSegment];
|
||||
}
|
||||
|
||||
@@ -241,7 +238,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (float)widthForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].width;
|
||||
return [_segments[aSegment] width];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -252,9 +249,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
|
||||
{
|
||||
var segment = _segments[aSegment];
|
||||
|
||||
segment.image = anImage;
|
||||
[_segments[aSegment] setImage:anImage];
|
||||
|
||||
[self tileWithChangedSegment:aSegment];
|
||||
}
|
||||
@@ -266,7 +261,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (CPImage)imageForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].image;
|
||||
return [_segments[aSegment] image];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -277,10 +272,8 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
|
||||
{
|
||||
var segment = _segments[aSegment];
|
||||
|
||||
_segments[aSegment].label = aLabel;
|
||||
|
||||
[_segments[aSegment] setLabel:aLabel];
|
||||
|
||||
[self tileWithChangedSegment:aSegment];
|
||||
}
|
||||
|
||||
@@ -291,7 +284,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (CPString)labelForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].label;
|
||||
return [_segments[aSegment] label];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -302,7 +295,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
|
||||
{
|
||||
_segments[aSegment].menu = aMenu;
|
||||
[_segments[aSegment] setMenu:aMenu];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -312,7 +305,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (CPMenu)menuForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].menu;
|
||||
return [_segments[aSegment] menu];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -325,12 +318,12 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
- (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment
|
||||
{
|
||||
var segment = _segments[aSegment];
|
||||
|
||||
|
||||
// If we're already in this state, bail.
|
||||
if (segment.selected == isSelected)
|
||||
if ([segment selected] == isSelected)
|
||||
return;
|
||||
|
||||
segment.selected = isSelected;
|
||||
|
||||
[segment setSelected:isSelected];
|
||||
|
||||
_themeStates[aSegment] = isSelected ? CPThemeStateSelected : CPThemeStateNormal;
|
||||
|
||||
@@ -338,18 +331,18 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
if (isSelected)
|
||||
{
|
||||
var oldSelectedSegment = _selectedSegment;
|
||||
|
||||
|
||||
_selectedSegment = aSegment;
|
||||
|
||||
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
|
||||
{
|
||||
_segments[oldSelectedSegment].selected = NO;
|
||||
[_segments[oldSelectedSegment] setSelected:NO];
|
||||
_themeStates[oldSelectedSegment] = CPThemeStateNormal;
|
||||
|
||||
[self drawSegmentBezel:oldSelectedSegment highlight:NO];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (_trackingMode != CPSegmentSwitchTrackingMomentary)
|
||||
[self drawSegmentBezel:aSegment highlight:NO];
|
||||
|
||||
@@ -364,7 +357,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (BOOL)isSelectedForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].selected;
|
||||
return [_segments[aSegment] selected];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -375,7 +368,12 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setEnabled:(BOOL)isEnabled forSegment:(unsigned)aSegment
|
||||
{
|
||||
_segments[aSegment].enabled = isEnabled;
|
||||
[_segments[aSegment] setEnabled:isEnabled];
|
||||
|
||||
if (isEnabled)
|
||||
_themeStates[aSegment] &= ~CPThemeStateDisabled;
|
||||
else
|
||||
_themeStates[aSegment] |= CPThemeStateDisabled;
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
@@ -388,7 +386,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (BOOL)isEnabledForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].enabled;
|
||||
return [_segments[aSegment] enabled];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -398,7 +396,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setTag:(int)aTag forSegment:(unsigned)aSegment
|
||||
{
|
||||
_segments[aSegment].tag = aTag;
|
||||
[_segments[aSegment] setTag:aTag];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -407,7 +405,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (int)tagForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].tag;
|
||||
return [_segments[aSegment] tag];
|
||||
}
|
||||
|
||||
// Drawings
|
||||
@@ -462,30 +460,28 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
}
|
||||
else if (aName === "right-segment-bezel")
|
||||
{
|
||||
var lastSegmentLeftOffset = [self _leftOffsetForSegment:[self _indexOfLastSegment]];
|
||||
|
||||
return CPRectMake(lastSegmentLeftOffset + [self widthForSegment:[self _indexOfLastSegment]] - contentInset.right,
|
||||
return CPRectMake(CGRectGetWidth([self bounds]) - contentInset.right,
|
||||
bezelInset.top,
|
||||
contentInset.right,
|
||||
height);
|
||||
}
|
||||
else if (aName.substring(0, "segment-bezel".length) == "segment-bezel")
|
||||
else if (aName.indexOf("segment-bezel") === 0)
|
||||
{
|
||||
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
|
||||
frame = CGRectCreateCopy(_segments[segment].frame);
|
||||
frame = CGRectCreateCopy([_segments[segment] frame]);
|
||||
|
||||
if (segment == 0)
|
||||
if (segment === 0)
|
||||
{
|
||||
frame.origin.x += contentInset.left;
|
||||
frame.size.width -= contentInset.left;
|
||||
}
|
||||
|
||||
if (segment == _segments.length - 1)
|
||||
frame.size.width -= contentInset.right;
|
||||
if (segment === _segments.length - 1)
|
||||
frame.size.width = CGRectGetWidth([self bounds]) - contentInset.right - frame.origin.x;
|
||||
|
||||
return frame;
|
||||
}
|
||||
else if (aName.substring(0, "divider-bezel".length) == "divider-bezel")
|
||||
else if (aName.indexOf("divider-bezel") === 0)
|
||||
{
|
||||
var segment = parseInt(aName.substring("divider-bezel-".length), 10),
|
||||
width = [self widthForSegment:segment],
|
||||
@@ -494,7 +490,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
return CGRectMake(left + width, bezelInset.top, thickness, height);
|
||||
}
|
||||
else if (aName.substring(0, "segment-content".length) == "segment-content")
|
||||
else if (aName.indexOf("segment-content") === 0)
|
||||
{
|
||||
var segment = parseInt(aName.substring("segment-content-".length), 10);
|
||||
|
||||
@@ -506,7 +502,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if (aName.substring(0, "segment-content".length) == "segment-content")
|
||||
if ([aName hasPrefix:@"segment-content"])
|
||||
return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
return [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
@@ -517,50 +513,56 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
if (_segments.length <= 0)
|
||||
return;
|
||||
|
||||
var leftCapColor = [self valueForThemeAttribute:@"left-segment-bezel-color"
|
||||
inState:_themeStates[0]];
|
||||
var themeState = _themeStates[0];
|
||||
|
||||
var leftBezelView = [self layoutEphemeralSubviewNamed:@"left-segment-bezel"
|
||||
themeState |= _themeState & CPThemeStateDisabled;
|
||||
|
||||
var leftCapColor = [self valueForThemeAttribute:@"left-segment-bezel-color"
|
||||
inState:themeState],
|
||||
|
||||
leftBezelView = [self layoutEphemeralSubviewNamed:@"left-segment-bezel"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[leftBezelView setBackgroundColor:leftCapColor];
|
||||
|
||||
var rightCapColor = [self valueForThemeAttribute:@"right-segment-bezel-color"
|
||||
inState:_themeStates[_themeStates.length - 1]];
|
||||
var themeState = _themeStates[_themeStates.length - 1];
|
||||
|
||||
var rightBezelView = [self layoutEphemeralSubviewNamed:@"right-segment-bezel"
|
||||
themeState |= _themeState & CPThemeStateDisabled;
|
||||
|
||||
var rightCapColor = [self valueForThemeAttribute:@"right-segment-bezel-color"
|
||||
inState:themeState],
|
||||
|
||||
rightBezelView = [self layoutEphemeralSubviewNamed:@"right-segment-bezel"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[rightBezelView setBackgroundColor:rightCapColor];
|
||||
|
||||
for (var i=0, count = _themeStates.length; i<count; i++)
|
||||
for (var i = 0, count = _themeStates.length; i < count; i++)
|
||||
{
|
||||
var themeState = _themeStates[i];
|
||||
|
||||
themeState |= _themeState & CPThemeStateDisabled;
|
||||
|
||||
var bezelColor = [self valueForThemeAttribute:@"center-segment-bezel-color"
|
||||
inState:themeState];
|
||||
var bezelColor = [self valueForThemeAttribute:@"center-segment-bezel-color"
|
||||
inState:themeState],
|
||||
|
||||
var bezelView = [self layoutEphemeralSubviewNamed:"segment-bezel-"+i
|
||||
positioned:CPWindowBelow
|
||||
bezelView = [self layoutEphemeralSubviewNamed:"segment-bezel-" + i
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[bezelView setBackgroundColor:bezelColor];
|
||||
|
||||
|
||||
//layout image/title views
|
||||
var segment = _segments[i];
|
||||
|
||||
|
||||
var contentView = [self layoutEphemeralSubviewNamed:@"segment-content-"+i
|
||||
// layout image/title views
|
||||
var segment = _segments[i],
|
||||
contentView = [self layoutEphemeralSubviewNamed:@"segment-content-" + i
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:@"segment-bezel-"+i];
|
||||
relativeToEphemeralSubviewNamed:@"segment-bezel-" + i];
|
||||
|
||||
[contentView setText:segment.label];
|
||||
[contentView setImage:segment.image];
|
||||
[contentView setText:[segment label]];
|
||||
[contentView setImage:[segment image]];
|
||||
|
||||
[contentView setFont:[self valueForThemeAttribute:@"font" inState:themeState]];
|
||||
[contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:themeState]];
|
||||
@@ -569,27 +571,27 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
[contentView setLineBreakMode:[self valueForThemeAttribute:@"line-break-mode" inState:themeState]];
|
||||
[contentView setTextShadowColor:[self valueForThemeAttribute:@"text-shadow-color" inState:themeState]];
|
||||
[contentView setTextShadowOffset:[self valueForThemeAttribute:@"text-shadow-offset" inState:themeState]];
|
||||
[contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling" inState:themeState]];
|
||||
|
||||
if (segment.image && segment.label)
|
||||
[contentView setImageScaling:[self valueForThemeAttribute:@"image-scaling" inState:themeState]];
|
||||
|
||||
if ([segment image] && [segment label])
|
||||
[contentView setImagePosition:[self valueForThemeAttribute:@"image-position" inState:themeState]];
|
||||
else if (segment.image)
|
||||
else if ([segment image])
|
||||
[contentView setImagePosition:CPImageOnly];
|
||||
|
||||
if (i == count - 1)
|
||||
continue;
|
||||
|
||||
var borderState = _themeStates[i] | _themeStates[i+1];
|
||||
|
||||
var borderState = _themeStates[i] | _themeStates[i + 1];
|
||||
|
||||
borderState = (borderState & CPThemeStateSelected & ~CPThemeStateHighlighted) ? CPThemeStateSelected : CPThemeStateNormal;
|
||||
|
||||
borderState |= _themeState & CPThemeStateDisabled;
|
||||
|
||||
var borderColor = [self valueForThemeAttribute:@"divider-bezel-color"
|
||||
inState:borderState];
|
||||
inState:borderState],
|
||||
|
||||
var borderView = [self layoutEphemeralSubviewNamed:"divider-bezel-"+i
|
||||
positioned:CPWindowBelow
|
||||
borderView = [self layoutEphemeralSubviewNamed:"divider-bezel-" + i
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[borderView setBackgroundColor:borderColor];
|
||||
@@ -612,48 +614,53 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
return;
|
||||
|
||||
var segment = _segments[aSegment],
|
||||
segmentWidth = segment.width,
|
||||
segmentWidth = [segment width],
|
||||
themeState = _themeStates[aSegment] | (_themeState & CPThemeStateDisabled),
|
||||
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
|
||||
font = [self valueForThemeAttribute:@"font" inState:themeState];
|
||||
font = [self font];
|
||||
|
||||
if (!segmentWidth)
|
||||
{
|
||||
if (segment.image && segment.label)
|
||||
segmentWidth = [segment.label sizeWithFont:font].width + [segment.image size].width + contentInset.left + contentInset.right;
|
||||
if ([segment image] && [segment label])
|
||||
segmentWidth = [[segment label] sizeWithFont:font].width + [[segment image] size].width + contentInset.left + contentInset.right;
|
||||
else if (segment.image)
|
||||
segmentWidth = [segment.image size].width + contentInset.left + contentInset.right;
|
||||
segmentWidth = [[segment image] size].width + contentInset.left + contentInset.right;
|
||||
else if (segment.label)
|
||||
segmentWidth = [segment.label sizeWithFont:font].width + contentInset.left + contentInset.right;
|
||||
segmentWidth = [[segment label] sizeWithFont:font].width + contentInset.left + contentInset.right;
|
||||
else
|
||||
segmentWidth = 0.0;
|
||||
}
|
||||
|
||||
var delta = segmentWidth - CGRectGetWidth(segment.frame);
|
||||
var delta = segmentWidth - CGRectGetWidth([segment frame]);
|
||||
|
||||
if (!delta)
|
||||
return;
|
||||
{
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
// Update Contorl Size
|
||||
return;
|
||||
}
|
||||
|
||||
// Update control size
|
||||
var frame = [self frame];
|
||||
|
||||
|
||||
[self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
|
||||
|
||||
// Update Segment Width
|
||||
segment.width = segmentWidth;
|
||||
segment.frame = [self frameForSegment:aSegment];;
|
||||
// Update segment width
|
||||
[segment setWidth:segmentWidth];
|
||||
[segment setFrame:[self frameForSegment:aSegment]];
|
||||
|
||||
// Update Following Segments Widths
|
||||
// Update following segments widths
|
||||
var index = aSegment + 1;
|
||||
|
||||
|
||||
for (; index < _segments.length; ++index)
|
||||
{
|
||||
_segments[index].frame.origin.x += delta;
|
||||
|
||||
[_segments[index] frame].origin.x += delta;
|
||||
|
||||
[self drawSegmentBezel:index highlight:NO];
|
||||
[self drawSegment:index highlight:NO];
|
||||
}
|
||||
|
||||
|
||||
[self drawSegmentBezel:aSegment highlight:NO];
|
||||
[self drawSegment:aSegment highlight:NO];
|
||||
|
||||
@@ -699,11 +706,20 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
var location = [self convertPoint:aPoint fromView:nil],
|
||||
count = _segments.length;
|
||||
|
||||
|
||||
while (count--)
|
||||
if (CGRectContainsPoint(_segments[count].frame, aPoint))
|
||||
if (CGRectContainsPoint([_segments[count] frame], aPoint))
|
||||
return count;
|
||||
|
||||
|
||||
if (_segments.length)
|
||||
{
|
||||
var adjustedLastFrame = CGRectCreateCopy([_segments[_segments.length - 1] frame]);
|
||||
adjustedLastFrame.size.width = CGRectGetWidth([self bounds]) - adjustedLastFrame.origin.x;
|
||||
|
||||
if (CGRectContainsPoint(adjustedLastFrame, aPoint))
|
||||
return _segments.length - 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -711,7 +727,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
|
||||
[self trackSegment:anEvent];
|
||||
}
|
||||
|
||||
@@ -725,74 +741,76 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
@param anEvent the event to handle
|
||||
*/
|
||||
- (void)trackSegment:(CPEvent)anEvent
|
||||
{
|
||||
{
|
||||
var type = [anEvent type],
|
||||
location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
|
||||
if (type == CPLeftMouseUp)
|
||||
{
|
||||
if (_trackingSegment == -1)
|
||||
return;
|
||||
|
||||
if (CGRectContainsPoint(_segments[_trackingSegment].frame, location))
|
||||
if (_trackingSegment === [self testSegment:location])
|
||||
{
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
|
||||
{
|
||||
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
|
||||
|
||||
|
||||
// With ANY, _selectedSegment means last pressed.
|
||||
_selectedSegment = _trackingSegment;
|
||||
}
|
||||
else
|
||||
[self setSelected:YES forSegment:_trackingSegment];
|
||||
|
||||
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
|
||||
|
||||
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
||||
{
|
||||
[self setSelected:NO forSegment:_trackingSegment];
|
||||
|
||||
|
||||
_selectedSegment = -1;
|
||||
}
|
||||
}
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||
|
||||
|
||||
_trackingSegment = -1;
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (type == CPLeftMouseDown)
|
||||
{
|
||||
_trackingHighlighted = YES;
|
||||
_trackingSegment = [self testSegment:location];
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
var trackingSegment = [self testSegment:location];
|
||||
if (trackingSegment > -1 && [self isEnabledForSegment:trackingSegment])
|
||||
{
|
||||
_trackingHighlighted = YES;
|
||||
_trackingSegment = trackingSegment;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
else if (type == CPLeftMouseDragged)
|
||||
{
|
||||
if (_trackingSegment == -1)
|
||||
return;
|
||||
|
||||
var highlighted = CGRectContainsPoint(_segments[_trackingSegment].frame, location);
|
||||
|
||||
var highlighted = [self testSegment:location] === _trackingSegment;
|
||||
if (highlighted != _trackingHighlighted)
|
||||
{
|
||||
_trackingHighlighted = highlighted;
|
||||
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
[super setFont:aFont];
|
||||
|
||||
|
||||
[self tileWithChangedSegment:0];
|
||||
}
|
||||
|
||||
@@ -808,12 +826,12 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
var frame = [self frame],
|
||||
originalWidth = frame.size.width;
|
||||
|
||||
|
||||
frame.size.width = 0;
|
||||
|
||||
[self setFrame:frame];
|
||||
@@ -824,38 +842,38 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
||||
|
||||
if ([aCoder containsValueForKey:CPSegmentedControlSelectedKey])
|
||||
_selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
|
||||
else
|
||||
else
|
||||
_selectedSegment = -1;
|
||||
|
||||
|
||||
if ([aCoder containsValueForKey:CPSegmentedControlTrackingModeKey])
|
||||
_trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey];
|
||||
else
|
||||
_trackingMode = CPSegmentSwitchTrackingSelectOne;
|
||||
|
||||
|
||||
// HACK
|
||||
|
||||
for (var i = 0; i < _segments.length; i++)
|
||||
{
|
||||
_themeStates[i] = _segments[i].selected ? CPThemeStateSelected : CPThemeStateNormal;
|
||||
_themeStates[i] = [_segments[i] selected] ? CPThemeStateSelected : CPThemeStateNormal;
|
||||
[self tileWithChangedSegment:i];
|
||||
}
|
||||
|
||||
var difference = MAX(originalWidth - [self frame].size.width, 0.0),
|
||||
remainingWidth = FLOOR(difference / _segments.length);
|
||||
|
||||
for (var i=0; i < _segments.length; i++)
|
||||
[self setWidth:_segments[i].width + remainingWidth forSegment:i];
|
||||
for (var i = 0; i < _segments.length; i++)
|
||||
[self setWidth:[_segments[i] width] + remainingWidth forSegment:i];
|
||||
|
||||
[self tileWithChangedSegment:0];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
|
||||
[aCoder encodeObject:_segments forKey:CPSegmentedControlSegmentsKey];
|
||||
[aCoder encodeInt:_selectedSegment forKey:CPSegmentedControlSelectedKey];
|
||||
[aCoder encodeInt:_segmentStyle forKey:CPSegmentedControlSegmentStyleKey];
|
||||
@@ -867,15 +885,15 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
||||
|
||||
@implementation _CPSegmentItem : CPObject
|
||||
{
|
||||
CPImage image;
|
||||
CPString label;
|
||||
CPMenu menu;
|
||||
BOOL selected;
|
||||
BOOL enabled;
|
||||
int tag;
|
||||
int width;
|
||||
CPImage image @accessors;
|
||||
CPString label @accessors;
|
||||
CPMenu menu @accessors;
|
||||
BOOL selected @accessors;
|
||||
BOOL enabled @accessors;
|
||||
int tag @accessors;
|
||||
int width @accessors;
|
||||
|
||||
CGRect frame;
|
||||
CGRect frame @accessors;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
@@ -886,8 +904,8 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
||||
label = @"";
|
||||
menu = nil;
|
||||
selected = NO;
|
||||
enabled = NO;
|
||||
tag = 0;
|
||||
enabled = YES;
|
||||
tag = -1;
|
||||
width = 0;
|
||||
|
||||
frame = CGRectMakeZero();
|
||||
@@ -910,7 +928,7 @@ var CPSegmentItemImageKey = "CPSegmentItemImageKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
image = [aCoder decodeObjectForKey:CPSegmentItemImageKey];
|
||||
@@ -923,7 +941,7 @@ var CPSegmentItemImageKey = "CPSegmentItemImageKey",
|
||||
|
||||
frame = CGRectMakeZero();
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -939,4 +957,3 @@ var CPSegmentItemImageKey = "CPSegmentItemImageKey",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
+6
-6
@@ -23,8 +23,8 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
/*!
|
||||
@deprecated
|
||||
/*!
|
||||
@deprecated
|
||||
@class CPShadow
|
||||
|
||||
Instances of this class contain the attributes of a drop shadow used in Cappuccino.
|
||||
@@ -34,7 +34,7 @@
|
||||
CPSize _offset;
|
||||
float _blurRadius;
|
||||
CPColor _color;
|
||||
|
||||
|
||||
CPString _cssString;
|
||||
}
|
||||
|
||||
@@ -54,16 +54,16 @@
|
||||
- (id)_initWithOffset:(CPSize)anOffset blurRadius:(float)aBlurRadius color:(CPColor)aColor
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_offset = anOffset;
|
||||
_blurRadius = aBlurRadius;
|
||||
_color = aColor;
|
||||
|
||||
|
||||
_cssString = [_color cssString] + " " + Math.round(anOffset.width) + @"px " + Math.round(anOffset.height) + @"px " + Math.round(_blurRadius) + @"px";
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
+41
-18
@@ -28,19 +28,17 @@
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
CPLightShadow = 0;
|
||||
CPHeavyShadow = 1;
|
||||
|
||||
var CPShadowViewLightBackgroundColor = nil,
|
||||
CPShadowViewHeavyBackgroundColor = nil;
|
||||
|
||||
|
||||
var LIGHT_LEFT_INSET = 3.0,
|
||||
LIGHT_RIGHT_INSET = 3.0,
|
||||
LIGHT_TOP_INSET = 3.0,
|
||||
LIGHT_BOTTOM_INSET = 5.0,
|
||||
|
||||
|
||||
HEAVY_LEFT_INSET = 7.0,
|
||||
HEAVY_RIGHT_INSET = 7.0,
|
||||
HEAVY_TOP_INSET = 5.0,
|
||||
@@ -61,51 +59,76 @@ var LIGHT_LEFT_INSET = 3.0,
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:[self class]];
|
||||
|
||||
|
||||
CPShadowViewLightBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopLeft.png"] size:CGSizeMake(9.0, 9.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTop.png"] size:CGSizeMake(1.0, 9.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightTopRight.png"] size:CGSizeMake(9.0, 9.0)],
|
||||
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightLeft.png"] size:CGSizeMake(9.0, 1.0)],
|
||||
nil,
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightRight.png"] size:CGSizeMake(9.0, 1.0)],
|
||||
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomLeft.png"] size:CGSizeMake(9.0, 9.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottom.png"] size:CGSizeMake(1.0, 9.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewLightBottomRight.png"] size:CGSizeMake(9.0, 9.0)]
|
||||
]]];
|
||||
|
||||
|
||||
CPShadowViewHeavyBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopLeft.png"] size:CGSizeMake(17.0, 17.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTop.png"] size:CGSizeMake(1.0, 17.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyTopRight.png"] size:CGSizeMake(17.0, 17.0)],
|
||||
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyLeft.png"] size:CGSizeMake(17.0, 1.0)],
|
||||
nil,
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyRight.png"] size:CGSizeMake(17.0, 1.0)],
|
||||
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomLeft.png"] size:CGSizeMake(17.0, 17.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottom.png"] size:CGSizeMake(1.0, 17.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPShadowView/CPShadowViewHeavyBottomRight.png"] size:CGSizeMake(17.0, 17.0)]
|
||||
]]];
|
||||
}
|
||||
|
||||
+ (id)shadowViewEnclosingView:(CPView)aView
|
||||
{
|
||||
return [self shadowViewEnclosingView:aView withWeight:CPLightShadow];
|
||||
}
|
||||
|
||||
+ (id)shadowViewEnclosingView:(CPView)aView withWeight:(CPShadowWeight)aWeight
|
||||
{
|
||||
var shadowView = [[CPShadowView alloc] initWithFrame:[aView frame]];
|
||||
[shadowView setWeight:aWeight];
|
||||
|
||||
var size = [shadowView frame].size,
|
||||
width = size.width - [shadowView leftInset] - [shadowView rightInset],
|
||||
height = size.height - [shadowView topInset] - [shadowView bottomInset],
|
||||
enclosingView = [aView superview];
|
||||
|
||||
[shadowView setHitTests:[aView hitTests]];
|
||||
[shadowView setAutoresizingMask:[aView autoresizingMask]];
|
||||
[aView removeFromSuperview];
|
||||
[shadowView addSubview:aView];
|
||||
[aView setFrame:CGRectMake([shadowView leftInset], [shadowView topInset], width, height)]
|
||||
[enclosingView addSubview:shadowView];
|
||||
|
||||
return shadowView;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_weight = CPLightShadow;
|
||||
|
||||
|
||||
[self setBackgroundColor:CPShadowViewLightBackgroundColor];
|
||||
|
||||
|
||||
[self setHitTests:NO];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -113,9 +136,9 @@ var LIGHT_LEFT_INSET = 3.0,
|
||||
{
|
||||
if (_weight == aWeight)
|
||||
return;
|
||||
|
||||
|
||||
_weight = aWeight;
|
||||
|
||||
|
||||
if (_weight == CPLightShadow)
|
||||
[self setBackgroundColor:CPShadowViewLightBackgroundColor];
|
||||
|
||||
@@ -147,7 +170,7 @@ var LIGHT_LEFT_INSET = 3.0,
|
||||
{
|
||||
if (_weight == CPLightShadow)
|
||||
return LIGHT_LEFT_INSET + LIGHT_RIGHT_INSET;
|
||||
|
||||
|
||||
return HEAVY_LEFT_INSET + HEAVY_RIGHT_INSET;
|
||||
}
|
||||
|
||||
@@ -155,7 +178,7 @@ var LIGHT_LEFT_INSET = 3.0,
|
||||
{
|
||||
if (_weight == CPLightShadow)
|
||||
return LIGHT_TOP_INSET + LIGHT_BOTTOM_INSET;
|
||||
|
||||
|
||||
return HEAVY_TOP_INSET + HEAVY_BOTTOM_INSET;
|
||||
}
|
||||
|
||||
|
||||
+51
-41
@@ -22,7 +22,6 @@
|
||||
|
||||
@import "CPControl.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
/*! SLIDER STATES */
|
||||
|
||||
@@ -42,14 +41,14 @@ CPCircularSlider = 1;
|
||||
BOOL _isVertical;
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return "slider";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[nil, _CGSizeMakeZero(), 0.0, nil]
|
||||
return [CPDictionary dictionaryWithObjects:[[CPNull null], _CGSizeMakeZero(), 0.0, [CPNull null]]
|
||||
forKeys:[@"knob-color", @"knob-size", @"track-width", @"track-color"]];
|
||||
}
|
||||
|
||||
@@ -61,14 +60,14 @@ CPCircularSlider = 1;
|
||||
{
|
||||
_minValue = 0.0;
|
||||
_maxValue = 100.0;
|
||||
|
||||
|
||||
[self setObjectValue:50.0];
|
||||
|
||||
|
||||
[self setContinuous:YES];
|
||||
|
||||
[self _recalculateIsVertical];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -76,11 +75,11 @@ CPCircularSlider = 1;
|
||||
{
|
||||
if (_minValue === aMinimumValue)
|
||||
return;
|
||||
|
||||
|
||||
_minValue = aMinimumValue;
|
||||
|
||||
var doubleValue = [self doubleValue];
|
||||
|
||||
|
||||
if (doubleValue < _minValue)
|
||||
[self setDoubleValue:_minValue];
|
||||
|
||||
@@ -98,11 +97,11 @@ CPCircularSlider = 1;
|
||||
{
|
||||
if (_maxValue === aMaximumValue)
|
||||
return;
|
||||
|
||||
|
||||
_maxValue = aMaximumValue;
|
||||
|
||||
|
||||
var doubleValue = [self doubleValue];
|
||||
|
||||
|
||||
if (doubleValue > _maxValue)
|
||||
[self setDoubleValue:_maxValue];
|
||||
|
||||
@@ -169,27 +168,27 @@ CPCircularSlider = 1;
|
||||
bounds.size.height = trackWidth;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
- (CGRect)knobRectForBounds:(CGRect)bounds
|
||||
{
|
||||
var knobSize = [self currentValueForThemeAttribute:@"knob-size"];
|
||||
|
||||
|
||||
if (knobSize.width <= 0 || knobSize.height <= 0)
|
||||
return _CGRectMakeZero();
|
||||
|
||||
|
||||
var knobRect = _CGRectMake(0.0, 0.0, knobSize.width, knobSize.height),
|
||||
trackRect = [self trackRectForBounds:bounds];
|
||||
|
||||
|
||||
// No track, do our best to approximate a place for this thing.
|
||||
if (!trackRect || _CGRectIsEmpty(trackRect))
|
||||
trackRect = bounds;
|
||||
|
||||
if ([self hasThemeState:CPThemeStateCircular])
|
||||
{
|
||||
var angle = 3*PI_2 - (1.0 - [self doubleValue] - _minValue) / (_maxValue - _minValue) * PI2,
|
||||
var angle = 3 * PI_2 - (1.0 - [self doubleValue] - _minValue) / (_maxValue - _minValue) * PI2,
|
||||
radius = CGRectGetWidth(trackRect) / 2.0 - 8.0;
|
||||
|
||||
knobRect.origin.x = radius * COS(angle) + CGRectGetMidX(trackRect) - 3.0;
|
||||
@@ -198,7 +197,7 @@ CPCircularSlider = 1;
|
||||
else if ([self isVertical])
|
||||
{
|
||||
knobRect.origin.x = _CGRectGetMidX(trackRect) - knobSize.width / 2.0;
|
||||
knobRect.origin.y = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (_CGRectGetHeight(trackRect) - knobSize.height);
|
||||
knobRect.origin.y = ((_maxValue - [self doubleValue]) / (_maxValue - _minValue)) * (_CGRectGetHeight(trackRect) - knobSize.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -213,10 +212,10 @@ CPCircularSlider = 1;
|
||||
{
|
||||
if (aName === "track-view")
|
||||
return [self trackRectForBounds:[self bounds]];
|
||||
|
||||
|
||||
else if (aName === "knob-view")
|
||||
return [self knobRectForBounds:[self bounds]];
|
||||
|
||||
|
||||
return [super rectForEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
@@ -225,12 +224,12 @@ CPCircularSlider = 1;
|
||||
if (aName === "track-view" || aName === "knob-view")
|
||||
{
|
||||
var view = [[CPView alloc] init];
|
||||
|
||||
|
||||
[view setHitTests:NO];
|
||||
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
return [super createEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
@@ -256,7 +255,7 @@ CPCircularSlider = 1;
|
||||
var bounds = [self bounds],
|
||||
width = _CGRectGetWidth(bounds),
|
||||
height = _CGRectGetHeight(bounds);
|
||||
|
||||
|
||||
_isVertical = width < height ? 1 : (width > height ? 0 : -1);
|
||||
|
||||
if (_isVertical === 1)
|
||||
@@ -282,7 +281,7 @@ CPCircularSlider = 1;
|
||||
var knobView = [self layoutEphemeralSubviewNamed:@"knob-view"
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:@"track-view"];
|
||||
|
||||
|
||||
if (knobView)
|
||||
[knobView setBackgroundColor:[self currentValueForThemeAttribute:"knob-color"]];
|
||||
}
|
||||
@@ -320,7 +319,7 @@ CPCircularSlider = 1;
|
||||
|
||||
var minValue = [self minValue];
|
||||
|
||||
return MAX(0.0, MIN(1.0, (aPoint.y - _CGRectGetMinY(trackRect)) / _CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue;
|
||||
return MAX(0.0, MIN(1.0, (_CGRectGetMaxY(trackRect) - aPoint.y) / _CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -339,44 +338,43 @@ CPCircularSlider = 1;
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
knobRect = [self knobRectForBounds:_CGRectMakeCopy(bounds)];
|
||||
|
||||
|
||||
if (_CGRectContainsPoint(knobRect, aPoint))
|
||||
_dragOffset = _CGSizeMake(_CGRectGetMidX(knobRect) - aPoint.x, _CGRectGetMidY(knobRect) - aPoint.y);
|
||||
|
||||
else
|
||||
else
|
||||
{
|
||||
var trackRect = [self trackRectForBounds:bounds];
|
||||
|
||||
|
||||
if (trackRect && _CGRectContainsPoint(trackRect, aPoint))
|
||||
{
|
||||
_dragOffset = _CGSizeMakeZero();
|
||||
|
||||
|
||||
[self setObjectValue:[self _valueAtPoint:aPoint]];
|
||||
}
|
||||
|
||||
|
||||
else
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
[self setHighlighted:YES];
|
||||
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
return YES;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
|
||||
{
|
||||
[self setObjectValue:[self _valueAtPoint:_CGPointMake(aPoint.x + _dragOffset.width, aPoint.y + _dragOffset.height)]];
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
||||
{
|
||||
[self setHighlighted:NO];
|
||||
|
||||
|
||||
if ([_target respondsToSelector:@selector(sliderDidFinish:)])
|
||||
[_target sliderDidFinish:self];
|
||||
|
||||
@@ -392,10 +390,22 @@ CPCircularSlider = 1;
|
||||
{
|
||||
if (flag)
|
||||
_sendActionOn |= CPLeftMouseDraggedMask;
|
||||
else
|
||||
else
|
||||
_sendActionOn &= ~CPLeftMouseDraggedMask;
|
||||
}
|
||||
|
||||
- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
|
||||
{
|
||||
var count = objects.length,
|
||||
value = [objects[0] valueForKeyPath:aKeyPath];
|
||||
|
||||
[self setObjectValue:value];
|
||||
|
||||
while (count-- > 1)
|
||||
if (value !== ([objects[count] valueForKeyPath:aKeyPath]))
|
||||
return [self setFloatValue:1.0];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPSliderMinValueKey = "CPSliderMinValueKey",
|
||||
@@ -442,14 +452,14 @@ var CPSliderMinValueKey = "CPSliderMinValueKey",
|
||||
- (id)value
|
||||
{
|
||||
CPLog.warn("[CPSlider value] is deprecated, use doubleValue or objectValue instead.");
|
||||
|
||||
return [self doubleValue];
|
||||
|
||||
return [self doubleValue];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue
|
||||
{
|
||||
CPLog.warn("[CPSlider setValue:] is deprecated, use setDoubleValue: or setObjectValue: instead.");
|
||||
|
||||
|
||||
[self setObjectValue:aValue];
|
||||
}
|
||||
|
||||
|
||||
+154
-195
@@ -20,22 +20,24 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPView.j"
|
||||
@import "CPColorPicker.j"
|
||||
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
@implementation CPSliderColorPicker : CPColorPicker
|
||||
{
|
||||
{
|
||||
CPView _contentView;
|
||||
|
||||
|
||||
CPSlider _redSlider;
|
||||
CPSlider _greenSlider;
|
||||
CPSlider _blueSlider;
|
||||
CPSlider _hueSlider;
|
||||
CPSlider _saturationSlider;
|
||||
CPSlider _brightnessSlider;
|
||||
|
||||
|
||||
CPTextField _rgbLabel;
|
||||
CPTextField _hsbLabel;
|
||||
CPTextField _redLabel;
|
||||
@@ -45,251 +47,162 @@
|
||||
CPTextField _saturationLabel;
|
||||
CPTextField _brightnessLabel;
|
||||
CPTextField _hexLabel;
|
||||
|
||||
DOMElement _redValue;
|
||||
DOMElement _greenValue;
|
||||
DOMElement _blueValue;
|
||||
DOMElement _hueValue;
|
||||
DOMElement _saturationValue;
|
||||
DOMElement _brightnessValue;
|
||||
DOMElement _hexValue;
|
||||
CPTextField _hexValue;
|
||||
|
||||
CPTextField _hexValue;
|
||||
CPTextField _redValue;
|
||||
CPTextField _greenValue;
|
||||
CPTextField _blueValue;
|
||||
CPTextField _hueValue;
|
||||
CPTextField _saturationValue;
|
||||
CPTextField _brightnessValue;
|
||||
}
|
||||
|
||||
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
|
||||
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
|
||||
{
|
||||
return [super initWithPickerMask:mask colorPanel: owningColorPanel];
|
||||
}
|
||||
|
||||
-(id)initView
|
||||
|
||||
- (id)initView
|
||||
{
|
||||
aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight);
|
||||
|
||||
aFrame = CGRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight);
|
||||
|
||||
_contentView = [[CPView alloc] initWithFrame:aFrame];
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||
|
||||
_rgbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 10, 100, 20)];
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
_rgbLabel = [[CPTextField alloc] initWithFrame: CGRectMake(0, 10, 100, 20)];
|
||||
[_rgbLabel setStringValue: "Red, Green, Blue"];
|
||||
[_rgbLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_redLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 35, 15, 20)];
|
||||
_redLabel = [[CPTextField alloc] initWithFrame: CGRectMake(0, 35, 15, 20)];
|
||||
[_redLabel setStringValue: "R"];
|
||||
[_redLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_redSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 35, aFrame.size.width - 70, 20)];
|
||||
|
||||
_redSlider = [[CPSlider alloc] initWithFrame: CGRectMake(15, 35, aFrame.size.width - 70, 20)];
|
||||
[_redSlider setMaxValue: 1.0];
|
||||
[_redSlider setMinValue: 0.0];
|
||||
[_redSlider setTarget: self];
|
||||
[_redSlider setAction: @selector(sliderChanged:)];
|
||||
[_redSlider setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
var updateFunction = function(aDOMEvent)
|
||||
{
|
||||
if(isNaN(this.value))
|
||||
return;
|
||||
|
||||
switch(this)
|
||||
{
|
||||
case _redValue: [_redSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||
//[self sliderChanged: _redSlider];
|
||||
break;
|
||||
|
||||
case _greenValue: [_greenSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||
//[self sliderChanged: _greenSlider];
|
||||
break;
|
||||
|
||||
case _blueValue: [_blueSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||
//[self sliderChanged: _blueSlider];
|
||||
break;
|
||||
|
||||
case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(this.value), 360), 0)];
|
||||
//[self sliderChanged: _hueSlider];
|
||||
break;
|
||||
|
||||
case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(this.value), 100), 0)];
|
||||
//[self sliderChanged: _saturationSlider];
|
||||
break;
|
||||
// red value input box
|
||||
_redValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 30, 45, 29)];
|
||||
[_redValue setAutoresizingMask: CPViewMinXMargin];
|
||||
[_redValue setEditable: YES];
|
||||
[_redValue setBezeled: YES];
|
||||
[_redValue setDelegate: self];
|
||||
[_contentView addSubview: _redValue];
|
||||
|
||||
case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(this.value), 100), 0)];
|
||||
//[self sliderChanged: _brightnessSlider];
|
||||
break;
|
||||
}
|
||||
|
||||
this.blur();
|
||||
};
|
||||
|
||||
var keypressFunction = function(aDOMEvent)
|
||||
{
|
||||
aDOMEvent = aDOMEvent || window.event;
|
||||
if (aDOMEvent.keyCode == 13)
|
||||
{
|
||||
updateFunction(aDOMEvent);
|
||||
|
||||
if(aDOMEvent.preventDefault)
|
||||
aDOMEvent.preventDefault();
|
||||
else if(aDOMEvent.stopPropagation)
|
||||
aDOMEvent.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
//red value input box
|
||||
var redValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 35, 45, 20)];
|
||||
[redValue setAutoresizingMask: CPViewMinXMargin];
|
||||
|
||||
_redValue = document.createElement("input");
|
||||
_redValue.style.width = "40px";
|
||||
_redValue.style.backgroundColor = "transparent";
|
||||
_redValue.style.border = "1px solid black";
|
||||
_redValue.style.color = "black";
|
||||
_redValue.style.position = "absolute";
|
||||
_redValue.style.top = "0px";
|
||||
_redValue.style.left = "0px";
|
||||
_redValue.onchange = updateFunction;
|
||||
|
||||
redValue._DOMElement.appendChild(_redValue);
|
||||
[_contentView addSubview: redValue];
|
||||
|
||||
_greenLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 58, 15, 20)];
|
||||
_greenLabel = [[CPTextField alloc] initWithFrame: CGRectMake(0, 58, 15, 20)];
|
||||
[_greenLabel setStringValue: "G"];
|
||||
[_greenLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_greenSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 58, aFrame.size.width - 70, 20)];
|
||||
_greenSlider = [[CPSlider alloc] initWithFrame: CGRectMake(15, 58, aFrame.size.width - 70, 20)];
|
||||
[_greenSlider setMaxValue: 1.0];
|
||||
[_greenSlider setMinValue: 0.0];
|
||||
[_greenSlider setTarget: self];
|
||||
[_greenSlider setAction: @selector(sliderChanged:)];
|
||||
[_greenSlider setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
//green value input box
|
||||
var greenValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 58, 45, 20)];
|
||||
[greenValue setAutoresizingMask: CPViewMinXMargin];
|
||||
// green value input box
|
||||
_greenValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 53, 45, 29)];
|
||||
[_greenValue setAutoresizingMask: CPViewMinXMargin];
|
||||
[_greenValue setEditable: YES];
|
||||
[_greenValue setBezeled: YES];
|
||||
[_greenValue setDelegate: self];
|
||||
[_contentView addSubview: _greenValue];
|
||||
|
||||
_greenValue = _redValue.cloneNode(false);
|
||||
_greenValue.onchange = updateFunction;
|
||||
|
||||
greenValue._DOMElement.appendChild(_greenValue);
|
||||
[_contentView addSubview: greenValue];
|
||||
|
||||
_blueLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 81, 15, 20)];
|
||||
_blueLabel = [[CPTextField alloc] initWithFrame: CGRectMake(0, 81, 15, 20)];
|
||||
[_blueLabel setStringValue: "B"];
|
||||
[_blueLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_blueSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 81, aFrame.size.width - 70, 20)];
|
||||
_blueSlider = [[CPSlider alloc] initWithFrame: CGRectMake(15, 81, aFrame.size.width - 70, 20)];
|
||||
[_blueSlider setMaxValue: 1.0];
|
||||
[_blueSlider setMinValue: 0.0];
|
||||
[_blueSlider setTarget: self];
|
||||
[_blueSlider setAction: @selector(sliderChanged:)];
|
||||
[_blueSlider setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
//blue value input box
|
||||
var blueValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 81, 45, 20)];
|
||||
[blueValue setAutoresizingMask: CPViewMinXMargin];
|
||||
// blue value input box
|
||||
_blueValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 76, 45, 29)];
|
||||
[_blueValue setAutoresizingMask: CPViewMinXMargin];
|
||||
[_blueValue setEditable: YES];
|
||||
[_blueValue setBezeled: YES];
|
||||
[_blueValue setDelegate: self];
|
||||
[_contentView addSubview: _blueValue];
|
||||
|
||||
_blueValue = _redValue.cloneNode(false);
|
||||
_blueValue.onchange = updateFunction;
|
||||
|
||||
blueValue._DOMElement.appendChild(_blueValue);
|
||||
[_contentView addSubview: blueValue];
|
||||
|
||||
_hsbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 120, 190, 20)];
|
||||
_hsbLabel = [[CPTextField alloc] initWithFrame: CGRectMake(0, 120, 190, 20)];
|
||||
[_hsbLabel setStringValue: "Hue, Saturation, Brightness"];
|
||||
[_hsbLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_hueLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 145, 15, 20)];
|
||||
_hueLabel = [[CPTextField alloc] initWithFrame: CGRectMake(0, 145, 15, 20)];
|
||||
[_hueLabel setStringValue: "H"];
|
||||
[_hueLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_hueSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 145, aFrame.size.width - 70, 20)];
|
||||
|
||||
_hueSlider = [[CPSlider alloc] initWithFrame: CGRectMake(15, 145, aFrame.size.width - 70, 20)];
|
||||
[_hueSlider setMaxValue: 359.0];
|
||||
[_hueSlider setMinValue: 0.0];
|
||||
[_hueSlider setTarget: self];
|
||||
[_hueSlider setAction: @selector(sliderChanged:)];
|
||||
[_hueSlider setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
//red value input box
|
||||
var hueValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 145, 45, 20)];
|
||||
[hueValue setAutoresizingMask: CPViewMinXMargin];
|
||||
// hue value input box
|
||||
_hueValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 140, 45, 29)];
|
||||
[_hueValue setAutoresizingMask: CPViewMinXMargin];
|
||||
[_hueValue setEditable: YES];
|
||||
[_hueValue setBezeled: YES];
|
||||
[_hueValue setDelegate: self];
|
||||
[_contentView addSubview: _hueValue];
|
||||
|
||||
_hueValue = _redValue.cloneNode(false);
|
||||
_hueValue.onchange = updateFunction;
|
||||
|
||||
hueValue._DOMElement.appendChild(_hueValue);
|
||||
[_contentView addSubview: hueValue];
|
||||
|
||||
_saturationLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 168, 15, 20)];
|
||||
_saturationLabel = [[CPTextField alloc] initWithFrame: CGRectMake(0, 168, 15, 20)];
|
||||
[_saturationLabel setStringValue: "S"];
|
||||
[_saturationLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_saturationSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 168, aFrame.size.width - 70, 20)];
|
||||
_saturationSlider = [[CPSlider alloc] initWithFrame: CGRectMake(15, 168, aFrame.size.width - 70, 20)];
|
||||
[_saturationSlider setMaxValue: 100.0];
|
||||
[_saturationSlider setMinValue: 0.0];
|
||||
[_saturationSlider setTarget: self];
|
||||
[_saturationSlider setAction: @selector(sliderChanged:)];
|
||||
[_saturationSlider setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
//green value input box
|
||||
var saturationValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 168, 45, 20)];
|
||||
[saturationValue setAutoresizingMask: CPViewMinXMargin];
|
||||
// saturation value input box
|
||||
_saturationValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 163, 45, 29)];
|
||||
[_saturationValue setAutoresizingMask: CPViewMinXMargin];
|
||||
[_saturationValue setEditable: YES];
|
||||
[_saturationValue setBezeled: YES];
|
||||
[_saturationValue setDelegate: self];
|
||||
[_contentView addSubview: _saturationValue];
|
||||
|
||||
_saturationValue = _redValue.cloneNode(false);
|
||||
_saturationValue.onchange = updateFunction;
|
||||
|
||||
saturationValue._DOMElement.appendChild(_saturationValue);
|
||||
[_contentView addSubview: saturationValue];
|
||||
|
||||
_brightnessLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 191, 15, 20)];
|
||||
_brightnessLabel = [[CPTextField alloc] initWithFrame: CGRectMake(0, 191, 15, 20)];
|
||||
[_brightnessLabel setStringValue: "B"];
|
||||
[_brightnessLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_brightnessSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 191, aFrame.size.width - 70, 20)];
|
||||
_brightnessSlider = [[CPSlider alloc] initWithFrame: CGRectMake(15, 191, aFrame.size.width - 70, 20)];
|
||||
[_brightnessSlider setMaxValue: 100.0];
|
||||
[_brightnessSlider setMinValue: 0.0];
|
||||
[_brightnessSlider setTarget: self];
|
||||
[_brightnessSlider setAction: @selector(sliderChanged:)];
|
||||
[_brightnessSlider setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
//blue value input box
|
||||
var brightnessValue = [[CPView alloc] initWithFrame: CPRectMake(aFrame.size.width - 45, 191, 45, 20)];
|
||||
[brightnessValue setAutoresizingMask: CPViewMinXMargin];
|
||||
// brightness value input box
|
||||
_brightnessValue = [[CPTextField alloc] initWithFrame: CGRectMake(aFrame.size.width - 45, 186, 45, 29)];
|
||||
[_brightnessValue setAutoresizingMask: CPViewMinXMargin];
|
||||
[_brightnessValue setEditable: YES];
|
||||
[_brightnessValue setBezeled: YES];
|
||||
[_brightnessValue setDelegate: self];
|
||||
[_contentView addSubview: _brightnessValue];
|
||||
|
||||
_brightnessValue = _redValue.cloneNode(false);
|
||||
_brightnessValue.onchange = updateFunction;
|
||||
|
||||
brightnessValue._DOMElement.appendChild(_brightnessValue);
|
||||
[_contentView addSubview: brightnessValue];
|
||||
|
||||
_hexLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 230, 30, 20)];
|
||||
_hexLabel = [[CPTextField alloc] initWithFrame: CGRectMake(0, 230, 30, 20)];
|
||||
[_hexLabel setStringValue: "Hex"];
|
||||
[_hexLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
//hex input box
|
||||
_hexValue = _redValue.cloneNode(false);
|
||||
_hexValue.style.top = "228px";
|
||||
_hexValue.style.width = "80px";
|
||||
_hexValue.style.left = "35px";
|
||||
_hexValue.onkeypress = function(aDOMEvent)
|
||||
{
|
||||
aDOMEvent = aDOMEvent || window.event;
|
||||
if (aDOMEvent.keyCode == 13)
|
||||
{
|
||||
var newColor = [CPColor colorWithHexString: this.value];
|
||||
|
||||
if(newColor)
|
||||
{
|
||||
[self setColor: newColor];
|
||||
[[self colorPanel] setColor: newColor];
|
||||
}
|
||||
|
||||
if(aDOMEvent.preventDefault)
|
||||
aDOMEvent.preventDefault();
|
||||
else if(aDOMEvent.stopPropagation)
|
||||
aDOMEvent.stopPropagation();
|
||||
|
||||
this.blur();
|
||||
}
|
||||
};
|
||||
|
||||
_contentView._DOMElement.appendChild(_hexValue);
|
||||
|
||||
//hex input box
|
||||
_hexValue = [[CPTextField alloc] initWithFrame: CGRectMake(32, 225, 80, 29)];
|
||||
[_hexValue setEditable: YES];
|
||||
[_hexValue setBezeled: YES];
|
||||
[_hexValue setDelegate: self];
|
||||
[_contentView addSubview: _hexValue];
|
||||
|
||||
[_contentView addSubview: _rgbLabel];
|
||||
[_contentView addSubview: _redLabel];
|
||||
[_contentView addSubview: _greenLabel];
|
||||
@@ -297,7 +210,7 @@
|
||||
[_contentView addSubview: _redSlider];
|
||||
[_contentView addSubview: _greenSlider];
|
||||
[_contentView addSubview: _blueSlider];
|
||||
|
||||
|
||||
[_contentView addSubview: _hsbLabel];
|
||||
[_contentView addSubview: _hueLabel];
|
||||
[_contentView addSubview: _saturationLabel];
|
||||
@@ -305,24 +218,24 @@
|
||||
[_contentView addSubview: _hueSlider];
|
||||
[_contentView addSubview: _saturationSlider];
|
||||
[_contentView addSubview: _brightnessSlider];
|
||||
|
||||
|
||||
[_contentView addSubview: _hexLabel];
|
||||
}
|
||||
|
||||
- (CPView)provideNewView:(BOOL)initialRequest
|
||||
- (CPView)provideNewView:(BOOL)initialRequest
|
||||
{
|
||||
if (initialRequest)
|
||||
if (initialRequest)
|
||||
[self initView];
|
||||
|
||||
|
||||
return _contentView;
|
||||
}
|
||||
|
||||
- (int)currentMode
|
||||
- (int)currentMode
|
||||
{
|
||||
return CPSliderColorPickerMode;
|
||||
}
|
||||
|
||||
- (BOOL)supportsMode:(int)mode
|
||||
- (BOOL)supportsMode:(int)mode
|
||||
{
|
||||
return (mode == CPSliderColorPickerMode) ? YES : NO;
|
||||
}
|
||||
@@ -333,7 +246,7 @@
|
||||
colorPanel = [self colorPanel],
|
||||
alpha = [colorPanel opacity];
|
||||
|
||||
switch(sender)
|
||||
switch (sender)
|
||||
{
|
||||
case _hueSlider:
|
||||
case _saturationSlider:
|
||||
@@ -341,21 +254,21 @@
|
||||
saturation: [_saturationSlider floatValue]
|
||||
brightness: [_brightnessSlider floatValue]
|
||||
alpha: alpha];
|
||||
|
||||
|
||||
[self updateRGBSliders: newColor];
|
||||
break;
|
||||
|
||||
|
||||
case _redSlider:
|
||||
case _greenSlider:
|
||||
case _blueSlider: newColor = [CPColor colorWithCalibratedRed: [_redSlider floatValue]
|
||||
green: [_greenSlider floatValue]
|
||||
blue: [_blueSlider floatValue]
|
||||
alpha: alpha];
|
||||
|
||||
|
||||
[self updateHSBSliders: newColor];
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
[self updateLabels];
|
||||
[self updateHex: newColor];
|
||||
[colorPanel setColor: newColor];
|
||||
@@ -372,7 +285,7 @@
|
||||
-(void)updateHSBSliders:(CPColor)aColor
|
||||
{
|
||||
var hsb = [aColor hsbComponents];
|
||||
|
||||
|
||||
[_hueSlider setFloatValue:hsb[0]];
|
||||
[_saturationSlider setFloatValue:hsb[1]];
|
||||
[_brightnessSlider setFloatValue:hsb[2]];
|
||||
@@ -380,7 +293,7 @@
|
||||
|
||||
- (void)updateHex:(CPColor)aColor
|
||||
{
|
||||
_hexValue.value = [aColor hexString];
|
||||
[_hexValue setStringValue:[aColor hexString]];
|
||||
}
|
||||
|
||||
- (void)updateRGBSliders:(CPColor)aColor
|
||||
@@ -394,13 +307,13 @@
|
||||
|
||||
- (void)updateLabels
|
||||
{
|
||||
_hueValue.value = ROUND([_hueSlider floatValue]);
|
||||
_saturationValue.value = ROUND([_saturationSlider floatValue]);
|
||||
_brightnessValue.value = ROUND([_brightnessSlider floatValue]);
|
||||
|
||||
_redValue.value = ROUND([_redSlider floatValue] * 255);
|
||||
_greenValue.value = ROUND([_greenSlider floatValue] * 255);
|
||||
_blueValue.value = ROUND([_blueSlider floatValue] * 255);
|
||||
[_hueValue setStringValue: ROUND([_hueSlider floatValue])];
|
||||
[_saturationValue setStringValue: ROUND([_saturationSlider floatValue])];
|
||||
[_brightnessValue setStringValue: ROUND([_brightnessSlider floatValue])];
|
||||
|
||||
[_redValue setStringValue: ROUND([_redSlider floatValue] * 255)];
|
||||
[_greenValue setStringValue: ROUND([_greenSlider floatValue] * 255)];
|
||||
[_blueValue setStringValue: ROUND([_blueSlider floatValue] * 255)];
|
||||
}
|
||||
|
||||
- (CPImage)provideNewButtonImage
|
||||
@@ -413,4 +326,50 @@
|
||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:CPColorPicker] pathForResource:"slider_button_h.png"] size:CGSizeMake(32, 32)];
|
||||
}
|
||||
|
||||
- (void)controlTextDidEndEditing:(CPNotification)aNotification
|
||||
{
|
||||
var field = [aNotification object],
|
||||
value = [[field stringValue] stringByTrimmingWhitespace];
|
||||
|
||||
if (field === _hexValue)
|
||||
{
|
||||
var newColor = [CPColor colorWithHexString: value];
|
||||
|
||||
if (newColor)
|
||||
{
|
||||
[self setColor: newColor];
|
||||
[[self colorPanel] setColor: newColor];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (field)
|
||||
{
|
||||
case _redValue: [_redSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
||||
[self sliderChanged: _redSlider];
|
||||
break;
|
||||
|
||||
case _greenValue: [_greenSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
||||
[self sliderChanged: _greenSlider];
|
||||
break;
|
||||
|
||||
case _blueValue: [_blueSlider setFloatValue:MAX(MIN(ROUND(value), 255) / 255.0, 0)];
|
||||
[self sliderChanged: _blueSlider];
|
||||
break;
|
||||
|
||||
case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(value), 360), 0)];
|
||||
[self sliderChanged: _hueSlider];
|
||||
break;
|
||||
|
||||
case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
|
||||
[self sliderChanged: _saturationSlider];
|
||||
break;
|
||||
|
||||
case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(value), 100), 0)];
|
||||
[self sliderChanged: _brightnessSlider];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* CPSound.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Antoine Mercadal
|
||||
* Copyright 2010, Antoine Mercadal
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
CPSoundLoadStateEmpty = 0;
|
||||
CPSoundLoadStateLoading = 1;
|
||||
CPSoundLoadStateCanBePlayed = 2;
|
||||
|
||||
CPSoundPlayBackStatePlay = 0;
|
||||
CPSoundPlayBackStateStop = 1;
|
||||
CPSoundPlayBackStatePause = 2;
|
||||
|
||||
|
||||
/*!
|
||||
CPSound provides a way to load and play sounds. In the browser it relies on the
|
||||
HTML5 audio tag being available.
|
||||
|
||||
CPSound delegate:
|
||||
- sound:didFinishPlaying: called when sound has finished to played
|
||||
|
||||
*/
|
||||
@implementation CPSound : CPObject
|
||||
{
|
||||
CPString _name @accessors(property=name);
|
||||
id _delegate @accessors(property=delegate);
|
||||
|
||||
HTMLAudioElement _audioTag;
|
||||
int _loadStatus;
|
||||
int _playBackStatus;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_loadStatus = CPSoundLoadStateEmpty;
|
||||
_loops = NO;
|
||||
_audioTag = document.createElement("audio");
|
||||
_audioTag.preload = YES;
|
||||
_audioTag.addEventListener("canplay", function()
|
||||
{
|
||||
[self _soundDidload];
|
||||
});
|
||||
_audioTag.addEventListener("ended", function()
|
||||
{
|
||||
[self _soundDidEnd];
|
||||
});
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Initialize with the sound contents of the URL represented by aFile.
|
||||
|
||||
@param aFile CPString the path of the sound
|
||||
@param byRef ignored (Cocoa compatibibility)
|
||||
*/
|
||||
- (id)initWithContentsOfFile:(CPString)aFile byReference:(BOOL)byRef
|
||||
{
|
||||
if (self = [self init])
|
||||
{
|
||||
_loadStatus = CPSoundLoadStateLoading;
|
||||
_audioTag.src = aFile;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Initialize with the sound contents of the file located at aURL.
|
||||
|
||||
@param aURL CPURL containing the URL of the sound
|
||||
@param byRef ignored (Cocoa compatibibility)
|
||||
*/
|
||||
- (id)initWithContentsOfURL:(CPURL)aURL byReference:(BOOL)byRef
|
||||
{
|
||||
return [self initWithContentsOfFile:[aURL absoluteString] byReference:NO];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initialize with the sound contents of someData.
|
||||
|
||||
@param someData CPData containing the sound
|
||||
@param byRef ignored (Cocoa compatibibility)
|
||||
*/
|
||||
- (id)initWithData:(CPData)someData
|
||||
{
|
||||
if (self = [self init])
|
||||
{
|
||||
_loadStatus = CPSoundLoadStateLoading;
|
||||
_audioTag.src = [someData rawString];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Events listener
|
||||
|
||||
/*! @ignore
|
||||
*/
|
||||
- (void)_soundDidload
|
||||
{
|
||||
_loadStatus = CPSoundLoadStateCanBePlayed;
|
||||
}
|
||||
|
||||
/*! @ignore
|
||||
*/
|
||||
- (void)_soundDidEnd
|
||||
{
|
||||
if (![self loops])
|
||||
[self stop];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Media controls
|
||||
|
||||
/*!
|
||||
Play the sound.
|
||||
*/
|
||||
- (void)play
|
||||
{
|
||||
if ((_loadStatus !== CPSoundLoadStateCanBePlayed)
|
||||
|| (_playBackStatus === CPSoundPlayBackStatePlay))
|
||||
return;
|
||||
|
||||
_audioTag.play();
|
||||
_playBackStatus = CPSoundPlayBackStatePlay;
|
||||
}
|
||||
|
||||
/*!
|
||||
Stop the sound.
|
||||
*/
|
||||
- (void)stop
|
||||
{
|
||||
if ((_loadStatus !== CPSoundLoadStateCanBePlayed)
|
||||
|| (_playBackStatus === CPSoundPlayBackStateStop))
|
||||
return;
|
||||
|
||||
_audioTag.pause();
|
||||
_audioTag.currentTime = 0.0;
|
||||
_playBackStatus = CPSoundPlayBackStateStop;
|
||||
|
||||
if (_delegate && [_delegate respondsToSelector:@selector(sound:didFinishPlaying:)])
|
||||
[_delegate sound:self didFinishPlaying:YES];
|
||||
}
|
||||
|
||||
/*!
|
||||
Pause the sound.
|
||||
*/
|
||||
- (void)pause
|
||||
{
|
||||
if ((_loadStatus !== CPSoundLoadStateCanBePlayed)
|
||||
|| (_playBackStatus === CPSoundPlayBackStatePause))
|
||||
return;
|
||||
|
||||
_audioTag.pause();
|
||||
_playBackStatus = CPSoundPlayBackStatePause;
|
||||
}
|
||||
|
||||
/*!
|
||||
Resume playback of a paused sound.
|
||||
*/
|
||||
- (void)resume
|
||||
{
|
||||
if ((_loadStatus !== CPSoundLoadStateCanBePlayed)
|
||||
|| (_playBackStatus !== CPSoundPlayBackStatePause))
|
||||
return;
|
||||
|
||||
_audioTag.play();
|
||||
_playBackStatus = CPSoundPlayBackStatePlay;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return YES if the sound is in loop mode.
|
||||
|
||||
@return BOOL YES if in loop mode, NO otherwise
|
||||
*/
|
||||
- (BOOL)loops
|
||||
{
|
||||
return _audioTag.loop;
|
||||
}
|
||||
|
||||
/*!
|
||||
Specifies whether the sound should repeat.
|
||||
|
||||
@param BOOL YES for loop mode, NO otherwise
|
||||
*/
|
||||
- (void)setLoops:(BOOL)shouldLoop
|
||||
{
|
||||
_audioTag.loop = shouldLoop;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the volume of the receiver.
|
||||
|
||||
@return double from 0.0 to 1.0
|
||||
*/
|
||||
- (double)volume
|
||||
{
|
||||
return _audioTag.volume;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the volume the sound should be played at.
|
||||
|
||||
@param double a volume value between 0.0 and 1.0
|
||||
*/
|
||||
- (void)setVolume:(double)aVolume
|
||||
{
|
||||
if (aVolume > 1.0)
|
||||
aVolume = 1.0;
|
||||
else if (aVolume < 0.0)
|
||||
aVolume = 0.0;
|
||||
|
||||
_audioTag.volume = aVolume;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Accessors
|
||||
|
||||
/*!
|
||||
Returns the duration in seconds of the sound.
|
||||
|
||||
@return double the duration
|
||||
*/
|
||||
- (double)duration
|
||||
{
|
||||
return _audioTag.duration;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns if the sound is playing or not.
|
||||
|
||||
@return BOOL YES if the sound is playing, NO otherwise
|
||||
*/
|
||||
- (BOOL)isPlaying
|
||||
{
|
||||
return (_playBackStatus === CPSoundPlayBackStatePlay);
|
||||
}
|
||||
|
||||
@end
|
||||
+288
-120
@@ -20,13 +20,10 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPButtonBar.j"
|
||||
@import "CPImage.j"
|
||||
@import "CPView.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
#include "Platform/Platform.h"
|
||||
#include "Platform/DOM/CPDOMDisplayServer.h"
|
||||
|
||||
|
||||
CPSplitViewDidResizeSubviewsNotification = @"CPSplitViewDidResizeSubviewsNotification";
|
||||
CPSplitViewWillResizeSubviewsNotification = @"CPSplitViewWillResizeSubviewsNotification";
|
||||
@@ -43,18 +40,32 @@ var CPSplitViewHorizontalImage = nil,
|
||||
id _delegate;
|
||||
BOOL _isVertical;
|
||||
BOOL _isPaneSplitter;
|
||||
|
||||
|
||||
int _currentDivider;
|
||||
float _initialOffset;
|
||||
|
||||
float _preCollapsePosition;
|
||||
|
||||
CPString _originComponent;
|
||||
CPString _sizeComponent;
|
||||
|
||||
|
||||
CPArray _DOMDividerElements;
|
||||
CPString _dividerImagePath;
|
||||
int _drawingDivider;
|
||||
|
||||
|
||||
BOOL _needsResizeSubviews;
|
||||
|
||||
CPArray _buttonBars;
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"splitview";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[10.0, 1.0, [CPColor grayColor]]
|
||||
forKeys:[@"divider-thickness", @"pane-divider-thickness", @"pane-divider-color"]];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -75,18 +86,19 @@ var CPSplitViewHorizontalImage = nil,
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_currentDivider = CPNotFound;
|
||||
|
||||
|
||||
_DOMDividerElements = [];
|
||||
|
||||
_buttonBars = [];
|
||||
|
||||
[self _setVertical:YES];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (float)dividerThickness
|
||||
{
|
||||
return _isPaneSplitter ? 1.0 : 10.0;
|
||||
return [self currentValueForThemeAttribute:[self isPaneSplitter] ? @"pane-divider-thickness" : @"divider-thickness"];
|
||||
}
|
||||
|
||||
- (BOOL)isVertical
|
||||
@@ -98,13 +110,13 @@ var CPSplitViewHorizontalImage = nil,
|
||||
{
|
||||
if (![self _setVertical:shouldBeVertical])
|
||||
return;
|
||||
|
||||
|
||||
// Just re-adjust evenly.
|
||||
var frame = [self frame],
|
||||
dividerThickness = [self dividerThickness];
|
||||
|
||||
|
||||
[self _postNotificationWillResize];
|
||||
|
||||
|
||||
var eachSize = ROUND((frame.size[_sizeComponent] - dividerThickness * (_subviews.length - 1)) / _subviews.length),
|
||||
index = 0,
|
||||
count = _subviews.length;
|
||||
@@ -115,9 +127,9 @@ var CPSplitViewHorizontalImage = nil,
|
||||
else
|
||||
for (; index < count; ++index)
|
||||
[_subviews[index] setFrame:CGRectMake(0, ROUND((eachSize + dividerThickness) * index), frame.size.width, eachSize)];
|
||||
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
|
||||
[self _postNotificationDidResize];
|
||||
|
||||
}
|
||||
@@ -125,13 +137,13 @@ var CPSplitViewHorizontalImage = nil,
|
||||
- (BOOL)_setVertical:(BOOL)shouldBeVertical
|
||||
{
|
||||
var changed = (_isVertical != shouldBeVertical);
|
||||
|
||||
|
||||
_isVertical = shouldBeVertical;
|
||||
|
||||
|
||||
_originComponent = [self isVertical] ? "x" : "y";
|
||||
_sizeComponent = [self isVertical] ? "width" : "height";
|
||||
_dividerImagePath = [self isVertical] ? [CPSplitViewVerticalImage filename] : [CPSplitViewHorizontalImage filename];
|
||||
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -144,20 +156,21 @@ var CPSplitViewHorizontalImage = nil,
|
||||
{
|
||||
if (_isPaneSplitter == shouldBePaneSplitter)
|
||||
return;
|
||||
|
||||
|
||||
_isPaneSplitter = shouldBePaneSplitter;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMDividerElements = [];
|
||||
#endif
|
||||
if (_DOMDividerElements[_drawingDivider])
|
||||
[self _setupDOMDivider];
|
||||
|
||||
// The divider changes size when pane splitter mode is toggled, so the
|
||||
// subviews need to change size too.
|
||||
_needsResizeSubviews = YES;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)didAddSubview:(CPView)aSubview
|
||||
{
|
||||
_needsResizeSubviews = YES;
|
||||
// [self adjustSubviews];
|
||||
}
|
||||
|
||||
- (BOOL)isSubviewCollapsed:(CPView)subview
|
||||
@@ -169,9 +182,9 @@ var CPSplitViewHorizontalImage = nil,
|
||||
{
|
||||
var frame = [_subviews[aDivider] frame],
|
||||
rect = CGRectMakeZero();
|
||||
|
||||
|
||||
rect.size = [self frame].size;
|
||||
|
||||
|
||||
rect.size[_sizeComponent] = [self dividerThickness];
|
||||
rect.origin[_originComponent] = frame.origin[_originComponent] + frame.size[_sizeComponent];
|
||||
|
||||
@@ -180,20 +193,19 @@ var CPSplitViewHorizontalImage = nil,
|
||||
|
||||
- (CGRect)effectiveRectOfDividerAtIndex:(int)aDivider
|
||||
{
|
||||
var realRect = [self rectOfDividerAtIndex:aDivider];
|
||||
|
||||
var padding = 2;
|
||||
|
||||
var realRect = [self rectOfDividerAtIndex:aDivider],
|
||||
padding = 2;
|
||||
|
||||
realRect.size[_sizeComponent] += padding * 2;
|
||||
realRect.origin[_originComponent] -= padding;
|
||||
|
||||
|
||||
return realRect;
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
var count = [_subviews count] - 1;
|
||||
|
||||
|
||||
while ((count--) > 0)
|
||||
{
|
||||
_drawingDivider = count;
|
||||
@@ -207,29 +219,33 @@ var CPSplitViewHorizontalImage = nil,
|
||||
if (!_DOMDividerElements[_drawingDivider])
|
||||
{
|
||||
_DOMDividerElements[_drawingDivider] = document.createElement("div");
|
||||
_DOMDividerElements[_drawingDivider].style.cursor = "move";
|
||||
|
||||
_DOMDividerElements[_drawingDivider].style.position = "absolute";
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundRepeat = "repeat";
|
||||
|
||||
CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]);
|
||||
}
|
||||
|
||||
if (_isPaneSplitter)
|
||||
{
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundColor = "#A5A5A5";
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundImage = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundColor = "";
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')";
|
||||
}
|
||||
}
|
||||
|
||||
[self _setupDOMDivider];
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, _CGRectGetMinX(aRect), _CGRectGetMinY(aRect));
|
||||
CPDOMDisplayServerSetStyleSize(_DOMDividerElements[_drawingDivider], _CGRectGetWidth(aRect), _CGRectGetHeight(aRect));
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)_setupDOMDivider
|
||||
{
|
||||
if (_isPaneSplitter)
|
||||
{
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundColor = [[self currentValueForThemeAttribute:@"pane-divider-color"] cssString];
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundImage = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundColor = "";
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')";
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillDraw
|
||||
{
|
||||
[self _adjustSubviewsWithCalculatedSize];
|
||||
@@ -239,13 +255,13 @@ var CPSplitViewHorizontalImage = nil,
|
||||
{
|
||||
if (!_needsResizeSubviews)
|
||||
return;
|
||||
|
||||
|
||||
_needsResizeSubviews = NO;
|
||||
|
||||
|
||||
var subviews = [self subviews],
|
||||
count = subviews.length,
|
||||
oldSize = CGSizeMakeZero();
|
||||
|
||||
|
||||
if ([self isVertical])
|
||||
{
|
||||
oldSize.width += [self dividerThickness] * (count - 1);
|
||||
@@ -256,10 +272,10 @@ var CPSplitViewHorizontalImage = nil,
|
||||
oldSize.width = CGRectGetWidth([self frame]);
|
||||
oldSize.height += [self dividerThickness] * (count - 1);
|
||||
}
|
||||
|
||||
|
||||
while (count--)
|
||||
oldSize[_sizeComponent] += [subviews[count] frame].size[_sizeComponent];
|
||||
|
||||
|
||||
[self resizeSubviewsWithOldSize:oldSize];
|
||||
}
|
||||
|
||||
@@ -268,31 +284,41 @@ var CPSplitViewHorizontalImage = nil,
|
||||
var frame = [_subviews[anIndex] frame],
|
||||
startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent],
|
||||
effectiveRect = [self effectiveRectOfDividerAtIndex:anIndex],
|
||||
buttonBar = _buttonBars[anIndex],
|
||||
buttonBarRect = null,
|
||||
additionalRect = null;
|
||||
|
||||
|
||||
if (buttonBar != null)
|
||||
{
|
||||
buttonBarRect = [buttonBar resizeControlFrame];
|
||||
buttonBarRect.origin = [self convertPoint:buttonBarRect.origin fromView:buttonBar];
|
||||
}
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitView:effectiveRect:forDrawnRect:ofDividerAtIndex:)])
|
||||
effectiveRect = [_delegate splitView:self effectiveRect:effectiveRect forDrawnRect:effectiveRect ofDividerAtIndex:anIndex];
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitView:additionalEffectiveRectOfDividerAtIndex:)])
|
||||
additionalRect = [_delegate splitView:self additionalEffectiveRectOfDividerAtIndex:anIndex];
|
||||
|
||||
return CGRectContainsPoint(effectiveRect, aPoint) || (additionalRect && CGRectContainsPoint(additionalRect, aPoint));
|
||||
return CGRectContainsPoint(effectiveRect, aPoint) ||
|
||||
(additionalRect && CGRectContainsPoint(additionalRect, aPoint)) ||
|
||||
(buttonBarRect && CGRectContainsPoint(buttonBarRect, aPoint));
|
||||
}
|
||||
|
||||
- (CPView)hitTest:(CGPoint)aPoint
|
||||
{
|
||||
if ([self isHidden] || ![self hitTests] || !CGRectContainsPoint([self frame], aPoint))
|
||||
return nil;
|
||||
|
||||
var point = [self convertPoint:aPoint fromView:[self superview]];
|
||||
|
||||
var count = [_subviews count] - 1;
|
||||
|
||||
var point = [self convertPoint:aPoint fromView:[self superview]],
|
||||
count = [_subviews count] - 1;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if ([self cursorAtPoint:point hitDividerAtIndex:i])
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
return [super hitTest:aPoint];
|
||||
}
|
||||
|
||||
@@ -309,12 +335,13 @@ var CPSplitViewHorizontalImage = nil,
|
||||
if (_currentDivider != CPNotFound)
|
||||
{
|
||||
_currentDivider = CPNotFound;
|
||||
[self _updateResizeCursor:anEvent];
|
||||
[self _postNotificationDidResize];
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (type == CPLeftMouseDown)
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
@@ -331,21 +358,21 @@ var CPSplitViewHorizontalImage = nil,
|
||||
if ([anEvent clickCount] == 2 &&
|
||||
[_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
|
||||
[_delegate respondsToSelector:@selector(splitView:shouldCollapseSubview:forDoubleClickOnDividerAtIndex:)])
|
||||
{
|
||||
{
|
||||
var minPosition = [self minPossiblePositionOfDividerAtIndex:i],
|
||||
maxPosition = [self maxPossiblePositionOfDividerAtIndex:i];
|
||||
|
||||
if ([_delegate splitView:self canCollapseSubview:_subviews[i]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
|
||||
{
|
||||
if ([self isSubviewCollapsed:_subviews[i]])
|
||||
[self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||
[self setPosition:_preCollapsePosition ? _preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||
else
|
||||
[self setPosition:minPosition ofDividerAtIndex:i];
|
||||
}
|
||||
else if ([_delegate splitView:self canCollapseSubview:_subviews[i+1]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i+1] forDoubleClickOnDividerAtIndex:i])
|
||||
else if ([_delegate splitView:self canCollapseSubview:_subviews[i + 1]] && [_delegate splitView:self shouldCollapseSubview:_subviews[i + 1] forDoubleClickOnDividerAtIndex:i])
|
||||
{
|
||||
if ([self isSubviewCollapsed:_subviews[i+1]])
|
||||
[self setPosition:(minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||
if ([self isSubviewCollapsed:_subviews[i + 1]])
|
||||
[self setPosition:_preCollapsePosition ? _preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||
else
|
||||
[self setPosition:maxPosition ofDividerAtIndex:i];
|
||||
}
|
||||
@@ -363,14 +390,16 @@ var CPSplitViewHorizontalImage = nil,
|
||||
if (_currentDivider === CPNotFound)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
else if (type == CPLeftMouseDragged && _currentDivider != CPNotFound)
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
|
||||
[self setPosition:(point[_originComponent] + _initialOffset) ofDividerAtIndex:_currentDivider];
|
||||
// Cursor might change if we reach a resize limit.
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackDivider:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
}
|
||||
|
||||
@@ -380,13 +409,89 @@ var CPSplitViewHorizontalImage = nil,
|
||||
[self trackDivider:anEvent];
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
// Enable split view resize cursors. Commented out pending CPTrackingArea implementation.
|
||||
//[[self window] setAcceptsMouseMovedEvents:YES];
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(CPEvent)anEvent
|
||||
{
|
||||
// Tracking code handles cursor by itself.
|
||||
if (_currentDivider == CPNotFound)
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(CPEvent)anEvent
|
||||
{
|
||||
if (_currentDivider == CPNotFound)
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(CPEvent)anEvent
|
||||
{
|
||||
if (_currentDivider == CPNotFound)
|
||||
// FIXME: we should use CPCursor push/pop (if previous currentCursor != arrow).
|
||||
[[CPCursor arrowCursor] set];
|
||||
}
|
||||
|
||||
- (void)_updateResizeCursor:(CPEvent)anEvent
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
if ([anEvent type] === CPLeftMouseUp && ![[self window] acceptsMouseMovedEvents])
|
||||
{
|
||||
[[CPCursor arrowCursor] set];
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0, count = [_subviews count] - 1; i < count; i++)
|
||||
{
|
||||
// If we are currently tracking, keep the resize cursor active even outside of hit areas.
|
||||
if (_currentDivider === i || (_currentDivider == CPNotFound && [self cursorAtPoint:point hitDividerAtIndex:i]))
|
||||
{
|
||||
var frame = [_subviews[i] frame],
|
||||
size = frame.size[_sizeComponent],
|
||||
startPosition = frame.origin[_originComponent] + size,
|
||||
canShrink = [self _realPositionForPosition:startPosition - 1 ofDividerAtIndex:i] < startPosition,
|
||||
canGrow = [self _realPositionForPosition:startPosition + 1 ofDividerAtIndex:i] > startPosition,
|
||||
cursor = [CPCursor arrowCursor];
|
||||
|
||||
if (size === 0)
|
||||
canGrow = YES; // Subview is collapsed.
|
||||
else if (!canShrink &&
|
||||
[_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)] &&
|
||||
[_delegate splitView:self canCollapseSubview:_subviews[i]])
|
||||
canShrink = YES; // Subview is collapsible.
|
||||
|
||||
if (_isVertical && canShrink && canGrow)
|
||||
cursor = [CPCursor resizeLeftRightCursor];
|
||||
else if (_isVertical && canShrink)
|
||||
cursor = [CPCursor resizeLeftCursor];
|
||||
else if (_isVertical && canGrow)
|
||||
cursor = [CPCursor resizeRightCursor];
|
||||
else if (canShrink && canGrow)
|
||||
cursor = [CPCursor resizeUpDownCursor];
|
||||
else if (canShrink)
|
||||
cursor = [CPCursor resizeUpCursor];
|
||||
else if (canGrow)
|
||||
cursor = [CPCursor resizeDownCursor];
|
||||
|
||||
[cursor set];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[[CPCursor arrowCursor] set];
|
||||
}
|
||||
|
||||
- (float)maxPossiblePositionOfDividerAtIndex:(int)dividerIndex
|
||||
{
|
||||
var frame = [_subviews[dividerIndex + 1] frame];
|
||||
|
||||
|
||||
if (dividerIndex + 1 < [_subviews count] - 1)
|
||||
return frame.origin[_originComponent] + frame.size[_sizeComponent] - [self dividerThickness];
|
||||
else
|
||||
else
|
||||
return [self frame].size[_sizeComponent] - [self dividerThickness];
|
||||
}
|
||||
|
||||
@@ -395,17 +500,15 @@ var CPSplitViewHorizontalImage = nil,
|
||||
if (dividerIndex > 0)
|
||||
{
|
||||
var frame = [_subviews[dividerIndex - 1] frame];
|
||||
|
||||
|
||||
return frame.origin[_originComponent] + frame.size[_sizeComponent] + [self dividerThickness];
|
||||
}
|
||||
else
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)setPosition:(float)position ofDividerAtIndex:(int)dividerIndex
|
||||
- (int)_realPositionForPosition:(float)position ofDividerAtIndex:(int)dividerIndex
|
||||
{
|
||||
[self _adjustSubviewsWithCalculatedSize];
|
||||
|
||||
// not sure where this should override other positions?
|
||||
if ([_delegate respondsToSelector:@selector(splitView:constrainSplitPosition:ofSubviewAt:)])
|
||||
position = [_delegate splitView:self constrainSplitPosition:position ofSubviewAt:dividerIndex];
|
||||
@@ -414,47 +517,64 @@ var CPSplitViewHorizontalImage = nil,
|
||||
proposedMin = [self minPossiblePositionOfDividerAtIndex:dividerIndex],
|
||||
actualMax = proposedMax,
|
||||
actualMin = proposedMin;
|
||||
|
||||
if([_delegate respondsToSelector:@selector(splitView:constrainMinCoordinate:ofSubviewAt:)])
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitView:constrainMinCoordinate:ofSubviewAt:)])
|
||||
actualMin = [_delegate splitView:self constrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex];
|
||||
|
||||
if([_delegate respondsToSelector:@selector(splitView:constrainMaxCoordinate:ofSubviewAt:)])
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitView:constrainMaxCoordinate:ofSubviewAt:)])
|
||||
actualMax = [_delegate splitView:self constrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
|
||||
|
||||
var frame = [self frame],
|
||||
viewA = _subviews[dividerIndex],
|
||||
frameA = [viewA frame],
|
||||
viewB = _subviews[dividerIndex + 1],
|
||||
frameB = [viewB frame];
|
||||
var viewA = _subviews[dividerIndex],
|
||||
realPosition = MAX(MIN(position, actualMax), actualMin);
|
||||
|
||||
var realPosition = MAX(MIN(position, actualMax), actualMin);
|
||||
|
||||
if (position < proposedMin + (actualMin - proposedMin) / 2)
|
||||
if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
|
||||
if ([_delegate splitView:self canCollapseSubview:viewA])
|
||||
realPosition = proposedMin;
|
||||
|
||||
|
||||
return realPosition;
|
||||
}
|
||||
|
||||
- (void)setPosition:(float)position ofDividerAtIndex:(int)dividerIndex
|
||||
{
|
||||
[self _adjustSubviewsWithCalculatedSize];
|
||||
|
||||
var realPosition = [self _realPositionForPosition:position ofDividerAtIndex:dividerIndex];
|
||||
|
||||
var viewA = _subviews[dividerIndex],
|
||||
frameA = [viewA frame],
|
||||
viewB = _subviews[dividerIndex + 1],
|
||||
frameB = [viewB frame];
|
||||
|
||||
_preCollapsePosition = 0;
|
||||
|
||||
var preSize = frameA.size[_sizeComponent];
|
||||
frameA.size[_sizeComponent] = realPosition - frameA.origin[_originComponent];
|
||||
if (preSize !== 0 && frameA.size[_sizeComponent] === 0)
|
||||
_preCollapsePosition = preSize;
|
||||
[_subviews[dividerIndex] setFrame:frameA];
|
||||
|
||||
|
||||
preSize = frameB.size[_sizeComponent];
|
||||
frameB.size[_sizeComponent] = frameB.origin[_originComponent] + frameB.size[_sizeComponent] - realPosition - [self dividerThickness];
|
||||
if (preSize !== 0 && frameB.size[_sizeComponent] === 0)
|
||||
_preCollapsePosition = preSize;
|
||||
frameB.origin[_originComponent] = realPosition + [self dividerThickness];
|
||||
[_subviews[dividerIndex + 1] setFrame:frameB];
|
||||
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
[self _adjustSubviewsWithCalculatedSize];
|
||||
|
||||
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)resizeSubviewsWithOldSize:(CPSize)oldSize
|
||||
{
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(splitView:resizeSubviewsWithOldSize:)])
|
||||
{
|
||||
[_delegate splitView:self resizeSubviewsWithOldSize:oldSize];
|
||||
@@ -462,7 +582,7 @@ var CPSplitViewHorizontalImage = nil,
|
||||
}
|
||||
|
||||
[self _postNotificationWillResize];
|
||||
|
||||
|
||||
var index = 0,
|
||||
count = [_subviews count],
|
||||
bounds = [self bounds],
|
||||
@@ -492,8 +612,8 @@ var CPSplitViewHorizontalImage = nil,
|
||||
|
||||
var nonSizableSpace = totalSizableSpace ? bounds.size[_sizeComponent] - totalSizableSpace : 0,
|
||||
remainingFlexibleSpace = bounds.size[_sizeComponent] - oldSize[_sizeComponent],
|
||||
oldDimension = (oldSize[_sizeComponent]- totalDividers*dividerThickness - nonSizableSpace),
|
||||
ratio = oldDimension <= 0 ? 0 : (bounds.size[_sizeComponent] - totalDividers*dividerThickness - nonSizableSpace) / oldDimension;
|
||||
oldDimension = (oldSize[_sizeComponent] - totalDividers * dividerThickness - nonSizableSpace),
|
||||
ratio = oldDimension <= 0 ? 0 : (bounds.size[_sizeComponent] - totalDividers * dividerThickness - nonSizableSpace) / oldDimension;
|
||||
|
||||
for (index = 0; index < count; ++index)
|
||||
{
|
||||
@@ -501,21 +621,22 @@ var CPSplitViewHorizontalImage = nil,
|
||||
viewFrame = CGRectMakeCopy(bounds),
|
||||
isSizable = isVertical ? [view autoresizingMask] & CPViewWidthSizable : [view autoresizingMask] & CPViewHeightSizable;
|
||||
|
||||
if (index + 1 == count)
|
||||
viewFrame.size[_sizeComponent] = bounds.size[_sizeComponent] - viewFrame.origin[_originComponent];
|
||||
else if (totalSizableSpace && isSizable && lastSizableIndex === index)
|
||||
viewFrame.size[_sizeComponent] = MAX(0, ROUND([view frame].size[_sizeComponent] + remainingFlexibleSpace))
|
||||
else if (isSizable || !totalSizableSpace)
|
||||
{
|
||||
viewFrame.size[_sizeComponent] = MAX(0, ROUND(ratio * [view frame].size[_sizeComponent]));
|
||||
remainingFlexibleSpace -= (viewFrame.size[_sizeComponent] - [view frame].size[_sizeComponent]);
|
||||
}
|
||||
else if (totalSizableSpace && !isSizable)
|
||||
viewFrame.size[_sizeComponent] = [view frame].size[_sizeComponent];
|
||||
else
|
||||
alert("SHOULD NEVER GET HERE");
|
||||
if (index + 1 === count)
|
||||
viewFrame.size[_sizeComponent] = bounds.size[_sizeComponent] - viewFrame.origin[_originComponent];
|
||||
|
||||
bounds.origin[_originComponent] += viewFrame.size[_sizeComponent] + dividerThickness;
|
||||
else if (totalSizableSpace && isSizable && lastSizableIndex === index)
|
||||
viewFrame.size[_sizeComponent] = MAX(0, ROUND([view frame].size[_sizeComponent] + remainingFlexibleSpace))
|
||||
|
||||
else if (isSizable || !totalSizableSpace)
|
||||
{
|
||||
viewFrame.size[_sizeComponent] = MAX(0, ROUND(ratio * [view frame].size[_sizeComponent]));
|
||||
remainingFlexibleSpace -= (viewFrame.size[_sizeComponent] - [view frame].size[_sizeComponent]);
|
||||
}
|
||||
|
||||
else if (totalSizableSpace && !isSizable)
|
||||
viewFrame.size[_sizeComponent] = [view frame].size[_sizeComponent];
|
||||
|
||||
bounds.origin[_originComponent] += viewFrame.size[_sizeComponent] + dividerThickness;
|
||||
|
||||
[view setFrame:viewFrame];
|
||||
}
|
||||
@@ -529,7 +650,7 @@ var CPSplitViewHorizontalImage = nil,
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewDidResizeSubviewsNotification object:self];
|
||||
if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewWillResizeSubviewsNotification object:self];
|
||||
|
||||
|
||||
_delegate = delegate;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
|
||||
@@ -544,6 +665,47 @@ var CPSplitViewHorizontalImage = nil,
|
||||
object:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the button bar who's resize control should act as a control for this splitview.
|
||||
Each divider can have at most one button bar assigned to it, and that button bar must be
|
||||
a subview of one of the split view's subviews.
|
||||
|
||||
Calling this method with nil as the button bar will remove any currently assigned button bar
|
||||
for the divider at that index. Indexes will not be adjusted as new subviews are added, so you
|
||||
should usually call this method after adding all the desired subviews to the split view.
|
||||
|
||||
This method will automatically configure the hasResizeControl and resizeControlIsLeftAligned
|
||||
parameters of the button bar, and will override any currently set values.
|
||||
*/
|
||||
- (void)setButtonBar:(CPButtonBar)aButtonBar forDividerAtIndex:(unsigned)dividerIndex
|
||||
{
|
||||
if (!aButtonBar)
|
||||
{
|
||||
_buttonBars[dividerIndex] = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
var view = [aButtonBar superview],
|
||||
subview = aButtonBar;
|
||||
|
||||
while (view && view !== self)
|
||||
{
|
||||
subview = view;
|
||||
view = [view superview];
|
||||
}
|
||||
|
||||
if (view !== self)
|
||||
[CPException raise:CPInvalidArgumentException
|
||||
reason:@"CPSplitView button bar must be a subview of the split view."];
|
||||
|
||||
var viewIndex = [[self subviews] indexOfObject:subview];
|
||||
|
||||
[aButtonBar setHasResizeControl:YES];
|
||||
[aButtonBar setResizeControlIsLeftAligned:dividerIndex < viewIndex];
|
||||
|
||||
_buttonBars[dividerIndex] = aButtonBar;
|
||||
}
|
||||
|
||||
- (void)_postNotificationWillResize
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewWillResizeSubviewsNotification object:self];
|
||||
@@ -558,7 +720,8 @@ var CPSplitViewHorizontalImage = nil,
|
||||
|
||||
var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
||||
CPSplitViewIsVerticalKey = "CPSplitViewIsVerticalKey",
|
||||
CPSplitViewIsPaneSplitterKey = "CPSplitViewIsPaneSplitterKey";
|
||||
CPSplitViewIsPaneSplitterKey = "CPSplitViewIsPaneSplitterKey",
|
||||
CPSplitViewButtonBarsKey = "CPSplitViewButtonBarsKey";
|
||||
|
||||
@implementation CPSplitView (CPCoding)
|
||||
|
||||
@@ -569,19 +732,21 @@ var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_currentDivider = CPNotFound;
|
||||
|
||||
|
||||
_DOMDividerElements = [];
|
||||
|
||||
_delegate = [aCoder decodeObjectForKey:CPSplitViewDelegateKey];;
|
||||
|
||||
|
||||
_buttonBars = [aCoder decodeObjectForKey:CPSplitViewButtonBarsKey] || [];
|
||||
|
||||
_delegate = [aCoder decodeObjectForKey:CPSplitViewDelegateKey];
|
||||
|
||||
_isPaneSplitter = [aCoder decodeBoolForKey:CPSplitViewIsPaneSplitterKey];
|
||||
[self _setVertical:[aCoder decodeBoolForKey:CPSplitViewIsVerticalKey]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -592,9 +757,12 @@ var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
|
||||
//FIXME how should we handle this?
|
||||
//[aCoder encodeObject:_buttonBars forKey:CPSplitViewButtonBarsKey];
|
||||
|
||||
[aCoder encodeConditionalObject:_delegate forKey:CPSplitViewDelegateKey];
|
||||
|
||||
|
||||
[aCoder encodeBool:_isVertical forKey:CPSplitViewIsVerticalKey];
|
||||
[aCoder encodeBool:_isPaneSplitter forKey:CPSplitViewIsPaneSplitterKey];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* CPStepper.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Antoine Mercadal
|
||||
* Copyright 2009, Antoine Mercadal
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <AppKit/CPControl.j>
|
||||
@import <AppKit/CPButton.j>
|
||||
@import <AppKit/CPTextField.j>
|
||||
|
||||
|
||||
var CPStepperButtonsSize = CPSizeMake(19, 13);
|
||||
|
||||
/*! CPStepper is an implementation of Cocoa NSStepper.
|
||||
|
||||
This control display a two part button that can be used to increase or decrease a value with a given interval.
|
||||
*/
|
||||
@implementation CPStepper: CPControl
|
||||
{
|
||||
BOOL _valueWraps @accessors(property=valueWraps);
|
||||
int _increment @accessors(property=increment);
|
||||
int _maxValue @accessors(property=maxValue);
|
||||
int _minValue @accessors(property=minValue);
|
||||
|
||||
_CPContinuousButton _buttonDown;
|
||||
_CPContinuousButton _buttonUp;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
/*! Initializes a CPStepper with given values
|
||||
@param aValue the initial value of the CPStepper
|
||||
@param minValue the minimal acceptable value of the stepper
|
||||
@param maxValue the maximal acceptable value of the stepper
|
||||
@return Initialized CPStepper
|
||||
*/
|
||||
+ (CPStepper)stepperWithInitialValue:(float)aValue minValue:(float)aMinValue maxValue:(float)aMaxValue
|
||||
{
|
||||
var stepper = [[CPStepper alloc] initWithFrame:CPRectMake(0, 0, 19, 25)];
|
||||
[stepper setDoubleValue:aValue];
|
||||
[stepper setMinValue:aMinValue];
|
||||
[stepper setMaxValue:aMaxValue];
|
||||
|
||||
return stepper;
|
||||
}
|
||||
|
||||
/*! Initializes a CPStepper with default values:
|
||||
- minValue = 0.0
|
||||
- maxValue = 59.0
|
||||
- value = 0.0
|
||||
@return Initialized CPStepper
|
||||
*/
|
||||
+ (CPStepper)stepper
|
||||
{
|
||||
return [CPStepper stepperWithInitialValue:0.0 minValue:0.0 maxValue:59.0];
|
||||
}
|
||||
|
||||
/*! Initializes the CPStepper
|
||||
@param aFrame the frame of the control
|
||||
@return initialized CPStepper
|
||||
*/
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_maxValue = 59.0;
|
||||
_minValue = 0.0;
|
||||
_increment = 1.0;
|
||||
_valueWraps = YES;
|
||||
|
||||
[self setDoubleValue:0.0];
|
||||
|
||||
_buttonUp = [[_CPContinuousButton alloc] initWithFrame:CPRectMake(aFrame.size.width - CPStepperButtonsSize.width, 0, CPStepperButtonsSize.width, CPStepperButtonsSize.height)];
|
||||
[_buttonUp setContinuous:YES];
|
||||
[_buttonUp setTarget:self];
|
||||
[_buttonUp setAction:@selector(_buttonDidClick:)];
|
||||
[_buttonUp setAutoresizingMask:CPViewNotSizable];
|
||||
[self addSubview:_buttonUp];
|
||||
|
||||
_buttonDown = [[_CPContinuousButton alloc] initWithFrame:CPRectMake(aFrame.size.width - CPStepperButtonsSize.width, CPStepperButtonsSize.height, CPStepperButtonsSize.width, CPStepperButtonsSize.height - 1)];
|
||||
[_buttonDown setContinuous:YES];
|
||||
[_buttonDown setTarget:self];
|
||||
[_buttonDown setAction:@selector(_buttonDidClick:)];
|
||||
[_buttonDown setAutoresizingMask:CPViewNotSizable];
|
||||
[self addSubview:_buttonDown];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Superclass overrides
|
||||
|
||||
/*! set if the CPStepper is enabled or not
|
||||
@param shouldEnabled BOOL that define if stepper is enabled or not.
|
||||
*/
|
||||
- (void)setEnabled:(BOOL)shouldEnabled
|
||||
{
|
||||
[super setEnabled:shouldEnabled];
|
||||
|
||||
[_buttonUp setEnabled:shouldEnabled];
|
||||
[_buttonDown setEnabled:shouldEnabled];
|
||||
}
|
||||
|
||||
/*! set the frame of the CPStepper and check if width is not smaller than theme min-size
|
||||
@param aFrame the frame
|
||||
*/
|
||||
- (void)setFrame:(CGRect)aFrame
|
||||
{
|
||||
if (aFrame.size.width >= CGRectGetWidth(aFrame))
|
||||
[super setFrame:aFrame];
|
||||
}
|
||||
|
||||
/*! @ignore
|
||||
*/
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered | CPThemeStateDisabled] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:CPThemeStateBordered | CPThemeStateHighlighted] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered | CPThemeStateDisabled] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateDisabled];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:CPThemeStateBordered | CPThemeStateHighlighted] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateHighlighted];
|
||||
}
|
||||
|
||||
/*! set if CPStepper should autorepeat
|
||||
@param shouldAutoRepeat if YES, the first mouse down does one increment (decrement) and, after each delay of 0.5 seconds
|
||||
*/
|
||||
- (void)setAutorepeat:(BOOL)shouldAutoRepeat
|
||||
{
|
||||
[_buttonUp setContinuous:shouldAutoRepeat];
|
||||
[_buttonDown setContinuous:shouldAutoRepeat];
|
||||
}
|
||||
|
||||
/*! set the current value of the stepper
|
||||
@param aValue a float contaning the value
|
||||
*/
|
||||
- (void)setDoubleValue:(float)aValue
|
||||
{
|
||||
if (aValue > _maxValue)
|
||||
[super setDoubleValue:_valueWraps ? _minValue : _maxValue];
|
||||
else if (aValue < _minValue)
|
||||
[super setDoubleValue:_valueWraps ? _maxValue : _minValue];
|
||||
else
|
||||
[super setDoubleValue:aValue];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Actions
|
||||
|
||||
/*! @ignore
|
||||
*/
|
||||
- (IBAction)_buttonDidClick:(id)aSender
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
if (aSender == _buttonUp)
|
||||
[self setDoubleValue:([self doubleValue] + _increment)];
|
||||
else
|
||||
[self setDoubleValue:([self doubleValue] - _increment)];
|
||||
|
||||
if (_target && _action && [_target respondsToSelector:_action])
|
||||
[self sendAction:_action to:_target];
|
||||
}
|
||||
|
||||
/*! @perform a programatic click on up button
|
||||
@param aSender sender of the action
|
||||
*/
|
||||
- (IBAction)performClickUp:(id)aSender
|
||||
{
|
||||
[_buttonUp performClick:aSender];
|
||||
}
|
||||
|
||||
/*! @perform a programatic click on down button
|
||||
@param aSender sender of the action
|
||||
*/
|
||||
- (IBAction)performClickDown:(id)aSender
|
||||
{
|
||||
[_buttonDown performClick:aSender];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Theming
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"stepper";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPNull null]]
|
||||
forKeys:[@"bezel-color-up-button", @"bezel-color-down-button"]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPStepper (CPCodingCompliance)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
_maxValue = [aCoder decodeObjectForKey:@"_maxValue"];
|
||||
_minValue = [aCoder decodeObjectForKey:@"_minValue"];
|
||||
_increment = [aCoder decodeObjectForKey:@"_increment"];
|
||||
_buttonUp = [aCoder decodeObjectForKey:@"_buttonUp"];
|
||||
_buttonDown = [aCoder decodeObjectForKey:@"_buttonDown"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeObject:_maxValue forKey:@"_maxValue"];
|
||||
[aCoder encodeObject:_minValue forKey:@"_minValue"];
|
||||
[aCoder encodeObject:_increment forKey:@"_increment"];
|
||||
[aCoder encodeObject:_buttonUp forKey:@"_buttonUp"];
|
||||
[aCoder encodeObject:_buttonDown forKey:@"_buttonDown"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/*! This is a subclass of CPButton that allows to send continuous action.
|
||||
This may should be include in CPButton..
|
||||
*/
|
||||
@implementation _CPContinuousButton : CPButton
|
||||
{
|
||||
CPTimer _continuousDelayTimer;
|
||||
CPTimer _continuousTimer;
|
||||
float _periodicDelay;
|
||||
float _periodicInterval;
|
||||
}
|
||||
|
||||
- (void)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_periodicInterval = 0.05;
|
||||
_periodicDelay = 0.5;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setPeriodicDelay:(float)aDelay interval:(float)anInterval
|
||||
{
|
||||
_periodicDelay = aDelay;
|
||||
_periodicInterval = anInterval;
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if ([self isContinuous])
|
||||
{
|
||||
_continuousDelayTimer = [CPTimer scheduledTimerWithTimeInterval:_periodicDelay callback: function()
|
||||
{
|
||||
if (!_continuousTimer)
|
||||
_continuousTimer = [CPTimer scheduledTimerWithTimeInterval:_periodicInterval target:self selector:@selector(onContinousEvent:) userInfo:anEvent repeats:YES];
|
||||
}
|
||||
|
||||
repeats:NO];
|
||||
}
|
||||
|
||||
[super mouseDown:anEvent];
|
||||
}
|
||||
|
||||
- (void)onContinousEvent:(CPTimer)aTimer
|
||||
{
|
||||
if (_target && _action && [_target respondsToSelector:_action])
|
||||
[_target performSelector:_action withObject:self];
|
||||
}
|
||||
|
||||
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
||||
{
|
||||
[self invalidateTimers];
|
||||
[super stopTracking:lastPoint at:aPoint mouseIsUp:mouseIsUp];
|
||||
}
|
||||
|
||||
- (void)invalidateTimers
|
||||
{
|
||||
if (_continuousTimer)
|
||||
{
|
||||
[_continuousTimer invalidate];
|
||||
_continuousTimer = nil;
|
||||
}
|
||||
|
||||
if (_continuousDelayTimer)
|
||||
{
|
||||
[_continuousDelayTimer invalidate];
|
||||
_continuousDelayTimer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation _CPContinuousButton (CPCodingCompliance)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
_periodicDelay = [aCoder decodeObjectForKey:@"_periodicDelay"];
|
||||
_periodicInterval = [aCoder decodeObjectForKey:@"_periodicInterval"];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[self invalidateTimers];
|
||||
[aCoder encodeObject:_periodicDelay forKey:@"_periodicDelay"];
|
||||
[aCoder encodeObject:_periodicInterval forKey:@"_periodicInterval"];
|
||||
}
|
||||
@end
|
||||
@@ -25,8 +25,20 @@
|
||||
@import "CPPlatformString.j"
|
||||
|
||||
|
||||
var CPStringSizeWithFontInWidthCache = {};
|
||||
|
||||
CPStringSizeCachingEnabled = YES;
|
||||
|
||||
@implementation CPString (CPStringDrawing)
|
||||
|
||||
/*!
|
||||
Returns a dictionary with the items "ascender", "descender", "lineHeight"
|
||||
*/
|
||||
+ (CPDictionary)metricsOfFont:(CPFont)aFont
|
||||
{
|
||||
return [CPPlatformString metricsOfFont:aFont];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the string
|
||||
*/
|
||||
@@ -42,7 +54,19 @@
|
||||
|
||||
- (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
|
||||
{
|
||||
return [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
|
||||
if (!CPStringSizeCachingEnabled)
|
||||
return [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
|
||||
|
||||
var cacheKey = self + [aFont cssString] + aWidth,
|
||||
size = CPStringSizeWithFontInWidthCache[cacheKey];
|
||||
|
||||
if (size === undefined)
|
||||
{
|
||||
size = [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
|
||||
CPStringSizeWithFontInWidthCache[cacheKey] = size;
|
||||
}
|
||||
|
||||
return CGSizeMakeCopy(size);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+214
-547
@@ -1,225 +1,66 @@
|
||||
/*
|
||||
* CPTabView.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2008, 280 North, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPImageView.j"
|
||||
@import "CPBox.j"
|
||||
@import "CPSegmentedControl.j"
|
||||
@import "CPTabViewItem.j"
|
||||
@import "CPView.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
/*
|
||||
Places tabs on top with a bezeled border.
|
||||
@global
|
||||
@group CPTabViewType
|
||||
*/
|
||||
CPTopTabsBezelBorder = 0;
|
||||
//CPLeftTabsBezelBorder = 1;
|
||||
//CPBottomTabsBezelBorder = 2;
|
||||
//CPRightTabsBezelBorder = 3;
|
||||
/*
|
||||
Displays no tabs and has a bezeled border.
|
||||
@global
|
||||
@group CPTabViewType
|
||||
*/
|
||||
CPNoTabsBezelBorder = 4;
|
||||
/*
|
||||
Has no tabs and displays a line border.
|
||||
@global
|
||||
@group CPTabViewType
|
||||
*/
|
||||
CPNoTabsLineBorder = 5;
|
||||
/*
|
||||
Displays no tabs and no border.
|
||||
@global
|
||||
@group CPTabViewType
|
||||
*/
|
||||
CPNoTabsNoBorder = 6;
|
||||
//CPLeftTabsBezelBorder = 1;
|
||||
CPBottomTabsBezelBorder = 2;
|
||||
//CPRightTabsBezelBorder = 3;
|
||||
CPNoTabsBezelBorder = 4; //Displays no tabs and has a bezeled border.
|
||||
CPNoTabsLineBorder = 5; //Has no tabs and displays a line border.
|
||||
CPNoTabsNoBorder = 6; //Displays no tabs and no border.
|
||||
|
||||
var CPTabViewBezelBorderLeftImage = nil,
|
||||
CPTabViewBackgroundCenterImage = nil,
|
||||
CPTabViewBezelBorderRightImage = nil,
|
||||
CPTabViewBezelBorderColor = nil,
|
||||
CPTabViewBezelBorderBackgroundColor = nil;
|
||||
|
||||
var LEFT_INSET = 7.0,
|
||||
RIGHT_INSET = 7.0;
|
||||
|
||||
var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
CPTabViewShouldSelectTabViewItemSelector = 2,
|
||||
CPTabViewWillSelectTabViewItemSelector = 4,
|
||||
CPTabViewDidChangeNumberOfTabViewItemsSelector = 8;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPTabView
|
||||
|
||||
This class represents a view that has multiple subviews (CPTabViewItem) presented as individual tabs.
|
||||
Only one CPTabViewItem is shown at a time, and other CPTabViewItems can be made visible
|
||||
(one at a time) by clicking on the CPTabViewItem's tab at the top of the tab view.
|
||||
|
||||
THe currently selected CPTabViewItem is the view that is displayed.
|
||||
*/
|
||||
@implementation CPTabView : CPView
|
||||
{
|
||||
CPView _labelsView;
|
||||
CPView _backgroundView;
|
||||
CPView _separatorView;
|
||||
|
||||
CPView _auxiliaryView;
|
||||
CPView _contentView;
|
||||
|
||||
CPArray _tabViewItems;
|
||||
CPTabViewItem _selectedTabViewItem;
|
||||
CPArray _items;
|
||||
|
||||
CPTabViewType _tabViewType;
|
||||
|
||||
id _delegate;
|
||||
unsigned _delegateSelectors;
|
||||
}
|
||||
CPSegmentedControl _tabs;
|
||||
CPBox _box;
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != CPTabView)
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self],
|
||||
|
||||
emptyImage = [[CPImage alloc] initByReferencingFile:@"" size:CGSizeMake(7.0, 0.0)],
|
||||
backgroundImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBackgroundCenter.png"] size:CGSizeMake(1.0, 1.0)],
|
||||
|
||||
bezelBorderLeftImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorderLeft.png"] size:CGSizeMake(7.0, 1.0)],
|
||||
bezerBorderImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorder.png"] size:CGSizeMake(1.0, 1.0)],
|
||||
bezelBorderRightImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/CPTabViewBezelBorderRight.png"] size:CGSizeMake(7.0, 1.0)];
|
||||
|
||||
CPTabViewBezelBorderBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
emptyImage,
|
||||
emptyImage,
|
||||
emptyImage,
|
||||
CPNumber _selectedIndex;
|
||||
|
||||
bezelBorderLeftImage,
|
||||
backgroundImage,
|
||||
bezelBorderRightImage,
|
||||
CPTabViewType _type;
|
||||
|
||||
bezelBorderLeftImage,
|
||||
bezerBorderImage,
|
||||
bezelBorderRightImage
|
||||
]]];
|
||||
|
||||
CPTabViewBezelBorderColor = [CPColor colorWithPatternImage:bezerBorderImage];
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
+ (CPColor)bezelBorderColor
|
||||
{
|
||||
return CPTabViewBezelBorderColor;
|
||||
id _delegate;
|
||||
unsigned _delegateSelectors;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_tabViewType = CPTopTabsBezelBorder;
|
||||
_tabViewItems = [];
|
||||
_items = [CPArray array];
|
||||
_selectedIndex = CPNotFound;
|
||||
|
||||
[self _init];
|
||||
[self setTabViewType:CPTopTabsBezelBorder];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
- (void)_init
|
||||
{
|
||||
if (_tabViewType != CPTopTabsBezelBorder || _labelsView)
|
||||
return;
|
||||
|
||||
[self _createBezelBorder];
|
||||
[self layoutSubviews];
|
||||
}
|
||||
_tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
||||
[_tabs setHitTests:NO];
|
||||
|
||||
/* @ignore */
|
||||
- (void)_createBezelBorder
|
||||
{
|
||||
var bounds = [self bounds];
|
||||
|
||||
_labelsView = [[_CPTabLabelsView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), 0.0)];
|
||||
var height = [_tabs valueForThemeAttribute:@"default-height"];
|
||||
[_tabs setFrameSize:CGSizeMake(0, height)];
|
||||
|
||||
[_labelsView setTabView:self];
|
||||
[_labelsView setAutoresizingMask:CPViewWidthSizable];
|
||||
_box = [[CPBox alloc] initWithFrame:[self bounds]];
|
||||
[self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]];
|
||||
|
||||
[self addSubview:_labelsView];
|
||||
[_box setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
[_tabs setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
|
||||
|
||||
_backgroundView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_backgroundView setBackgroundColor:CPTabViewBezelBorderBackgroundColor];
|
||||
|
||||
[_backgroundView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
[self addSubview:_backgroundView];
|
||||
|
||||
_separatorView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_separatorView setBackgroundColor:[[self class] bezelBorderColor]];
|
||||
[_separatorView setAutoresizingMask:CPViewWidthSizable | CPViewMaxYMargin];
|
||||
|
||||
[self addSubview:_separatorView];
|
||||
}
|
||||
|
||||
/*
|
||||
Lays out the subviews
|
||||
@ignore
|
||||
*/
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
if (_tabViewType == CPTopTabsBezelBorder)
|
||||
{
|
||||
var backgroundRect = [self bounds],
|
||||
labelsViewHeight = [_CPTabLabelsView height];
|
||||
|
||||
backgroundRect.origin.y += labelsViewHeight;
|
||||
backgroundRect.size.height -= labelsViewHeight;
|
||||
|
||||
[_backgroundView setFrame:backgroundRect];
|
||||
|
||||
var auxiliaryViewHeight = 5.0;
|
||||
|
||||
if (_auxiliaryView)
|
||||
{
|
||||
auxiliaryViewHeight = CGRectGetHeight([_auxiliaryView frame]);
|
||||
|
||||
[_auxiliaryView setFrame:CGRectMake(LEFT_INSET, labelsViewHeight, CGRectGetWidth(backgroundRect) - LEFT_INSET - RIGHT_INSET, auxiliaryViewHeight)];
|
||||
}
|
||||
|
||||
[_separatorView setFrame:CGRectMake(LEFT_INSET, labelsViewHeight + auxiliaryViewHeight, CGRectGetWidth(backgroundRect) - LEFT_INSET - RIGHT_INSET, 1.0)];
|
||||
}
|
||||
|
||||
// CPNoTabsNoBorder
|
||||
[_contentView setFrame:[self contentRect]];
|
||||
[self addSubview:_box];
|
||||
[self addSubview:_tabs];
|
||||
}
|
||||
|
||||
// Adding and Removing Tabs
|
||||
@@ -229,7 +70,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (void)addTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
[self insertTabViewItem:aTabViewItem atIndex:[_tabViewItems count]];
|
||||
[self insertTabViewItem:aTabViewItem atIndex:[_items count]];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -240,15 +81,12 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(unsigned)anIndex
|
||||
{
|
||||
if (!_labelsView && _tabViewType == CPTopTabsBezelBorder)
|
||||
[self _createBezelBorder];
|
||||
[_items insertObject:aTabViewItem atIndex:anIndex];
|
||||
|
||||
[self _updateItems];
|
||||
[self _repositionTabs];
|
||||
|
||||
[_tabViewItems insertObject:aTabViewItem atIndex:anIndex];
|
||||
|
||||
[_labelsView tabView:self didAddTabViewItem:aTabViewItem];
|
||||
|
||||
if ([_tabViewItems count] == 1)
|
||||
[self selectFirstTabViewItem:self];
|
||||
[aTabViewItem _setTabView:self];
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||
@@ -260,12 +98,21 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
var index = [self indexOfTabViewItem:aTabViewItem];
|
||||
var count = [_items count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if ([_items objectAtIndex:i] === aTabViewItem)
|
||||
{
|
||||
[_items removeObjectAtIndex:i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[_tabViewItems removeObjectIdenticalTo:aTabViewItem];
|
||||
|
||||
[_labelsView tabView:self didRemoveTabViewItemAtIndex:index];
|
||||
[self _updateItems];
|
||||
[self _repositionTabs];
|
||||
|
||||
[aTabViewItem _setTabView:nil];
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||
}
|
||||
@@ -277,7 +124,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
return [_tabViewItems indexOfObjectIdenticalTo:aTabViewItem];
|
||||
return [_items indexOfObjectIdenticalTo:aTabViewItem];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -286,14 +133,11 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
var index = 0,
|
||||
count = [_tabViewItems count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
if ([[_tabViewItems[index] identifier] isEqual:anIdentifier])
|
||||
for (var index = [_items count]; index >= 0; index--)
|
||||
if ([[_items[index] identifier] isEqual:anIdentifier])
|
||||
return index;
|
||||
|
||||
return index;
|
||||
return CPNotFound;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -301,7 +145,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (unsigned)numberOfTabViewItems
|
||||
{
|
||||
return [_tabViewItems count];
|
||||
return [_items count];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -309,7 +153,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (CPTabViewItem)tabViewItemAtIndex:(unsigned)anIndex
|
||||
{
|
||||
return _tabViewItems[anIndex];
|
||||
return [_items objectAtIndex:anIndex];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -317,7 +161,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (CPArray)tabViewItems
|
||||
{
|
||||
return _tabViewItems;
|
||||
return [_items copy]; // Copy?
|
||||
}
|
||||
|
||||
// Selecting a Tab
|
||||
@@ -327,10 +171,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (void)selectFirstTabViewItem:(id)aSender
|
||||
{
|
||||
var count = [_tabViewItems count];
|
||||
|
||||
if (count)
|
||||
[self selectTabViewItemAtIndex:0];
|
||||
if ([_items count] === 0)
|
||||
return; // throw?
|
||||
|
||||
[self selectTabViewItemAtIndex:0];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -339,10 +183,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (void)selectLastTabViewItem:(id)aSender
|
||||
{
|
||||
var count = [_tabViewItems count];
|
||||
|
||||
if (count)
|
||||
[self selectTabViewItemAtIndex:count - 1];
|
||||
if ([_items count] === 0)
|
||||
return; // throw?
|
||||
|
||||
[self selectTabViewItemAtIndex:[_items count] - 1];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -351,13 +195,16 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (void)selectNextTabViewItem:(id)aSender
|
||||
{
|
||||
if (!_selectedTabViewItem)
|
||||
if (_selectedIndex === CPNotFound)
|
||||
return;
|
||||
|
||||
var index = [self indexOfTabViewItem:_selectedTabViewItem],
|
||||
count = [_tabViewItems count];
|
||||
|
||||
[self selectTabViewItemAtIndex:index + 1 % count];
|
||||
|
||||
var nextIndex = _selectedIndex + 1;
|
||||
|
||||
if (nextIndex === [_items count])
|
||||
// does nothing. According to spec at (http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSTabView_Class/Reference/Reference.html#//apple_ref/occ/instm/NSTabView/selectNextTabViewItem:)
|
||||
return;
|
||||
|
||||
[self selectTabViewItemAtIndex:nextIndex];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -366,13 +213,15 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (void)selectPreviousTabViewItem:(id)aSender
|
||||
{
|
||||
if (!_selectedTabViewItem)
|
||||
if (_selectedIndex === CPNotFound)
|
||||
return;
|
||||
|
||||
var index = [self indexOfTabViewItem:_selectedTabViewItem],
|
||||
count = [_tabViewItems count];
|
||||
|
||||
[self selectTabViewItemAtIndex:index == 0 ? count : index - 1];
|
||||
|
||||
var previousIndex = _selectedIndex - 1;
|
||||
|
||||
if (previousIndex < 0)
|
||||
return; // does nothing. See above.
|
||||
|
||||
[self selectTabViewItemAtIndex:previousIndex];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -381,41 +230,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (void)selectTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
|
||||
return;
|
||||
|
||||
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
|
||||
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
|
||||
|
||||
if (_selectedTabViewItem)
|
||||
{
|
||||
_selectedTabViewItem._tabState = CPBackgroundTab;
|
||||
[_labelsView tabView:self didChangeStateOfTabViewItem:_selectedTabViewItem];
|
||||
|
||||
[_contentView removeFromSuperview];
|
||||
[_auxiliaryView removeFromSuperview];
|
||||
}
|
||||
_selectedTabViewItem = aTabViewItem;
|
||||
|
||||
_selectedTabViewItem._tabState = CPSelectedTab;
|
||||
|
||||
_contentView = [_selectedTabViewItem view];
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
_auxiliaryView = [_selectedTabViewItem auxiliaryView];
|
||||
[_auxiliaryView setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[self addSubview:_contentView];
|
||||
|
||||
if (_auxiliaryView)
|
||||
[self addSubview:_auxiliaryView];
|
||||
|
||||
[_labelsView tabView:self didChangeStateOfTabViewItem:_selectedTabViewItem];
|
||||
|
||||
[self layoutSubviews];
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
|
||||
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
|
||||
[self selectTabViewItemAtIndex:[self indexOfTabViewItem:aTabViewItem]];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -424,7 +239,24 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (void)selectTabViewItemAtIndex:(unsigned)anIndex
|
||||
{
|
||||
[self selectTabViewItem:_tabViewItems[anIndex]];
|
||||
var aTabViewItem = [_items objectAtIndex:anIndex];
|
||||
|
||||
if (anIndex === _selectedIndex)
|
||||
return;
|
||||
|
||||
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
|
||||
|
||||
if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
|
||||
return;
|
||||
|
||||
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
|
||||
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
|
||||
|
||||
[_tabs selectSegmentWithTag:anIndex];
|
||||
[self _setSelectedIndex:anIndex];
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
|
||||
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -432,32 +264,52 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (CPTabViewItem)selectedTabViewItem
|
||||
{
|
||||
return _selectedTabViewItem;
|
||||
return [_items objectAtIndex:_selectedIndex];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
/*!
|
||||
Sets the tab view type.
|
||||
@param aTabViewType the view type
|
||||
*/
|
||||
- (void)setTabViewType:(CPTabViewType)aTabViewType
|
||||
{
|
||||
if (_tabViewType == aTabViewType)
|
||||
if (_type === aTabViewType)
|
||||
return;
|
||||
|
||||
_tabViewType = aTabViewType;
|
||||
|
||||
if (_tabViewType == CPNoTabsBezelBorder || _tabViewType == CPNoTabsLineBorder || _tabViewType == CPNoTabsNoBorder)
|
||||
[_labelsView removeFromSuperview];
|
||||
else if (![_labelsView superview])
|
||||
[self addSubview:_labelsView];
|
||||
|
||||
if (_tabViewType == CPNoTabsLineBorder || _tabViewType == CPNoTabsNoBorder)
|
||||
[_backgroundView removeFromSuperview];
|
||||
else if (![_backgroundView superview])
|
||||
[self addSubview:_backgroundView];
|
||||
|
||||
[self layoutSubviews];
|
||||
|
||||
_type = aTabViewType;
|
||||
|
||||
if (_type !== CPTopTabsBezelBorder && _type !== CPBottomTabsBezelBorder)
|
||||
{
|
||||
[_box setFrame:[self bounds]];
|
||||
[_tabs removeFromSuperview];
|
||||
}
|
||||
else
|
||||
{
|
||||
var aFrame = [self frame],
|
||||
segmentedHeight = CGRectGetHeight([_tabs frame]),
|
||||
origin = _type === CPTopTabsBezelBorder ? segmentedHeight / 2 : 0;
|
||||
|
||||
[_box setFrame:CGRectMake(0, origin, CGRectGetWidth(aFrame),
|
||||
CGRectGetHeight(aFrame) - segmentedHeight / 2)];
|
||||
|
||||
[self addSubview:_tabs];
|
||||
}
|
||||
|
||||
switch (_type)
|
||||
{
|
||||
case CPTopTabsBezelBorder:
|
||||
case CPBottomTabsBezelBorder:
|
||||
case CPNoTabsBezelBorder:
|
||||
[_box setBorderType:CPBezelBorder];
|
||||
break;
|
||||
case CPNoTabsLineBorder:
|
||||
[_box setBorderType:CPLineBorder];
|
||||
break;
|
||||
case CPNoTabsNoBorder:
|
||||
[_box setBorderType:CPNoBorder];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -465,31 +317,15 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
*/
|
||||
- (CPTabViewType)tabViewType
|
||||
{
|
||||
return _tabViewType;
|
||||
return _type;
|
||||
}
|
||||
|
||||
// Determining the Size
|
||||
/*!
|
||||
Returns the content rectangle.
|
||||
Returns the receiver's delegate.
|
||||
*/
|
||||
- (CGRect)contentRect
|
||||
- (id)delegate
|
||||
{
|
||||
var contentRect = CGRectMakeCopy([self bounds]);
|
||||
|
||||
if (_tabViewType == CPTopTabsBezelBorder)
|
||||
{
|
||||
var labelsViewHeight = [_CPTabLabelsView height],
|
||||
auxiliaryViewHeight = _auxiliaryView ? CGRectGetHeight([_auxiliaryView frame]) : 5.0,
|
||||
separatorViewHeight = 1.0;
|
||||
|
||||
contentRect.origin.y += labelsViewHeight + auxiliaryViewHeight + separatorViewHeight;
|
||||
contentRect.size.height -= labelsViewHeight + auxiliaryViewHeight + separatorViewHeight * 2.0; // 2 for the bottom border as well.
|
||||
|
||||
contentRect.origin.x += LEFT_INSET;
|
||||
contentRect.size.width -= LEFT_INSET + RIGHT_INSET;
|
||||
}
|
||||
|
||||
return contentRect;
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -500,9 +336,9 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
{
|
||||
if (_delegate == aDelegate)
|
||||
return;
|
||||
|
||||
|
||||
_delegate = aDelegate;
|
||||
|
||||
|
||||
_delegateSelectors = 0;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(tabView:shouldSelectTabViewItem:)])
|
||||
@@ -515,18 +351,63 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
_delegateSelectors |= CPTabViewDidSelectTabViewItemSelector;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(tabViewDidChangeNumberOfTabViewItems:)])
|
||||
_delegateSelectors |= CPTabViewDidChangeNumberOfTabViewItemsSelector;
|
||||
_delegateSelectors |= CPTabViewDidChangeNumberOfTabViewItemsSelector;
|
||||
}
|
||||
|
||||
//
|
||||
- (void)setBackgroundColor:(CPColor)aColor
|
||||
{
|
||||
[_box setBackgroundColor:aColor];
|
||||
}
|
||||
|
||||
- (CPColor)backgroundColor
|
||||
{
|
||||
return [_box backgroundColor];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
var location = [_labelsView convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
tabViewItem = [_labelsView representedTabViewItemAtPoint:location];
|
||||
|
||||
if (tabViewItem)
|
||||
[self selectTabViewItem:tabViewItem];
|
||||
var segmentIndex = [_tabs testSegment:[_tabs convertPoint:[anEvent locationInWindow] fromView:nil]];
|
||||
|
||||
if (segmentIndex != CPNotFound)
|
||||
{
|
||||
[self selectTabViewItemAtIndex:segmentIndex];
|
||||
[_tabs trackSegment:anEvent];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_repositionTabs
|
||||
{
|
||||
var horizontalCenterOfSelf = CGRectGetWidth([self bounds]) / 2,
|
||||
verticalCenterOfTabs = CGRectGetHeight([_tabs bounds]) / 2;
|
||||
|
||||
if (_type === CPBottomTabsBezelBorder)
|
||||
[_tabs setCenter:CGPointMake(horizontalCenterOfSelf, CGRectGetHeight([self bounds]) - verticalCenterOfTabs)];
|
||||
else
|
||||
[_tabs setCenter:CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)];
|
||||
}
|
||||
|
||||
- (void)_setSelectedIndex:(CPNumber)index
|
||||
{
|
||||
_selectedIndex = index;
|
||||
|
||||
[_box setContentView:[[_items objectAtIndex:_selectedIndex] view]];
|
||||
}
|
||||
|
||||
- (void)_updateItems
|
||||
{
|
||||
var count = [_items count];
|
||||
[_tabs setSegmentCount:count];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
[_tabs setLabel:[[_items objectAtIndex:i] label] forSegment:i];
|
||||
[_tabs setTag:i forSegment:i];
|
||||
}
|
||||
|
||||
if (_selectedIndex === CPNotFound)
|
||||
{
|
||||
[self selectFirstTabViewItem:self];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -542,21 +423,20 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
_tabViewType = [aCoder decodeIntForKey:CPTabViewTypeKey];
|
||||
_tabViewItems = [];
|
||||
|
||||
// FIXME: this is somewhat hacky
|
||||
[self _createBezelBorder];
|
||||
|
||||
var items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
|
||||
for (var i = 0; items && i < items.length; i++)
|
||||
[self insertTabViewItem:items[i] atIndex:i];
|
||||
|
||||
[self _init];
|
||||
|
||||
_items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
|
||||
|
||||
[self _updateItems];
|
||||
[self _repositionTabs];
|
||||
|
||||
var selected = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
||||
if (selected)
|
||||
[self selectTabViewItem:selected];
|
||||
|
||||
|
||||
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
|
||||
|
||||
[self setTabViewType:[aCoder decodeIntForKey:CPTabViewTypeKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -564,227 +444,14 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
var actualSubviews = _subviews;
|
||||
_subviews = [];
|
||||
[super encodeWithCoder:aCoder];
|
||||
_subviews = actualSubviews;
|
||||
|
||||
[aCoder encodeObject:_tabViewItems forKey:CPTabViewItemsKey];;
|
||||
[aCoder encodeObject:_selectedTabViewItem forKey:CPTabViewSelectedItemKey];
|
||||
|
||||
[aCoder encodeInt:_tabViewType forKey:CPTabViewTypeKey];
|
||||
|
||||
|
||||
[aCoder encodeObject:_items forKey:CPTabViewItemsKey];
|
||||
[aCoder encodeObject:[self selectedTabViewItem] forKey:CPTabViewSelectedItemKey];
|
||||
|
||||
[aCoder encodeInt:_type forKey:CPTabViewTypeKey];
|
||||
|
||||
[aCoder encodeConditionalObject:_delegate forKey:CPTabViewDelegateKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var _CPTabLabelsViewBackgroundColor = nil,
|
||||
_CPTabLabelsViewInsideMargin = 10.0,
|
||||
_CPTabLabelsViewOutsideMargin = 15.0;
|
||||
|
||||
/* @ignore */
|
||||
@implementation _CPTabLabelsView : CPView
|
||||
{
|
||||
CPTabView _tabView;
|
||||
CPDictionary _tabLabels;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPTabLabelsView class])
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPTabLabelsViewBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelsViewLeft.png"] size:CGSizeMake(12.0, 26.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelsViewCenter.png"] size:CGSizeMake(1.0, 26.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelsViewRight.png"] size:CGSizeMake(12.0, 26.0)]
|
||||
] isVertical:NO]];
|
||||
}
|
||||
|
||||
+ (float)height
|
||||
{
|
||||
return 26.0;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_tabLabels = [];
|
||||
|
||||
[self setBackgroundColor:_CPTabLabelsViewBackgroundColor];
|
||||
|
||||
[self setFrameSize:CGSizeMake(CGRectGetWidth(aFrame), 26.0)];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setTabView:(CPTabView)aTabView
|
||||
{
|
||||
_tabView = aTabView;
|
||||
}
|
||||
|
||||
- (CPTabView)tabView
|
||||
{
|
||||
return _tabView;
|
||||
}
|
||||
|
||||
- (void)tabView:(CPTabView)aTabView didAddTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
var label = [[_CPTabLabel alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[label setTabViewItem:aTabViewItem];
|
||||
|
||||
_tabLabels.push(label);
|
||||
|
||||
[self addSubview:label];
|
||||
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
- (void)tabView:(CPTabView)aTabView didRemoveTabViewItemAtIndex:(unsigned)index
|
||||
{
|
||||
var label = _tabLabels[index];
|
||||
|
||||
[_tabLabels removeObjectAtIndex:index];
|
||||
|
||||
[label removeFromSuperview];
|
||||
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
-(void)tabView:(CPTabView)aTabView didChangeStateOfTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
[_tabLabels[[aTabView indexOfTabViewItem:aTabViewItem]] setTabState:[aTabViewItem tabState]];
|
||||
}
|
||||
|
||||
- (CPTabViewItem)representedTabViewItemAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var index = 0,
|
||||
count = _tabLabels.length;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var label = _tabLabels[index];
|
||||
|
||||
if (CGRectContainsPoint([label frame], aPoint))
|
||||
return [label tabViewItem];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var index = 0,
|
||||
count = _tabLabels.length,
|
||||
width = (_CGRectGetWidth([self bounds]) - (count - 1) * _CPTabLabelsViewInsideMargin - 2 * _CPTabLabelsViewOutsideMargin) / count,
|
||||
x = _CPTabLabelsViewOutsideMargin;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var label = _tabLabels[index],
|
||||
frame = _CGRectMake(x, 8.0, width, 18.0);
|
||||
|
||||
[label setFrame:frame];
|
||||
|
||||
x = _CGRectGetMaxX(frame) + _CPTabLabelsViewInsideMargin;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
if (CGSizeEqualToSize([self frame].size, aSize))
|
||||
return;
|
||||
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPTabLabelBackgroundColor = nil,
|
||||
_CPTabLabelSelectedBackgroundColor = nil;
|
||||
|
||||
/* @ignore */
|
||||
@implementation _CPTabLabel : CPView
|
||||
{
|
||||
CPTabViewItem _tabViewItem;
|
||||
CPTextField _labelField;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPTabLabel class])
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
_CPTabLabelBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundLeft.png"] size:CGSizeMake(6.0, 18.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundCenter.png"] size:CGSizeMake(1.0, 18.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelBackgroundRight.png"] size:CGSizeMake(6.0, 18.0)]
|
||||
] isVertical:NO]];
|
||||
|
||||
_CPTabLabelSelectedBackgroundColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelSelectedLeft.png"] size:CGSizeMake(3.0, 18.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelSelectedCenter.png"] size:CGSizeMake(1.0, 18.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPTabView/_CPTabLabelSelectedRight.png"] size:CGSizeMake(3.0, 18.0)]
|
||||
] isVertical:NO]];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_labelField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_labelField setAlignment:CPCenterTextAlignment];
|
||||
[_labelField setFrame:CGRectMake(5.0, 0.0, CGRectGetWidth(aFrame) - 10.0, 20.0)];
|
||||
[_labelField setAutoresizingMask:CPViewWidthSizable];
|
||||
[_labelField setFont:[CPFont boldSystemFontOfSize:11.0]];
|
||||
|
||||
[self addSubview:_labelField];
|
||||
|
||||
[self setTabState:CPBackgroundTab];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setTabState:(CPTabState)aTabState
|
||||
{
|
||||
[self setBackgroundColor:aTabState == CPSelectedTab ? _CPTabLabelSelectedBackgroundColor : _CPTabLabelBackgroundColor];
|
||||
}
|
||||
|
||||
- (void)setTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
_tabViewItem = aTabViewItem;
|
||||
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (CPTabViewItem)tabViewItem
|
||||
{
|
||||
return _tabViewItem;
|
||||
}
|
||||
|
||||
- (void)update
|
||||
{
|
||||
[_labelField setStringValue:[_tabViewItem label]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+19
-9
@@ -22,7 +22,7 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import <AppKit/CPView.j>
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
/*
|
||||
@@ -44,7 +44,7 @@ CPBackgroundTab = 1;
|
||||
*/
|
||||
CPPressedTab = 2;
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPTabViewItem
|
||||
|
||||
@@ -55,9 +55,11 @@ CPPressedTab = 2;
|
||||
{
|
||||
id _identifier;
|
||||
CPString _label;
|
||||
|
||||
|
||||
CPView _view;
|
||||
CPView _auxiliaryView;
|
||||
|
||||
CPTabView _tabView;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
@@ -72,10 +74,10 @@ CPPressedTab = 2;
|
||||
- (id)initWithIdentifier:(id)anIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
_identifier = anIdentifier;
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -168,6 +170,14 @@ CPPressedTab = 2;
|
||||
return _tabView;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_setTabView:(CPTabView)aView
|
||||
{
|
||||
_tabView = aView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||
@@ -181,16 +191,16 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_identifier = [aCoder decodeObjectForKey:CPTabViewItemIdentifierKey];
|
||||
_label = [aCoder decodeObjectForKey:CPTabViewItemLabelKey];
|
||||
|
||||
|
||||
_view = [aCoder decodeObjectForKey:CPTabViewItemViewKey];
|
||||
_auxiliaryView = [aCoder decodeObjectForKey:CPTabViewItemAuxViewKey];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -198,7 +208,7 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:CPTabViewItemIdentifierKey];
|
||||
[aCoder encodeObject:_label forKey:CPTabViewItemLabelKey];
|
||||
|
||||
|
||||
[aCoder encodeObject:_view forKey:CPTabViewItemViewKey];
|
||||
[aCoder encodeObject:_auxiliaryView forKey:CPTabViewItemAuxViewKey];
|
||||
}
|
||||
|
||||
+281
-53
@@ -22,28 +22,25 @@
|
||||
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
@import <Foundation/CPSortDescriptor.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import "CPTableHeaderView.j"
|
||||
|
||||
|
||||
/*
|
||||
@global
|
||||
@class CPTableColumn
|
||||
*/
|
||||
CPTableColumnNoResizing = 0;
|
||||
/*
|
||||
@global
|
||||
@class CPTableColumn
|
||||
*/
|
||||
CPTableColumnAutoresizingMask = 1;
|
||||
/*
|
||||
@global
|
||||
@class CPTableColumn
|
||||
*/
|
||||
CPTableColumnUserResizingMask = 2;
|
||||
CPTableColumnAutoresizingMask = 1 << 0;
|
||||
CPTableColumnUserResizingMask = 1 << 1;
|
||||
|
||||
/*!
|
||||
@class CPTableColumn
|
||||
|
||||
A CPTableColumn contains a dataview to display for its column of the CPTableView.
|
||||
A CPTableColumn determines its own size constrains and resizing behaviour.
|
||||
|
||||
The default dataview is a CPTextField but you can set it to any view you'd like. See -setDataView:
|
||||
*/
|
||||
@implementation CPTableColumn : CPObject
|
||||
{
|
||||
CPTableView _tableView;
|
||||
@@ -54,19 +51,34 @@ CPTableColumnUserResizingMask = 2;
|
||||
float _width;
|
||||
float _minWidth;
|
||||
float _maxWidth;
|
||||
unsigned _resizingMask;
|
||||
|
||||
id _identifier;
|
||||
BOOL _isEditable;
|
||||
CPSortDescriptor _sortDescriptorPrototype;
|
||||
BOOL _isHidden;
|
||||
CPString _headerToolTip;
|
||||
|
||||
BOOL _disableResizingPosting @accessors(property=disableResizingPosting);
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
{
|
||||
return [CPBinder class];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithIdentifier:@""];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes a newly created CPTableColumn with a given identifier.
|
||||
|
||||
*/
|
||||
- (id)initWithIdentifier:(id)anIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
@@ -78,25 +90,64 @@ CPTableColumnUserResizingMask = 2;
|
||||
_width = 100.0;
|
||||
_minWidth = 10.0;
|
||||
_maxWidth = 1000000.0;
|
||||
_resizingMask = CPTableColumnAutoresizingMask | CPTableColumnUserResizingMask;
|
||||
_disableResizingPosting = NO;
|
||||
|
||||
[self setIdentifier:anIdentifier];
|
||||
[self setHeaderView:[CPTextField new]];
|
||||
|
||||
var header = [[_CPTableColumnHeaderView alloc] initWithFrame:CGRectMakeZero()];
|
||||
[self setHeaderView:header];
|
||||
|
||||
[self setDataView:[CPTextField new]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the columns's parent tableview
|
||||
*/
|
||||
- (void)setTableView:(CPTableView)aTableView
|
||||
{
|
||||
_tableView = aTableView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the column's parent tableview
|
||||
*/
|
||||
- (CPTableView)tableView
|
||||
{
|
||||
return _tableView;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
this method tries to resize a column called via the tableview via autoresizing
|
||||
it returns the delta from the actual resize and the proposed resize
|
||||
|
||||
for example if the column should have been resized 50px but the maxWidth was hit only
|
||||
after 25px then the return value would be 25px;
|
||||
|
||||
if no edge has been hit zero will be returned
|
||||
*/
|
||||
- (int)_tryToResizeToWidth:(int)width
|
||||
{
|
||||
var min = [self minWidth],
|
||||
max = [self maxWidth],
|
||||
newWidth = MIN(MAX(width, min), max);
|
||||
|
||||
[self setWidth:newWidth];
|
||||
|
||||
return newWidth - width;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the width of the column
|
||||
Default value is: 100
|
||||
|
||||
If the value is greater than the maxWidth the maxWidth will be reset with the supplied width here
|
||||
If the value is less than the minWidth the minWidth will be reset with the supplied width.
|
||||
*/
|
||||
- (void)setWidth:(float)aWidth
|
||||
{
|
||||
aWidth = +aWidth;
|
||||
@@ -117,26 +168,38 @@ CPTableColumnUserResizingMask = 2;
|
||||
|
||||
if (tableView)
|
||||
{
|
||||
var index = [[tableView tableColumns] indexOfObjectIdenticalTo:self];
|
||||
var index = [[tableView tableColumns] indexOfObjectIdenticalTo:self],
|
||||
dirtyTableColumnRangeIndex = tableView._dirtyTableColumnRangeIndex;
|
||||
|
||||
// FIXME: THIS IS HORRIBLE. Don't just reload everything when a table column changes, just relayout the changed widths.
|
||||
tableView._reloadAllRows = YES;
|
||||
tableView._dirtyTableColumnRangeIndex = tableView._dirtyTableColumnRangeIndex < 0 ? index : MIN(index, tableView._dirtyTableColumnRangeIndex);
|
||||
if (dirtyTableColumnRangeIndex < 0)
|
||||
tableView._dirtyTableColumnRangeIndex = index;
|
||||
else
|
||||
tableView._dirtyTableColumnRangeIndex = MIN(index, tableView._dirtyTableColumnRangeIndex);
|
||||
|
||||
var rows = tableView._exposedRows,
|
||||
columns = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(index, [tableView._exposedColumns lastIndex] - index + 1)];
|
||||
|
||||
// FIXME: Would be faster with some sort of -setNeedsDisplayInColumns: that updates a dirtyTableColumnForDisplay cache; then marked columns would relayout their data views at display time.
|
||||
[tableView _layoutDataViewsInRows:rows columns:columns];
|
||||
[tableView tile];
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPTableViewColumnDidResizeNotification
|
||||
object:tableView
|
||||
userInfo:[CPDictionary dictionaryWithObjects:[self, oldWidth] forKeys:[@"CPTableColumn", "CPOldWidth"]]];
|
||||
if (!_disableResizingPosting)
|
||||
[self _postDidResizeNotificationWithOldWidth:oldWidth];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the column's width
|
||||
*/
|
||||
- (float)width
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the minimum width of the column.
|
||||
Default value is 10.
|
||||
*/
|
||||
- (void)setMinWidth:(float)aMinWidth
|
||||
{
|
||||
aMinWidth = +aMinWidth;
|
||||
@@ -153,11 +216,18 @@ CPTableColumnUserResizingMask = 2;
|
||||
[self setWidth:newWidth];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the minimum width of the column.
|
||||
*/
|
||||
- (float)minWidth
|
||||
{
|
||||
return _minWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the maximum width of the table column.
|
||||
Default value is: 1000000
|
||||
*/
|
||||
- (void)setMaxWidth:(float)aMaxWidth
|
||||
{
|
||||
aMaxWidth = +aMaxWidth;
|
||||
@@ -168,27 +238,46 @@ CPTableColumnUserResizingMask = 2;
|
||||
_maxWidth = aMaxWidth;
|
||||
|
||||
var width = [self width],
|
||||
newWidth = MAX(width, [self maxWidth]);
|
||||
newWidth = MIN(width, [self maxWidth]);
|
||||
|
||||
if (width !== newWidth)
|
||||
[self setWidth:newWidth];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the maximum width of the column
|
||||
*/
|
||||
- (float)maxWidth
|
||||
{
|
||||
return _maxWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the resizing mask of the column.
|
||||
By default the column can be resized automatically with the tableview and manaully by the user
|
||||
|
||||
Possible masking values are:
|
||||
CPTableColumnNoResizing
|
||||
CPTableColumnAutoresizingMask
|
||||
CPTableColumnUserResizingMask
|
||||
*/
|
||||
- (void)setResizingMask:(unsigned)aResizingMask
|
||||
{
|
||||
_resizingMask = aResizingMask;
|
||||
}
|
||||
|
||||
- (float)resizingMask
|
||||
|
||||
/*!
|
||||
Returns the resizing mask of the column
|
||||
*/
|
||||
- (unsigned)resizingMask
|
||||
{
|
||||
return _resizingMask;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sizes the column to fix the column header text.
|
||||
*/
|
||||
- (void)sizeToFit
|
||||
{
|
||||
var width = _CGRectGetWidth([_headerView frame]);
|
||||
@@ -202,7 +291,11 @@ CPTableColumnUserResizingMask = 2;
|
||||
[self setWidth:width];
|
||||
}
|
||||
|
||||
//Setting Component Cells
|
||||
|
||||
/*!
|
||||
Sets the header view for the column.
|
||||
The headerview handles the display of sort indicators, text, etc
|
||||
*/
|
||||
- (void)setHeaderView:(CPView)aView
|
||||
{
|
||||
if (!aView)
|
||||
@@ -216,19 +309,39 @@ CPTableColumnUserResizingMask = 2;
|
||||
[tableHeaderView setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the headerview for the column
|
||||
*/
|
||||
- (CPView)headerView
|
||||
{
|
||||
return _headerView;
|
||||
}
|
||||
|
||||
/*!
|
||||
This method sets the "prototype" view which will be used to create all table cells in this column.
|
||||
|
||||
It creates a snapshot of aView, using keyed archiving, which is then copied over and over for each
|
||||
individual cell that is shown. As a result, changes made after calling this method won't be reflected.
|
||||
|
||||
Example:
|
||||
|
||||
[tableColumn setDataView:someView]; // snapshot taken
|
||||
[[tableColumn dataView] setSomething:x]; //won't work
|
||||
|
||||
This doesn't work because the snapshot is taken before the new property is applied. Instead, do:
|
||||
|
||||
[someView setSomething:x];
|
||||
[tableColumn setDataView:someView];
|
||||
|
||||
REMEMBER: you should implement CPKeyedArchiving otherwise you might see unexpected results
|
||||
*/
|
||||
- (void)setDataView:(CPView)aView
|
||||
{
|
||||
if (_dataView === aView)
|
||||
return;
|
||||
|
||||
if (_dataView)
|
||||
_dataViewData[[_dataView UID]] = nil;
|
||||
|
||||
[aView setThemeState:CPThemeStateTableDataView];
|
||||
|
||||
_dataView = aView;
|
||||
_dataViewData[[aView UID]] = [CPKeyedArchiver archivedDataWithRootObject:aView];
|
||||
}
|
||||
@@ -251,14 +364,17 @@ CPTableColumnUserResizingMask = 2;
|
||||
return [self dataView];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (id)_newDataViewForRow:(int)aRowIndex
|
||||
{
|
||||
var dataView = [self dataViewForRow:aRowIndex],
|
||||
dataViewUID = [dataView UID];
|
||||
|
||||
var x = [self tableView]._cachedDataViews[dataViewUID];
|
||||
if (x && x.length)
|
||||
return x.pop();
|
||||
var x = [self tableView]._cachedDataViews[dataViewUID];
|
||||
if (x && x.length)
|
||||
return x.pop();
|
||||
|
||||
// if we haven't cached an archive of the data view, do it now
|
||||
if (!_dataViewData[dataViewUID])
|
||||
@@ -266,7 +382,11 @@ return x.pop();
|
||||
|
||||
// unarchive the data view cache
|
||||
var newDataView = [CPKeyedUnarchiver unarchiveObjectWithData:_dataViewData[dataViewUID]];
|
||||
newDataView.identifier = dataViewUID;
|
||||
newDataView.identifier = dataViewUID;
|
||||
|
||||
// make sure only we have control over the size and placement
|
||||
[newDataView setAutoresizingMask:CPViewNotSizable];
|
||||
|
||||
return newDataView;
|
||||
}
|
||||
|
||||
@@ -299,7 +419,7 @@ newDataView.identifier = dataViewUID;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns YES if the user can edit cells associated with the receiver by double-clicking the
|
||||
Returns YES if the user can edit cells associated with the receiver by double-clicking the
|
||||
column in the NSTableView, NO otherwise.
|
||||
*/
|
||||
- (BOOL)isEditable
|
||||
@@ -307,24 +427,41 @@ newDataView.identifier = dataViewUID;
|
||||
return _isEditable;
|
||||
}
|
||||
|
||||
//Sorting
|
||||
/*!
|
||||
Sets the sort descriptor prototype for the column.
|
||||
*/
|
||||
- (void)setSortDescriptorPrototype:(CPSortDescriptor)aSortDescriptor
|
||||
{
|
||||
_sortDescriptorPrototype = aSortDescriptor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the sort descriptor prototype for the column.
|
||||
*/
|
||||
- (CPSortDescriptor)sortDescriptorPrototype
|
||||
{
|
||||
return _sortDescriptorPrototype;
|
||||
}
|
||||
|
||||
//Setting Column Visibility
|
||||
|
||||
/*!
|
||||
If NO the tablecolumn will no longer be visisble in the tableview
|
||||
If YES the tablecolumn will be visible in the tableview.
|
||||
*/
|
||||
- (void)setHidden:(BOOL)shouldBeHidden
|
||||
{
|
||||
shouldBeHidden = !!shouldBeHidden
|
||||
if (_isHidden === shouldBeHidden)
|
||||
return;
|
||||
|
||||
_isHidden = shouldBeHidden;
|
||||
|
||||
[[self headerView] setHidden:shouldBeHidden];
|
||||
[[self tableView] _tableColumnVisibilityDidChange:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the visibility status of the column.
|
||||
*/
|
||||
- (BOOL)isHidden
|
||||
{
|
||||
return _isHidden;
|
||||
@@ -333,7 +470,7 @@ newDataView.identifier = dataViewUID;
|
||||
//Setting Tool Tips
|
||||
|
||||
/*
|
||||
Sets the tooltip string that is displayed when the cursor pauses over the
|
||||
Sets the tooltip string that is displayed when the cursor pauses over the
|
||||
header cell of the receiver.
|
||||
*/
|
||||
- (void)setHeaderToolTip:(CPString)aToolTip
|
||||
@@ -341,11 +478,94 @@ newDataView.identifier = dataViewUID;
|
||||
_headerToolTip = aToolTip;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the tooltip for the column header
|
||||
*/
|
||||
- (CPString)headerToolTip
|
||||
{
|
||||
return _headerToolTip;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_postDidResizeNotificationWithOldWidth:(float)oldWidth
|
||||
{
|
||||
[[self tableView] _didResizeTableColumn:self];
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPTableViewColumnDidResizeNotification
|
||||
object:[self tableView]
|
||||
userInfo:[CPDictionary dictionaryWithObjects:[self, oldWidth] forKeys:[@"CPTableColumn", "CPOldWidth"]]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTableColumn (Bindings)
|
||||
- (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
|
||||
{
|
||||
[super bind:aBinding toObject:anObject withKeyPath:aKeyPath options:options];
|
||||
|
||||
if (![aBinding isEqual:@"someListOfExceptedBindings(notAcceptedBindings)"])
|
||||
[[self tableView] _establishBindingsIfUnbound:anObject];
|
||||
}
|
||||
|
||||
- (void)prepareDataView:(CPView)aDataView forRow:(unsigned)aRow
|
||||
{
|
||||
var bindingsDictionary = [CPBinder allBindingsForObject:self],
|
||||
keys = [bindingsDictionary allKeys];
|
||||
|
||||
for (var i = 0, count = [keys count]; i < count; i++)
|
||||
{
|
||||
var bindingName = keys[i],
|
||||
bindingPath = [aDataView _replacementKeyPathForBinding:bindingName],
|
||||
binding = [bindingsDictionary objectForKey:bindingName],
|
||||
bindingInfo = binding._info,
|
||||
destination = [bindingInfo objectForKey:CPObservedObjectKey],
|
||||
keyPath = [bindingInfo objectForKey:CPObservedKeyPathKey],
|
||||
dotIndex = keyPath.lastIndexOf("."),
|
||||
value;
|
||||
|
||||
if (dotIndex === CPNotFound)
|
||||
value = [[destination valueForKeyPath:keyPath] objectAtIndex:aRow];
|
||||
else
|
||||
{
|
||||
/*
|
||||
Optimize the prototypical use case where the key path describes a value
|
||||
in an array. Without this optimization, we call CPArray's valueForKey
|
||||
which generates as many values as objects in the array, of which we then
|
||||
pick one and throw away the rest.
|
||||
|
||||
The optimization is to get the array and access the value directly. This
|
||||
turns the operation into a single access regardless of how long the model
|
||||
array is.
|
||||
*/
|
||||
|
||||
var firstPart = keyPath.substring(0, dotIndex),
|
||||
secondPart = keyPath.substring(dotIndex + 1),
|
||||
firstValue = [destination valueForKeyPath:firstPart];
|
||||
|
||||
if ([firstValue isKindOfClass:CPArray])
|
||||
value = [[firstValue objectAtIndex:aRow] valueForKeyPath:secondPart];
|
||||
else
|
||||
value = [[firstValue valueForKeyPath:secondPart] objectAtIndex:aRow];
|
||||
}
|
||||
|
||||
value = [binding transformValue:value withOptions:[bindingInfo objectForKey:CPOptionsKey]];
|
||||
[aDataView setValue:value forKey:@"objectValue"];
|
||||
}
|
||||
}
|
||||
|
||||
//- (void)objectValue
|
||||
//{
|
||||
// return nil;
|
||||
//}
|
||||
|
||||
- (void)setValue:(CPArray)content
|
||||
{
|
||||
[[self tableView] reloadData];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
@@ -354,7 +574,10 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
CPTableColumnWidthKey = @"CPTableColumnWidthKey",
|
||||
CPTableColumnMinWidthKey = @"CPTableColumnMinWidthKey",
|
||||
CPTableColumnMaxWidthKey = @"CPTableColumnMaxWidthKey",
|
||||
CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey";
|
||||
CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey",
|
||||
CPTableColumnIsHiddenKey = @"CPTableColumnIsHiddenKey",
|
||||
CPSortDescriptorPrototypeKey = @"CPSortDescriptorPrototypeKey",
|
||||
CPTableColumnIsEditableKey = @"CPTableColumnIsEditableKey";
|
||||
|
||||
@implementation CPTableColumn (CPCoding)
|
||||
|
||||
@@ -371,14 +594,15 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
_maxWidth = [aCoder decodeFloatForKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
[self setIdentifier:[aCoder decodeObjectForKey:CPTableColumnIdentifierKey]];
|
||||
// [self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||
// [self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
||||
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||
[self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
||||
[self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||
|
||||
[self setHeaderView:[CPTextField new]];
|
||||
[self setDataView:[CPTextField new]];
|
||||
_resizingMask = [aCoder decodeIntForKey:CPTableColumnResizingMaskKey];
|
||||
_isHidden = [aCoder decodeBoolForKey:CPTableColumnIsHiddenKey];
|
||||
_isEditable = [aCoder decodeBoolForKey:CPTableColumnIsEditableKey];
|
||||
|
||||
|
||||
// _resizingMask = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey];
|
||||
_sortDescriptorPrototype = [aCoder decodeObjectForKey:CPSortDescriptorPrototypeKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -392,10 +616,14 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
[aCoder encodeObject:_minWidth forKey:CPTableColumnMinWidthKey];
|
||||
[aCoder encodeObject:_maxWidth forKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
// [aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
||||
// [aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
||||
[aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
||||
[aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
||||
|
||||
// [aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
|
||||
[aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
|
||||
[aCoder encodeBool:_isHidden forKey:CPTableColumnIsHiddenKey];
|
||||
[aCoder encodeBool:_isEditable forKey:CPTableColumnIsEditableKey];
|
||||
|
||||
[aCoder encodeObject:_sortDescriptorPrototype forKey:CPSortDescriptorPrototypeKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -405,25 +633,25 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
- (void)setHeaderCell:(CPView)aView
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"setHeaderCell: is not supported. -setHeaderCell:aView instead."];
|
||||
reason:@"setHeaderCell: is not supported. Use -setHeaderView:aView instead."];
|
||||
}
|
||||
|
||||
- (CPView)headerCell
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"headCell is not supported. -headerView instead."];
|
||||
reason:@"headCell is not supported. Use -headerView instead."];
|
||||
}
|
||||
|
||||
- (void)setDataCell:(CPView)aView
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"setDataCell: is not supported. Use -setHeaderCell:aView instead."];
|
||||
reason:@"setDataCell: is not supported. Use -setDataView:aView instead."];
|
||||
}
|
||||
|
||||
- (CPView)dataCell
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"dataCell is not supported. Use -dataCell instead."];
|
||||
reason:@"dataCell is not supported. Use -dataView instead."];
|
||||
}
|
||||
|
||||
- (id)dataCellForRow:(int)row
|
||||
@@ -432,4 +660,4 @@ var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
reason:@"dataCellForRow: is not supported. Use -dataViewForRow:row instead."];
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
+617
-69
@@ -20,126 +20,674 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
|
||||
@import "CPTableColumn.j"
|
||||
@import "CPTableView.j"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
@implementation CPTableHeaderView : CPView
|
||||
@implementation _CPTableColumnHeaderView : CPView
|
||||
{
|
||||
int _resizedColumn @accessors(readonly, property=resizedColumn);
|
||||
int _draggedColumn @accessors(readonly, property=draggedColumn);
|
||||
|
||||
float _draggedDistance @accessors(readonly, property=draggedDistance);
|
||||
|
||||
CPTableView _tableView @accessors(property=tableView);
|
||||
_CPImageAndTextView _textField;
|
||||
}
|
||||
|
||||
- (void)initWithFrame:(CGRect)aFrame
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"columnHeader";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPNull null], CGInsetMakeZero(), [CPNull null], [CPNull null], [CPNull null], CGSizeMakeZero()]
|
||||
forKeys:[@"background-color", @"text-alignment", @"text-inset", @"text-color", @"text-font", @"text-shadow-color", @"text-shadow-offset"]];
|
||||
}
|
||||
|
||||
- (void)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
[self _init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_textField = [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
[_textField setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
[_textField setLineBreakMode:CPLineBreakByTruncatingTail];
|
||||
[_textField setAlignment:CPLeftTextAlignment];
|
||||
[_textField setVerticalAlignment:CPCenterVerticalTextAlignment];
|
||||
|
||||
[self addSubview:_textField];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
|
||||
|
||||
var inset = [self currentValueForThemeAttribute:@"text-inset"],
|
||||
bounds = [self bounds];
|
||||
|
||||
[_textField setFrame:_CGRectMake(inset.right, inset.top, bounds.size.width - inset.right - inset.left, bounds.size.height - inset.top - inset.bottom)];
|
||||
[_textField setTextColor:[self currentValueForThemeAttribute:@"text-color"]];
|
||||
[_textField setFont:[self currentValueForThemeAttribute:@"text-font"]];
|
||||
[_textField setTextShadowColor:[self currentValueForThemeAttribute:@"text-shadow-color"]];
|
||||
[_textField setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
|
||||
[_textField setAlignment:[self currentValueForThemeAttribute:@"text-alignment"]];
|
||||
}
|
||||
|
||||
- (void)setStringValue:(CPString)string
|
||||
{
|
||||
[_textField setText:string];
|
||||
}
|
||||
|
||||
- (CPString)stringValue
|
||||
{
|
||||
return [_textField text];
|
||||
}
|
||||
|
||||
- (void)textField
|
||||
{
|
||||
return _textField;
|
||||
}
|
||||
|
||||
- (void)sizeToFit
|
||||
{
|
||||
[_textField sizeToFit];
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
[self setValue:aFont forThemeAttribute:"text-font"];
|
||||
}
|
||||
|
||||
- (void)_setIndicatorImage:(CPImage)anImage
|
||||
{
|
||||
if (anImage)
|
||||
{
|
||||
[_textField setImage:anImage];
|
||||
[_textField setImagePosition:CPImageRight];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_textField setImagePosition:CPNoImage];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringValueKey",
|
||||
_CPTableColumnHeaderViewFontKey = @"_CPTableColumnHeaderViewFontKey",
|
||||
_CPTableColumnHeaderViewImageKey = @"_CPTableColumnHeaderViewImageKey";
|
||||
|
||||
@implementation _CPTableColumnHeaderView (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
[self _init];
|
||||
[self _setIndicatorImage:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewImageKey]];
|
||||
[self setStringValue:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewStringValueKey]];
|
||||
[self setFont:[aCoder decodeObjectForKey:_CPTableColumnHeaderViewFontKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeObject:[_textField text] forKey:_CPTableColumnHeaderViewStringValueKey];
|
||||
[aCoder encodeObject:[_textField image] forKey:_CPTableColumnHeaderViewImageKey];
|
||||
[aCoder encodeObject:[_textField font] forKey:_CPTableColumnHeaderViewFontKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTableHeaderView : CPView
|
||||
{
|
||||
CGPoint _mouseDownLocation;
|
||||
CGPoint _previousTrackingLocation;
|
||||
int _activeColumn;
|
||||
int _pressedColumn;
|
||||
|
||||
BOOL _isResizing;
|
||||
BOOL _isDragging;
|
||||
BOOL _isTrackingColumn;
|
||||
BOOL _drawsColumnLines;
|
||||
|
||||
float _columnOldWidth;
|
||||
|
||||
CPTableView _tableView @accessors(property=tableView);
|
||||
}
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"tableHeaderRow";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[[CPNull null], [CPColor grayColor]]
|
||||
forKeys:[@"background-color", @"divider-color"]];
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_mouseDownLocation = _CGPointMakeZero();
|
||||
_previousTrackingLocation = _CGPointMakeZero();
|
||||
_activeColumn = -1;
|
||||
_pressedColumn = -1;
|
||||
|
||||
_isResizing = NO;
|
||||
_isDragging = NO;
|
||||
_isTrackingColumn = NO;
|
||||
_drawsColumnLines = YES;
|
||||
|
||||
_columnOldWidth = 0.0;
|
||||
|
||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_resizedColumn = CPNotFound;
|
||||
_draggedColumn = CPNotFound;
|
||||
_draggedDistance = 0.0;
|
||||
}
|
||||
[self _init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (int)columnAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
if (!CGRectContainsPoint([self bounds], aPoint))
|
||||
return CPNotFound;
|
||||
|
||||
// at this point, we can essentially ignore height, because all columns have equal heights
|
||||
// and that height is equal to the height of our own bounds, which we know we are inside of
|
||||
|
||||
var index = 0,
|
||||
count = [[_tableView tableColumns] count],
|
||||
tableSpacing = [_tableView intercellSpacing],
|
||||
tableColumns = [_tableView tableColumns],
|
||||
leftOffset = 0,
|
||||
pointX = aPoint.x;
|
||||
|
||||
for (; index < count; index++)
|
||||
{
|
||||
var width = [tableColumns[index] width] + tableSpacing.width;
|
||||
|
||||
if (pointX >= leftOffset && pointX < leftOffset + width)
|
||||
return index;
|
||||
|
||||
leftOffset += width;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
return [_tableView columnAtPoint:CGPointMake(aPoint.x, aPoint.y)];
|
||||
}
|
||||
|
||||
- (CGRect)headerRectOfColumn:(int)aColumnIndex
|
||||
{
|
||||
var headerRect = [self bounds],
|
||||
columnRect = [_tableView rectOfColumn:aColumnIndex];
|
||||
|
||||
headerRect.origin.x = _CGRectGetMinX(columnRect);
|
||||
headerRect.size.width = _CGRectGetWidth(columnRect);
|
||||
|
||||
return headerRect;
|
||||
}
|
||||
|
||||
- (void)setDrawsColumnLines:(BOOL)aFlag
|
||||
{
|
||||
_drawsColumnLines = aFlag;
|
||||
}
|
||||
|
||||
- (BOOL)drawsColumnLines
|
||||
{
|
||||
return _drawsColumnLines;
|
||||
}
|
||||
|
||||
- (CGRect)_cursorRectForColumn:(int)column
|
||||
{
|
||||
if (column == -1 || !([_tableView._tableColumns[column] resizingMask] & CPTableColumnUserResizingMask))
|
||||
return _CGRectMakeZero();
|
||||
|
||||
var rect = [self headerRectOfColumn:column];
|
||||
|
||||
rect.origin.x = _CGRectGetMaxX(rect) - 5;
|
||||
rect.size.width = 20;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
- (void)_setPressedColumn:(CPInteger)column
|
||||
{
|
||||
if (_pressedColumn != -1)
|
||||
{
|
||||
var headerView = [_tableView._tableColumns[_pressedColumn] headerView];
|
||||
[headerView unsetThemeState:CPThemeStateHighlighted];
|
||||
}
|
||||
|
||||
if (column != -1)
|
||||
{
|
||||
var headerView = [_tableView._tableColumns[column] headerView];
|
||||
[headerView setThemeState:CPThemeStateHighlighted];
|
||||
}
|
||||
|
||||
_pressedColumn = column;
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)theEvent
|
||||
{
|
||||
[self trackMouse:theEvent];
|
||||
}
|
||||
|
||||
- (void)trackMouse:(CPEvent)theEvent
|
||||
{
|
||||
var type = [theEvent type],
|
||||
currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil];
|
||||
|
||||
// Take the right columns resize tracking area into account
|
||||
currentLocation.x -= 5.0;
|
||||
|
||||
var columnIndex = [self columnAtPoint:currentLocation],
|
||||
shouldResize = [self shouldResizeTableColumn:columnIndex at:_CGPointMake(currentLocation.x + 5.0, currentLocation.y)];
|
||||
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
if (shouldResize)
|
||||
[self stopResizingTableColumn:_activeColumn at:currentLocation];
|
||||
else if ([self _shouldStopTrackingTableColumn:columnIndex at:currentLocation])
|
||||
{
|
||||
[_tableView _didClickTableColumn:columnIndex modifierFlags:[theEvent modifierFlags]];
|
||||
[self stopTrackingTableColumn:columnIndex at:currentLocation];
|
||||
|
||||
_isTrackingColumn = NO;
|
||||
}
|
||||
|
||||
[self _updateResizeCursor:[CPApp currentEvent]];
|
||||
|
||||
_activeColumn = CPNotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === CPLeftMouseDown)
|
||||
{
|
||||
if (columnIndex === -1)
|
||||
return;
|
||||
|
||||
_mouseDownLocation = currentLocation;
|
||||
_activeColumn = columnIndex;
|
||||
|
||||
[_tableView _sendDelegateDidMouseDownInHeader:columnIndex];
|
||||
|
||||
if (shouldResize)
|
||||
[self startResizingTableColumn:columnIndex at:currentLocation];
|
||||
else
|
||||
{
|
||||
[self startTrackingTableColumn:columnIndex at:currentLocation];
|
||||
_isTrackingColumn = YES;
|
||||
}
|
||||
}
|
||||
else if (type === CPLeftMouseDragged)
|
||||
{
|
||||
if (shouldResize)
|
||||
[self continueResizingTableColumn:_activeColumn at:currentLocation];
|
||||
else
|
||||
{
|
||||
if (_activeColumn === columnIndex && _CGRectContainsPoint([self headerRectOfColumn:columnIndex], currentLocation))
|
||||
{
|
||||
if (_isTrackingColumn && _pressedColumn !== -1)
|
||||
{
|
||||
if (![self continueTrackingTableColumn:columnIndex at:currentLocation])
|
||||
return; // Stop tracking the column, because it's being dragged
|
||||
} else
|
||||
[self startTrackingTableColumn:columnIndex at:currentLocation];
|
||||
|
||||
} else if (_isTrackingColumn && _pressedColumn !== -1)
|
||||
[self stopTrackingTableColumn:_activeColumn at:currentLocation];
|
||||
}
|
||||
}
|
||||
|
||||
_previousTrackingLocation = currentLocation;
|
||||
[CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
}
|
||||
|
||||
- (void)startTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||
{
|
||||
[self _setPressedColumn:aColumnIndex];
|
||||
}
|
||||
|
||||
- (BOOL)continueTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||
{
|
||||
if ([self _shouldDragTableColumn:aColumnIndex at:aPoint])
|
||||
{
|
||||
var columnRect = [self headerRectOfColumn:aColumnIndex],
|
||||
offset = _CGPointMakeZero(),
|
||||
view = [_tableView _dragViewForColumn:aColumnIndex event:[CPApp currentEvent] offset:offset],
|
||||
viewLocation = _CGPointMakeZero();
|
||||
|
||||
viewLocation.x = ( _CGRectGetMinX(columnRect) + offset.x ) + ( aPoint.x - _mouseDownLocation.x );
|
||||
viewLocation.y = _CGRectGetMinY(columnRect) + offset.y;
|
||||
|
||||
[self dragView:view at:viewLocation offset:_CGSizeMakeZero() event:[CPApp currentEvent]
|
||||
pasteboard:[CPPasteboard pasteboardWithName:CPDragPboard] source:self slideBack:YES];
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)_shouldStopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||
{
|
||||
return _isTrackingColumn && _activeColumn === aColumnIndex &&
|
||||
_CGRectContainsPoint([self headerRectOfColumn:aColumnIndex], aPoint);
|
||||
}
|
||||
|
||||
- (void)stopTrackingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||
{
|
||||
[self _setPressedColumn:CPNotFound];
|
||||
[self _updateResizeCursor:[CPApp currentEvent]];
|
||||
}
|
||||
|
||||
- (BOOL)_shouldDragTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||
{
|
||||
return [_tableView allowsColumnReordering] && ABS(aPoint.x - _mouseDownLocation.x) >= 10.0;
|
||||
}
|
||||
|
||||
- (CGRect)_headerRectOfLastVisibleColumn
|
||||
{
|
||||
var tableColumns = [_tableView tableColumns],
|
||||
tableSpacing = [_tableView intercellSpacing],
|
||||
bounds = [self bounds];
|
||||
columnIndex = [tableColumns count];
|
||||
|
||||
if (aColumnIndex < 0 || aColumnIndex > [tableColumns count])
|
||||
[CPException raise:"invalid" reason:"tried to get headerRectOfColumn: on invalid column"];
|
||||
while (columnIndex--)
|
||||
{
|
||||
var tableColumn = [tableColumns objectAtIndex:columnIndex];
|
||||
|
||||
bounds.size.width = [tableColumns[aColumnIndex] width] + tableSpacing.width;
|
||||
if (![tableColumn isHidden])
|
||||
return [self headerRectOfColumn:columnIndex];
|
||||
}
|
||||
|
||||
while (--aColumnIndex >= 0)
|
||||
bounds.origin.x += [tableColumns[aColumnIndex] width] + tableSpacing.width;
|
||||
return nil;
|
||||
}
|
||||
|
||||
return bounds;
|
||||
- (void)_constrainDragView:(CPView)theDragView at:(CGPoint)aPoint
|
||||
{
|
||||
var tableColumns = [_tableView tableColumns],
|
||||
lastColumnRect = [self _headerRectOfLastVisibleColumn],
|
||||
activeColumnRect = [self headerRectOfColumn:_activeColumn],
|
||||
dragWindow = [theDragView window],
|
||||
frame = [dragWindow frame];
|
||||
|
||||
// Convert the frame origin from the global coordinate system to the windows' coordinate system
|
||||
frame.origin = [[self window] convertGlobalToBase:frame.origin];
|
||||
// the from the window to the view
|
||||
frame.origin = [self convertPoint:frame.origin fromView:nil];
|
||||
|
||||
// This effectively clamps the value between the minimum and maximum
|
||||
frame.origin.x = MAX(0.0, MIN(_CGRectGetMinX(frame), _CGRectGetMaxX(lastColumnRect) - _CGRectGetWidth(activeColumnRect)));
|
||||
|
||||
// Make sure the column cannot move vertically
|
||||
frame.origin.y = _CGRectGetMinY(lastColumnRect);
|
||||
|
||||
// Convert the calculated origin back to the window coordinate system
|
||||
frame.origin = [self convertPoint:frame.origin toView:nil];
|
||||
// Then back to the global coordinate system
|
||||
frame.origin = [[self window] convertBaseToGlobal:frame.origin];
|
||||
|
||||
[dragWindow setFrame:frame];
|
||||
}
|
||||
|
||||
- (void)_moveColumn:(int)aFromIndex toColumn:(int)aToIndex
|
||||
{
|
||||
[_tableView moveColumn:aFromIndex toColumn:aToIndex];
|
||||
_activeColumn = aToIndex;
|
||||
_pressedColumn = _activeColumn;
|
||||
}
|
||||
|
||||
- (void)draggedView:(CPView)aView beganAt:(CGPoint)aPoint
|
||||
{
|
||||
_isDragging = YES;
|
||||
|
||||
var column = [[_tableView tableColumns] objectAtIndex:_activeColumn];
|
||||
|
||||
[[column headerView] setHidden:YES];
|
||||
[_tableView _setDraggedColumn:column];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)draggedView:(CPView)aView movedTo:(CGPoint)aPoint
|
||||
{
|
||||
[self _constrainDragView:aView at:aPoint];
|
||||
|
||||
var dragWindow = [aView window],
|
||||
dragWindowFrame = [dragWindow frame];
|
||||
|
||||
var hoverPoint = CGPointCreateCopy(aPoint);
|
||||
|
||||
if (aPoint.x < _previousTrackingLocation.x)
|
||||
hoverPoint = _CGPointMake(_CGRectGetMinX(dragWindowFrame), _CGRectGetMinY(dragWindowFrame));
|
||||
else if (aPoint.x > _previousTrackingLocation.x)
|
||||
hoverPoint = _CGPointMake(_CGRectGetMaxX(dragWindowFrame), _CGRectGetMinY(dragWindowFrame));
|
||||
|
||||
// Convert the hover point from the global coordinate system to windows' coordinate system
|
||||
hoverPoint = [[self window] convertGlobalToBase:hoverPoint];
|
||||
// then to the view
|
||||
hoverPoint = [self convertPoint:hoverPoint fromView:nil];
|
||||
|
||||
var hoveredColumn = [self columnAtPoint:hoverPoint];
|
||||
|
||||
if (hoveredColumn !== -1)
|
||||
{
|
||||
var columnRect = [self headerRectOfColumn:hoveredColumn],
|
||||
columnCenterPoint = [self convertPoint:CGPointMake(_CGRectGetMidX(columnRect), _CGRectGetMidY(columnRect)) fromView:self];
|
||||
if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
|
||||
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
|
||||
else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
|
||||
[self _moveColumn:_activeColumn toColumn:hoveredColumn];
|
||||
}
|
||||
|
||||
_previousTrackingLocation = aPoint;
|
||||
}
|
||||
|
||||
- (void)draggedView:(CPImage)aView endedAt:(CGPoint)aLocation operation:(CPDragOperation)anOperation
|
||||
{
|
||||
_isDragging = NO;
|
||||
_isTrackingColumn = NO; // We need to do this explicitly because the mouse up section of trackMouse is never reached
|
||||
|
||||
[_tableView _setDraggedColumn:nil];
|
||||
[[[[_tableView tableColumns] objectAtIndex:_activeColumn] headerView] setHidden:NO];
|
||||
[self stopTrackingTableColumn:_activeColumn at:aLocation];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (BOOL)shouldResizeTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||
{
|
||||
if (_isResizing)
|
||||
return YES;
|
||||
|
||||
if (_isTrackingColumn)
|
||||
return NO;
|
||||
|
||||
return [_tableView allowsColumnResizing] && _CGRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
|
||||
}
|
||||
|
||||
- (void)startResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||
{
|
||||
_isResizing = YES;
|
||||
|
||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
|
||||
|
||||
[tableColumn setDisableResizingPosting:YES];
|
||||
[_tableView setDisableAutomaticResizing:YES];
|
||||
}
|
||||
|
||||
- (void)continueResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||
{
|
||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
|
||||
newWidth = [tableColumn width] + aPoint.x - _previousTrackingLocation.x;
|
||||
|
||||
if (newWidth < [tableColumn minWidth])
|
||||
[[CPCursor resizeRightCursor] set];
|
||||
else if (newWidth > [tableColumn maxWidth])
|
||||
[[CPCursor resizeLeftCursor] set];
|
||||
else
|
||||
{
|
||||
_tableView._lastColumnShouldSnap = NO;
|
||||
[tableColumn setWidth:newWidth];
|
||||
|
||||
[[CPCursor resizeLeftRightCursor] set];
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopResizingTableColumn:(int)aColumnIndex at:(CGPoint)aPoint
|
||||
{
|
||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
|
||||
[tableColumn _postDidResizeNotificationWithOldWidth:_columnOldWidth];
|
||||
[tableColumn setDisableResizingPosting:NO];
|
||||
[_tableView setDisableAutomaticResizing:NO];
|
||||
|
||||
_isResizing = NO;
|
||||
}
|
||||
|
||||
- (void)_updateResizeCursor:(CPEvent)theEvent
|
||||
{
|
||||
// never get stuck in resize cursor mode (FIXME take out when we turn on tracking rects)
|
||||
if (![_tableView allowsColumnResizing] || ([theEvent type] === CPLeftMouseUp && ![[self window] acceptsMouseMovedEvents]))
|
||||
{
|
||||
[[CPCursor arrowCursor] set];
|
||||
return;
|
||||
}
|
||||
|
||||
var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
|
||||
mouseOverLocation = CGPointMake(mouseLocation.x - 5, mouseLocation.y),
|
||||
overColumn = [self columnAtPoint:mouseOverLocation];
|
||||
|
||||
if (overColumn >= 0 && _CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
|
||||
{
|
||||
var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
|
||||
width = [tableColumn width];
|
||||
|
||||
if (width == [tableColumn minWidth])
|
||||
[[CPCursor resizeRightCursor] set];
|
||||
else if (width == [tableColumn maxWidth])
|
||||
[[CPCursor resizeLeftCursor] set];
|
||||
else
|
||||
[[CPCursor resizeLeftRightCursor] set];
|
||||
}
|
||||
else
|
||||
[[CPCursor arrowCursor] set];
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(CPEvent)theEvent
|
||||
{
|
||||
[self _updateResizeCursor:theEvent];
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(CPEvent)theEvent
|
||||
{
|
||||
[self _updateResizeCursor:theEvent];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(CPEvent)theEvent
|
||||
{
|
||||
// FIXME: we should use CPCursor push/pop (if previous currentCursor != arrow).
|
||||
[[CPCursor arrowCursor] set];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var tableColumns = [_tableView tableColumns],
|
||||
count = [tableColumns count],
|
||||
columnRect = [self bounds],
|
||||
spacing = [_tableView intercellSpacing];
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
var tableColumns = [_tableView tableColumns],
|
||||
count = [tableColumns count];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var column = [tableColumns objectAtIndex:i],
|
||||
headerView = [column headerView];
|
||||
|
||||
columnRect.size.width = [column width] + spacing.width;
|
||||
var frame = [self headerRectOfColumn:i];
|
||||
frame.size.height -= 0.5;
|
||||
if (i > 0)
|
||||
{
|
||||
frame.origin.x += 0.5;
|
||||
frame.size.width -= 1;
|
||||
}
|
||||
|
||||
[headerView setFrame:columnRect];
|
||||
[headerView setFrame:frame];
|
||||
|
||||
columnRect.origin.x += [column width] + spacing.width;
|
||||
|
||||
[self addSubview:headerView];
|
||||
if([headerView superview] != self)
|
||||
[self addSubview:headerView];
|
||||
}
|
||||
|
||||
[self setBackgroundColor:[self currentValueForThemeAttribute:@"background-color"]];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
[[_tableView gridColor] setStroke];
|
||||
if (!_tableView || ![self drawsColumnLines])
|
||||
return;
|
||||
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
exposedColumnIndexes = exposedColumnIndexes = [_tableView columnIndexesInRect:aRect],
|
||||
columnsArray = [];
|
||||
exposedColumnIndexes = [_tableView columnIndexesInRect:aRect],
|
||||
columnsArray = [],
|
||||
tableColumns = [_tableView tableColumns],
|
||||
exposedTableColumns = _tableView._exposedColumns,
|
||||
firstIndex = [exposedTableColumns firstIndex],
|
||||
exposedRange = CPMakeRange(firstIndex, [exposedTableColumns lastIndex] - firstIndex + 1);
|
||||
|
||||
[exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:nil];
|
||||
CGContextSetLineWidth(context, 1);
|
||||
CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:@"divider-color"]);
|
||||
|
||||
[exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:exposedRange];
|
||||
|
||||
var columnArrayIndex = 0,
|
||||
columnArrayCount = columnsArray.length;
|
||||
columnArrayCount = columnsArray.length,
|
||||
columnMaxX;
|
||||
|
||||
for(; columnArrayIndex < columnArrayCount; ++columnArrayIndex)
|
||||
CGContextBeginPath(context);
|
||||
for (; columnArrayIndex < columnArrayCount; columnArrayIndex++)
|
||||
{
|
||||
// grab each column rect and add horizontal lines
|
||||
var columnToStroke = [self headerRectOfColumn:columnArrayIndex];
|
||||
// grab each column rect and add vertical lines
|
||||
var columnIndex = columnsArray[columnArrayIndex],
|
||||
columnToStroke = [self headerRectOfColumn:columnIndex];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextMoveToPoint(context, ROUND(columnToStroke.origin.x + columnToStroke.size.width) - 0.5, ROUND(columnToStroke.origin.y) - 0.5);
|
||||
CGContextAddLineToPoint(context, ROUND(columnToStroke.origin.x + columnToStroke.size.width) - 0.5, ROUND(columnToStroke.origin.y + columnToStroke.size.height) - 0.5);
|
||||
CGContextSetLineWidth(context, 1);
|
||||
CGContextStrokePath(context);
|
||||
columnMaxX = _CGRectGetMaxX(columnToStroke);
|
||||
|
||||
CGContextMoveToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(_CGRectGetMinY(columnToStroke)));
|
||||
CGContextAddLineToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(_CGRectGetMaxY(columnToStroke)));
|
||||
}
|
||||
|
||||
CGContextClosePath(context);
|
||||
CGContextStrokePath(context);
|
||||
|
||||
/*if (_isDragging)
|
||||
{
|
||||
CGContextSetFillColor(context, [CPColor grayColor]);
|
||||
CGContextFillRect(context, [self headerRectOfColumn:_activeColumn])
|
||||
}*/
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey",
|
||||
CPTableHeaderViewDrawsColumnLines = @"CPTableHeaderViewDrawsColumnLines";
|
||||
|
||||
@implementation CPTableHeaderView (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
[self _init];
|
||||
_tableView = [aCoder decodeObjectForKey:CPTableHeaderViewTableViewKey];
|
||||
|
||||
// FIX ME: Take this out before 1.0
|
||||
if ([aCoder containsValueForKey:CPTableHeaderViewDrawsColumnLines])
|
||||
_drawsColumnLines = [aCoder decodeBoolForKey:CPTableHeaderViewDrawsColumnLines];
|
||||
else
|
||||
{
|
||||
_drawsColumnLines = YES;
|
||||
CPLog.warn("The tableview header being decoded is using an old cib. Please run Nib2Cib.");
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
[aCoder encodeObject:_tableView forKey:CPTableHeaderViewTableViewKey];
|
||||
[aCoder encodeBool:_drawsColumnLines forKey:CPTableHeaderViewDrawsColumnLines];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+3329
-379
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* CPText.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Alexander Ljungberg.
|
||||
* Copyright 2010, WireLoad, LLC.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
CPEnterCharacter = "\u0003";
|
||||
CPBackspaceCharacter = "\u0008";
|
||||
CPTabCharacter = "\u0009";
|
||||
CPNewlineCharacter = "\u000a";
|
||||
CPFormFeedCharacter = "\u000c";
|
||||
CPCarriageReturnCharacter = "\u000d";
|
||||
CPBackTabCharacter = "\u0019";
|
||||
CPDeleteCharacter = "\u007f";
|
||||
+444
-134
@@ -24,18 +24,8 @@
|
||||
@import "CPControl.j"
|
||||
@import "CPStringDrawing.j"
|
||||
@import "CPCompatibility.j"
|
||||
@import "_CPImageAndTextView.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
#include "Platform/Platform.h"
|
||||
#include "Platform/DOM/CPDOMDisplayServer.h"
|
||||
|
||||
|
||||
CPLineBreakByWordWrapping = 0;
|
||||
CPLineBreakByCharWrapping = 1;
|
||||
CPLineBreakByClipping = 2;
|
||||
CPLineBreakByTruncatingHead = 3;
|
||||
CPLineBreakByTruncatingTail = 4;
|
||||
CPLineBreakByTruncatingMiddle = 5;
|
||||
|
||||
CPTextFieldSquareBezel = 0; /*! A textfield bezel with a squared corners. */
|
||||
CPTextFieldRoundedBezel = 1; /*! A textfield bezel with rounded corners. */
|
||||
@@ -55,12 +45,8 @@ var CPTextFieldDOMInputElement = nil,
|
||||
CPTextFieldInputIsActive = NO,
|
||||
CPTextFieldCachedSelectStartFunction = nil,
|
||||
CPTextFieldCachedDragFunction = nil,
|
||||
|
||||
CPTextFieldBlurFunction = nil,
|
||||
CPTextFieldKeyUpFunction = nil,
|
||||
CPTextFieldKeyPressFunction = nil,
|
||||
CPTextFieldKeyDownFunction = nil;
|
||||
|
||||
CPTextFieldBlurFunction = nil;
|
||||
|
||||
#endif
|
||||
|
||||
var CPSecureTextFieldCharacter = "\u2022";
|
||||
@@ -91,15 +77,17 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
BOOL _isEditable;
|
||||
BOOL _isSelectable;
|
||||
BOOL _isSecure;
|
||||
BOOL _willBecomeFirstResponderByClick;
|
||||
|
||||
BOOL _drawsBackground;
|
||||
|
||||
|
||||
CPColor _textFieldBackgroundColor;
|
||||
|
||||
|
||||
id _placeholderString;
|
||||
|
||||
BOOL _currentValueIsPlaceholder;
|
||||
|
||||
id _delegate;
|
||||
|
||||
|
||||
CPString _textDidChangeValue;
|
||||
|
||||
// NS-style Display Properties
|
||||
@@ -166,14 +154,22 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
return textField;
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return "textfield";
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)theBinding
|
||||
{
|
||||
if (theBinding === CPValueBinding)
|
||||
return [_CPTextFieldValueBinder class];
|
||||
|
||||
return [super _binderClassForBinding:theBinding];
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMake(2.0, 2.0, 2.0, 2.0), nil]
|
||||
return [CPDictionary dictionaryWithObjects:[_CGInsetMakeZero(), _CGInsetMake(2.0, 2.0, 2.0, 2.0), [CPNull null]]
|
||||
forKeys:[@"bezel-inset", @"content-inset", @"bezel-color"]];
|
||||
}
|
||||
|
||||
@@ -210,7 +206,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
|
||||
CPTextFieldHandleBlur = function(anEvent)
|
||||
{
|
||||
{
|
||||
CPTextFieldInputOwner = nil;
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
@@ -218,10 +214,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
//FIXME make this not onblur
|
||||
CPTextFieldDOMInputElement.onblur = CPTextFieldBlurFunction;
|
||||
|
||||
|
||||
CPTextFieldDOMStandardInputElement = CPTextFieldDOMInputElement;
|
||||
}
|
||||
|
||||
|
||||
if (CPFeatureIsCompatible(CPInputTypeCanBeChangedFeature))
|
||||
{
|
||||
if ([self isSecure])
|
||||
@@ -246,20 +242,16 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
CPTextFieldDOMPasswordInputElement.style.outline = "none";
|
||||
CPTextFieldDOMPasswordInputElement.type = "password";
|
||||
|
||||
CPTextFieldDOMPasswordInputElement.attachEvent("on" + CPDOMEventKeyUp, CPTextFieldKeyUpFunction);
|
||||
CPTextFieldDOMPasswordInputElement.attachEvent("on" + CPDOMEventKeyDown, CPTextFieldKeyDownFunction);
|
||||
CPTextFieldDOMPasswordInputElement.attachEvent("on" + CPDOMEventKeyPress, CPTextFieldKeyPressFunction);
|
||||
|
||||
CPTextFieldDOMPasswordInputElement.onblur = CPTextFieldBlurFunction;
|
||||
}
|
||||
|
||||
|
||||
CPTextFieldDOMInputElement = CPTextFieldDOMPasswordInputElement;
|
||||
}
|
||||
else
|
||||
{
|
||||
CPTextFieldDOMInputElement = CPTextFieldDOMStandardInputElement;
|
||||
}
|
||||
|
||||
|
||||
return CPTextFieldDOMInputElement;
|
||||
}
|
||||
#endif
|
||||
@@ -277,18 +269,29 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
[self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Controlling Editability and Selectability
|
||||
|
||||
/*!
|
||||
Sets whether or not the receiver text field can be edited
|
||||
/*!
|
||||
Sets whether or not the receiver text field can be edited. If NO, any
|
||||
ongoing edit is ended.
|
||||
*/
|
||||
- (void)setEditable:(BOOL)shouldBeEditable
|
||||
{
|
||||
if (_isEditable === shouldBeEditable)
|
||||
return;
|
||||
|
||||
_isEditable = shouldBeEditable;
|
||||
|
||||
if (shouldBeEditable)
|
||||
_isSelectable = YES;
|
||||
|
||||
// We only allow first responder status if the field is editable and enabled.
|
||||
if (!shouldBeEditable && [[self window] firstResponder] === self)
|
||||
[[self window] makeFirstResponder:nil];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -299,6 +302,19 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
return _isEditable;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the field reacts to events. If NO, any ongoing edit is
|
||||
ended.
|
||||
*/
|
||||
- (void)setEnabled:(BOOL)shouldBeEnabled
|
||||
{
|
||||
[super setEnabled:shouldBeEnabled];
|
||||
|
||||
// We only allow first responder status if the field is editable and enabled.
|
||||
if (!shouldBeEnabled && [[self window] firstResponder] === self)
|
||||
[[self window] makeFirstResponder:nil];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the field's text is selectable by the user.
|
||||
@param aFlag \c YES makes the text selectable
|
||||
@@ -361,7 +377,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
- (void)setBezelStyle:(CPTextFieldBezelStyle)aBezelStyle
|
||||
{
|
||||
var shouldBeRounded = aBezelStyle === CPTextFieldRoundedBezel;
|
||||
|
||||
|
||||
if (shouldBeRounded)
|
||||
[self setThemeState:CPTextFieldStateRounded];
|
||||
else
|
||||
@@ -407,9 +423,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
{
|
||||
if (_drawsBackground == shouldDrawBackground)
|
||||
return;
|
||||
|
||||
|
||||
_drawsBackground = shouldDrawBackground;
|
||||
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
@@ -430,9 +446,9 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
{
|
||||
if (_textFieldBackgroundColor == aColor)
|
||||
return;
|
||||
|
||||
|
||||
_textFieldBackgroundColor = aColor;
|
||||
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
@@ -495,28 +511,34 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
_DOMElement.appendChild(element);
|
||||
|
||||
window.setTimeout(function()
|
||||
{
|
||||
window.setTimeout(function()
|
||||
{
|
||||
element.focus();
|
||||
|
||||
// Select the text if the textfield became first responder through keyboard interaction
|
||||
if (!_willBecomeFirstResponderByClick)
|
||||
[self selectText:self];
|
||||
|
||||
_willBecomeFirstResponderByClick = NO;
|
||||
|
||||
[self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
|
||||
CPTextFieldInputOwner = self;
|
||||
}, 0.0);
|
||||
|
||||
|
||||
element.value = [self stringValue];
|
||||
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
|
||||
|
||||
CPTextFieldInputIsActive = YES;
|
||||
|
||||
if (document.attachEvent)
|
||||
{
|
||||
CPTextFieldCachedSelectStartFunction = document.body.onselectstart;
|
||||
CPTextFieldCachedDragFunction = document.body.ondrag;
|
||||
|
||||
document.body.ondrag = function () {};
|
||||
document.body.onselectstart = function () {};
|
||||
CPTextFieldCachedSelectStartFunction = [[self window] platformWindow]._DOMBodyElement.onselectstart;
|
||||
CPTextFieldCachedDragFunction = [[self window] platformWindow]._DOMBodyElement.ondrag;
|
||||
|
||||
[[self window] platformWindow]._DOMBodyElement.ondrag = function () {};
|
||||
[[self window] platformWindow]._DOMBodyElement.onselectstart = function () {};
|
||||
}
|
||||
|
||||
[self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
|
||||
#endif
|
||||
|
||||
return YES;
|
||||
@@ -535,14 +557,15 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
var element = [self _inputElement];
|
||||
|
||||
[self setObjectValue:element.value];
|
||||
if ([self stringValue] !== element.value)
|
||||
[self _setStringValue:element.value];
|
||||
|
||||
CPTextFieldInputResigning = YES;
|
||||
element.blur();
|
||||
|
||||
|
||||
if (!CPTextFieldInputDidBlur)
|
||||
CPTextFieldBlurFunction();
|
||||
|
||||
|
||||
CPTextFieldInputDidBlur = NO;
|
||||
CPTextFieldInputResigning = NO;
|
||||
|
||||
@@ -553,13 +576,13 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
if (document.attachEvent)
|
||||
{
|
||||
[[self window] platformWindow]._DOMBodyElement.ondrag = CPTextFieldCachedDragFunction;
|
||||
[[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
|
||||
|
||||
CPTextFieldCachedSelectStartFunction = nil;
|
||||
CPTextFieldCachedDragFunction = nil;
|
||||
|
||||
document.body.ondrag = CPTextFieldCachedDragFunction
|
||||
document.body.onselectstart = CPTextFieldCachedSelectStartFunction
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
//post CPControlTextDidEndEditingNotification
|
||||
@@ -577,25 +600,62 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
Text fields require panels to become key window, so this returns \c YES.
|
||||
*/
|
||||
- (BOOL)needsPanelToBecomeKey
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
// Don't track! (ever?)
|
||||
if ([self isEditable] && [self isEnabled])
|
||||
return [[self window] makeFirstResponder:self];
|
||||
{
|
||||
_willBecomeFirstResponderByClick = YES;
|
||||
[[self window] makeFirstResponder:self];
|
||||
}
|
||||
else if ([self isSelectable])
|
||||
{
|
||||
if (document.attachEvent)
|
||||
{
|
||||
CPTextFieldCachedSelectStartFunction = [[self window] platformWindow]._DOMBodyElement.onselectstart;
|
||||
CPTextFieldCachedDragFunction = [[self window] platformWindow]._DOMBodyElement.ondrag;
|
||||
|
||||
[[self window] platformWindow]._DOMBodyElement.ondrag = function () {};
|
||||
[[self window] platformWindow]._DOMBodyElement.onselectstart = function () {};
|
||||
}
|
||||
return [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
}
|
||||
else
|
||||
return [[self nextResponder] mouseDown:anEvent];
|
||||
}
|
||||
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEditable] || ![self isEnabled])
|
||||
if (![self isSelectable] && (![self isEditable] || ![self isEnabled]))
|
||||
[[self nextResponder] mouseUp:anEvent];
|
||||
else if ([self isSelectable])
|
||||
{
|
||||
if (document.attachEvent)
|
||||
{
|
||||
[[self window] platformWindow]._DOMBodyElement.ondrag = CPTextFieldCachedDragFunction;
|
||||
[[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
|
||||
|
||||
CPTextFieldCachedSelectStartFunction = nil
|
||||
CPTextFieldCachedDragFunction = nil;
|
||||
}
|
||||
return [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEditable] || ![self isEnabled])
|
||||
if (![self isSelectable] && (![self isEditable] || ![self isEnabled]))
|
||||
[[self nextResponder] mouseDragged:anEvent];
|
||||
else if ([self isSelectable])
|
||||
return [[[anEvent window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
}
|
||||
|
||||
- (void)keyUp:(CPEvent)anEvent
|
||||
@@ -619,8 +679,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
- (void)keyDown:(CPEvent)anEvent
|
||||
{
|
||||
if ([anEvent keyCode] === CPReturnKeyCode)
|
||||
var characters = [anEvent characters];
|
||||
// Treat \r and \n the same. See issue #710.
|
||||
if (characters === CPNewlineCharacter || characters === CPCarriageReturnCharacter)
|
||||
{
|
||||
// selectText: has a side effect - it can change first responder of the window
|
||||
// we have to prevent such behaviour inside this method because target should be able to change first responder after receiving action.
|
||||
[self selectText:nil];
|
||||
|
||||
if (_isEditing)
|
||||
{
|
||||
_isEditing = NO;
|
||||
@@ -628,16 +694,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
[self selectText:nil];
|
||||
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:NO];
|
||||
}
|
||||
else if ([anEvent keyCode] === CPTabKeyCode)
|
||||
{
|
||||
if ([anEvent modifierFlags] & CPShiftKeyMask)
|
||||
[[self window] selectPreviousKeyView:self];
|
||||
else
|
||||
[[self window] selectNextKeyView:self];
|
||||
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:NO];
|
||||
}
|
||||
@@ -647,11 +703,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
|
||||
- (void)textDidBlur:(CPNotification)note
|
||||
{
|
||||
//this looks to prevent false propagation of notifications for other objects
|
||||
if([note object] != self)
|
||||
// this looks to prevent false propagation of notifications for other objects
|
||||
if ([note object] != self)
|
||||
return;
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotification:note];
|
||||
@@ -659,13 +714,22 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
- (void)textDidFocus:(CPNotification)note
|
||||
{
|
||||
//this looks to prevent false propagation of notifications for other objects
|
||||
if([note object] != self)
|
||||
// this looks to prevent false propagation of notifications for other objects
|
||||
if ([note object] != self)
|
||||
return;
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotification:note];
|
||||
}
|
||||
|
||||
- (void)sendAction:(SEL)anAction to:(id)anObject
|
||||
{
|
||||
// Don't reverse set our empty value
|
||||
if (!_currentValueIsPlaceholder)
|
||||
[self _reverseSetBinding];
|
||||
|
||||
[CPApp sendAction:anAction to:anObject from:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the string the text field.
|
||||
*/
|
||||
@@ -676,6 +740,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
/*
|
||||
@ignore
|
||||
Sets the internal string value without updating the value in the input element
|
||||
*/
|
||||
- (void)_setStringValue:(id)aValue
|
||||
{
|
||||
@@ -690,7 +755,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[super setObjectValue:aValue];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (CPTextFieldInputOwner === self)
|
||||
|
||||
if (CPTextFieldInputOwner === self || [[self window] firstResponder] === self)
|
||||
[self _inputElement].value = aValue;
|
||||
#endif
|
||||
|
||||
@@ -711,11 +777,11 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
Sets a placeholder string for the receiver. The placeholder is displayed until editing begins,
|
||||
and after editing ends, if the text field has an empty string value
|
||||
*/
|
||||
-(void)setPlaceholderString:(CPString)aStringValue
|
||||
- (void)setPlaceholderString:(CPString)aStringValue
|
||||
{
|
||||
if (_placeholderString === aStringValue)
|
||||
return;
|
||||
|
||||
|
||||
_placeholderString = aStringValue;
|
||||
|
||||
// Only update things if we need to show the placeholder
|
||||
@@ -735,55 +801,229 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
|
||||
/*!
|
||||
Size to fit has two behavior, depending on if the receiver is an editable text field or not.
|
||||
|
||||
For non-editable text fields (typically, a label), sizeToFit will change the frame of the
|
||||
receiver to perfectly fit the current text in stringValue in the current font, and respecting
|
||||
the current theme values for content-inset, min-size, and max-size.
|
||||
|
||||
For editable text fields, sizeToFit will ONLY change the HEIGHT of the text field. It will not
|
||||
change the width of the text field. You can use setFrameSize: with the current height to set the
|
||||
width, and you can get the size of a string with [CPString sizeWithFont:].
|
||||
|
||||
For non-bezeled text fields (typically a label), sizeToFit has two behaviors, depending
|
||||
on the line break mode of the receiver.
|
||||
|
||||
For non-bezeled receivers with a non-wrapping line break mode, sizeToFit will change the frame of the
|
||||
receiver to perfectly fit the current text in stringValue in the current font, respecting
|
||||
the current theme value for content-inset. For receivers with a wrapping line break mode,
|
||||
sizeToFit will wrap the text within the current width (respecting the current content-inset),
|
||||
so it will ONLY change the HEIGHT.
|
||||
|
||||
For bezeled text fields (typically editable fields), sizeToFit will ONLY change the HEIGHT
|
||||
of the text field. It will not change the width of the text field. sizeToFit will attempt to
|
||||
change the height to fit a single line of text, respecting the current theme values for min-size,
|
||||
max-size and content-inset.
|
||||
|
||||
The logic behind this decision is that most of the time you do not know what content will be placed
|
||||
in an editable text field, so you want to just choose a fixed width and leave it at that size.
|
||||
in a bezeled text field, so you want to just choose a fixed width and leave it at that size.
|
||||
However, since you don't know how tall it needs to be if you change the font, sizeToFit will still be
|
||||
useful for making the textfield an appropriate height.
|
||||
*/
|
||||
|
||||
- (void)sizeToFit
|
||||
{
|
||||
var size = [([self stringValue] || " ") sizeWithFont:[self currentValueForThemeAttribute:@"font"]],
|
||||
[self setFrameSize:[self _minimumFrameSize]];
|
||||
}
|
||||
|
||||
- (CGSize)_minimumFrameSize
|
||||
{
|
||||
var frameSize = [self frameSize],
|
||||
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
minSize = [self currentValueForThemeAttribute:@"min-size"],
|
||||
maxSize = [self currentValueForThemeAttribute:@"max-size"];
|
||||
maxSize = [self currentValueForThemeAttribute:@"max-size"],
|
||||
lineBreakMode = [self lineBreakMode],
|
||||
text = ([self stringValue] || @" "),
|
||||
textSize = _CGSizeMakeCopy(frameSize),
|
||||
font = [self currentValueForThemeAttribute:@"font"];
|
||||
|
||||
size.width = MAX(size.width + contentInset.left + contentInset.right, minSize.width);
|
||||
size.height = MAX(size.height + contentInset.top + contentInset.bottom, minSize.height);
|
||||
textSize.width -= contentInset.left + contentInset.right;
|
||||
textSize.height -= contentInset.top + contentInset.bottom;
|
||||
|
||||
if (maxSize.width >= 0.0)
|
||||
size.width = MIN(size.width, maxSize.width);
|
||||
if (frameSize.width !== 0 &&
|
||||
![self isBezeled] &&
|
||||
(lineBreakMode === CPLineBreakByWordWrapping || lineBreakMode === CPLineBreakByCharWrapping))
|
||||
{
|
||||
textSize = [text sizeWithFont:font inWidth:textSize.width];
|
||||
}
|
||||
else
|
||||
textSize = [text sizeWithFont:font];
|
||||
|
||||
if (maxSize.height >= 0.0)
|
||||
size.height = MIN(size.height, maxSize.height);
|
||||
frameSize.height = textSize.height + contentInset.top + contentInset.bottom;
|
||||
|
||||
if ([self isEditable])
|
||||
size.width = CGRectGetWidth([self frame]);
|
||||
if ([self isBezeled])
|
||||
{
|
||||
frameSize.height = MAX(frameSize.height, minSize.height);
|
||||
|
||||
[self setFrameSize:size];
|
||||
if (maxSize.width > 0.0)
|
||||
frameSize.width = MIN(frameSize.width, maxSize.width);
|
||||
|
||||
if (maxSize.height > 0.0)
|
||||
frameSize.height = MIN(frameSize.height, maxSize.height);
|
||||
}
|
||||
else
|
||||
frameSize.width = textSize.width + contentInset.left + contentInset.right;
|
||||
|
||||
frameSize.width = MAX(frameSize.width, minSize.width);
|
||||
|
||||
return frameSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
Select all the text in the CPTextField.
|
||||
Make the receiver the first responder and select all the text in the field.
|
||||
*/
|
||||
- (void)selectText:(id)sender
|
||||
{
|
||||
// FIXME Should this really make the text field the first responder?
|
||||
|
||||
if (([self isEditable] || [self isSelectable]))
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var element = [self _inputElement];
|
||||
|
||||
if (element.parentNode === _DOMElement && ([self isEditable] || [self isSelectable]))
|
||||
window.setTimeout(function() { element.select(); }, 0);
|
||||
var element = [self _inputElement];
|
||||
|
||||
if ([[self window] firstResponder] === self)
|
||||
window.setTimeout(function() { element.select(); }, 0);
|
||||
else if ([self window] !== nil && [[self window] makeFirstResponder:self])
|
||||
window.setTimeout(function() {[self selectText:sender];}, 0);
|
||||
#else
|
||||
// Even if we can't actually select the text we need to preserve the first
|
||||
// responder side effect.
|
||||
if ([self window] !== nil && [[self window] firstResponder] !== self)
|
||||
[[self window] makeFirstResponder:self];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
- (void)copy:(id)sender
|
||||
{
|
||||
if (![CPPlatform isBrowser])
|
||||
{
|
||||
var selectedRange = [self selectedRange];
|
||||
|
||||
if (selectedRange.length < 1)
|
||||
return;
|
||||
|
||||
var pasteboard = [CPPasteboard generalPasteboard],
|
||||
stringValue = [self stringValue],
|
||||
stringForPasting = [stringValue substringWithRange:selectedRange];
|
||||
|
||||
[pasteboard declareTypes:[CPStringPboardType] owner:nil];
|
||||
[pasteboard setString:stringForPasting forType:CPStringPboardType];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cut:(id)sender
|
||||
{
|
||||
if (![CPPlatform isBrowser])
|
||||
{
|
||||
[self copy:sender];
|
||||
[self deleteBackward:sender];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)paste:(id)sender
|
||||
{
|
||||
if (![CPPlatform isBrowser])
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
|
||||
if (![[pasteboard types] containsObject:CPStringPboardType])
|
||||
return;
|
||||
|
||||
[self deleteBackward:sender];
|
||||
|
||||
var selectedRange = [self selectedRange],
|
||||
stringValue = [self stringValue],
|
||||
pasteString = [pasteboard stringForType:CPStringPboardType],
|
||||
newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:pasteString];
|
||||
|
||||
[self setStringValue:newValue];
|
||||
[self setSelectedRange:CPMakeRange(selectedRange.location + pasteString.length, 0)];
|
||||
}
|
||||
}
|
||||
|
||||
- (CPRange)selectedRange
|
||||
{
|
||||
if ([[self window] firstResponder] !== self)
|
||||
return CPMakeRange(0, 0);
|
||||
|
||||
// we wrap this in try catch because firefox will throw an exception in certain instances
|
||||
try
|
||||
{
|
||||
var inputElement = [self _inputElement],
|
||||
selectionStart = inputElement.selectionStart,
|
||||
selectionEnd = inputElement.selectionEnd;
|
||||
|
||||
if ([selectionStart isKindOfClass:CPNumber])
|
||||
return CPMakeRange(selectionStart, selectionEnd - selectionStart);
|
||||
|
||||
// browsers which don't support selectionStart/selectionEnd (aka IE).
|
||||
var theDocument = inputElement.ownerDocument || inputElement.document,
|
||||
selectionRange = theDocument.selection.createRange(),
|
||||
range = inputElement.createTextRange();
|
||||
|
||||
if (range.inRange(selectionRange))
|
||||
{
|
||||
range.setEndPoint('EndToStart', selectionRange);
|
||||
return CPMakeRange(range.text.length, selectionRange.text.length);
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// fall through to the return
|
||||
}
|
||||
|
||||
return CPMakeRange(0, 0);
|
||||
}
|
||||
|
||||
- (void)setSelectedRange:(CPRange)aRange
|
||||
{
|
||||
if (![[self window] firstResponder] === self)
|
||||
return;
|
||||
|
||||
var inputElement = [self _inputElement];
|
||||
|
||||
try
|
||||
{
|
||||
if ([inputElement.selectionStart isKindOfClass:CPNumber])
|
||||
{
|
||||
inputElement.selectionStart = aRange.location;
|
||||
inputElement.selectionEnd = CPMaxRange(aRange);
|
||||
}
|
||||
else
|
||||
{
|
||||
// browsers which don't support selectionStart/selectionEnd (aka IE).
|
||||
var theDocument = inputElement.ownerDocument || inputElement.document,
|
||||
existingRange = theDocument.selection.createRange(),
|
||||
range = inputElement.createTextRange();
|
||||
|
||||
if (range.inRange(existingRange))
|
||||
{
|
||||
range.collapse(true);
|
||||
range.move('character', aRange.location);
|
||||
range.moveEnd('character', aRange.length);
|
||||
range.select();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
- (void)selectAll:(id)sender
|
||||
{
|
||||
[self selectText:sender];
|
||||
}
|
||||
|
||||
- (void)deleteBackward:(id)sender
|
||||
{
|
||||
var selectedRange = [self selectedRange],
|
||||
stringValue = [self stringValue],
|
||||
newValue = [stringValue stringByReplacingCharactersInRange:selectedRange withString:""];
|
||||
|
||||
[self setStringValue:newValue];
|
||||
[self setSelectedRange:CPMakeRange(selectedRange.location, 0)];
|
||||
}
|
||||
|
||||
#pragma mark Setting the Delegate
|
||||
@@ -791,7 +1031,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
- (void)setDelegate:(id)aDelegate
|
||||
{
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
|
||||
//unsubscribe the existing delegate if it exists
|
||||
if (_delegate)
|
||||
{
|
||||
@@ -801,24 +1041,24 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[defaultCenter removeObserver:_delegate name:CPTextFieldDidFocusNotification object:self];
|
||||
[defaultCenter removeObserver:_delegate name:CPTextFieldDidBlurNotification object:self];
|
||||
}
|
||||
|
||||
|
||||
_delegate = aDelegate;
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(controlTextDidBeginEditing:)])
|
||||
[defaultCenter
|
||||
addObserver:_delegate
|
||||
selector:@selector(controlTextDidBeginEditing:)
|
||||
name:CPControlTextDidBeginEditingNotification
|
||||
object:self];
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(controlTextDidChange:)])
|
||||
[defaultCenter
|
||||
addObserver:_delegate
|
||||
selector:@selector(controlTextDidChange:)
|
||||
name:CPControlTextDidChangeNotification
|
||||
object:self];
|
||||
|
||||
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(controlTextDidEndEditing:)])
|
||||
[defaultCenter
|
||||
addObserver:_delegate
|
||||
@@ -849,30 +1089,30 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
- (CGRect)contentRectForBounds:(CGRect)bounds
|
||||
{
|
||||
var contentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
||||
|
||||
|
||||
if (!contentInset)
|
||||
return bounds;
|
||||
|
||||
|
||||
bounds.origin.x += contentInset.left;
|
||||
bounds.origin.y += contentInset.top;
|
||||
bounds.size.width -= contentInset.left + contentInset.right;
|
||||
bounds.size.height -= contentInset.top + contentInset.bottom;
|
||||
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
- (CGRect)bezelRectForBounds:(CFRect)bounds
|
||||
- (CGRect)bezelRectForBounds:(CGRect)bounds
|
||||
{
|
||||
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
|
||||
|
||||
if (_CGInsetIsEmpty(bezelInset))
|
||||
return bounds;
|
||||
|
||||
|
||||
bounds.origin.x += bezelInset.left;
|
||||
bounds.origin.y += bezelInset.top;
|
||||
bounds.size.width -= bezelInset.left + bezelInset.right;
|
||||
bounds.size.height -= bezelInset.top + bezelInset.bottom;
|
||||
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@@ -880,10 +1120,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
{
|
||||
if (aName === "bezel-view")
|
||||
return [self bezelRectForBounds:[self bounds]];
|
||||
|
||||
|
||||
else if (aName === "content-view")
|
||||
return [self contentRectForBounds:[self bounds]];
|
||||
|
||||
|
||||
return [super rectForEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
@@ -894,17 +1134,19 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
[view setHitTests:NO];
|
||||
|
||||
|
||||
return view;
|
||||
}
|
||||
else
|
||||
{
|
||||
var view = [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
//[view setImagePosition:CPNoImage];
|
||||
|
||||
|
||||
[view setHitTests:NO];
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
return [super createEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
@@ -913,10 +1155,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
var bezelView = [self layoutEphemeralSubviewNamed:@"bezel-view"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:@"content-view"];
|
||||
|
||||
|
||||
if (bezelView)
|
||||
[bezelView setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
|
||||
|
||||
|
||||
var contentView = [self layoutEphemeralSubviewNamed:@"content-view"
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:@"bezel-view"];
|
||||
@@ -926,7 +1168,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[contentView setHidden:[self hasThemeState:CPThemeStateEditing]];
|
||||
|
||||
var string = "";
|
||||
|
||||
|
||||
if ([self hasThemeState:CPTextFieldStatePlaceholder])
|
||||
string = [self placeholderString];
|
||||
else
|
||||
@@ -949,6 +1191,22 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
|
||||
{
|
||||
var count = objects.length,
|
||||
value = [objects[0] valueForKeyPath:aKeyPath];
|
||||
|
||||
[self setStringValue:value];
|
||||
[self setPlaceholderString:@""];
|
||||
|
||||
while (count-- > 1)
|
||||
if (value !== [objects[count] valueForKeyPath:aKeyPath])
|
||||
{
|
||||
[self setPlaceholderString:@"Multiple Values"];
|
||||
[self setStringValue:@""];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var secureStringForString = function(aString)
|
||||
@@ -957,7 +1215,7 @@ var secureStringForString = function(aString)
|
||||
if (!aString)
|
||||
return "";
|
||||
|
||||
return Array(aString.length+1).join(CPSecureTextFieldCharacter);
|
||||
return Array(aString.length + 1).join(CPSecureTextFieldCharacter);
|
||||
}
|
||||
|
||||
|
||||
@@ -968,6 +1226,7 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
CPTextFieldBezelStyleKey = "CPTextFieldBezelStyleKey",
|
||||
CPTextFieldDrawsBackgroundKey = "CPTextFieldDrawsBackgroundKey",
|
||||
CPTextFieldLineBreakModeKey = "CPTextFieldLineBreakModeKey",
|
||||
CPTextFieldAlignmentKey = "CPTextFieldAlignmentKey",
|
||||
CPTextFieldBackgroundColorKey = "CPTextFieldBackgroundColorKey",
|
||||
CPTextFieldPlaceholderStringKey = "CPTextFieldPlaceholderStringKey";
|
||||
|
||||
@@ -981,7 +1240,7 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setEditable:[aCoder decodeBoolForKey:CPTextFieldIsEditableKey]];
|
||||
@@ -991,9 +1250,12 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
|
||||
[self setTextFieldBackgroundColor:[aCoder decodeObjectForKey:CPTextFieldBackgroundColorKey]];
|
||||
|
||||
[self setLineBreakMode:[aCoder decodeIntForKey:CPTextFieldLineBreakModeKey]];
|
||||
[self setAlignment:[aCoder decodeIntForKey:CPTextFieldAlignmentKey]];
|
||||
|
||||
[self setPlaceholderString:[aCoder decodeObjectForKey:CPTextFieldPlaceholderStringKey]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -1004,16 +1266,64 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
|
||||
[aCoder encodeBool:_isEditable forKey:CPTextFieldIsEditableKey];
|
||||
[aCoder encodeBool:_isSelectable forKey:CPTextFieldIsSelectableKey];
|
||||
|
||||
|
||||
[aCoder encodeBool:_drawsBackground forKey:CPTextFieldDrawsBackgroundKey];
|
||||
|
||||
|
||||
[aCoder encodeObject:_textFieldBackgroundColor forKey:CPTextFieldBackgroundColorKey];
|
||||
|
||||
|
||||
[aCoder encodeInt:[self lineBreakMode] forKey:CPTextFieldLineBreakModeKey];
|
||||
[aCoder encodeInt:[self alignment] forKey:CPTextFieldAlignmentKey];
|
||||
|
||||
[aCoder encodeObject:_placeholderString forKey:CPTextFieldPlaceholderStringKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPTextFieldValueBinder : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setValueFor:(CPString)theBinding
|
||||
{
|
||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [_info objectForKey:CPObservedKeyPathKey],
|
||||
options = [_info objectForKey:CPOptionsKey],
|
||||
newValue = [destination valueForKeyPath:keyPath],
|
||||
isPlaceholder = CPIsControllerMarker(newValue);
|
||||
|
||||
if (isPlaceholder)
|
||||
{
|
||||
switch (newValue)
|
||||
{
|
||||
case CPMultipleValuesMarker:
|
||||
newValue = [options objectForKey:CPMultipleValuesPlaceholderBindingOption] || @"Multiple Values";
|
||||
break;
|
||||
|
||||
case CPNoSelectionMarker:
|
||||
newValue = [options objectForKey:CPNoSelectionPlaceholderBindingOption] || @"No Selection";
|
||||
break;
|
||||
|
||||
case CPNotApplicableMarker:
|
||||
if ([options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
|
||||
[CPException raise:CPGenericException
|
||||
reason:@"can't transform non applicable key on: "+_source+" value: "+newValue];
|
||||
|
||||
newValue = [options objectForKey:CPNotApplicablePlaceholderBindingOption] || @"Not Applicable";
|
||||
break;
|
||||
}
|
||||
|
||||
[_source setPlaceholderString:newValue];
|
||||
[_source setObjectValue:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
newValue = [self transformValue:newValue withOptions:options];
|
||||
[_source setObjectValue:newValue];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
+189
-20
@@ -22,10 +22,12 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
@import <Foundation/CPKeyedUnarchiver.j>
|
||||
|
||||
|
||||
var CPThemesByName = { },
|
||||
CPThemeDefaultTheme = nil;
|
||||
var CPThemesByName = { },
|
||||
CPThemeDefaultTheme = nil,
|
||||
CPThemeDefaultHudTheme = nil;
|
||||
|
||||
|
||||
/*!
|
||||
@@ -48,6 +50,27 @@ var CPThemesByName = { },
|
||||
return CPThemeDefaultTheme;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the default HUD theme. If set to nil, the default described in defaultHudTheme
|
||||
will be used.
|
||||
*/
|
||||
+ (void)setDefaultHudTheme:(CPTheme)aTheme
|
||||
{
|
||||
CPThemeDefaultHudTheme = aTheme;
|
||||
}
|
||||
|
||||
/*!
|
||||
The default HUD theme is (sometimes) used for windows with the CPHUDBackgroundWindowMask
|
||||
style mask. The default is theme with the name of the default theme with -HUD appended
|
||||
at the end.
|
||||
*/
|
||||
+ (CPTheme)defaultHudTheme
|
||||
{
|
||||
if (!CPThemeDefaultHudTheme)
|
||||
CPThemeDefaultHudTheme = [CPTheme themeNamed:[[self defaultTheme] name] + "-HUD"];
|
||||
return CPThemeDefaultHudTheme;
|
||||
}
|
||||
|
||||
+ (CPTheme)themeNamed:(CPString)aName
|
||||
{
|
||||
return CPThemesByName[aName];
|
||||
@@ -73,9 +96,110 @@ var CPThemesByName = { },
|
||||
return _name;
|
||||
}
|
||||
|
||||
- (_CPThemeAttribute)_attributeWithName:(CPString)aName forClass:(CPString)aClass
|
||||
/*!
|
||||
Returns an array of names of themed classes defined in this theme, as found in its
|
||||
ThemeDescriptors.j file.
|
||||
|
||||
NOTE: The names are not class names (such as "CPButton"), but the names returned
|
||||
by the class' +defaultThemeClass method. For example, the name for CPCheckBox is "check-box",
|
||||
as defined in CPCheckBox::themeClass.
|
||||
*/
|
||||
- (CPArray)classNames
|
||||
{
|
||||
var attributes = [_attributes objectForKey:aClass];
|
||||
return [_attributes allKeys];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a dictionary of all theme attributes defined for the given class, as found in the
|
||||
theme's ThemeDescriptors.j file. The keys of the dictionary are attribute names, and the values
|
||||
are instances of _CPThemeAttribute.
|
||||
|
||||
For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
|
||||
|
||||
@param aClass The themed class whose attributes you want to retrieve
|
||||
@return A dictionary of attributes or nil
|
||||
*/
|
||||
- (CPDictionary)attributesForClass:(id)aClass
|
||||
{
|
||||
if (!aClass)
|
||||
return nil;
|
||||
|
||||
var className = nil;
|
||||
|
||||
if ([aClass isKindOfClass:[CPString class]])
|
||||
{
|
||||
// See if it is a class name
|
||||
var theClass = CPClassFromString(aClass);
|
||||
|
||||
if (theClass)
|
||||
aClass = theClass;
|
||||
else
|
||||
className = aClass;
|
||||
}
|
||||
|
||||
if (!className)
|
||||
{
|
||||
if ([aClass isKindOfClass:[CPView class]])
|
||||
{
|
||||
if ([aClass respondsToSelector:@selector(defaultThemeClass)])
|
||||
className = [aClass defaultThemeClass];
|
||||
else if ([aClass respondsToSelector:@selector(themeClass)])
|
||||
{
|
||||
CPLog.warn(@"%@ themeClass is deprecated in favor of defaultThemeClass",CPStringFromClass([anObject class]));
|
||||
className = [aClass themeClass];
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
else
|
||||
[CPException raise:CPInvalidArgumentException reason:@"aClass must be a class object or a string."];
|
||||
}
|
||||
|
||||
return [_attributes objectForKey:className];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns an array of names of all theme attributes defined for the given class, as found in the
|
||||
theme's ThemeDescriptors.j file.
|
||||
|
||||
The \c aClass parameter can be one of the following:
|
||||
|
||||
- A class instance, for example the result of [CPCheckBox class]. The class must be a subclass
|
||||
of CPView.
|
||||
- A class name, for example "CPCheckBox".
|
||||
- A themed class name, for example "check-box".
|
||||
|
||||
If \c aClass does not refer to a themed class in this theme, nil is returned.
|
||||
|
||||
@param aClass The themed class whose attributes you want to retrieve
|
||||
@return An array of attribute names or nil
|
||||
*/
|
||||
- (CPDictionary)attributeNamesForClass:(id)aClass
|
||||
{
|
||||
var attributes = [self attributesForClass:aClass];
|
||||
|
||||
if (attributes)
|
||||
return [attributes allKeys];
|
||||
else
|
||||
return [CPArray array];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a theme attribute defined for the given class, as found in the
|
||||
theme's ThemeDescriptors.j file.
|
||||
|
||||
\c aName should be the attribute name as you would pass to the method
|
||||
CPView::valueForThemeAttribute:.
|
||||
|
||||
For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
|
||||
|
||||
@param aName The name of the attribute you want to retrieve
|
||||
@param aClass The themed class in which to look for the attribute
|
||||
@return An instance of _CPThemeAttribute or nil
|
||||
*/
|
||||
- (_CPThemeAttribute)attributeWithName:(CPString)aName forClass:(id)aClass
|
||||
{
|
||||
var attributes = [self attributesForClass:aClass];
|
||||
|
||||
if (!attributes)
|
||||
return nil;
|
||||
@@ -83,12 +207,53 @@ var CPThemesByName = { },
|
||||
return [attributes objectForKey:aName];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the value for a theme attribute in its normal state, as defined for the given class
|
||||
in the theme's ThemeDescriptors.j file.
|
||||
|
||||
\c aName should be the attribute name as you would pass to the method
|
||||
CPView::valueForThemeAttribute:.
|
||||
|
||||
For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
|
||||
|
||||
@param aName The name of the attribute whose value you want to retrieve
|
||||
@param aClass The themed class in which to look for the attribute
|
||||
@return A value or nil
|
||||
*/
|
||||
- (id)valueForAttributeWithName:(CPString)aName forClass:(id)aClass
|
||||
{
|
||||
return [self valueForAttributeWithName:aName inState:CPThemeStateNormal forClass:aClass];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the value for a theme attribute in a given state, as defined for the given class
|
||||
in the theme's ThemeDescriptors.j file. This is the equivalent of the method
|
||||
CPView::valueForThemeAttribute:inState:, but retrieves the value from the theme definition as
|
||||
opposed to a single view's current theme state.
|
||||
|
||||
For a description of valid values for \c aClass, see \ref attributeNamesForClass:.
|
||||
|
||||
@param aName The name of the attribute whose value you want to retrieve
|
||||
@param aState The state qualifier for the attribute
|
||||
@param aClass The themed class in which to look for the attribute
|
||||
@return A value or nil
|
||||
*/
|
||||
- (id)valueForAttributeWithName:(CPString)aName inState:(CPThemeState)aState forClass:(id)aClass
|
||||
{
|
||||
var attribute = [self attributeWithName:aName forClass:aClass];
|
||||
|
||||
if (!attribute)
|
||||
return nil;
|
||||
|
||||
return [attribute valueForState:aState];
|
||||
}
|
||||
|
||||
- (void)takeThemeFromObject:(id)anObject
|
||||
{
|
||||
var attributes = [anObject _themeAttributeDictionary],
|
||||
attributeName = nil,
|
||||
attributeNames = [attributes keyEnumerator],
|
||||
objectThemeClass = [[anObject class] themeClass];
|
||||
objectThemeClass = [anObject themeClass];
|
||||
|
||||
while (attributeName = [attributeNames nextObject])
|
||||
[self _recordAttribute:[attributes objectForKey:attributeName] forClass:objectThemeClass];
|
||||
@@ -240,8 +405,12 @@ function CPThemeStateName(aState)
|
||||
CPThemeStateNames[0] = "normal";
|
||||
CPThemeStateNormal = CPThemeStates["normal"] = 0;
|
||||
CPThemeStateDisabled = CPThemeState("disabled");
|
||||
CPThemeStateHovered = CPThemeState("hovered");
|
||||
CPThemeStateHighlighted = CPThemeState("highlighted");
|
||||
CPThemeStateSelected = CPThemeState("selected");
|
||||
CPThemeStateTableDataView = CPThemeState("tableDataView");
|
||||
CPThemeStateSelectedDataView = CPThemeStateSelectedTableDataView = CPThemeState("selectedTableDataView");
|
||||
CPThemeStateGroupRow = CPThemeState("CPThemeStateGroupRow");
|
||||
CPThemeStateBezeled = CPThemeState("bezeled");
|
||||
CPThemeStateBordered = CPThemeState("bordered");
|
||||
CPThemeStateEditable = CPThemeState("editable");
|
||||
@@ -254,10 +423,10 @@ CPThemeStateCircular = CPThemeState("circular");
|
||||
{
|
||||
CPString _name;
|
||||
id _defaultValue;
|
||||
CPDictionary _values;
|
||||
CPDictionary _values @accessors(readonly, getter=values);
|
||||
|
||||
JSObject _cache;
|
||||
CPThemeAttribute _parentAttribute;
|
||||
_CPThemeAttribute _parentAttribute;
|
||||
}
|
||||
|
||||
- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue
|
||||
@@ -378,7 +547,7 @@ CPThemeStateCircular = CPThemeState("circular");
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void)setParentAttribute:(CPThemeAttribute)anAttribute
|
||||
- (void)setParentAttribute:(_CPThemeAttribute)anAttribute
|
||||
{
|
||||
if (_parentAttribute === anAttribute)
|
||||
return;
|
||||
@@ -387,7 +556,7 @@ CPThemeStateCircular = CPThemeState("circular");
|
||||
_parentAttribute = anAttribute;
|
||||
}
|
||||
|
||||
- (CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
|
||||
- (_CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
|
||||
{
|
||||
var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue];
|
||||
|
||||
@@ -472,15 +641,15 @@ CPThemeStateCircular = CPThemeState("circular");
|
||||
|
||||
@end
|
||||
|
||||
var cachedNumberOfOnes = [ 0 /*000000*/, 1 /*000001*/, 1 /*000010*/, 2 /*000011*/, 1 /*000100*/, 2 /*000101*/, 2 /*000110*/,
|
||||
3 /*000111*/, 1 /*001000*/, 2 /*001001*/, 2 /*001010*/, 3 /*001011*/, 2 /*001100*/, 3 /*001101*/,
|
||||
3 /*001110*/, 4 /*001111*/, 1 /*010000*/, 2 /*010001*/, 2 /*010010*/, 3 /*010011*/, 2 /*010100*/,
|
||||
3 /*010101*/, 3 /*010110*/, 4 /*010111*/, 2 /*011000*/, 3 /*011001*/, 3 /*011010*/, 4 /*011011*/,
|
||||
3 /*011100*/, 4 /*011101*/, 4 /*011110*/, 5 /*011111*/, 1 /*100000*/, 2 /*100001*/, 2 /*100010*/,
|
||||
3 /*100011*/, 2 /*100100*/, 3 /*100101*/, 3 /*100110*/, 4 /*100111*/, 2 /*101000*/, 3 /*101001*/,
|
||||
3 /*101010*/, 4 /*101011*/, 3 /*101100*/, 4 /*101101*/, 4 /*101110*/, 5 /*101111*/, 2 /*110000*/,
|
||||
3 /*110001*/, 3 /*110010*/, 4 /*110011*/, 3 /*110100*/, 4 /*110101*/, 4 /*110110*/, 5 /*110111*/,
|
||||
3 /*111000*/, 4 /*111001*/, 4 /*111010*/, 5 /*111011*/, 4 /*111100*/, 5 /*111101*/, 5 /*111110*/,
|
||||
var cachedNumberOfOnes = [ 0 /*000000*/, 1 /*000001*/, 1 /*000010*/, 2 /*000011*/, 1 /*000100*/, 2 /*000101*/, 2 /*000110*/,
|
||||
3 /*000111*/, 1 /*001000*/, 2 /*001001*/, 2 /*001010*/, 3 /*001011*/, 2 /*001100*/, 3 /*001101*/,
|
||||
3 /*001110*/, 4 /*001111*/, 1 /*010000*/, 2 /*010001*/, 2 /*010010*/, 3 /*010011*/, 2 /*010100*/,
|
||||
3 /*010101*/, 3 /*010110*/, 4 /*010111*/, 2 /*011000*/, 3 /*011001*/, 3 /*011010*/, 4 /*011011*/,
|
||||
3 /*011100*/, 4 /*011101*/, 4 /*011110*/, 5 /*011111*/, 1 /*100000*/, 2 /*100001*/, 2 /*100010*/,
|
||||
3 /*100011*/, 2 /*100100*/, 3 /*100101*/, 3 /*100110*/, 4 /*100111*/, 2 /*101000*/, 3 /*101001*/,
|
||||
3 /*101010*/, 4 /*101011*/, 3 /*101100*/, 4 /*101101*/, 4 /*101110*/, 5 /*101111*/, 2 /*110000*/,
|
||||
3 /*110001*/, 3 /*110010*/, 4 /*110011*/, 3 /*110100*/, 4 /*110101*/, 4 /*110110*/, 5 /*110111*/,
|
||||
3 /*111000*/, 4 /*111001*/, 4 /*111010*/, 5 /*111011*/, 4 /*111100*/, 5 /*111101*/, 5 /*111110*/,
|
||||
6 /*111111*/ ];
|
||||
|
||||
var numberOfOnes = function(aNumber)
|
||||
@@ -551,7 +720,7 @@ function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme,
|
||||
}
|
||||
|
||||
if (aTheme && aClass)
|
||||
[attribute setParentAttribute:[aTheme _attributeWithName:anAttributeName forClass:aClass]];
|
||||
[attribute setParentAttribute:[aTheme attributeWithName:anAttributeName forClass:aClass]];
|
||||
|
||||
return attribute;
|
||||
}
|
||||
@@ -585,4 +754,4 @@ for (i = 0;i < Math.pow(2,6);++i)
|
||||
}
|
||||
print(str+']');
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
+36
-12
@@ -22,9 +22,10 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import <AppKit/CPTheme.j>
|
||||
@import <AppKit/_CPCibCustomResource.j>
|
||||
@import <AppKit/_CPCibKeyedUnarchiver.j>
|
||||
@import "CPTheme.j"
|
||||
@import "_CPCibCustomResource.j"
|
||||
@import "_CPCibKeyedUnarchiver.j"
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -33,39 +34,62 @@
|
||||
@implementation CPThemeBlend : CPObject
|
||||
{
|
||||
CPBundle _bundle;
|
||||
CPArray _themes @accessors(readonly, getter=themes);
|
||||
CPArray _themes;
|
||||
id _loadDelegate;
|
||||
}
|
||||
|
||||
- (id)initWithContentsOfURL:(CPURL)aURL
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_bundle = [[CPBundle alloc] initWithPath:aURL + "/Info.plist"];
|
||||
_bundle = [[CPBundle alloc] initWithPath:aURL];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns an array of names of the keyed theme archives that make up this blend.
|
||||
Each item in the array will have the extension ".keyedtheme".
|
||||
*/
|
||||
- (CPArray)themes
|
||||
{
|
||||
return _themes;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns an array of names of the themes that make up this blend.
|
||||
*/
|
||||
- (CPArray)themeNames
|
||||
{
|
||||
var names = [];
|
||||
|
||||
for (var i = 0; i < _themes.length; ++i)
|
||||
names.push(_themes[i].substring(0, _themes[i].indexOf(".keyedtheme")));
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
- (void)loadWithDelegate:(id)aDelegate
|
||||
{
|
||||
_loadDelegate = aDelegate;
|
||||
|
||||
|
||||
[_bundle loadWithDelegate:self];
|
||||
}
|
||||
|
||||
- (void)bundleDidFinishLoading:(CPBundle)aBundle
|
||||
{
|
||||
var themes = [_bundle objectForInfoDictionaryKey:@"CPKeyedThemes"],
|
||||
count = themes.length;
|
||||
_themes = [_bundle objectForInfoDictionaryKey:@"CPKeyedThemes"];
|
||||
|
||||
var count = _themes.length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var path = [aBundle pathForResource:themes[count]],
|
||||
var path = [aBundle pathForResource:_themes[count]],
|
||||
unarchiver = [[_CPThemeKeyedUnarchiver alloc]
|
||||
initForReadingWithData:[CPData dataWithString:objj_files[path].contents]
|
||||
initForReadingWithData:[[CPURL URLWithString:path] staticResourceData]
|
||||
bundle:_bundle];
|
||||
|
||||
[unarchiver decodeObjectForKey:@"root"];
|
||||
|
||||
Executable
+1294
File diff suppressed because it is too large
Load Diff
+240
-128
@@ -28,8 +28,6 @@
|
||||
@import "CPPopUpButton.j"
|
||||
@import "CPToolbarItem.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
/*
|
||||
@global
|
||||
@@ -58,7 +56,7 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPToolbar
|
||||
|
||||
|
||||
A CPToolbar is displayed at the top of a window with multiple
|
||||
buttons (tools) that offer the user quick access to features.
|
||||
|
||||
@@ -73,7 +71,7 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
Called to obtain the toolbar's default item identifiers. Required.
|
||||
@param toolbar the toolbar to obtain identifiers for
|
||||
@return an array of default item identifiers in the order on the toolbar
|
||||
|
||||
|
||||
@delegate - (CPToolbarItem)toolbar:(CPToolbar)toolbar itemForItemIdentifier:(CPString)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag;
|
||||
Called to obtain a toolbar item. Required.
|
||||
@param toolbar the toolbar the item belongs to
|
||||
@@ -89,19 +87,19 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
BOOL _showsBaselineSeparator;
|
||||
BOOL _allowsUserCustomization;
|
||||
BOOL _isVisible;
|
||||
|
||||
|
||||
id _delegate;
|
||||
|
||||
|
||||
CPArray _itemIdentifiers;
|
||||
|
||||
|
||||
CPDictionary _identifiedItems;
|
||||
CPArray _defaultItems;
|
||||
CPArray _allowedItems;
|
||||
CPArray _selectableItems;
|
||||
|
||||
|
||||
CPArray _items;
|
||||
CPArray _itemsSortedByVisibilityPriority;
|
||||
|
||||
|
||||
CPView _toolbarView;
|
||||
CPWindow _window;
|
||||
}
|
||||
@@ -111,7 +109,7 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
{
|
||||
if (self != [CPToolbar class])
|
||||
return;
|
||||
|
||||
|
||||
CPToolbarsByIdentifier = [CPDictionary dictionary];
|
||||
CPToolbarConfigurationsByIdentifier = [CPDictionary dictionary];
|
||||
}
|
||||
@@ -120,13 +118,13 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
+ (void)_addToolbar:(CPToolbar)toolbar forIdentifier:(CPString)identifier
|
||||
{
|
||||
var toolbarsSharingIdentifier = [CPToolbarsByIdentifier objectForKey:identifier];
|
||||
|
||||
|
||||
if (!toolbarsSharingIdentifier)
|
||||
{
|
||||
toolbarsSharingIdentifier = []
|
||||
[CPToolbarsByIdentifier setObject:toolbarsSharingIdentifier forKey:identifier];
|
||||
}
|
||||
|
||||
|
||||
[toolbarsSharingIdentifier addObject:toolbar];
|
||||
}
|
||||
|
||||
@@ -143,17 +141,17 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
- (id)initWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_items = [];
|
||||
|
||||
|
||||
_identifier = anIdentifier;
|
||||
_isVisible = YES;
|
||||
|
||||
|
||||
[CPToolbar _addToolbar:self forIdentifier:_identifier];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -163,7 +161,7 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
*/
|
||||
- (void)setDisplayMode:(CPToolbarDisplayMode)aDisplayMode
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -198,7 +196,7 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
{
|
||||
if (_isVisible === aFlag)
|
||||
return;
|
||||
|
||||
|
||||
_isVisible = aFlag;
|
||||
|
||||
[_window _noteToolbarChanged];
|
||||
@@ -220,11 +218,11 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
*/
|
||||
- (void)setDelegate:(id)aDelegate
|
||||
{
|
||||
if (_delegate == aDelegate)
|
||||
if (_delegate === aDelegate)
|
||||
return;
|
||||
|
||||
|
||||
_delegate = aDelegate;
|
||||
|
||||
|
||||
[self _reloadToolbarItems];
|
||||
}
|
||||
|
||||
@@ -240,57 +238,68 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
if (!_toolbarView)
|
||||
{
|
||||
_toolbarView = [[_CPToolbarView alloc] initWithFrame:CPRectMake(0.0, 0.0, 1200.0, 59.0)];
|
||||
|
||||
|
||||
[_toolbarView setToolbar:self];
|
||||
[_toolbarView setAutoresizingMask:CPViewWidthSizable];
|
||||
[_toolbarView reloadToolbarItems];
|
||||
}
|
||||
|
||||
|
||||
return _toolbarView;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_reloadToolbarItems
|
||||
{
|
||||
if (!_delegate)
|
||||
return;
|
||||
// As of OS X 10.5 (Leopard), toolbar items can be set in IB and a
|
||||
// toolbar delegate is optional. Toolbar items can be combined from
|
||||
// both IB and a delegate (see Apple's NSToolbar guide for IB, for more details).
|
||||
|
||||
var count = [_itemIdentifiers count];
|
||||
|
||||
if (!count)
|
||||
// _defaultItems may have been loaded from Cib
|
||||
_itemIdentifiers = [_defaultItems valueForKey:@"itemIdentifier"] || [];
|
||||
|
||||
if (_delegate)
|
||||
{
|
||||
_itemIdentifiers = [[_delegate toolbarDefaultItemIdentifiers:self] mutableCopy];
|
||||
count = [_itemIdentifiers count];
|
||||
var itemIdentifiersFromDelegate = [[_delegate toolbarDefaultItemIdentifiers:self] mutableCopy];
|
||||
|
||||
if (itemIdentifiersFromDelegate)
|
||||
_itemIdentifiers = [_itemIdentifiers arrayByAddingObjectsFromArray:itemIdentifiersFromDelegate];
|
||||
}
|
||||
|
||||
var index = 0,
|
||||
count = [_itemIdentifiers count];
|
||||
|
||||
_items = [];
|
||||
|
||||
var index = 0;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var identifier = _itemIdentifiers[index],
|
||||
item = [CPToolbarItem _standardItemWithItemIdentifier:identifier];
|
||||
|
||||
|
||||
// May come from a Cib.
|
||||
if (!item)
|
||||
item = [_identifiedItems objectForKey:identifier];
|
||||
|
||||
if (!item && _delegate)
|
||||
item = [_delegate toolbar:self itemForItemIdentifier:identifier willBeInsertedIntoToolbar:YES];
|
||||
|
||||
|
||||
item = [item copy];
|
||||
|
||||
if (item == nil)
|
||||
|
||||
if (item === nil)
|
||||
[CPException raise:CPInvalidArgumentException
|
||||
reason:sprintf(@"_delegate %s returned nil toolbar item returned for identifier %s", _delegate, identifier)];
|
||||
|
||||
reason:@"Toolbar delegate " + _delegate + " returned nil toolbar item for identifier \"" + identifier + "\""];
|
||||
|
||||
item._toolbar = self;
|
||||
|
||||
[_items addObject:item];
|
||||
}
|
||||
|
||||
|
||||
// _items = [[self _defaultToolbarItems] mutableCopy];
|
||||
|
||||
|
||||
// Store items sorted by priority. We want items to be removed first at the end of the array,
|
||||
// items to be removed last at the front.
|
||||
|
||||
|
||||
_itemsSortedByVisibilityPriority = [_items sortedArrayUsingFunction:_CPToolbarItemVisibilityPriorityCompare context:NULL];
|
||||
|
||||
|
||||
[_toolbarView reloadToolbarItems];
|
||||
}
|
||||
|
||||
@@ -318,7 +327,11 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
return _itemsSortedByVisibilityPriority;
|
||||
}
|
||||
|
||||
- (void)validateVisibleToolbarItems
|
||||
/*!
|
||||
Validates the visible toolbar items by sending a validate message to
|
||||
each visible toolbar item.
|
||||
*/
|
||||
- (void)validateVisibleItems
|
||||
{
|
||||
var toolbarItems = [self visibleItems],
|
||||
count = [toolbarItems count];
|
||||
@@ -339,9 +352,9 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
item = [[_delegate toolbar:self itemForItemIdentifier:identifier willBeInsertedIntoToolbar:toolbar] copy];
|
||||
if (!item)
|
||||
[CPException raise:CPInvalidArgumentException
|
||||
reason:sprintf(@"_delegate %s returned nil toolbar item returned for identifier %s", _delegate, identifier)];
|
||||
reason:@"Toolbar delegate " + _delegate + " returned nil toolbar item for identifier " + identifier];
|
||||
}
|
||||
|
||||
|
||||
[_identifiedItems setObject:item forKey:identifier];
|
||||
}
|
||||
|
||||
@@ -350,7 +363,7 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
|
||||
/* @ignore */
|
||||
- (id)_itemsWithIdentifiers:(CPArray)identifiers
|
||||
{
|
||||
{
|
||||
var items = [];
|
||||
for (var i = 0; i < identifiers.length; i++)
|
||||
[items addObject:[self _itemForItemIdentifier:identifiers[i] willBeInsertedIntoToolbar:NO]];
|
||||
@@ -359,28 +372,62 @@ var CPToolbarConfigurationsByIdentifier = nil;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
-(id)_defaultToolbarItems
|
||||
- (id)_defaultToolbarItems
|
||||
{
|
||||
if (!_defaultItems)
|
||||
if ([_delegate respondsToSelector:@selector(toolbarDefaultItemIdentifiers:)])
|
||||
_defaultItems = [self _itemsWithIdentifiers:[_delegate toolbarDefaultItemIdentifiers:self]];
|
||||
|
||||
if (!_defaultItems && [_delegate respondsToSelector:@selector(toolbarDefaultItemIdentifiers:)])
|
||||
{
|
||||
_defaultItems = [];
|
||||
|
||||
var identifiers = [_delegate toolbarDefaultItemIdentifiers:self],
|
||||
index = 0,
|
||||
count = [identifiers count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
[_defaultItems addObject:[self _itemForItemIdentifier:identifiers[index] willBeInsertedIntoToolbar:NO]];
|
||||
}
|
||||
|
||||
return _defaultItems;
|
||||
}
|
||||
|
||||
/*!
|
||||
Notifies the toolbar that an item has been changed. This will cause the toolbar to reload its items.
|
||||
@param anItem the item that has been changed
|
||||
*/
|
||||
- (void)toolbarItemDidChange:(CPToolbarItem)anItem
|
||||
{
|
||||
if ([_identifiedItems objectForKey:[anItem itemIdentifier]])
|
||||
[_identifiedItems setObject:anItem forKey:[anItem itemIdentifier]];
|
||||
|
||||
var index = 0,
|
||||
count = [_items count];
|
||||
|
||||
for (; index <= count; ++index)
|
||||
{
|
||||
var item = _items[index];
|
||||
|
||||
if ([item itemIdentifier] === [anItem itemIdentifier])
|
||||
{
|
||||
_items[index] = anItem;
|
||||
_itemsSortedByVisibilityPriority = [_items sortedArrayUsingFunction:_CPToolbarItemVisibilityPriorityCompare context:NULL];
|
||||
|
||||
[_toolbarView reloadToolbarItems];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPToolbarIdentifierKey = "CPToolbarIdentifierKey",
|
||||
CPToolbarDisplayModeKey = "CPToolbarDisplayModeKey",
|
||||
CPToolbarShowsBaselineSeparatorKey = "CPToolbarShowsBaselineSeparatorKey",
|
||||
CPToolbarAllowsUserCustomizationKey = "CPToolbarAllowsUserCustomizationKey",
|
||||
CPToolbarIsVisibleKey = "CPToolbarIsVisibleKey",
|
||||
CPToolbarDelegateKey = "CPToolbarDelegateKey",
|
||||
CPToolbarIdentifiedItemsKey = "CPToolbarIdentifiedItemsKey",
|
||||
CPToolbarDefaultItemsKey = "CPToolbarDefaultItemsKey",
|
||||
CPToolbarAllowedItemsKey = "CPToolbarAllowedItemsKey",
|
||||
CPToolbarSelectableItemsKey = "CPToolbarSelectableItemsKey";
|
||||
var CPToolbarIdentifierKey = @"CPToolbarIdentifierKey",
|
||||
CPToolbarDisplayModeKey = @"CPToolbarDisplayModeKey",
|
||||
CPToolbarShowsBaselineSeparatorKey = @"CPToolbarShowsBaselineSeparatorKey",
|
||||
CPToolbarAllowsUserCustomizationKey = @"CPToolbarAllowsUserCustomizationKey",
|
||||
CPToolbarIsVisibleKey = @"CPToolbarIsVisibleKey",
|
||||
CPToolbarDelegateKey = @"CPToolbarDelegateKey",
|
||||
CPToolbarIdentifiedItemsKey = @"CPToolbarIdentifiedItemsKey",
|
||||
CPToolbarDefaultItemsKey = @"CPToolbarDefaultItemsKey",
|
||||
CPToolbarAllowedItemsKey = @"CPToolbarAllowedItemsKey",
|
||||
CPToolbarSelectableItemsKey = @"CPToolbarSelectableItemsKey";
|
||||
|
||||
@implementation CPToolbar (CPCoding)
|
||||
|
||||
@@ -391,26 +438,46 @@ var CPToolbarIdentifierKey = "CPToolbarIdentifierKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_identifier = [aCoder decodeObjectForKey:CPToolbarIdentifierKey];
|
||||
_displayMode = [aCoder decodeIntForKey:CPToolbarDisplayModeKey];
|
||||
_showsBaselineSeparator = [aCoder decodeBoolForKey:CPToolbarShowsBaselineSeparatorKey];
|
||||
_allowsUserCustomization = [aCoder decodeBoolForKey:CPToolbarAllowsUserCustomizationKey];
|
||||
_isVisible = [aCoder decodeBoolForKey:CPToolbarIsVisibleKey];
|
||||
|
||||
_identifiedItems = [aCoder decodeObjectForKey:CPToolbarIdentifiedItemsKey];
|
||||
_defaultItems = [aCoder decodeObjectForKey:CPToolbarDefaultItemsKey];
|
||||
_allowedItems = [aCoder decodeObjectForKey:CPToolbarAllowedItemsKey];
|
||||
_selectableItems = [aCoder decodeObjectForKey:CPToolbarSelectableItemsKey];
|
||||
|
||||
_identifier = [aCoder decodeObjectForKey:CPToolbarIdentifierKey];
|
||||
_displayMode = [aCoder decodeIntForKey:CPToolbarDisplayModeKey];
|
||||
_showsBaselineSeparator = [aCoder decodeBoolForKey:CPToolbarShowsBaselineSeparatorKey];
|
||||
_allowsUserCustomization = [aCoder decodeBoolForKey:CPToolbarAllowsUserCustomizationKey];
|
||||
_isVisible = [aCoder decodeBoolForKey:CPToolbarIsVisibleKey];
|
||||
|
||||
_identifiedItems = [aCoder decodeObjectForKey:CPToolbarIdentifiedItemsKey];
|
||||
_defaultItems = [aCoder decodeObjectForKey:CPToolbarDefaultItemsKey];
|
||||
_allowedItems = [aCoder decodeObjectForKey:CPToolbarAllowedItemsKey];
|
||||
_selectableItems = [aCoder decodeObjectForKey:CPToolbarSelectableItemsKey];
|
||||
|
||||
[[_identifiedItems allValues] makeObjectsPerformSelector:@selector(_setToolbar:) withObject:self];
|
||||
|
||||
_items = [];
|
||||
|
||||
[CPToolbar _addToolbar:self forIdentifier:_identifier];
|
||||
|
||||
|
||||
// This won't come from a Cib, but can come from manual encoding.
|
||||
[self setDelegate:[aCoder decodeObjectForKey:CPToolbarDelegateKey]];
|
||||
|
||||
// Because we don't know if a delegate will be set later (it is optional
|
||||
// as of OS X 10.5), we need to call -_reloadToolbarItems here.
|
||||
// In order to load any toolbar items that may have been configured in the
|
||||
// Cib. Unfortunatelly this means that if there is a delegate
|
||||
// specified, it will be read later and the resulting call to -setDelegate:
|
||||
// will cause -_reloadToolbarItems] to run again :-(
|
||||
// FIXME: Can we make this better?
|
||||
|
||||
// Do this at the end of the run loop to allow all the cib-stuff to
|
||||
// finish (establishing connections, etc.).
|
||||
[[CPRunLoop currentRunLoop]
|
||||
performSelector:@selector(_reloadToolbarItems)
|
||||
target:self
|
||||
argument:nil
|
||||
order:0 modes:[CPDefaultRunLoopMode]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -420,18 +487,18 @@ var CPToolbarIdentifierKey = "CPToolbarIdentifierKey",
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:CPToolbarIdentifierKey];
|
||||
[aCoder encodeInt:_displayMode forKey:CPToolbarDisplayModeKey];
|
||||
[aCoder encodeBool:_showsBaselineSeparator forKey:CPToolbarShowsBaselineSeparatorKey];
|
||||
[aCoder encodeObject:_identifier forKey:CPToolbarIdentifierKey];
|
||||
[aCoder encodeInt:_displayMode forKey:CPToolbarDisplayModeKey];
|
||||
[aCoder encodeBool:_showsBaselineSeparator forKey:CPToolbarShowsBaselineSeparatorKey];
|
||||
[aCoder encodeBool:_allowsUserCustomization forKey:CPToolbarAllowsUserCustomizationKey];
|
||||
[aCoder encodeBool:_isVisible forKey:CPToolbarIsVisibleKey];
|
||||
|
||||
[aCoder encodeObject:_identifiedItems forKey:CPToolbarIdentifiedItemsKey];
|
||||
[aCoder encodeObject:_defaultItems forKey:CPToolbarDefaultItemsKey];
|
||||
[aCoder encodeObject:_allowedItems forKey:CPToolbarAllowedItemsKey];
|
||||
[aCoder encodeObject:_selectableItems forKey:CPToolbarSelectableItemsKey];
|
||||
|
||||
[aCoder encodeConditionalObject:_delegate forKey:CPToolbarDelegateKey];
|
||||
[aCoder encodeBool:_isVisible forKey:CPToolbarIsVisibleKey];
|
||||
|
||||
[aCoder encodeObject:_identifiedItems forKey:CPToolbarIdentifiedItemsKey];
|
||||
[aCoder encodeObject:_defaultItems forKey:CPToolbarDefaultItemsKey];
|
||||
[aCoder encodeObject:_allowedItems forKey:CPToolbarAllowedItemsKey];
|
||||
[aCoder encodeObject:_selectableItems forKey:CPToolbarSelectableItemsKey];
|
||||
|
||||
[aCoder encodeConditionalObject:_delegate forKey:CPToolbarDelegateKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -454,10 +521,10 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
@implementation _CPToolbarView : CPView
|
||||
{
|
||||
CPToolbar _toolbar;
|
||||
|
||||
|
||||
CPIndexSet _flexibleWidthIndexes;
|
||||
CPIndexSet _visibleFlexibleWidthIndexes;
|
||||
|
||||
|
||||
CPDictionary _itemInfos;
|
||||
JSObject _viewsForToolbarItems;
|
||||
|
||||
@@ -467,7 +534,7 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
CPPopUpButton _additionalItemsButton;
|
||||
CPColor _labelColor;
|
||||
CPColor _labelShadowColor;
|
||||
|
||||
|
||||
float _minWidth;
|
||||
|
||||
BOOL _FIXME_isHUD;
|
||||
@@ -475,11 +542,11 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPToolbarView class])
|
||||
if (self !== [_CPToolbarView class])
|
||||
return;
|
||||
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
|
||||
_CPToolbarViewExtraItemsImage = [[CPImage alloc] initWithContentsOfFile: [bundle pathForResource:"_CPToolbarView/_CPToolbarViewExtraItemsImage.png"] size: CPSizeMake(10.0, 15.0)];
|
||||
|
||||
_CPToolbarViewExtraItemsAlternateImage = [[CPImage alloc] initWithContentsOfFile: [bundle pathForResource:"_CPToolbarView/_CPToolbarViewExtraItemsAlternateImage.png"] size:CGSizeMake(10.0, 15.0)];
|
||||
@@ -488,23 +555,24 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_minWidth = 0;
|
||||
|
||||
|
||||
_labelColor = [CPColor blackColor];
|
||||
_labelShadowColor = [CPColor colorWithWhite:1.0 alpha:0.75];
|
||||
|
||||
|
||||
_additionalItemsButton = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 15.0) pullsDown:YES];
|
||||
[_additionalItemsButton setBordered:NO];
|
||||
|
||||
[_additionalItemsButton setImagePosition:CPImageOnly];
|
||||
[[_additionalItemsButton menu] setShowsStateColumn:NO];
|
||||
|
||||
[[_additionalItemsButton menu] setAutoenablesItems:NO];
|
||||
|
||||
[_additionalItemsButton setAlternateImage:_CPToolbarViewExtraItemsAlternateImage];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -551,17 +619,17 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
minWidth = _minWidth,
|
||||
// FIXME: This should be a CPSet.
|
||||
invisibleItemsSortedByPriority = [];
|
||||
|
||||
|
||||
_visibleItems = items;
|
||||
|
||||
// We only have hidden items if our actual width is smaller than our
|
||||
// We only have hidden items if our actual width is smaller than our
|
||||
// minimum width for hiding items.
|
||||
if (itemsWidth < minWidth)
|
||||
{
|
||||
itemsWidth -= TOOLBAR_EXTRA_ITEMS_WIDTH;
|
||||
|
||||
|
||||
_visibleItems = [_visibleItems copy];
|
||||
|
||||
|
||||
var itemsSortedByVisibilityPriority = [_toolbar itemsSortedByVisibilityPriority],
|
||||
count = itemsSortedByVisibilityPriority.length;
|
||||
|
||||
@@ -599,7 +667,7 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
|
||||
// Determine all the items that have flexible width.
|
||||
// Also determine the height of the toolbar.
|
||||
var count = _visibleItems.length
|
||||
var count = _visibleItems.length,
|
||||
flexibleItemIndexes = [CPIndexSet indexSet];
|
||||
|
||||
while (count--)
|
||||
@@ -620,18 +688,18 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
|
||||
[view setHidden:NO];
|
||||
}
|
||||
|
||||
|
||||
var remainingSpace = itemsWidth - minWidth,
|
||||
proportionate = 0.0;
|
||||
|
||||
// Continue to distribute space proportionately while we have it,
|
||||
// and there are flexible items left that want it. (Those with max
|
||||
// Continue to distribute space proportionately while we have it,
|
||||
// and there are flexible items left that want it. (Those with max
|
||||
// widths may eventually not want it anymore).
|
||||
while (remainingSpace && [flexibleItemIndexes count])
|
||||
{
|
||||
// Divy out the space.
|
||||
proportionate += remainingSpace / [flexibleItemIndexes count];
|
||||
|
||||
|
||||
// Reset the remaining space to 0
|
||||
remainingSpace = 0.0;
|
||||
|
||||
@@ -654,7 +722,7 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
[view setFrameSize:CGSizeMake(constrainedWidth, height)];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now that all the visible items are the correct width, give them their final frames.
|
||||
var index = 0,
|
||||
count = _visibleItems.length,
|
||||
@@ -676,9 +744,9 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
{
|
||||
var index = 0,
|
||||
count = [items count];
|
||||
|
||||
|
||||
_invisibleItems = [];
|
||||
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var item = items[index];
|
||||
@@ -731,19 +799,30 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
|
||||
hasNonSeparatorItem = YES;
|
||||
|
||||
[_additionalItemsButton addItemWithTitle:[item label]];
|
||||
|
||||
var menuItem = [_additionalItemsButton itemArray][index + 1];
|
||||
|
||||
var menuItem = [[CPMenuItem alloc] initWithTitle:[item label] action:@selector(didSelectMenuItem:) keyEquivalent:nil];
|
||||
|
||||
[menuItem setRepresentedObject:item];
|
||||
[menuItem setImage:[item image]];
|
||||
|
||||
[menuItem setTarget:[item target]];
|
||||
[menuItem setAction:[item action]];
|
||||
[menuItem setTarget:self];
|
||||
[menuItem setEnabled:[item isEnabled]];
|
||||
|
||||
[_additionalItemsButton addItem:menuItem];
|
||||
}
|
||||
}
|
||||
else
|
||||
[_additionalItemsButton removeFromSuperview];
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Used privately.
|
||||
@ignore
|
||||
*/
|
||||
|
||||
- (void)didSelectMenuItem:(id)aSender
|
||||
{
|
||||
var toolbarItem = [aSender representedObject];
|
||||
|
||||
[CPApp sendAction:[toolbarItem action] to:[toolbarItem target] from:toolbarItem];
|
||||
}
|
||||
|
||||
- (void)reloadToolbarItems
|
||||
@@ -751,14 +830,14 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
// Get rid of all our current subviews.
|
||||
var subviews = [self subviews],
|
||||
count = subviews.length;
|
||||
|
||||
|
||||
while (count--)
|
||||
[subviews[count] removeFromSuperview];
|
||||
|
||||
|
||||
// Populate with new subviews.
|
||||
var items = [_toolbar items],
|
||||
index = 0;
|
||||
|
||||
|
||||
count = items.length;
|
||||
|
||||
_minWidth = TOOLBAR_ITEM_MARGIN;
|
||||
@@ -774,7 +853,7 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth)
|
||||
|
||||
_minWidth += [view minSize].width + TOOLBAR_ITEM_MARGIN;
|
||||
}
|
||||
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@@ -785,13 +864,13 @@ var _CPToolbarItemVisibilityPriorityCompare = function(lhs, rhs)
|
||||
{
|
||||
var lhsVisibilityPriority = [lhs visibilityPriority],
|
||||
rhsVisibilityPriority = [rhs visibilityPriority];
|
||||
|
||||
|
||||
if (lhsVisibilityPriority == rhsVisibilityPriority)
|
||||
return CPOrderedSame;
|
||||
|
||||
|
||||
if (lhsVisibilityPriority > rhsVisibilityPriority)
|
||||
return CPOrderedAscending;
|
||||
|
||||
|
||||
return CPOrderedDescending;
|
||||
}
|
||||
|
||||
@@ -837,7 +916,16 @@ var TOP_MARGIN = 5.0,
|
||||
|
||||
_toolbar = aToolbar;
|
||||
|
||||
[_toolbarItem addObserver:self forKeyPath:"enabled" options:0 context:NULL];
|
||||
var keyPaths = [@"label", @"image", @"alternateImage", @"minSize", @"maxSize", @"target", @"action", @"enabled"],
|
||||
index = 0,
|
||||
count = [keyPaths count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
[_toolbarItem
|
||||
addObserver:self
|
||||
forKeyPath:keyPaths[index]
|
||||
options:0
|
||||
context:NULL];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -904,7 +992,7 @@ var TOP_MARGIN = 5.0,
|
||||
{
|
||||
_imageView = [[CPImageView alloc] initWithFrame:[self bounds]];
|
||||
|
||||
[_imageView setImageScaling:CPScaleNone];
|
||||
[_imageView setImageScaling:CPScaleProportionally];
|
||||
|
||||
[self addSubview:_imageView];
|
||||
}
|
||||
@@ -918,6 +1006,8 @@ var TOP_MARGIN = 5.0,
|
||||
[_labelField setStringValue:[_toolbarItem label]];
|
||||
[_labelField sizeToFit]; // FIXME
|
||||
|
||||
[self setEnabled:[_toolbarItem isEnabled]];
|
||||
|
||||
_labelSize = [_labelField frame].size;
|
||||
|
||||
_minSize = CGSizeMake(MAX(_labelSize.width, minSize.width), _labelSize.height + minSize.height + LABEL_MARGIN + TOP_MARGIN);
|
||||
@@ -957,7 +1047,14 @@ var TOP_MARGIN = 5.0,
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if ([_toolbarItem view])
|
||||
return;
|
||||
return [[self nextResponder] mouseDown:anEvent];
|
||||
|
||||
var identifier = [_toolbarItem itemIdentifier];
|
||||
|
||||
if (identifier === CPToolbarSpaceItemIdentifier ||
|
||||
identifier === CPToolbarFlexibleSpaceItemIdentifier ||
|
||||
identifier === CPToolbarSeparatorItemIdentifier)
|
||||
return [[self nextResponder] mouseDown:anEvent];
|
||||
|
||||
[super mouseDown:anEvent];
|
||||
}
|
||||
@@ -976,6 +1073,8 @@ var TOP_MARGIN = 5.0,
|
||||
[_imageView setAlphaValue:0.5];
|
||||
[_labelField setAlphaValue:0.5];
|
||||
}
|
||||
|
||||
[_toolbar tile];
|
||||
}
|
||||
|
||||
- (CPColor)FIXME_labelColor
|
||||
@@ -1020,14 +1119,27 @@ var TOP_MARGIN = 5.0,
|
||||
[_labelField setTextShadowColor:[self FIXME_labelShadowColor]];
|
||||
}
|
||||
|
||||
- (void)sendAction:(SEL)anAction to:(id)aSender
|
||||
{
|
||||
[CPApp sendAction:anAction to:aSender from:_toolbarItem];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath
|
||||
ofObject:(id)anObject
|
||||
change:(CPDictionary)aChange
|
||||
context:(id)aContext
|
||||
{
|
||||
// FIXME: Not clear if -synchronizeWindowTitleWithDocumentName is the best way to go.
|
||||
if (aKeyPath === "enabled")
|
||||
[self setEnabled:[anObject isEnabled]];
|
||||
|
||||
else if (aKeyPath === @"target")
|
||||
[self setTarget:[anObject target]];
|
||||
|
||||
else if (aKeyPath === @"action")
|
||||
[self setAction:[anObject action]];
|
||||
|
||||
else
|
||||
[self updateFromItem];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+154
-83
@@ -23,40 +23,24 @@
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import <AppKit/CPImage.j>
|
||||
@import <AppKit/CPView.j>
|
||||
@import "CPImage.j"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
/*
|
||||
@global
|
||||
@class CPToolbarItem
|
||||
*/
|
||||
CPToolbarItemVisibilityPriorityStandard = 0;
|
||||
/*
|
||||
@global
|
||||
@class CPToolbarItem
|
||||
*/
|
||||
CPToolbarItemVisibilityPriorityLow = -1000;
|
||||
/*
|
||||
@global
|
||||
@class CPToolbarItem
|
||||
*/
|
||||
CPToolbarItemVisibilityPriorityHigh = 1000;
|
||||
/*
|
||||
@global
|
||||
@class CPToolbarItem
|
||||
*/
|
||||
CPToolbarItemVisibilityPriorityUser = 2000;
|
||||
|
||||
CPToolbarSeparatorItemIdentifier = @"CPToolbarSeparatorItemIdentifier";
|
||||
CPToolbarSpaceItemIdentifier = @"CPToolbarSpaceItemIdentifier";
|
||||
CPToolbarFlexibleSpaceItemIdentifier = @"CPToolbarFlexibleSpaceItemIdentifier";
|
||||
CPToolbarShowColorsItemIdentifier = @"CPToolbarShowColorsItemIdentifier";
|
||||
CPToolbarShowFontsItemIdentifier = @"CPToolbarShowFontsItemIdentifier";
|
||||
CPToolbarCustomizeToolbarItemIdentifier = @"CPToolbarCustomizeToolbarItemIdentifier";
|
||||
CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
CPToolbarSeparatorItemIdentifier = @"CPToolbarSeparatorItem";
|
||||
CPToolbarSpaceItemIdentifier = @"CPToolbarSpaceItem";
|
||||
CPToolbarFlexibleSpaceItemIdentifier = @"CPToolbarFlexibleSpaceItem";
|
||||
CPToolbarShowColorsItemIdentifier = @"CPToolbarShowColorsItem";
|
||||
CPToolbarShowFontsItemIdentifier = @"CPToolbarShowFontsItem";
|
||||
CPToolbarCustomizeToolbarItemIdentifier = @"CPToolbarCustomizeToolbarItem";
|
||||
CPToolbarPrintItemIdentifier = @"CPToolbarPrintItem";
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPToolbarItem
|
||||
|
||||
@@ -81,7 +65,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
CPView _view;
|
||||
|
||||
CGSize _minSize;
|
||||
CGSize _maxSize;
|
||||
CGSize _maxSize;
|
||||
|
||||
int _visibilityPriority;
|
||||
|
||||
@@ -102,20 +86,21 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
- (id)initWithItemIdentifier:(CPString)anItemIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_itemIdentifier = anItemIdentifier;
|
||||
|
||||
|
||||
_tag = 0;
|
||||
_isEnabled = YES;
|
||||
|
||||
|
||||
_minSize = CGSizeMakeZero();
|
||||
_maxSize = CGSizeMakeZero();
|
||||
|
||||
|
||||
_visibilityPriority = CPToolbarItemVisibilityPriorityStandard;
|
||||
_autovalidates = YES;
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -136,6 +121,12 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
return _toolbar;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_setToolbar:(CPToolbar)aToolbar
|
||||
{
|
||||
_toolbar = aToolbar;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the item's label
|
||||
*/
|
||||
@@ -179,7 +170,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(toolTip)])
|
||||
return [_view toolTip];
|
||||
|
||||
|
||||
return _toolTip;
|
||||
}
|
||||
|
||||
@@ -190,8 +181,8 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
- (void)setToolTip:(CPString)aToolTip
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(setToolTip:)])
|
||||
[view setToolTip:aToolTip];
|
||||
|
||||
[_view setToolTip:aToolTip];
|
||||
|
||||
_toolTip = aToolTip;
|
||||
}
|
||||
|
||||
@@ -202,7 +193,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(tag)])
|
||||
return [_view tag];
|
||||
|
||||
|
||||
return _tag;
|
||||
}
|
||||
|
||||
@@ -214,7 +205,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(setTag:)])
|
||||
[_view setTag:aTag];
|
||||
|
||||
|
||||
_tag = aTag;
|
||||
}
|
||||
|
||||
@@ -225,12 +216,12 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if (_view)
|
||||
return [_view respondsToSelector:@selector(target)] ? [_view target] : nil;
|
||||
|
||||
|
||||
return _target;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the target of the action that is triggered when the user clicks this item. \c nil will cause
|
||||
Sets the target of the action that is triggered when the user clicks this item. \c nil will cause
|
||||
the action to be passed on to the first responder.
|
||||
@param aTarget the new target
|
||||
*/
|
||||
@@ -238,7 +229,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if (!_view)
|
||||
_target = aTarget;
|
||||
|
||||
|
||||
else if ([_view respondsToSelector:@selector(setTarget:)])
|
||||
[_view setTarget:aTarget];
|
||||
}
|
||||
@@ -250,7 +241,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if (_view)
|
||||
return [_view respondsToSelector:@selector(action)] ? [_view action] : nil;
|
||||
|
||||
|
||||
return _action;
|
||||
}
|
||||
|
||||
@@ -274,7 +265,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(isEnabled)])
|
||||
return [_view isEnabled];
|
||||
|
||||
|
||||
return _isEnabled;
|
||||
}
|
||||
|
||||
@@ -286,7 +277,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(setEnabled:)])
|
||||
[_view setEnabled:shouldBeEnabled];
|
||||
|
||||
|
||||
_isEnabled = shouldBeEnabled;
|
||||
}
|
||||
|
||||
@@ -297,7 +288,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(image)])
|
||||
return [_view image];
|
||||
|
||||
|
||||
return _image;
|
||||
}
|
||||
|
||||
@@ -311,12 +302,12 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
[_view setImage:anImage];
|
||||
|
||||
_image = anImage;
|
||||
|
||||
|
||||
if (!_image)
|
||||
return;
|
||||
|
||||
if (_minSize.width == 0 && _minSize.height == 0 &&
|
||||
_maxSize.width == 0 && _maxSize.height == 0)
|
||||
|
||||
if (_minSize.width === 0 && _minSize.height === 0 &&
|
||||
_maxSize.width === 0 && _maxSize.height === 0)
|
||||
{
|
||||
var imageSize = [_image size];
|
||||
|
||||
@@ -336,7 +327,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(setAlternateImage:)])
|
||||
[_view setAlternateImage:anImage];
|
||||
|
||||
|
||||
_alternateImage = anImage;
|
||||
}
|
||||
|
||||
@@ -347,7 +338,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(alternateIamge)])
|
||||
return [_view alternateImage];
|
||||
|
||||
|
||||
return _alternateImage;
|
||||
}
|
||||
|
||||
@@ -367,7 +358,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
{
|
||||
if (_view == aView)
|
||||
return;
|
||||
|
||||
|
||||
_view = aView;
|
||||
|
||||
if (_view)
|
||||
@@ -375,7 +366,7 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
// Tags get forwarded.
|
||||
if (_tag !== 0 && [_view respondsToSelector:@selector(setTag:)])
|
||||
[_view setTag:_tag];
|
||||
|
||||
|
||||
_target = nil;
|
||||
_action = nil;
|
||||
}
|
||||
@@ -395,8 +386,11 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
*/
|
||||
- (void)setMinSize:(CGSize)aMinSize
|
||||
{
|
||||
if (!aMinSize.height || !aMinSize.width)
|
||||
return;
|
||||
|
||||
_minSize = CGSizeMakeCopy(aMinSize);
|
||||
|
||||
|
||||
// Try to provide some sanity: Make maxSize >= minSize
|
||||
_maxSize = CGSizeMake(MAX(_minSize.width, _maxSize.width), MAX(_minSize.height, _maxSize.height));
|
||||
}
|
||||
@@ -415,8 +409,11 @@ CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
*/
|
||||
- (void)setMaxSize:(CGSize)aMaxSize
|
||||
{
|
||||
if (!aMaxSize.height || !aMaxSize.width)
|
||||
return;
|
||||
|
||||
_maxSize = CGSizeMakeCopy(aMaxSize);
|
||||
|
||||
|
||||
// Try to provide some sanity: Make minSize <= maxSize
|
||||
_minSize = CGSizeMake(MIN(_minSize.width, _maxSize.width), MIN(_minSize.height, _maxSize.height));
|
||||
}
|
||||
@@ -453,6 +450,9 @@ CPToolbarItemVisibilityPriorityUser
|
||||
|
||||
- (void)validate
|
||||
{
|
||||
var action = [self action],
|
||||
target = [self target];
|
||||
|
||||
// View items do not do any target-action analysis.
|
||||
if (_view)
|
||||
{
|
||||
@@ -462,13 +462,9 @@ CPToolbarItemVisibilityPriorityUser
|
||||
return;
|
||||
}
|
||||
|
||||
var action = [self action];
|
||||
|
||||
if (!action)
|
||||
return [self setEnabled:NO];
|
||||
|
||||
var target = [self target];
|
||||
|
||||
if (target && ![target respondsToSelector:action])
|
||||
return [self setEnabled:NO];
|
||||
|
||||
@@ -495,15 +491,97 @@ CPToolbarItemVisibilityPriorityUser
|
||||
|
||||
@end
|
||||
|
||||
var CPToolbarItemItemIdentifierKey = @"CPToolbarItemItemIdentifierKey",
|
||||
CPToolbarItemLabelKey = @"CPToolbarItemLabelKey",
|
||||
CPToolbarItemPaletteLabelKey = @"CPToolbarItemPaletteLabelKey",
|
||||
CPToolbarItemToolTipKey = @"CPToolbarItemToolTipKey",
|
||||
CPToolbarItemTagKey = @"CPToolbarItemTagKey",
|
||||
CPToolbarItemTargetKey = @"CPToolbarItemTargetKey",
|
||||
CPToolbarItemActionKey = @"CPToolbarItemActionKey",
|
||||
CPToolbarItemEnabledKey = @"CPToolbarItemEnabledKey",
|
||||
CPToolbarItemImageKey = @"CPToolbarItemImageKey",
|
||||
CPToolbarItemAlternateImageKey = @"CPToolbarItemAlternateImageKey",
|
||||
CPToolbarItemViewKey = @"CPToolbarItemViewKey",
|
||||
CPToolbarItemMinSizeKey = @"CPToolbarItemMinSizeKey",
|
||||
CPToolbarItemMaxSizeKey = @"CPToolbarItemMaxSizeKey",
|
||||
CPToolbarItemVisibilityPriorityKey = @"CPToolbarItemVisibilityPriorityKey",
|
||||
CPToolbarItemAutovalidatesKey = @"CPToolbarItemAutovalidatesKey";
|
||||
|
||||
@implementation CPToolbarItem (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_itemIdentifier = [aCoder decodeObjectForKey:CPToolbarItemItemIdentifierKey];
|
||||
|
||||
_minSize = [aCoder decodeSizeForKey:CPToolbarItemMinSizeKey];
|
||||
_maxSize = [aCoder decodeSizeForKey:CPToolbarItemMaxSizeKey];
|
||||
|
||||
[self setLabel:[aCoder decodeObjectForKey:CPToolbarItemLabelKey]];
|
||||
[self setPaletteLabel:[aCoder decodeObjectForKey:CPToolbarItemPaletteLabelKey]];
|
||||
[self setToolTip:[aCoder decodeObjectForKey:CPToolbarItemToolTipKey]];
|
||||
|
||||
[self setTag:[aCoder decodeObjectForKey:CPToolbarItemTagKey]];
|
||||
[self setTarget:[aCoder decodeObjectForKey:CPToolbarItemTargetKey]];
|
||||
[self setAction:CPSelectorFromString([aCoder decodeObjectForKey:CPToolbarItemActionKey])];
|
||||
|
||||
[self setEnabled:[aCoder decodeBoolForKey:CPToolbarItemEnabledKey]];
|
||||
|
||||
[self setImage:[aCoder decodeObjectForKey:CPToolbarItemImageKey]];
|
||||
[self setAlternateImage:[aCoder decodeObjectForKey:CPToolbarItemAlternateImageKey]];
|
||||
|
||||
[self setView:[aCoder decodeObjectForKey:CPToolbarItemViewKey]];
|
||||
|
||||
[self setVisibilityPriority:[aCoder decodeIntForKey:CPToolbarItemVisibilityPriorityKey]];
|
||||
[self setAutovalidates:[aCoder decodeBoolForKey:CPToolbarItemAutovalidatesKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_itemIdentifier forKey:CPToolbarItemItemIdentifierKey];
|
||||
|
||||
[aCoder encodeObject:[self label] forKey:CPToolbarItemLabelKey];
|
||||
[aCoder encodeObject:[self paletteLabel] forKey:CPToolbarItemPaletteLabelKey];
|
||||
|
||||
[aCoder encodeObject:[self toolTip] forKey:CPToolbarItemToolTipKey];
|
||||
|
||||
[aCoder encodeObject:[self tag] forKey:CPToolbarItemTagKey];
|
||||
[aCoder encodeObject:[self target] forKey:CPToolbarItemTargetKey];
|
||||
[aCoder encodeObject:[self action] forKey:CPToolbarItemActionKey];
|
||||
|
||||
[aCoder encodeObject:[self isEnabled] forKey:CPToolbarItemEnabledKey];
|
||||
|
||||
[aCoder encodeObject:[self image] forKey:CPToolbarItemImageKey];
|
||||
[aCoder encodeObject:[self alternateImage] forKey:CPToolbarItemAlternateImageKey];
|
||||
|
||||
[aCoder encodeObject:[self view] forKey:CPToolbarItemViewKey];
|
||||
|
||||
[aCoder encodeSize:[self minSize] forKey:CPToolbarItemMinSizeKey];
|
||||
[aCoder encodeSize:[self maxSize] forKey:CPToolbarItemMaxSizeKey];
|
||||
|
||||
[aCoder encodeObject:[self visibilityPriority] forKey:CPToolbarItemVisibilityPriorityKey];
|
||||
[aCoder encodeBool:[self autovalidates] forKey:CPToolbarItemAutovalidatesKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPToolbarItem (CPCopying)
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
var copy = [[[self class] alloc] initWithItemIdentifier:_itemIdentifier];
|
||||
|
||||
|
||||
if (_view)
|
||||
[copy setView:[CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:_view]]];
|
||||
|
||||
|
||||
[copy _setToolbar:_toolbar];
|
||||
|
||||
[copy setLabel:_label];
|
||||
[copy setPaletteLabel:_paletteLabel];
|
||||
[copy setToolTip:[self toolTip]];
|
||||
@@ -511,7 +589,7 @@ CPToolbarItemVisibilityPriorityUser
|
||||
[copy setTag:[self tag]];
|
||||
[copy setTarget:[self target]];
|
||||
[copy setAction:[self action]];
|
||||
|
||||
|
||||
[copy setEnabled:[self isEnabled]];
|
||||
|
||||
[copy setImage:[self image]];
|
||||
@@ -519,9 +597,10 @@ CPToolbarItemVisibilityPriorityUser
|
||||
|
||||
[copy setMinSize:_minSize];
|
||||
[copy setMaxSize:_maxSize];
|
||||
|
||||
[copy setVisibilityPriority:_visibilityPriority];
|
||||
|
||||
|
||||
[copy setVisibilityPriority:[self visibilityPriority]];
|
||||
[copy setAutovalidates:[self autovalidates]];
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
@@ -534,32 +613,24 @@ CPToolbarItemVisibilityPriorityUser
|
||||
/* @ignore */
|
||||
+ (CPToolbarItem)_standardItemWithItemIdentifier:(CPString)anItemIdentifier
|
||||
{
|
||||
var item = [[CPToolbarItem alloc] initWithItemIdentifier:anItemIdentifier];
|
||||
|
||||
switch (anItemIdentifier)
|
||||
{
|
||||
case CPToolbarSeparatorItemIdentifier: [item setMinSize:CGSizeMake(2.0, 0.0)];
|
||||
[item setMaxSize:CGSizeMake(2.0, 100000.0)];
|
||||
|
||||
return item;
|
||||
|
||||
case CPToolbarSpaceItemIdentifier: [item setMinSize:CGSizeMake(32.0, 32.0)];
|
||||
[item setMaxSize:CGSizeMake(32.0, 32.0)];
|
||||
|
||||
return item;
|
||||
|
||||
case CPToolbarFlexibleSpaceItemIdentifier: [item setMinSize:CGSizeMake(32.0, 32.0)];
|
||||
[item setMaxSize:CGSizeMake(10000.0, 32.0)];
|
||||
|
||||
return item;
|
||||
|
||||
case CPToolbarShowColorsItemIdentifier: return nil;
|
||||
case CPToolbarSeparatorItemIdentifier: return [_CPToolbarSeparatorItem new];
|
||||
case CPToolbarSpaceItemIdentifier: return [_CPToolbarSpaceItem new];
|
||||
case CPToolbarFlexibleSpaceItemIdentifier: return [_CPToolbarFlexibleSpaceItem new];
|
||||
case CPToolbarShowColorsItemIdentifier: return [_CPToolbarShowColorsItem new];
|
||||
case CPToolbarShowFontsItemIdentifier: return nil;
|
||||
case CPToolbarCustomizeToolbarItemIdentifier: return nil;
|
||||
case CPToolbarPrintItemIdentifier: return nil;
|
||||
}
|
||||
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@import "_CPToolbarFlexibleSpaceItem.j"
|
||||
@import "_CPToolbarShowColorsItem.j"
|
||||
@import "_CPToolbarSeparatorItem.j"
|
||||
@import "_CPToolbarSpaceItem.j"
|
||||
|
||||
|
||||
+59
-9
@@ -1,13 +1,14 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPIndexPath.j>
|
||||
|
||||
|
||||
@implementation CPTreeNode : CPObject
|
||||
{
|
||||
id _representedObject @accessors(readonly, property=representedObject);
|
||||
|
||||
|
||||
CPTreeNode _parentNode @accessors(readonly, property=parentNode);
|
||||
CPMutableArray _childNodes @accessors(readonly, property=childNodes);
|
||||
CPMutableArray _childNodes;
|
||||
}
|
||||
|
||||
+ (id)treeNodeWithRepresentedObject:(id)anObject
|
||||
@@ -33,16 +34,23 @@
|
||||
return [_childNodes count] <= 0;
|
||||
}
|
||||
|
||||
- (CPArray)childNodes
|
||||
{
|
||||
return [_childNodes copy];
|
||||
}
|
||||
|
||||
- (CPMutableArray)mutableChildNodes
|
||||
{
|
||||
return [self mutableArrayValueForKey:@"childNodes"];
|
||||
}
|
||||
|
||||
- (void)insertObject:(id)anObject inChildNodesAtIndex:(CPInteger)anIndex
|
||||
- (void)insertObject:(id)aTreeNode inChildNodesAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
anObject._parentNode = self;
|
||||
[[aTreeNode._parentNode mutableChildNodes] removeObjectIdenticalTo:aTreeNode];
|
||||
|
||||
[_childNodes addObject:anObject];
|
||||
aTreeNode._parentNode = self;
|
||||
|
||||
[_childNodes insertObject:aTreeNode atIndex:anIndex];
|
||||
}
|
||||
|
||||
- (void)removeObjectFromChildNodesAtIndex:(CPInteger)anIndex
|
||||
@@ -52,13 +60,14 @@
|
||||
[_childNodes removeObjectAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (void)replaceObjectFromChildNodesAtIndex:(CPInteger)anIndex withObject:(id)anObject
|
||||
- (void)replaceObjectFromChildNodesAtIndex:(CPInteger)anIndex withObject:(id)aTreeNode
|
||||
{
|
||||
var oldObject = [_childNodes objectAtIndex:anIndex];
|
||||
var oldTreeNode = [_childNodes objectAtIndex:anIndex];
|
||||
|
||||
oldObject._parentNode = nil;
|
||||
oldTreeNode._parentNode = nil;
|
||||
aTreeNode._parentNode = self;
|
||||
|
||||
[_childNodes replaceObjectAtIndex:anIndex withObject:anObject];
|
||||
[_childNodes replaceObjectAtIndex:anIndex withObject:aTreeNode];
|
||||
}
|
||||
|
||||
- (id)objectInChildNodesAtIndex:(CPInteger)anIndex
|
||||
@@ -79,4 +88,45 @@
|
||||
[_childNodes[count] sortWithSortDescriptors:sortDescriptors recursively:YES];
|
||||
}
|
||||
|
||||
- (CPTreeNode)descendantNodeAtIndexPath:(CPIndexPath)indexPath
|
||||
{
|
||||
var index = 0,
|
||||
count = [indexPath length],
|
||||
node = self;
|
||||
|
||||
for (; index < count; ++index)
|
||||
node = [node objectInChildNodesAtIndex:[indexPath indexAtPosition:index]];
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTreeNodeRepresentedObjectKey = @"CPTreeNodeRepresentedObjectKey",
|
||||
CPTreeNodeParentNodeKey = @"CPTreeNodeParentNodeKey",
|
||||
CPTreeNodeChildNodesKey = @"CPTreeNodeChildNodesKey";
|
||||
|
||||
@implementation CPTreeNode (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_representedObject = [aCoder decodeObjectForKey:CPTreeNodeRepresentedObjectKey];
|
||||
_parentNode = [aCoder decodeObjectForKey:CPTreeNodeParentNodeKey];
|
||||
_childNodes = [aCoder decodeObjectForKey:CPTreeNodeChildNodesKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_representedObject forKey:CPTreeNodeRepresentedObjectKey];
|
||||
[aCoder encodeConditionalObject:_parentNode forKey:CPTreeNodeParentNodeKey];
|
||||
[aCoder encodeObject:_childNodes forKey:CPTreeNodeChildNodesKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+554
-253
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* CPViewAnimation.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Klaas Pieter Annema on September 3, 2009.
|
||||
* Copyright 2009, Sofa BV
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPAnimation.j"
|
||||
|
||||
|
||||
CPViewAnimationTargetKey = @"CPViewAnimationTargetKey";
|
||||
CPViewAnimationStartFrameKey = @"CPViewAnimationStartFrameKey";
|
||||
CPViewAnimationEndFrameKey = @"CPViewAnimationEndFrameKey";
|
||||
CPViewAnimationEffectKey = @"CPViewAnimationEffectKey";
|
||||
|
||||
CPViewAnimationFadeInEffect = @"CPViewAnimationFadeInEffect";
|
||||
CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOutEffect";
|
||||
|
||||
@implementation CPViewAnimation : CPAnimation
|
||||
{
|
||||
CPArray _viewAnimations;
|
||||
}
|
||||
|
||||
- (id)initWithViewAnimations:(CPArray)viewAnimations
|
||||
{
|
||||
if (self = [super initWithDuration:0.5 animationCurve:CPAnimationLinear])
|
||||
{
|
||||
[self setViewAnimations:viewAnimations];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startAnimation
|
||||
{
|
||||
var animationIndex = [_viewAnimations count];
|
||||
while (animationIndex--)
|
||||
{
|
||||
var dictionary = [_viewAnimations objectAtIndex:animationIndex],
|
||||
view = [self _targetView:dictionary],
|
||||
startFrame = [self _startFrame:dictionary];
|
||||
|
||||
[view setFrame:startFrame];
|
||||
|
||||
var effect = [self _effect:dictionary];
|
||||
if (effect === CPViewAnimationFadeInEffect)
|
||||
{
|
||||
[view setAlphaValue:0.0];
|
||||
[self _targetView:view setHidden:NO];
|
||||
}
|
||||
else if (effect === CPViewAnimationFadeOutEffect)
|
||||
[view setAlphaValue:1.0];
|
||||
}
|
||||
|
||||
[super startAnimation];
|
||||
}
|
||||
|
||||
- (void)setCurrentProgress:(CPAnimationProgress)progress
|
||||
{
|
||||
[super setCurrentProgress:progress];
|
||||
|
||||
var animationIndex = [_viewAnimations count];
|
||||
while (animationIndex--)
|
||||
{
|
||||
var dictionary = [_viewAnimations objectAtIndex:animationIndex],
|
||||
view = [self _targetView:dictionary],
|
||||
startFrame = [self _startFrame:dictionary],
|
||||
endFrame = [self _endFrame:dictionary],
|
||||
differenceFrame = _CGRectMakeZero();
|
||||
|
||||
differenceFrame.origin.x = endFrame.origin.x - startFrame.origin.x;
|
||||
differenceFrame.origin.y = endFrame.origin.y - startFrame.origin.y;
|
||||
differenceFrame.size.width = endFrame.size.width - startFrame.size.width;
|
||||
differenceFrame.size.height = endFrame.size.height - startFrame.size.height;
|
||||
|
||||
var intermediateFrame = _CGRectMakeZero();
|
||||
intermediateFrame.origin.x = startFrame.origin.x + differenceFrame.origin.x * progress;
|
||||
intermediateFrame.origin.y = startFrame.origin.y + differenceFrame.origin.y * progress;
|
||||
intermediateFrame.size.width = startFrame.size.width + differenceFrame.size.width * progress;
|
||||
intermediateFrame.size.height = startFrame.size.height + differenceFrame.size.height * progress;
|
||||
|
||||
[view setFrame:intermediateFrame];
|
||||
|
||||
// Update the view's alpha value
|
||||
var effect = [self _effect:dictionary];
|
||||
if (effect === CPViewAnimationFadeInEffect)
|
||||
[view setAlphaValue:1.0 * progress];
|
||||
else if (effect === CPViewAnimationFadeOutEffect)
|
||||
[view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * progress];
|
||||
|
||||
if (progress === 1.0)
|
||||
[self _targetView:view setHidden:_CGRectIsNull(endFrame) || [view alphaValue] === 0.0];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopAnimation
|
||||
{
|
||||
var animationIndex = [_viewAnimations count];
|
||||
while (animationIndex--)
|
||||
{
|
||||
var dictionary = [_viewAnimations objectAtIndex:animationIndex],
|
||||
view = [self _targetView:dictionary],
|
||||
endFrame = [self _endFrame:dictionary];
|
||||
|
||||
[view setFrame:endFrame];
|
||||
|
||||
var effect = [self _effect:dictionary];
|
||||
if (effect === CPViewAnimationFadeInEffect)
|
||||
[view setAlphaValue:1.0];
|
||||
else if (effect === CPViewAnimationFadeOutEffect)
|
||||
[view setAlphaValue:0.0];
|
||||
|
||||
[self _targetView:view setHidden:_CGRectIsNull(endFrame) || [view alphaValue] === 0.0];
|
||||
}
|
||||
|
||||
[super stopAnimation];
|
||||
}
|
||||
|
||||
- (void)_targetView:(id)theView setHidden:(BOOL)isHidden
|
||||
{
|
||||
if ([theView isKindOfClass:[CPWindow class]])
|
||||
{
|
||||
if (isHidden)
|
||||
[theView orderOut:self];
|
||||
else
|
||||
[theView orderFront:self];
|
||||
}
|
||||
else
|
||||
[theView setHidden:isHidden];
|
||||
}
|
||||
|
||||
- (id)_targetView:(CPDictionary)dictionary
|
||||
{
|
||||
var targetView = [dictionary valueForKey:CPViewAnimationTargetKey];
|
||||
if (!targetView)
|
||||
[CPException raise:CPInternalInconsistencyException reason:[CPString stringWithFormat:@"view animation: %@ does not have a target view", [dictionary description]]];
|
||||
|
||||
return targetView;
|
||||
}
|
||||
|
||||
- (CGRect)_startFrame:(CPDictionary)dictionary
|
||||
{
|
||||
var startFrame = [dictionary valueForKey:CPViewAnimationStartFrameKey];
|
||||
if (!startFrame)
|
||||
return [[self _targetView:dictionary] frame];
|
||||
|
||||
return startFrame;
|
||||
}
|
||||
|
||||
- (CGRect)_endFrame:(CPDictionary)dictionary
|
||||
{
|
||||
var endFrame = [dictionary valueForKey:CPViewAnimationEndFrameKey];
|
||||
if (!endFrame)
|
||||
return [[self _targetView:dictionary] frame];
|
||||
|
||||
return endFrame;
|
||||
}
|
||||
|
||||
- (CPString)_effect:(CPDictionary)dictionary
|
||||
{
|
||||
return [dictionary valueForKey:CPViewAnimationEffectKey];
|
||||
}
|
||||
|
||||
- (CPArray)viewAnimations
|
||||
{
|
||||
return _viewAnimations;
|
||||
}
|
||||
|
||||
- (void)setViewAnimations:(CPArray)viewAnimations
|
||||
{
|
||||
if (viewAnimations != _viewAnimations)
|
||||
{
|
||||
[self stopAnimation];
|
||||
_viewAnimations = [viewAnimations copy];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
+57
-10
@@ -20,8 +20,14 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <AppKit/CPResponder.j>
|
||||
@import <Foundation/CPArray.j>
|
||||
|
||||
@import "CPApplication.j"
|
||||
@import "CPCib.j"
|
||||
@import "CPResponder.j"
|
||||
|
||||
|
||||
var CPViewControllerCachedCibs;
|
||||
|
||||
/*! @class CPViewController
|
||||
The CPViewController class provides the fundamental view-management controller for Cappuccino applications.
|
||||
@@ -46,7 +52,8 @@
|
||||
*/
|
||||
@implementation CPViewController : CPResponder
|
||||
{
|
||||
CPView _view;
|
||||
CPView _view @accessors(property=view);
|
||||
BOOL _isLoading;
|
||||
|
||||
id _representedObject @accessors(property=representedObject);
|
||||
CPString _title @accessors(property=title);
|
||||
@@ -56,6 +63,12 @@
|
||||
CPDictionary _cibExternalNameTable @accessors(property=cibExternalNameTable, readonly);
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self === CPViewController)
|
||||
CPViewControllerCachedCibs = [CPDictionary dictionary];
|
||||
}
|
||||
|
||||
/*!
|
||||
Convenience initializer calls -initWithCibName:bundle: with nil for both parameters.
|
||||
*/
|
||||
@@ -92,6 +105,8 @@
|
||||
_cibName = aCibNameOrNil;
|
||||
_cibBundle = aCibBundleOrNil || [CPBundle mainBundle];
|
||||
_cibExternalNameTable = anExternalNameTable || [CPDictionary dictionaryWithObject:self forKey:CPCibOwner];
|
||||
|
||||
_isLoading = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -104,20 +119,25 @@
|
||||
If you create your views manually, you must override this method and use it to create your view and assign it to the view property.
|
||||
The default implementation for programmatic views is to create a plain view. You can invoke super to utilize this view.
|
||||
|
||||
If you use Interface Builder to create your views and initialize the view controllerŃthat is, you initialize the view using the
|
||||
initWithCibName:bundle: methodŃthen you must not override this method. The consequences risk shattering the space-time continuum.
|
||||
If you use Interface Builder to create your views, you initialize the view using the
|
||||
initWithCibName:bundle: method then you must not override this method. The consequences risk shattering the space-time continuum.
|
||||
|
||||
Note: The cib loading system is currently asynchronous.
|
||||
Note: The cib loading system is currently synchronous.
|
||||
*/
|
||||
- (void)loadView
|
||||
{
|
||||
if (_view)
|
||||
return;
|
||||
|
||||
// if (_cibName)
|
||||
// [CPException raise: reason:];
|
||||
// check if a cib is already cached for the current _cibName
|
||||
var cib = [CPViewControllerCachedCibs objectForKey:_cibName];
|
||||
|
||||
var cib = [[CPCib alloc] initWithContentsOfURL:[_cibBundle pathForResource:_cibName + @".cib"]];
|
||||
if (!cib)
|
||||
{
|
||||
// if the cib isn't cached yet : fetch it and cache it
|
||||
cib = [[CPCib alloc] initWithContentsOfURL:[_cibBundle pathForResource:_cibName + @".cib"]];
|
||||
[CPViewControllerCachedCibs setObject:cib forKey:_cibName];
|
||||
}
|
||||
|
||||
[cib instantiateCibWithExternalNameTable:_cibExternalNameTable];
|
||||
}
|
||||
@@ -126,13 +146,13 @@
|
||||
Returns the view that the controller manages.
|
||||
If this property is nil, the controller sends loadView to itself to create the view that it manages.
|
||||
Subclasses should override the loadView method to create any custom views. The default value is nil.
|
||||
|
||||
Note: An error will not be thrown if after -loadView, the view property is still nil. -view will simply return nil, but will continue to call -loadView on subsequent calls.
|
||||
*/
|
||||
- (CPView)view
|
||||
{
|
||||
if (!_view)
|
||||
{
|
||||
_isLoading = YES;
|
||||
|
||||
var cibOwner = [_cibExternalNameTable objectForKey:CPCibOwner];
|
||||
|
||||
if ([cibOwner respondsToSelector:@selector(viewControllerWillLoadCib:)])
|
||||
@@ -143,14 +163,35 @@
|
||||
if (_view === nil && [cibOwner isKindOfClass:[CPDocument class]])
|
||||
[self setView:[cibOwner valueForKey:@"view"]];
|
||||
|
||||
if (!_view)
|
||||
{
|
||||
var reason = [CPString stringWithFormat:@"View for %@ could not be loaded from Cib or no view specified. Override loadView to load the view manually.", self];
|
||||
|
||||
[CPException raise:CPInternalInconsistencyException reason:reason];
|
||||
}
|
||||
|
||||
if ([cibOwner respondsToSelector:@selector(viewControllerDidLoadCib:)])
|
||||
[cibOwner viewControllerDidLoadCib:self];
|
||||
|
||||
_isLoading = NO;
|
||||
[self viewDidLoad];
|
||||
}
|
||||
|
||||
return _view;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
This method is called after the view controller has loaded its associated views into memory.
|
||||
This method is called regardless of whether the views were stored in a nib file or created programmatically in the loadView method.
|
||||
This method is most commonly used to perform additional initialization steps on views that are loaded from cib files.
|
||||
*/
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Manually sets the view that the controller manages.
|
||||
Setting to nil will cause -loadView to be called on all subsequent calls of -view.
|
||||
@@ -159,7 +200,13 @@
|
||||
*/
|
||||
- (void)setView:(CPView)aView
|
||||
{
|
||||
var viewWasLoaded = !_view;
|
||||
|
||||
_view = aView;
|
||||
|
||||
// Make sure the viewDidLoad method is called if the view is set directly
|
||||
if (!_isLoading && viewWasLoaded)
|
||||
[self viewDidLoad];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+124
-80
@@ -20,9 +20,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
@import "CPView.j"
|
||||
@import "CPScrollView.j"
|
||||
|
||||
|
||||
// FIXME: implement these where possible:
|
||||
@@ -50,30 +49,32 @@ CPWebViewScrollNative = 2;
|
||||
{
|
||||
CPScrollView _scrollView;
|
||||
CPView _frameView;
|
||||
|
||||
|
||||
IFrame _iframe;
|
||||
CPString _mainFrameURL;
|
||||
CPArray _backwardStack;
|
||||
CPArray _forwardStack;
|
||||
|
||||
|
||||
BOOL _ignoreLoadStart;
|
||||
BOOL _ignoreLoadEnd;
|
||||
|
||||
|
||||
id _downloadDelegate;
|
||||
id _frameLoadDelegate;
|
||||
id _policyDelegate;
|
||||
id _resourceLoadDelegate;
|
||||
id _UIDelegate;
|
||||
|
||||
|
||||
CPWebScriptObject _wso;
|
||||
|
||||
|
||||
CPString _url;
|
||||
CPString _html;
|
||||
|
||||
|
||||
Function _loadCallback;
|
||||
|
||||
|
||||
int _scrollMode;
|
||||
CGSize _scrollSize;
|
||||
|
||||
int _loadHTMLStringTimer;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)frameRect frameName:(CPString)frameName groupName:(CPString)groupName
|
||||
@@ -93,10 +94,10 @@ CPWebViewScrollNative = 2;
|
||||
_backwardStack = [];
|
||||
_forwardStack = [];
|
||||
_scrollMode = CPWebViewScrollNative;
|
||||
|
||||
|
||||
[self _initDOMWithFrame:aFrame];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -104,59 +105,61 @@ CPWebViewScrollNative = 2;
|
||||
{
|
||||
_ignoreLoadStart = YES;
|
||||
_ignoreLoadEnd = YES;
|
||||
|
||||
|
||||
_iframe = document.createElement("iframe");
|
||||
_iframe.name = "iframe_" + Math.floor(Math.random()*10000);
|
||||
_iframe.name = "iframe_" + FLOOR(RAND() * 10000);
|
||||
_iframe.style.width = "100%";
|
||||
_iframe.style.height = "100%";
|
||||
_iframe.style.borderWidth = "0px";
|
||||
|
||||
_iframe.frameBorder = "0";
|
||||
|
||||
[self setDrawsBackground:YES];
|
||||
|
||||
|
||||
_loadCallback = function() {
|
||||
// HACK: this block handles the case where we don't know about loads initiated by the user clicking a link
|
||||
if (!_ignoreLoadStart)
|
||||
{
|
||||
// post the start load notification
|
||||
[self _startedLoading];
|
||||
|
||||
if (_mainFrameURL)
|
||||
[_backwardStack addObject:_mainFrameURL];
|
||||
|
||||
// FIXME: this doesn't actually get the right URL for different domains. Not possible due to browser security restrictions.
|
||||
// HACK: this block handles the case where we don't know about loads initiated by the user clicking a link
|
||||
if (!_ignoreLoadStart)
|
||||
{
|
||||
// post the start load notification
|
||||
[self _startedLoading];
|
||||
|
||||
if (_mainFrameURL)
|
||||
[_backwardStack addObject:_mainFrameURL];
|
||||
|
||||
// FIXME: this doesn't actually get the right URL for different domains. Not possible due to browser security restrictions.
|
||||
_mainFrameURL = _iframe.src;
|
||||
_mainFrameURL = _iframe.src;
|
||||
|
||||
|
||||
// clear the forward
|
||||
[_forwardStack removeAllObjects];
|
||||
}
|
||||
else
|
||||
[_forwardStack removeAllObjects];
|
||||
}
|
||||
else
|
||||
_ignoreLoadStart = NO;
|
||||
|
||||
if (!_ignoreLoadEnd)
|
||||
{
|
||||
|
||||
if (!_ignoreLoadEnd)
|
||||
{
|
||||
[self _finishedLoading];
|
||||
}
|
||||
else
|
||||
_ignoreLoadEnd = NO;
|
||||
|
||||
}
|
||||
else
|
||||
_ignoreLoadEnd = NO;
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
if (_iframe.addEventListener)
|
||||
_iframe.addEventListener("load", _loadCallback, false);
|
||||
else if (_iframe.attachEvent)
|
||||
_iframe.attachEvent("onload", _loadCallback);
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (_iframe.addEventListener)
|
||||
_iframe.addEventListener("load", _loadCallback, false);
|
||||
else if (_iframe.attachEvent)
|
||||
_iframe.attachEvent("onload", _loadCallback);
|
||||
|
||||
_frameView = [[CPView alloc] initWithFrame:[self bounds]];
|
||||
[_frameView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
_scrollView = [[CPScrollView alloc] initWithFrame:[self bounds]];
|
||||
[_scrollView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||
[_scrollView setAutohidesScrollers:YES];
|
||||
[_scrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
[_scrollView setDocumentView:_frameView];
|
||||
|
||||
|
||||
_frameView._DOMElement.appendChild(_iframe);
|
||||
|
||||
|
||||
[self _setScrollMode:_scrollMode];
|
||||
|
||||
[self addSubview:_scrollView];
|
||||
@@ -164,13 +167,37 @@ CPWebViewScrollNative = 2;
|
||||
|
||||
|
||||
- (void)setFrameSize:(CPSize)aSize
|
||||
{
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
[self _resizeWebFrame];
|
||||
}
|
||||
|
||||
- (BOOL)_resizeWebFrame
|
||||
- (void)_attachScrollEventIfNecessary
|
||||
{
|
||||
if (_scrollMode !== CPWebViewScrollAppKit)
|
||||
return;
|
||||
|
||||
var win = null;
|
||||
try { win = [self DOMWindow]; } catch (e) {}
|
||||
|
||||
if (win && win.addEventListener)
|
||||
{
|
||||
var scrollEventHandler = function(anEvent)
|
||||
{
|
||||
var frameBounds = [self bounds],
|
||||
frameCenter = CGPointMake(CGRectGetMidX(frameBounds), CGRectGetMidY(frameBounds)),
|
||||
windowOrigin = [self convertPoint:frameCenter toView:nil],
|
||||
globalOrigin = [[self window] convertBaseToBridge:windowOrigin];
|
||||
|
||||
anEvent._overrideLocation = globalOrigin;
|
||||
[[[self window] platformWindow] scrollEvent:anEvent];
|
||||
};
|
||||
|
||||
win.addEventListener("DOMMouseScroll", scrollEventHandler, false);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_resizeWebFrame
|
||||
{
|
||||
if (_scrollMode === CPWebViewScrollAppKit)
|
||||
{
|
||||
@@ -180,13 +207,14 @@ CPWebViewScrollNative = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
[_frameView setFrameSize:[_scrollView bounds].size];
|
||||
|
||||
var visibleRect = [_frameView visibleRect];
|
||||
[_frameView setFrameSize:CGSizeMake(CGRectGetMaxX(visibleRect), CGRectGetMaxY(visibleRect))];
|
||||
|
||||
// try to get the document size so we can correctly set the frame
|
||||
var win = null;
|
||||
try { win = [self DOMWindow]; } catch (e) {}
|
||||
|
||||
if (win && win.document)
|
||||
if (win && win.document && win.document.body)
|
||||
{
|
||||
var width = win.document.body.scrollWidth,
|
||||
height = win.document.body.scrollHeight;
|
||||
@@ -199,9 +227,11 @@ CPWebViewScrollNative = 2;
|
||||
else
|
||||
{
|
||||
CPLog.warn("using default size 800*1600");
|
||||
|
||||
|
||||
[_frameView setFrameSize:CGSizeMake(800, 1600)];
|
||||
}
|
||||
|
||||
[_frameView scrollRectToVisible:visibleRect];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,14 +240,17 @@ CPWebViewScrollNative = 2;
|
||||
{
|
||||
if (_scrollMode == aScrollMode)
|
||||
return;
|
||||
|
||||
|
||||
[self _setScrollMode:aScrollMode];
|
||||
}
|
||||
|
||||
- (void)_setScrollMode:(int)aScrollMode
|
||||
{
|
||||
_scrollMode = aScrollMode;
|
||||
|
||||
if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
|
||||
_scrollMode = CPWebViewScrollNative;
|
||||
else
|
||||
_scrollMode = aScrollMode;
|
||||
|
||||
_ignoreLoadStart = YES;
|
||||
_ignoreLoadEnd = YES;
|
||||
|
||||
@@ -228,16 +261,16 @@ CPWebViewScrollNative = 2;
|
||||
{
|
||||
[_scrollView setHasHorizontalScroller:YES];
|
||||
[_scrollView setHasVerticalScroller:YES];
|
||||
|
||||
|
||||
_iframe.setAttribute("scrolling", "no");
|
||||
}
|
||||
else
|
||||
{
|
||||
[_scrollView setHasHorizontalScroller:NO];
|
||||
[_scrollView setHasVerticalScroller:NO];
|
||||
|
||||
|
||||
_iframe.setAttribute("scrolling", "auto");
|
||||
|
||||
|
||||
[_frameView setFrameSize:[_scrollView bounds].size];
|
||||
}
|
||||
|
||||
@@ -255,14 +288,16 @@ CPWebViewScrollNative = 2;
|
||||
|
||||
[self _setScrollMode:CPWebViewScrollAppKit];
|
||||
|
||||
[_frameView setFrameSize:[_scrollView contentSize]];
|
||||
|
||||
[self _startedLoading];
|
||||
|
||||
|
||||
_ignoreLoadStart = YES;
|
||||
_ignoreLoadEnd = NO;
|
||||
|
||||
|
||||
_url = null;
|
||||
_html = aString;
|
||||
|
||||
|
||||
[self _load];
|
||||
}
|
||||
|
||||
@@ -271,13 +306,13 @@ CPWebViewScrollNative = 2;
|
||||
[self _setScrollMode:CPWebViewScrollNative];
|
||||
|
||||
[self _startedLoading];
|
||||
|
||||
|
||||
_ignoreLoadStart = YES;
|
||||
_ignoreLoadEnd = NO;
|
||||
|
||||
|
||||
_url = _mainFrameURL;
|
||||
_html = null;
|
||||
|
||||
|
||||
[self _load];
|
||||
}
|
||||
|
||||
@@ -292,11 +327,19 @@ CPWebViewScrollNative = 2;
|
||||
// clear the iframe
|
||||
_iframe.src = "";
|
||||
|
||||
// need to give the browser a chance to reset iframe, otherwise we'll be document.write()-ing the previous document
|
||||
window.setTimeout(function() {
|
||||
if (_loadHTMLStringTimer !== nil)
|
||||
{
|
||||
window.clearTimeout(_loadHTMLStringTimer);
|
||||
_loadHTMLStringTimer = nil;
|
||||
}
|
||||
|
||||
// need to give the browser a chance to reset iframe, otherwise we'll be document.write()-ing the previous document
|
||||
_loadHTMLStringTimer = window.setTimeout(function()
|
||||
{
|
||||
var win = [self DOMWindow];
|
||||
|
||||
win.document.write(_html);
|
||||
|
||||
if (win)
|
||||
win.document.write(_html);
|
||||
|
||||
window.setTimeout(_loadCallback, 1);
|
||||
}, 0);
|
||||
@@ -314,7 +357,8 @@ CPWebViewScrollNative = 2;
|
||||
- (void)_finishedLoading
|
||||
{
|
||||
[self _resizeWebFrame];
|
||||
|
||||
[self _attachScrollEventIfNecessary];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPWebViewProgressFinishedNotification object:self];
|
||||
|
||||
if ([_frameLoadDelegate respondsToSelector:@selector(webView:didFinishLoadForFrame:)])
|
||||
@@ -327,7 +371,7 @@ CPWebViewScrollNative = 2;
|
||||
}
|
||||
|
||||
- (void)setMainFrameURL:(CPString)URLString
|
||||
{
|
||||
{
|
||||
if (_mainFrameURL)
|
||||
[_backwardStack addObject:_mainFrameURL];
|
||||
_mainFrameURL = URLString;
|
||||
@@ -344,9 +388,9 @@ CPWebViewScrollNative = 2;
|
||||
[_forwardStack addObject:_mainFrameURL];
|
||||
_mainFrameURL = [_backwardStack lastObject];
|
||||
[_backwardStack removeLastObject];
|
||||
|
||||
|
||||
[self _loadMainFrameURL];
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
@@ -360,9 +404,9 @@ CPWebViewScrollNative = 2;
|
||||
[_backwardStack addObject:_mainFrameURL];
|
||||
_mainFrameURL = [_forwardStack lastObject];
|
||||
[_forwardStack removeLastObject];
|
||||
|
||||
|
||||
[self _loadMainFrameURL];
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
@@ -585,7 +629,7 @@ CPWebViewScrollNative = 2;
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
// FIXME: encode/decode these?
|
||||
@@ -593,14 +637,14 @@ CPWebViewScrollNative = 2;
|
||||
_backwardStack = [];
|
||||
_forwardStack = [];
|
||||
_scrollMode = CPWebViewScrollNative;
|
||||
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
[self _initDOMWithFrame:[self frame]];
|
||||
#endif
|
||||
|
||||
[self setBackgroundColor:[CPColor whiteColor]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
+563
-233
File diff suppressed because it is too large
Load Diff
@@ -71,11 +71,12 @@ var _CPToolbarViewBackgroundColor = nil;
|
||||
[self addSubview:_toolbarBackgroundView positioned:CPWindowBelow relativeTo:nil];
|
||||
}
|
||||
|
||||
var frame = CGRectMakeZero();
|
||||
|
||||
frame.origin = CGPointMakeCopy([self toolbarOffset]);
|
||||
var frame = CGRectMakeZero(),
|
||||
toolbarOffset = [self toolbarOffset];
|
||||
|
||||
frame.origin = CGPointMake(toolbarOffset.width, toolbarOffset.height);
|
||||
frame.size = [_toolbarView frame].size;
|
||||
|
||||
|
||||
[_toolbarBackgroundView setFrame:frame];
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ var _CPStandardWindowViewBodyBackgroundColor = nil;
|
||||
@implementation _CPDocModalWindowView : _CPWindowView
|
||||
{
|
||||
CPView _bodyView;
|
||||
CPView _shadowView;
|
||||
}
|
||||
|
||||
+ (CPColor)bodyBackgroundColor
|
||||
@@ -25,13 +26,19 @@ var _CPStandardWindowViewBodyBackgroundColor = nil;
|
||||
var theClass = [self class],
|
||||
bounds = [self bounds];
|
||||
|
||||
_bodyView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), CGRectGetHeight(bounds))];
|
||||
_bodyView = [[CPView alloc] initWithFrame:_CGRectMake(0.0, 0.0, _CGRectGetWidth(bounds), _CGRectGetHeight(bounds))];
|
||||
|
||||
[_bodyView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
[_bodyView setBackgroundColor:[theClass bodyBackgroundColor]];
|
||||
[_bodyView setHitTests:NO];
|
||||
|
||||
[self addSubview:_bodyView];
|
||||
|
||||
var bundle = [CPBundle bundleForClass:[CPWindow class]];
|
||||
_shadowView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, _CGRectGetWidth(bounds), 8)];
|
||||
[_shadowView setAutoresizingMask:CPViewWidthSizable];
|
||||
[_shadowView setBackgroundColor:[CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/CPWindowAttachedSheetShadow.png"] size:CGSizeMake(9,8)]]];
|
||||
[self addSubview:_shadowView];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
@@ -19,9 +19,11 @@
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
@import "CPTextField.j"
|
||||
|
||||
var _CPHUDWindowViewBackgroundColor = nil,
|
||||
|
||||
|
||||
CPHUDCloseButtonImage = nil;
|
||||
|
||||
var HUD_TITLEBAR_HEIGHT = 26.0;
|
||||
@@ -29,7 +31,7 @@ var HUD_TITLEBAR_HEIGHT = 26.0;
|
||||
@implementation _CPHUDWindowView : _CPWindowView
|
||||
{
|
||||
CPView _toolbarView;
|
||||
|
||||
|
||||
CPTextField _titleField;
|
||||
CPButton _closeButton;
|
||||
}
|
||||
@@ -38,33 +40,33 @@ var HUD_TITLEBAR_HEIGHT = 26.0;
|
||||
{
|
||||
if (self != [_CPHUDWindowView class])
|
||||
return;
|
||||
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
|
||||
_CPHUDWindowViewBackgroundColor = [CPColor colorWithPatternImage:[[CPNinePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground0.png"] size:CPSizeMake(6.0, 78.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground1.png"] size:CPSizeMake(1.0, 78.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground2.png"] size:CPSizeMake(6.0, 78.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground3.png"] size:CPSizeMake(6.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground4.png"] size:CPSizeMake(1.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground5.png"] size:CPSizeMake(6.0, 1.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground6.png"] size:CPSizeMake(6.0, 6.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground7.png"] size:CPSizeMake(6.0, 6.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground8.png"] size:CPSizeMake(6.0, 6.0)]
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground0.png"] size:CPSizeMake(7.0, 37.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground1.png"] size:CPSizeMake(1.0, 37.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground2.png"] size:CPSizeMake(7.0, 37.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground3.png"] size:CPSizeMake(7.0, 1.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground4.png"] size:CPSizeMake(2.0, 2.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground5.png"] size:CPSizeMake(7.0, 1.0)],
|
||||
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground6.png"] size:CPSizeMake(7.0, 3.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground7.png"] size:CPSizeMake(1.0, 3.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/HUD/CPWindowHUDBackground8.png"] size:CPSizeMake(7.0, 3.0)]
|
||||
]]];
|
||||
|
||||
_CPHUDWindowViewCloseImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowClose.png"] size:CPSizeMake(20.0, 20.0)];
|
||||
_CPHUDWindowViewCloseActiveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowCloseActive.png"] size:CPSizeMake(20.0, 20.0)];
|
||||
_CPHUDWindowViewCloseImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowClose.png"] size:CPSizeMake(18.0, 18.0)];
|
||||
_CPHUDWindowViewCloseActiveImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"HUDTheme/WindowCloseActive.png"] size:CPSizeMake(18.0, 18.0)];
|
||||
}
|
||||
|
||||
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
{
|
||||
var contentRect = CGRectMakeCopy(aFrameRect),
|
||||
titleBarHeight = HUD_TITLEBAR_HEIGHT;
|
||||
|
||||
|
||||
contentRect.origin.y += titleBarHeight;
|
||||
contentRect.size.height -= titleBarHeight;
|
||||
|
||||
@@ -75,92 +77,92 @@ var HUD_TITLEBAR_HEIGHT = 26.0;
|
||||
{
|
||||
var frameRect = CGRectMakeCopy(aContentRect),
|
||||
titleBarHeight = HUD_TITLEBAR_HEIGHT;
|
||||
|
||||
|
||||
frameRect.origin.y -= titleBarHeight;
|
||||
frameRect.size.height += titleBarHeight;
|
||||
|
||||
|
||||
return frameRect;
|
||||
}
|
||||
|
||||
- (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
{
|
||||
var contentRect = [[self class] contentRectForFrameRect:aFrameRect];
|
||||
|
||||
|
||||
if ([[[self window] toolbar] isVisible])
|
||||
{
|
||||
toolbarHeight = CGRectGetHeight([[self toolbarView] frame]);
|
||||
|
||||
|
||||
contentRect.origin.y += toolbarHeight;
|
||||
contentRect.size.height -= toolbarHeight;
|
||||
}
|
||||
|
||||
|
||||
return contentRect;
|
||||
}
|
||||
|
||||
- (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
||||
{
|
||||
var frameRect = [[self class] frameRectForContentRect:aContentRect];
|
||||
|
||||
|
||||
if ([[[self window] toolbar] isVisible])
|
||||
{
|
||||
toolbarHeight = CGRectGetHeight([[self toolbarView] frame]);
|
||||
|
||||
|
||||
frameRect.origin.y -= toolbarHeight;
|
||||
frameRect.size.height += toolbarHeight;
|
||||
}
|
||||
|
||||
|
||||
return frameRect;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask
|
||||
{
|
||||
self = [super initWithFrame:aFrame styleMask:aStyleMask];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
var bounds = [self bounds];
|
||||
|
||||
|
||||
[self setBackgroundColor:_CPHUDWindowViewBackgroundColor];
|
||||
|
||||
|
||||
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
|
||||
[_titleField setHitTests:NO];
|
||||
[_titleField setFont:[CPFont systemFontOfSize:11.0]];
|
||||
[_titleField setTextColor:[CPColor whiteColor]];
|
||||
[_titleField setTextShadowColor:[CPColor blackColor]];
|
||||
[_titleField setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
[_titleField setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
|
||||
// FIXME: Make this to CPLineBreakByTruncatingMiddle once it's implemented.
|
||||
[_titleField setLineBreakMode:CPLineBreakByTruncatingTail];
|
||||
[_titleField setAlignment:CPCenterTextAlignment];
|
||||
|
||||
|
||||
[_titleField setStringValue:@"Untitled"];
|
||||
[_titleField sizeToFit];
|
||||
[_titleField setAutoresizingMask:CPViewWidthSizable];
|
||||
[_titleField setStringValue:@""];
|
||||
|
||||
|
||||
[_titleField setFrame:CGRectMake(20.0, 3.0, CGRectGetWidth([self bounds]) - 40.0, CGRectGetHeight([_titleField frame]))];
|
||||
|
||||
|
||||
[self addSubview:_titleField];
|
||||
|
||||
|
||||
if (_styleMask & CPClosableWindowMask)
|
||||
{
|
||||
var closeSize = [_CPHUDWindowViewCloseImage size];
|
||||
|
||||
_closeButton = [[CPButton alloc] initWithFrame:CGRectMake(4.0, 4.0, closeSize.width, closeSize.height)];
|
||||
|
||||
|
||||
_closeButton = [[CPButton alloc] initWithFrame:CGRectMake(8.0, 5.0, closeSize.width, closeSize.height)];
|
||||
|
||||
[_closeButton setBordered:NO];
|
||||
|
||||
|
||||
[_closeButton setImage:_CPHUDWindowViewCloseImage];
|
||||
[_closeButton setAlternateImage:_CPHUDWindowViewCloseActiveImage];
|
||||
|
||||
|
||||
[self addSubview:_closeButton];
|
||||
}
|
||||
|
||||
|
||||
[self setResizeIndicatorOffset:CGSizeMake(5.0, 5.0)];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPTextField.j"
|
||||
@import "_CPWindowView.j"
|
||||
|
||||
|
||||
var GRADIENT_HEIGHT = 41.0;
|
||||
|
||||
var _CPTexturedWindowHeadGradientColor = nil,
|
||||
@@ -39,7 +41,7 @@ var _CPTexturedWindowHeadGradientColor = nil,
|
||||
if (!_CPTexturedWindowHeadGradientColor)
|
||||
{
|
||||
var bundle = [CPBundle bundleForClass:[_CPWindowView class]];
|
||||
|
||||
|
||||
_CPTexturedWindowHeadGradientColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
||||
[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardTop0.png"] size:CGSizeMake(6.0, 41.0)],
|
||||
@@ -49,7 +51,7 @@ var _CPTexturedWindowHeadGradientColor = nil,
|
||||
isVertical:NO
|
||||
]];
|
||||
}
|
||||
|
||||
|
||||
return _CPTexturedWindowHeadGradientColor;
|
||||
}
|
||||
|
||||
@@ -57,37 +59,37 @@ var _CPTexturedWindowHeadGradientColor = nil,
|
||||
{
|
||||
if (!_CPTexturedWindowHeadSolidColor)
|
||||
_CPTexturedWindowHeadSolidColor = [CPColor colorWithCalibratedRed:195.0 / 255.0 green:195.0 / 255.0 blue:195.0 / 255.0 alpha:1.0];
|
||||
|
||||
|
||||
return _CPTexturedWindowHeadSolidColor;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
var theClass = [self class],
|
||||
bounds = [self bounds];
|
||||
|
||||
|
||||
_gradientView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), GRADIENT_HEIGHT)];
|
||||
[_gradientView setBackgroundColor:[theClass gradientColor]];
|
||||
|
||||
|
||||
[self addSubview:_gradientView];
|
||||
|
||||
|
||||
_solidView = [[CPView alloc] initWithFrame:CGRectMake(0.0, GRADIENT_HEIGHT, CGRectGetWidth(bounds), CGRectGetHeight(bounds) - GRADIENT_HEIGHT)];
|
||||
[_solidView setBackgroundColor:[theClass solidColor]];
|
||||
|
||||
|
||||
[self addSubview:_solidView];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)resizeSubviewsWithOldSize:(CGSize)aSize
|
||||
{
|
||||
var bounds = [self bounds];
|
||||
|
||||
|
||||
[_gradientView setFrameSize:CGSizeMake(CGRectGetWidth(bounds), GRADIENT_HEIGHT)];
|
||||
[_solidView setFrameSize:CGSizeMake(CGRectGetWidth(bounds), CGRectGetHeight(bounds) - GRADIENT_HEIGHT)];
|
||||
}
|
||||
@@ -113,7 +115,7 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
||||
CPView _dividerView;
|
||||
CPView _bodyView;
|
||||
CPView _toolbarView;
|
||||
|
||||
|
||||
CPTextField _titleField;
|
||||
CPButton _closeButton;
|
||||
CPButton _minimizeButton;
|
||||
@@ -125,15 +127,15 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
||||
{
|
||||
if (!_CPStandardWindowViewBodyBackgroundColor)
|
||||
_CPStandardWindowViewBodyBackgroundColor = [CPColor colorWithWhite:0.96 alpha:1.0];
|
||||
|
||||
return _CPStandardWindowViewBodyBackgroundColor;
|
||||
|
||||
return _CPStandardWindowViewBodyBackgroundColor;
|
||||
}
|
||||
|
||||
+ (CPColor)dividerBackgroundColor
|
||||
{
|
||||
if (!_CPStandardWindowViewDividerBackgroundColor)
|
||||
_CPStandardWindowViewDividerBackgroundColor = [CPColor colorWithCalibratedRed:125.0 / 255.0 green:125.0 / 255.0 blue:125.0 / 255.0 alpha:1.0];
|
||||
|
||||
|
||||
return _CPStandardWindowViewDividerBackgroundColor;
|
||||
}
|
||||
|
||||
@@ -141,7 +143,7 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
||||
{
|
||||
if (!_CPStandardWindowViewTitleBackgroundColor)
|
||||
_CPStandardWindowViewTitleBackgroundColor = [CPColor colorWithCalibratedRed:44.0 / 255.0 green:44.0 / 255.0 blue:44.0 / 255.0 alpha:1.0];
|
||||
|
||||
|
||||
return _CPStandardWindowViewTitleBackgroundColor;
|
||||
}
|
||||
|
||||
@@ -149,7 +151,7 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
||||
{
|
||||
var contentRect = CGRectMakeCopy(aFrameRect),
|
||||
titleBarHeight = [self titleBarHeight] + 1.0;
|
||||
|
||||
|
||||
contentRect.origin.y += titleBarHeight;
|
||||
contentRect.size.height -= titleBarHeight;
|
||||
|
||||
@@ -160,10 +162,10 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
||||
{
|
||||
var frameRect = CGRectMakeCopy(aContentRect),
|
||||
titleBarHeight = [self titleBarHeight] + 1.0;
|
||||
|
||||
|
||||
frameRect.origin.y -= titleBarHeight;
|
||||
frameRect.size.height += titleBarHeight;
|
||||
|
||||
|
||||
return frameRect;
|
||||
}
|
||||
|
||||
@@ -176,15 +178,15 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
||||
{
|
||||
var contentRect = [[self class] contentRectForFrameRect:aFrameRect],
|
||||
theToolbar = [[self window] toolbar];
|
||||
|
||||
|
||||
if ([theToolbar isVisible])
|
||||
{
|
||||
toolbarHeight = CGRectGetHeight([[theToolbar _toolbarView] frame]);
|
||||
|
||||
|
||||
contentRect.origin.y += toolbarHeight;
|
||||
contentRect.size.height -= toolbarHeight;
|
||||
}
|
||||
|
||||
|
||||
return contentRect;
|
||||
}
|
||||
|
||||
@@ -192,72 +194,72 @@ var STANDARD_GRADIENT_HEIGHT = 41.0;
|
||||
{
|
||||
var frameRect = [[self class] frameRectForContentRect:aContentRect],
|
||||
theToolbar = [[self window] toolbar];
|
||||
|
||||
|
||||
if ([theToolbar isVisible])
|
||||
{
|
||||
toolbarHeight = CGRectGetHeight([[theToolbar _toolbarView] frame]);
|
||||
|
||||
|
||||
frameRect.origin.y -= toolbarHeight;
|
||||
frameRect.size.height += toolbarHeight;
|
||||
}
|
||||
|
||||
|
||||
return frameRect;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask
|
||||
{
|
||||
self = [super initWithFrame:aFrame styleMask:aStyleMask];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
var theClass = [self class],
|
||||
bounds = [self bounds];
|
||||
|
||||
|
||||
_headView = [[_CPTexturedWindowHeadView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(bounds), [[self class] titleBarHeight])];
|
||||
|
||||
|
||||
[_headView setAutoresizingMask:CPViewWidthSizable];;
|
||||
[_headView setHitTests:NO];
|
||||
|
||||
|
||||
[self addSubview:_headView];
|
||||
|
||||
|
||||
_dividerView = [[CPView alloc] initWithFrame:CGRectMake(0.0, CGRectGetMaxY([_headView frame]), CGRectGetWidth(bounds), 1.0)];
|
||||
|
||||
|
||||
[_dividerView setAutoresizingMask:CPViewWidthSizable];
|
||||
[_dividerView setBackgroundColor:[theClass dividerBackgroundColor]];
|
||||
[_dividerView setHitTests:NO];
|
||||
|
||||
|
||||
[self addSubview:_dividerView];
|
||||
|
||||
|
||||
var y = CGRectGetMaxY([_dividerView frame]);
|
||||
|
||||
|
||||
_bodyView = [[CPView alloc] initWithFrame:CGRectMake(0.0, y, CGRectGetWidth(bounds), CGRectGetHeight(bounds) - y)];
|
||||
|
||||
|
||||
[_bodyView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
[_bodyView setBackgroundColor:[theClass bodyBackgroundColor]];
|
||||
[_bodyView setHitTests:NO];
|
||||
|
||||
|
||||
[self addSubview:_bodyView];
|
||||
|
||||
[self setResizeIndicatorOffset:CGSizeMake(2.0, 2.0)];
|
||||
|
||||
|
||||
_titleField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
|
||||
[_titleField setFont:[CPFont boldSystemFontOfSize:12.0]];
|
||||
[_titleField setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
|
||||
// FIXME: Make this to CPLineBreakByTruncatingMiddle once it's implemented.
|
||||
[_titleField setLineBreakMode:CPLineBreakByTruncatingTail];
|
||||
[_titleField setAlignment:CPCenterTextAlignment];
|
||||
[_titleField setTextShadowColor:[CPColor whiteColor]];
|
||||
[_titleField setTextShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
|
||||
|
||||
[_titleField setStringValue:@"Untitled"];
|
||||
[_titleField sizeToFit];
|
||||
[_titleField setAutoresizingMask:CPViewWidthSizable];
|
||||
[_titleField setStringValue:@""];
|
||||
|
||||
|
||||
[self addSubview:_titleField];
|
||||
|
||||
|
||||
if (_styleMask & CPClosableWindowMask)
|
||||
{
|
||||
if (!_CPStandardWindowViewCloseButtonImage)
|
||||
|
||||
@@ -20,10 +20,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "CPView.j"
|
||||
@import "CPImageView.j"
|
||||
|
||||
#include "../CoreGraphics/CGGeometry.h"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
var _CPWindowViewResizeIndicatorImage = nil;
|
||||
@@ -31,13 +29,14 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
@implementation _CPWindowView : CPView
|
||||
{
|
||||
unsigned _styleMask;
|
||||
|
||||
|
||||
CPImageView _resizeIndicator;
|
||||
CGSize _resizeIndicatorOffset;
|
||||
|
||||
|
||||
CPView _toolbarView;
|
||||
CGSize _toolbarOffset;
|
||||
// BOOL _isAnimatingToolbar;
|
||||
|
||||
|
||||
CGRect _resizeFrame;
|
||||
CGPoint _mouseDraggedPoint;
|
||||
|
||||
@@ -48,7 +47,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
{
|
||||
if (self != [_CPWindowView class])
|
||||
return;
|
||||
|
||||
|
||||
_CPWindowViewResizeIndicatorImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPWindow class]] pathForResource:@"_CPWindowView/_CPWindowViewResizeIndicator.png"] size:CGSizeMake(12.0, 12.0)];
|
||||
}
|
||||
|
||||
@@ -75,14 +74,14 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_styleMask = aStyleMask;
|
||||
_resizeIndicatorOffset = CGSizeMake(0.0, 0.0);
|
||||
_toolbarOffset = CGSizeMake(0.0, 0.0);
|
||||
_resizeIndicatorOffset = CGSizeMakeZero();
|
||||
_toolbarOffset = CGSizeMakeZero();
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -107,14 +106,14 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
{
|
||||
// FIXME: This should be better
|
||||
var frame = [_resizeIndicator frame];
|
||||
|
||||
|
||||
if (CGRectContainsPoint(frame, [self convertPoint:[anEvent locationInWindow] fromView:nil]))
|
||||
return [self trackResizeWithEvent:anEvent];
|
||||
}
|
||||
|
||||
|
||||
if ([theWindow isMovableByWindowBackground])
|
||||
[self trackMoveWithEvent:anEvent];
|
||||
|
||||
|
||||
else
|
||||
[super mouseDown:anEvent];
|
||||
}
|
||||
@@ -123,29 +122,29 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
{
|
||||
var location = [anEvent locationInWindow],
|
||||
type = [anEvent type];
|
||||
|
||||
|
||||
if (type === CPLeftMouseUp)
|
||||
return;
|
||||
|
||||
|
||||
var theWindow = [self window];
|
||||
|
||||
|
||||
if (type === CPLeftMouseDown)
|
||||
{
|
||||
var frame = [theWindow frame];
|
||||
|
||||
|
||||
_resizeFrame = CGRectMake(location.x, location.y, CGRectGetWidth(frame), CGRectGetHeight(frame));
|
||||
}
|
||||
|
||||
|
||||
else if (type === CPLeftMouseDragged)
|
||||
{
|
||||
var newSize = CGSizeMake(CGRectGetWidth(_resizeFrame) + location.x - CGRectGetMinX(_resizeFrame), CGRectGetHeight(_resizeFrame) + location.y - CGRectGetMinY(_resizeFrame));
|
||||
|
||||
{
|
||||
var newSize = CGSizeMake(CGRectGetWidth(_resizeFrame) + location.x - CGRectGetMinX(_resizeFrame), CGRectGetHeight(_resizeFrame) + location.y - CGRectGetMinY(_resizeFrame));
|
||||
|
||||
if (theWindow._isSheet && theWindow._parentView && (theWindow._frame.size.width !== newSize.width))
|
||||
[theWindow._parentView _setAttachedSheetFrameOrigin];
|
||||
|
||||
[theWindow setFrameSize:newSize];
|
||||
[theWindow._parentView _setAttachedSheetFrameOrigin];
|
||||
|
||||
[theWindow setFrameSize:newSize];
|
||||
}
|
||||
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackResizeWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
}
|
||||
|
||||
@@ -160,10 +159,15 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
if (!visibleFrame)
|
||||
visibleFrame = [[CPPlatformWindow primaryPlatformWindow] visibleFrame];
|
||||
|
||||
var minPointY = 0;
|
||||
|
||||
if([CPMenu menuBarVisible])
|
||||
minPointY = [[CPApp mainMenu] menuBarHeight];
|
||||
|
||||
var restrictedPoint = CGPointMake(0, 0);
|
||||
|
||||
restrictedPoint.x = MIN(MAX(aPoint.x, -_frame.size.width + 4.0), CGRectGetMaxX(visibleFrame) - 4.0);
|
||||
restrictedPoint.y = MIN(MAX(aPoint.y, 0.0), CGRectGetMaxY(visibleFrame) - 8.0);
|
||||
restrictedPoint.y = MIN(MAX(aPoint.y, minPointY), CGRectGetMaxY(visibleFrame) - 8.0);
|
||||
|
||||
return restrictedPoint;
|
||||
}
|
||||
@@ -189,14 +193,14 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
var theWindow = [self window],
|
||||
frame = [theWindow frame],
|
||||
location = [theWindow convertBaseToGlobal:[anEvent locationInWindow]],
|
||||
origin = [self _pointWithinScreenFrame:CGPointMake(_CGRectGetMinX(frame) + (location.x - _mouseDraggedPoint.x),
|
||||
origin = [self _pointWithinScreenFrame:CGPointMake(_CGRectGetMinX(frame) + (location.x - _mouseDraggedPoint.x),
|
||||
_CGRectGetMinY(frame) + (location.y - _mouseDraggedPoint.y))];
|
||||
|
||||
[theWindow setFrameOrigin:origin];
|
||||
|
||||
_mouseDraggedPoint = [self _pointWithinScreenFrame:location];
|
||||
}
|
||||
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackMoveWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
}
|
||||
|
||||
@@ -206,9 +210,9 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
{
|
||||
var size = [_CPWindowViewResizeIndicatorImage size],
|
||||
boundsSize = [self frame].size;
|
||||
|
||||
|
||||
_resizeIndicator = [[CPImageView alloc] initWithFrame:CGRectMake(boundsSize.width - size.width - _resizeIndicatorOffset.width, boundsSize.height - size.height - _resizeIndicatorOffset.height, size.width, size.height)];
|
||||
|
||||
|
||||
[_resizeIndicator setImage:_CPWindowViewResizeIndicatorImage];
|
||||
[_resizeIndicator setAutoresizingMask:CPViewMinXMargin | CPViewMinYMargin];
|
||||
|
||||
@@ -222,7 +226,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (CPImage)showsResizeIndicator
|
||||
- (BOOL)showsResizeIndicator
|
||||
{
|
||||
return _resizeIndicator !== nil;
|
||||
}
|
||||
@@ -231,15 +235,15 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
{
|
||||
if (CGSizeEqualToSize(_resizeIndicatorOffset, anOffset))
|
||||
return;
|
||||
|
||||
|
||||
_resizeIndicatorOffset = anOffset;
|
||||
|
||||
|
||||
if (!_resizeIndicator)
|
||||
return;
|
||||
|
||||
var size = [_resizeIndicator frame].size,
|
||||
boundsSize = [self frame].size;
|
||||
|
||||
|
||||
[_resizeIndicator setFrameOrigin:CGPointMake(boundsSize.width - size.width - anOffset.width, boundsSize.height - size.height - anOffset.height)];
|
||||
}
|
||||
|
||||
@@ -263,7 +267,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
|
||||
- (CGSize)toolbarOffset
|
||||
{
|
||||
return CGSizeMakeZero();
|
||||
return _toolbarOffset;
|
||||
}
|
||||
|
||||
- (CPColor)toolbarLabelColor
|
||||
@@ -275,7 +279,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
{
|
||||
if (!_toolbarView || [_toolbarView isHidden])
|
||||
return [self toolbarOffset].height;
|
||||
|
||||
|
||||
return CGRectGetMaxY([_toolbarView frame]);
|
||||
}
|
||||
|
||||
@@ -289,20 +293,20 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
var theWindow = [self window],
|
||||
bounds = [self bounds],
|
||||
width = CGRectGetWidth(bounds);
|
||||
|
||||
|
||||
if ([[theWindow toolbar] isVisible])
|
||||
{
|
||||
var toolbarView = [self toolbarView],
|
||||
toolbarOffset = [self toolbarOffset];
|
||||
|
||||
|
||||
[toolbarView setFrame:CGRectMake(toolbarOffset.width, toolbarOffset.height, width, CGRectGetHeight([toolbarView frame]))];
|
||||
}
|
||||
|
||||
|
||||
if ([self showsResizeIndicator])
|
||||
{
|
||||
var size = [_resizeIndicator frame].size,
|
||||
boundsSize = [self bounds].size;
|
||||
|
||||
|
||||
[_resizeIndicator setFrameOrigin:CGPointMake(boundsSize.width - size.width - _resizeIndicatorOffset.width, boundsSize.height - size.height - _resizeIndicatorOffset.height)];
|
||||
}
|
||||
}
|
||||
@@ -312,11 +316,11 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
var theWindow = [self window],
|
||||
toolbar = [theWindow toolbar],
|
||||
toolbarView = [toolbar _toolbarView];
|
||||
|
||||
|
||||
if (_toolbarView !== toolbarView)
|
||||
{
|
||||
[_toolbarView removeFromSuperview];
|
||||
|
||||
|
||||
if (toolbarView)
|
||||
{
|
||||
[toolbarView removeFromSuperview];
|
||||
@@ -324,7 +328,7 @@ var _CPWindowViewResizeIndicatorImage = nil;
|
||||
|
||||
[self addSubview:toolbarView];
|
||||
}
|
||||
|
||||
|
||||
_toolbarView = toolbarView;
|
||||
}
|
||||
|
||||
|
||||
+18
-27
@@ -27,10 +27,8 @@
|
||||
@import "CPWindow.j"
|
||||
@import "CPDocument.j"
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
|
||||
|
||||
/*!
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPWindowController
|
||||
|
||||
@@ -133,7 +131,7 @@
|
||||
if (_window)
|
||||
return;
|
||||
|
||||
[[CPBundle bundleForClass:[_cibOwner class]] loadCibFile:[self windowCibPath] externalNameTable:[CPDictionary dictionaryWithObject:_cibOwner forKey:CPCibOwner]];
|
||||
[[CPBundle mainBundle] loadCibFile:[self windowCibPath] externalNameTable:[CPDictionary dictionaryWithObject:_cibOwner forKey:CPCibOwner]];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -144,7 +142,7 @@
|
||||
{
|
||||
var theWindow = [self window];
|
||||
|
||||
if ([theWindow respondsToSelector:@selector(becomesKeyOnlyIfNeeded)] && [theWindow becomesKeyOnlyIfNeeded])
|
||||
if ([theWindow respondsToSelector:@selector(becomesKeyOnlyIfNeeded)] && [theWindow becomesKeyOnlyIfNeeded])
|
||||
[theWindow orderFront:aSender];
|
||||
else
|
||||
[theWindow makeKeyAndOrderFront:aSender];
|
||||
@@ -174,6 +172,14 @@
|
||||
if (_window === nil && [_cibOwner isKindOfClass:[CPDocument class]])
|
||||
[self setWindow:[_cibOwner valueForKey:@"window"]];
|
||||
|
||||
if (!_window)
|
||||
{
|
||||
var reason = [CPString stringWithFormat:@"Window for %@ could not be loaded from Cib or no window specified. \
|
||||
Override loadWindow to load the window manually.", self];
|
||||
|
||||
[CPException raise:CPInternalInconsistencyException reason:reason];
|
||||
}
|
||||
|
||||
[self windowDidLoad];
|
||||
[_document windowControllerDidLoadCib:self];
|
||||
|
||||
@@ -226,7 +232,7 @@
|
||||
{
|
||||
if (![self supportsMultipleDocuments])
|
||||
[self removeDocument:_document];
|
||||
|
||||
|
||||
[defaultCenter removeObserver:self
|
||||
name:CPDocumentWillSaveNotification
|
||||
object:_document];
|
||||
@@ -270,6 +276,10 @@
|
||||
[self setViewController:viewController];
|
||||
|
||||
[self synchronizeWindowTitleWithDocumentName];
|
||||
|
||||
// Change of document means toolbar items may no longer make sense.
|
||||
// FIXME: DOCUMENT ARCHITECTURE Should we setToolbar: as well?
|
||||
[[[self window] toolbar] validateVisibleItems];
|
||||
}
|
||||
|
||||
- (void)setSupportsMultipleDocuments:(BOOL)shouldSupportMultipleDocuments
|
||||
@@ -314,25 +324,6 @@
|
||||
return _documents;
|
||||
}
|
||||
|
||||
- (void)setViewController:(CPViewController)aViewController
|
||||
{
|
||||
var containerView = [self viewControllerContainerView] || [[self window] contentView],
|
||||
view = [_viewController view],
|
||||
frame = view ? [view frame] : [containerView bounds];
|
||||
|
||||
[view removeFromSuperview];
|
||||
|
||||
_viewController = aViewController;
|
||||
|
||||
view = [_viewController view];
|
||||
|
||||
if (view)
|
||||
{
|
||||
[view setFrame:frame];
|
||||
[containerView addSubview:view];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setViewControllerContainerView:(CPView)aView
|
||||
{
|
||||
_viewControllerContainerView = aView;
|
||||
@@ -394,7 +385,7 @@
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the document has unsaved changes. The window can use this as a hint to
|
||||
Sets whether the document has unsaved changes. The window can use this as a hint to
|
||||
@param isEdited \c YES means the document has unsaved changes.
|
||||
*/
|
||||
- (void)setDocumentEdited:(BOOL)isEdited
|
||||
@@ -435,7 +426,7 @@
|
||||
if (_windowCibPath)
|
||||
return _windowCibPath;
|
||||
|
||||
return [[CPBundle bundleForClass:[_cibOwner class]] pathForResource:_windowCibName + @".cib"];
|
||||
return [[CPBundle mainBundle] pathForResource:_windowCibName + @".cib"];
|
||||
}
|
||||
|
||||
// Setting and Getting Window Attributes
|
||||
|
||||
+9
-4
@@ -60,7 +60,7 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
|
||||
if (self)
|
||||
{
|
||||
_data = [CPURLConnection sendSynchronousRequest:[CPURLRequest requestWithURL:aURL] returningResponse:nil error:nil];
|
||||
_data = [CPURLConnection sendSynchronousRequest:[CPURLRequest requestWithURL:aURL] returningResponse:nil];
|
||||
_awakenCustomResources = YES;
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
|
||||
var topLevelObjects = [anExternalNameTable objectForKey:CPCibTopLevelObjects];
|
||||
|
||||
[objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects]
|
||||
[objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects];
|
||||
[objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects];
|
||||
[objectData awakeWithOwner:owner topLevelObjects:topLevelObjects];
|
||||
|
||||
@@ -171,12 +171,17 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
|
||||
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)data
|
||||
{
|
||||
_data = [CPData dataWithString:data];
|
||||
// FIXME: Why aren't we getting connection:didFailWithError:
|
||||
if (!data)
|
||||
return [self connection:aConnection didFailWithError:nil];
|
||||
|
||||
_data = [CPData dataWithRawString:data];
|
||||
}
|
||||
|
||||
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
|
||||
{
|
||||
alert("cib: connection failed.");
|
||||
if ([_loadDelegate respondsToSelector:@selector(cibDidFailToLoad:)])
|
||||
[_loadDelegate cibDidFailToLoad:self];
|
||||
|
||||
_loadDelegate = nil;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user