mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-09 12:17:13 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bce83e45b5 | ||
|
|
77bd8d3a76 | ||
|
|
118ece9f1c | ||
|
|
971956a6ba | ||
|
|
48cd362015 | ||
|
|
b12b1bf662 | ||
|
|
6829d502d7 | ||
|
|
aa8689a4f0 | ||
|
|
44cbcc60fa | ||
|
|
d4edb28837 | ||
|
|
3245f38788 | ||
|
|
392cca1b2a | ||
|
|
ad901ed4b0 | ||
|
|
8f1e56e398 | ||
|
|
b694437dba | ||
|
|
370efb35aa | ||
|
|
dc93f39b58 | ||
|
|
629c8669c2 | ||
|
|
558dbdc612 | ||
|
|
1944f51f97 | ||
|
|
312a48a3fe | ||
|
|
5fc67ff7de | ||
|
|
d584b4dccf | ||
|
|
9d50b33268 | ||
|
|
722166a9d2 | ||
|
|
ec83a0f779 | ||
|
|
e20311c25a | ||
|
|
c21fb3bb26 | ||
|
|
9dfdde674f | ||
|
|
f4b37fbf3b | ||
|
|
13c5736a0f | ||
|
|
47de360719 | ||
|
|
19e3d93f1a | ||
|
|
2d5aef6157 | ||
|
|
29785e21cd | ||
|
|
95612b256f | ||
|
|
a3a03d702c | ||
|
|
742df0df82 | ||
|
|
aaac5e429d | ||
|
|
c3ac3ddd3f | ||
|
|
61c0d33b4d | ||
|
|
67672ebb0c | ||
|
|
6132243968 | ||
|
|
88635ed440 | ||
|
|
3d42cba823 | ||
|
|
51a58f650e | ||
|
|
6c79a31b38 | ||
|
|
91ffb0ea0c | ||
|
|
77c74412dc | ||
|
|
008a320e6a | ||
|
|
f3e90f87b1 | ||
|
|
11b881273e | ||
|
|
229debc2b7 | ||
|
|
0c24a60bac | ||
|
|
2aa061a6af | ||
|
|
8f9d83495c | ||
|
|
cb18896137 | ||
|
|
b13eee1fc9 | ||
|
|
95c3401640 | ||
|
|
2f40cff3e2 | ||
|
|
848828222b | ||
|
|
d3e6819fd7 | ||
|
|
55bbfc9205 | ||
|
|
b6e9625729 | ||
|
|
fe91331dc4 | ||
|
|
cf946c62a8 | ||
|
|
420ce1799a | ||
|
|
72adaa51ee | ||
|
|
695f60aefc | ||
|
|
e00c2f3c05 | ||
|
|
7ae0f1fec9 | ||
|
|
6b3f05f517 | ||
|
|
93c90274b5 | ||
|
|
2d1d30a663 | ||
|
|
baa1318298 | ||
|
|
8fddc2370b | ||
|
|
ea8245a6b3 | ||
|
|
d508459480 | ||
|
|
6fc56226ba | ||
|
|
c98107b6b8 | ||
|
|
17d68eeb97 | ||
|
|
a457145312 | ||
|
|
0c13138a20 | ||
|
|
e8b1ff8846 | ||
|
|
eeae653c94 | ||
|
|
6d402bdc00 | ||
|
|
fddf3d70ff | ||
|
|
87e39bc707 | ||
|
|
7c4f01d81b | ||
|
|
96523aca07 | ||
|
|
1ceeec8b5b | ||
|
|
cba546880b | ||
|
|
2355cf4f6c | ||
|
|
2c0e717b23 | ||
|
|
8745843105 | ||
|
|
ef252a97c4 | ||
|
|
14ad8f6d6d | ||
|
|
bcd8c72ae4 | ||
|
|
499d2d93d5 | ||
|
|
84f131808d | ||
|
|
79fa65624d | ||
|
|
9ca96bc909 | ||
|
|
3dbddcf186 | ||
|
|
df8c648d70 | ||
|
|
ee7c71a184 | ||
|
|
de163e22d9 | ||
|
|
d9ef4342b6 | ||
|
|
e75ef74ea5 | ||
|
|
2e26db5bf5 | ||
|
|
fe1c45980d | ||
|
|
0423546451 | ||
|
|
8da4ea0823 | ||
|
|
c48d9a01c6 | ||
|
|
4ca0044bbd | ||
|
|
9f2ea190fc | ||
|
|
4248348308 | ||
|
|
eaf701beca | ||
|
|
a48a305cd8 | ||
|
|
cc334296bb | ||
|
|
bb2726fb9a | ||
|
|
95b9ea336b | ||
|
|
ba84e6d3e2 | ||
|
|
22c248454e | ||
|
|
42611f3a84 | ||
|
|
d7b778e8ac | ||
|
|
1be9d6816e | ||
|
|
d263c74d5d | ||
|
|
25634a66cb | ||
|
|
228e801c91 | ||
|
|
abc715facb | ||
|
|
a0a81be042 | ||
|
|
64948053df | ||
|
|
039de1b6e8 | ||
|
|
b050559532 | ||
|
|
064a062cd8 | ||
|
|
b2d994bd66 | ||
|
|
315e0eac0c | ||
|
|
06eac3e79b | ||
|
|
9e825ea62e | ||
|
|
d8249b6035 | ||
|
|
7b14f41cd8 | ||
|
|
4bb4d6cd62 | ||
|
|
82404265fb | ||
|
|
9ee71e762c | ||
|
|
d52aa1575e | ||
|
|
717c8520b4 | ||
|
|
76edc4f914 | ||
|
|
66b447de7b | ||
|
|
afaa816d5c | ||
|
|
13e88c6295 | ||
|
|
b6cf4ad0a4 | ||
|
|
b457119925 | ||
|
|
b5cdcec004 | ||
|
|
86d5ea6c64 | ||
|
|
424611e7c7 | ||
|
|
de920e959a | ||
|
|
9ab75eb662 | ||
|
|
4870ac86ef | ||
|
|
24c6f0ec9f | ||
|
|
250ceb69f4 | ||
|
|
0561f69c86 | ||
|
|
341eddb9f9 | ||
|
|
ee8fef4e85 | ||
|
|
e6d1753e74 | ||
|
|
891d2a28c1 | ||
|
|
0af70c04a0 | ||
|
|
efaff96168 | ||
|
|
6fc02623df | ||
|
|
a27f104a63 | ||
|
|
9838bbf084 | ||
|
|
6076185da7 | ||
|
|
810f82cc60 | ||
|
|
fce91c7ad3 | ||
|
|
e6ea4c267a | ||
|
|
95274f618c | ||
|
|
385bec7400 | ||
|
|
41cb21cd0f | ||
|
|
c7520593da | ||
|
|
666ac4acd1 | ||
|
|
ea15b247d3 | ||
|
|
ea252323d2 | ||
|
|
42775cb152 | ||
|
|
f925585475 | ||
|
|
aba7ea8bf1 | ||
|
|
29b0aa7cde | ||
|
|
375b490678 | ||
|
|
27c2285600 | ||
|
|
1d1f664e07 | ||
|
|
f2e95f7e63 | ||
|
|
3055522645 | ||
|
|
7347428e94 | ||
|
|
4a8a205ae2 | ||
|
|
090bf3c46d | ||
|
|
1691b6f07e | ||
|
|
22578c3650 | ||
|
|
57c7757c2e | ||
|
|
16deb3700d | ||
|
|
f5d05a7c93 | ||
|
|
40b5332c83 | ||
|
|
238da6b80f | ||
|
|
1c4f11c1d2 | ||
|
|
e57e7cbbc5 | ||
|
|
97cfb5c1f9 | ||
|
|
9d7ba990a8 | ||
|
|
cac6204a96 | ||
|
|
b7f41c104c | ||
|
|
1123e848bf | ||
|
|
e3fe4ff92f | ||
|
|
d6b338b160 | ||
|
|
ddada18069 | ||
|
|
b43b097d40 | ||
|
|
2cc723752e | ||
|
|
6b02a4105b | ||
|
|
3098bfe6db | ||
|
|
5e658354ab | ||
|
|
2f5e4d8335 | ||
|
|
a607d79c7d | ||
|
|
81c71beaa7 | ||
|
|
5bd30fc4e5 | ||
|
|
46d452be5e | ||
|
|
2338ca2fa2 | ||
|
|
2900163644 | ||
|
|
e90f0188c3 | ||
|
|
5fa457621b | ||
|
|
e125139777 | ||
|
|
bc6f168afa | ||
|
|
fc85ae16a5 | ||
|
|
d5f2654eae | ||
|
|
5e951c1e53 | ||
|
|
69cdc2ccd0 | ||
|
|
3474e8baf1 | ||
|
|
6e7e3ef35e | ||
|
|
8a2cd8c5c6 | ||
|
|
306c1a962a | ||
|
|
8441e62f49 | ||
|
|
9b948dcac7 | ||
|
|
1fb9c973fe | ||
|
|
048bcc1be7 | ||
|
|
1d8cebd5be | ||
|
|
0e55a5d7c4 | ||
|
|
c6b934a035 | ||
|
|
5b531b2005 | ||
|
|
570a2a56ff | ||
|
|
d79ed12b5c | ||
|
|
7864a9fa8e | ||
|
|
3aa7378699 | ||
|
|
1d76ac1b91 | ||
|
|
99697926d9 | ||
|
|
32d0c6ca17 | ||
|
|
2dac122c30 | ||
|
|
e9092007ee | ||
|
|
b94c82f4aa | ||
|
|
6a32314c0b | ||
|
|
eff3170df1 | ||
|
|
62d825d970 | ||
|
|
cb416e0277 | ||
|
|
5066a1d395 | ||
|
|
df3658bc96 | ||
|
|
3ac8ac40bc | ||
|
|
e5c5378ec0 | ||
|
|
ef41abec97 | ||
|
|
72d4675ec4 | ||
|
|
1cd8acb92c | ||
|
|
9856e6bfd7 | ||
|
|
be2ffe95c5 | ||
|
|
3f2d6f24d2 | ||
|
|
714cd402df | ||
|
|
ffd99e3ed7 | ||
|
|
64a8e4c5c0 | ||
|
|
bb0a1eadda | ||
|
|
a8ab16002e | ||
|
|
9dc0c3ab6d | ||
|
|
82df59e7dc | ||
|
|
47694c2d80 | ||
|
|
4993d16738 | ||
|
|
43c0db44ea | ||
|
|
ebbf3c4576 | ||
|
|
f109313d32 | ||
|
|
e305d30c1b | ||
|
|
317f1d356c | ||
|
|
25f3d99732 | ||
|
|
b0443fe5e3 | ||
|
|
3f5bb77230 | ||
|
|
1cf292279c | ||
|
|
2e350f6bc5 | ||
|
|
cfd91b7a61 | ||
|
|
1a8062fbbc | ||
|
|
32096ade9f | ||
|
|
b7493586e2 | ||
|
|
a02cda6376 | ||
|
|
2348a8ebd3 | ||
|
|
7728d8abfb | ||
|
|
b21b08f899 | ||
|
|
c752db905d | ||
|
|
7ca50e4d28 | ||
|
|
1862eacc27 | ||
|
|
0497888edf | ||
|
|
25d76c9510 | ||
|
|
46dbce0dbb | ||
|
|
98ef1bfd05 | ||
|
|
7205146e1b | ||
|
|
02907a599d | ||
|
|
d6ac69029b | ||
|
|
c8ed0f5436 | ||
|
|
20d14d2f81 | ||
|
|
668540d0ce | ||
|
|
ee46ab23ba | ||
|
|
dba418a93d | ||
|
|
186adee0f2 | ||
|
|
abd3abdfea | ||
|
|
7aa58fa98a | ||
|
|
a32368f89a | ||
|
|
4f1d9fe836 | ||
|
|
98df1859af | ||
|
|
1bdea9ff45 | ||
|
|
17273ecbfa | ||
|
|
fd62233960 | ||
|
|
5b0876b7ed | ||
|
|
96afa4b950 | ||
|
|
5e74290486 | ||
|
|
d00197b1f7 | ||
|
|
8b9f36043d | ||
|
|
041f8200df | ||
|
|
b77eef0cd3 | ||
|
|
774e958d49 | ||
|
|
f7c00df9a9 | ||
|
|
98e18340b7 | ||
|
|
58e06142b9 | ||
|
|
c895170597 | ||
|
|
a619bc15c2 | ||
|
|
369cacb3aa | ||
|
|
78fe93be68 | ||
|
|
1a7a07b51a | ||
|
|
2996404ad4 | ||
|
|
669dff7a9b | ||
|
|
3a7dee6cc2 | ||
|
|
7017000c81 | ||
|
|
379eb05144 | ||
|
|
8fc45cf345 | ||
|
|
dc85900780 | ||
|
|
0a28127667 | ||
|
|
879561f67b | ||
|
|
7f9bc98929 | ||
|
|
4c01bebd07 | ||
|
|
2fbe026215 | ||
|
|
efcc5d1054 | ||
|
|
2cf5b90183 | ||
|
|
75060a3385 | ||
|
|
b55ddcf4ce | ||
|
|
744e78b7ad | ||
|
|
f360ab9475 | ||
|
|
58ac49e0b0 | ||
|
|
d1c0237bf3 | ||
|
|
abc1c6f2db | ||
|
|
14ba9c95eb | ||
|
|
0b03025924 | ||
|
|
b369aa6aff | ||
|
|
f189ec6eb9 | ||
|
|
3b761ec55e | ||
|
|
1b4a5dc48b | ||
|
|
356d2e9ea7 | ||
|
|
d3bebfd064 | ||
|
|
da9221bf71 | ||
|
|
8990f6c387 | ||
|
|
cdaa363a82 | ||
|
|
e910fee03f | ||
|
|
7528c452c4 | ||
|
|
824cd6d5f1 | ||
|
|
3f7db4ca12 | ||
|
|
2ee3b93e0d | ||
|
|
bd69683dc2 | ||
|
|
1b8f263224 | ||
|
|
58aef6ef74 | ||
|
|
8a1c1b931f | ||
|
|
db77e826d4 | ||
|
|
35fa76064c | ||
|
|
d2df0919a5 | ||
|
|
b378a4d0fa | ||
|
|
e174edd0dc | ||
|
|
3060c3f100 | ||
|
|
14528d5a49 | ||
|
|
35c1221905 | ||
|
|
300f74bf4d | ||
|
|
e8dd56bda7 | ||
|
|
0c9f247a82 | ||
|
|
51e6f92fee | ||
|
|
d1d0d72e32 | ||
|
|
aef723e160 | ||
|
|
ab8add681e | ||
|
|
07da83715b | ||
|
|
7db40a6b0d | ||
|
|
55a8084b39 | ||
|
|
475cd6a21f | ||
|
|
4c3a43989b | ||
|
|
0d7ba4e872 | ||
|
|
a40714e14a | ||
|
|
c850c0cf45 | ||
|
|
ddc9334af3 | ||
|
|
654b0493bb | ||
|
|
64cbf98140 | ||
|
|
5fc8cd697e | ||
|
|
2423faa8c6 | ||
|
|
172c7330ab | ||
|
|
1a5bf1c8eb | ||
|
|
377f331b47 | ||
|
|
3e85bf0e8b | ||
|
|
3f496f1a10 | ||
|
|
b52da00d46 | ||
|
|
69fa064509 | ||
|
|
0c349a8d7f | ||
|
|
3b8664e6ff | ||
|
|
2bb6da5132 | ||
|
|
0876653ae3 | ||
|
|
4876c7142f | ||
|
|
856373adec | ||
|
|
fbf559043e | ||
|
|
bbda6e6e77 | ||
|
|
4cd7d35499 | ||
|
|
a8393eaa8a | ||
|
|
6a9804939e | ||
|
|
b50067e39e | ||
|
|
028be939f9 | ||
|
|
b5e84342ac | ||
|
|
5bc92b686b | ||
|
|
e8f1763615 | ||
|
|
41dc660140 | ||
|
|
a05ecd2759 | ||
|
|
f51222dff6 | ||
|
|
598411c072 | ||
|
|
241cc2ac06 | ||
|
|
38e2ca3589 | ||
|
|
5ccc638baf | ||
|
|
9e6ba1ea06 | ||
|
|
c0400d6362 | ||
|
|
e9fee72744 | ||
|
|
59bec06e6e | ||
|
|
64b81cfcdb | ||
|
|
865a2b513f | ||
|
|
2e37945b4f | ||
|
|
92594a7ef9 | ||
|
|
a34e23fa1c | ||
|
|
fa5352ad40 | ||
|
|
4d179b1db7 | ||
|
|
d2b1df88e8 | ||
|
|
756885fd53 | ||
|
|
0ef4e9bca5 | ||
|
|
4370604165 | ||
|
|
5ce96b45bb | ||
|
|
4f4a488762 | ||
|
|
d53207efd5 | ||
|
|
ba81b1356d | ||
|
|
ae7fbad2b9 | ||
|
|
e69d770583 | ||
|
|
8382bb1ae8 | ||
|
|
82cfc5f994 | ||
|
|
16be11325f | ||
|
|
5829c8b682 | ||
|
|
57ee7bcfce | ||
|
|
4824f22e3c | ||
|
|
88be473da4 | ||
|
|
3ff945663f | ||
|
|
9f989297df | ||
|
|
12d03a5128 | ||
|
|
369e02fb37 | ||
|
|
48f0aa0b78 | ||
|
|
3e8cb8f9c6 | ||
|
|
4865d6b20f | ||
|
|
745e24d2f5 | ||
|
|
d7b679dc78 | ||
|
|
9b99457871 | ||
|
|
6592328553 | ||
|
|
9c226ae574 | ||
|
|
215619e6e2 | ||
|
|
eee6fe0e67 | ||
|
|
8e3ac66a2d | ||
|
|
cc90f2e5f0 | ||
|
|
0356c6e622 | ||
|
|
308bbb2f8c | ||
|
|
d0599aef8f | ||
|
|
afba1909bb | ||
|
|
3761fb8037 | ||
|
|
adc2344454 | ||
|
|
a5a43de26e | ||
|
|
c281d01974 | ||
|
|
8660c72015 | ||
|
|
cb81eeed15 | ||
|
|
6ad5ec4d66 | ||
|
|
b2f35b61d3 | ||
|
|
ddbc1e0e94 | ||
|
|
4086d37dac | ||
|
|
b9208c2f59 | ||
|
|
92bce1a663 | ||
|
|
a5b468bbeb | ||
|
|
761e32a847 | ||
|
|
1f356053db | ||
|
|
9d64526f26 | ||
|
|
3c087f01b4 | ||
|
|
0ef7f53250 | ||
|
|
528ef4394a | ||
|
|
b12017cd51 | ||
|
|
714fc9d5bb | ||
|
|
d131127717 | ||
|
|
b8046c479f | ||
|
|
af062da99d | ||
|
|
21da2c6f56 | ||
|
|
7cc6e6af0a | ||
|
|
ab2cb38362 | ||
|
|
8c171e5a40 | ||
|
|
ac6c03f2c7 | ||
|
|
e97b78477b | ||
|
|
849dda0725 | ||
|
|
b8755c113f | ||
|
|
d57e7050db | ||
|
|
dd4db720ef | ||
|
|
202d59475b | ||
|
|
1fecae3dc0 | ||
|
|
998f52c4a4 | ||
|
|
bd07054a1b | ||
|
|
adde315e2d | ||
|
|
c97bb6cbdd | ||
|
|
f4171f9afa | ||
|
|
d165761740 | ||
|
|
ca1ea3721f | ||
|
|
7d158de356 | ||
|
|
71a99deada | ||
|
|
c7f81691bf | ||
|
|
05cdc2bab3 | ||
|
|
8beb71aa76 | ||
|
|
201fda96a5 | ||
|
|
4e94fa51fe | ||
|
|
f920202bec | ||
|
|
19f1657824 | ||
|
|
5d1db76be2 | ||
|
|
c6795fdfcf | ||
|
|
35a9305347 | ||
|
|
e36ab855e8 | ||
|
|
d516037286 | ||
|
|
b4abc5a99d | ||
|
|
9bb9db760e | ||
|
|
831ea11a6b | ||
|
|
bfcb94a844 | ||
|
|
1e3b79de17 | ||
|
|
fe21fc82c4 | ||
|
|
cb9d81aa7f | ||
|
|
8f1a02cb0f | ||
|
|
1f0e4b0eca | ||
|
|
e0f53b89ca | ||
|
|
8b25240d04 | ||
|
|
7e88b1abfd | ||
|
|
e38dfc4335 | ||
|
|
90fe0d64f5 | ||
|
|
eab4f4bf78 | ||
|
|
ece999662d | ||
|
|
8c049c3de4 | ||
|
|
6cab77e005 | ||
|
|
c74642b641 | ||
|
|
e7e804cae3 | ||
|
|
7ca3473b64 | ||
|
|
927b94244a | ||
|
|
a5c467fba5 | ||
|
|
fc74bba68c | ||
|
|
656c94a8cd | ||
|
|
896eaf7937 | ||
|
|
886b4ef118 | ||
|
|
c42fb68d8a | ||
|
|
3f27fca518 | ||
|
|
c7823a92f8 | ||
|
|
c61c892732 | ||
|
|
b0470ca2d7 | ||
|
|
7c305c45fc | ||
|
|
cfda93d1e3 | ||
|
|
b4c7af498b | ||
|
|
6ddb7851f6 | ||
|
|
7f60bdec4f | ||
|
|
8971c320bd | ||
|
|
a710dc974f | ||
|
|
e1b63b848f | ||
|
|
5409face8c | ||
|
|
c9e40cbfba | ||
|
|
35947697f2 | ||
|
|
3e1f09bb7a | ||
|
|
82ef2356a5 | ||
|
|
33f96dacd1 | ||
|
|
cc24cd9a73 | ||
|
|
0fff3bade6 | ||
|
|
b1c410d42c | ||
|
|
f4b4d38165 | ||
|
|
f46aa870a7 | ||
|
|
7d4c2840eb | ||
|
|
8fb0abd3d6 | ||
|
|
f6bd4ddd96 | ||
|
|
5f5efffe9d | ||
|
|
48f31743dd | ||
|
|
1224ca9493 | ||
|
|
45ec895df0 | ||
|
|
318d0456af | ||
|
|
5446e1f98f | ||
|
|
d4b9c4c5e1 | ||
|
|
5cb9f5f7c2 | ||
|
|
a7bd5e1bf0 | ||
|
|
bf21fbd000 | ||
|
|
377107d63a | ||
|
|
25d20df5f3 | ||
|
|
cfdb6b0df2 | ||
|
|
9955346958 | ||
|
|
a71822cee8 | ||
|
|
3523a9ff12 | ||
|
|
1f64cb6393 | ||
|
|
0235076fb6 | ||
|
|
c56e552d6e | ||
|
|
5a3f1c81a0 | ||
|
|
25c145e490 | ||
|
|
44dc4f6595 | ||
|
|
d3938f3031 | ||
|
|
cfa21b0c96 | ||
|
|
83bceaa867 | ||
|
|
f6a731ff91 | ||
|
|
354fddf6b2 | ||
|
|
aebbdbe0a3 | ||
|
|
15bff97388 | ||
|
|
ffab4bdf56 | ||
|
|
fff40499cc | ||
|
|
0b24bf8fe8 | ||
|
|
429dd80409 | ||
|
|
bee5d36fbf | ||
|
|
8428edad37 | ||
|
|
2ba2bb8115 | ||
|
|
b8be5d3bf5 | ||
|
|
b99ba2c25e | ||
|
|
289a21156c | ||
|
|
66ca70acd2 | ||
|
|
578113d557 | ||
|
|
79c9a2781f | ||
|
|
fd7436527d | ||
|
|
20f128b9c3 | ||
|
|
e6a1370ae5 | ||
|
|
4758154014 | ||
|
|
a7ed62a108 | ||
|
|
8f8e2343a0 | ||
|
|
6d8e1705b8 | ||
|
|
a84bc49540 | ||
|
|
fe56cbb77f | ||
|
|
ac9b6af56a | ||
|
|
4ff99126d6 | ||
|
|
3e4763df68 | ||
|
|
f3fdcf327d | ||
|
|
e5dd36d807 | ||
|
|
ad03ab01e2 | ||
|
|
be72a3ff47 | ||
|
|
d8b2946fb5 | ||
|
|
aa3e049565 | ||
|
|
bf49d7b766 | ||
|
|
b9cbf8274e | ||
|
|
07ec530442 | ||
|
|
12cbbcd485 | ||
|
|
d2be23e354 | ||
|
|
ea1783504d | ||
|
|
8391f593d9 | ||
|
|
dc3ffdff08 | ||
|
|
ef6cf18aaa | ||
|
|
a31c8891ba | ||
|
|
36b65f3f30 | ||
|
|
dff2a5656f | ||
|
|
5507855bee | ||
|
|
7266821eae | ||
|
|
27e79c2222 | ||
|
|
d3f67947ef | ||
|
|
a0cd03caa4 | ||
|
|
850125b8ab | ||
|
|
6b9b9b544b | ||
|
|
45e3f776cf | ||
|
|
1a00998357 | ||
|
|
ac02592c13 | ||
|
|
52d3115ad6 | ||
|
|
97492fcb29 | ||
|
|
1d39235a45 | ||
|
|
15dc89fe2f | ||
|
|
e27e4309da | ||
|
|
7f733fdf34 | ||
|
|
e190ba82b4 | ||
|
|
f2f99833a6 | ||
|
|
5daf874f6c | ||
|
|
8beb7848a2 | ||
|
|
c7313f519f | ||
|
|
43a3e6d984 | ||
|
|
0bafdfd43a | ||
|
|
3fc7b7573d | ||
|
|
8fc213a134 | ||
|
|
eb96d48230 | ||
|
|
4ff6590e74 | ||
|
|
b8cac31e85 | ||
|
|
c3d698bcac | ||
|
|
28da8ccacf | ||
|
|
00b1d893e2 | ||
|
|
4a1630b7ba | ||
|
|
7ea048f5bd | ||
|
|
bd3895e6c1 | ||
|
|
71200ca5f3 | ||
|
|
00456fe62c | ||
|
|
269b61d6c4 | ||
|
|
b23d62190d | ||
|
|
113131a2b3 | ||
|
|
d4436acea3 | ||
|
|
fc581e3d3d | ||
|
|
83cdbec1d3 | ||
|
|
9f237c2d1a | ||
|
|
cfed10bbac | ||
|
|
67cfe69f38 | ||
|
|
94f83a92b7 | ||
|
|
738d9186a6 | ||
|
|
1b494389a6 | ||
|
|
4588d1db08 | ||
|
|
8c1f6f3014 | ||
|
|
abb49664f9 | ||
|
|
c0fc298082 | ||
|
|
32af35492d | ||
|
|
ad58952a32 | ||
|
|
e05816419e | ||
|
|
3b0feec801 | ||
|
|
688fd5725d | ||
|
|
9c7f07979b | ||
|
|
22f7985d20 | ||
|
|
675b9df44c | ||
|
|
af0b06ff28 | ||
|
|
a71a984a03 | ||
|
|
f4231dd890 | ||
|
|
cfaf2d26a5 | ||
|
|
43197b3099 | ||
|
|
3c30a2fc3d | ||
|
|
4c31b25110 | ||
|
|
a5ef85c676 | ||
|
|
b747cd339a | ||
|
|
8e7ce7c814 | ||
|
|
a4b75b0132 | ||
|
|
453845dd20 | ||
|
|
33ff286635 | ||
|
|
a45c3efa38 | ||
|
|
0925d41db6 | ||
|
|
c63f92d1ea | ||
|
|
e249937457 | ||
|
|
645affad93 | ||
|
|
77c2ab3822 | ||
|
|
6aa4940e2b | ||
|
|
78ec4046cc | ||
|
|
5604fa23a8 | ||
|
|
41f3146c47 | ||
|
|
e395d6e0e0 | ||
|
|
99c29c5e61 | ||
|
|
862af9cbae | ||
|
|
6c0a281ac2 | ||
|
|
dcec480f08 | ||
|
|
6e11a66cb0 | ||
|
|
b31abab595 | ||
|
|
ff23f320ac | ||
|
|
2d55fb3443 | ||
|
|
9f3a1f1e7e | ||
|
|
a8394baa1a | ||
|
|
0533ddcc3e | ||
|
|
2825560c1f | ||
|
|
1497944eff | ||
|
|
bda437985e | ||
|
|
4f024a0974 | ||
|
|
3d2cf3f6b4 | ||
|
|
0cd33120cf | ||
|
|
c5f4e0a268 | ||
|
|
aaafb59976 | ||
|
|
06e3221c0f | ||
|
|
120768c74b | ||
|
|
864c62b6e0 | ||
|
|
854d1c0afa | ||
|
|
ae8fd0c2d4 | ||
|
|
81d23aafab | ||
|
|
7bc26d5e40 | ||
|
|
fedf0ef7ce | ||
|
|
bfefe5e80f | ||
|
|
f76b14dd83 | ||
|
|
06947ef9d3 | ||
|
|
8dc8d6f367 | ||
|
|
31ed6254db | ||
|
|
88d8a409bb | ||
|
|
c976557346 | ||
|
|
6cd92e1278 | ||
|
|
dd41c319ee | ||
|
|
0501f12017 | ||
|
|
2176785370 | ||
|
|
ad866e44f5 | ||
|
|
b28146aa47 | ||
|
|
390cf2c16c | ||
|
|
586a1e347d | ||
|
|
08b4bdada1 | ||
|
|
0f357c24a7 | ||
|
|
f887aca66f | ||
|
|
ff6487caf3 | ||
|
|
aa146613ac | ||
|
|
dadabd899d | ||
|
|
2f790c6503 | ||
|
|
120dcdac54 | ||
|
|
a95aae22ab | ||
|
|
64010672eb | ||
|
|
f9640fb99e | ||
|
|
1614855353 | ||
|
|
f9a1fb8d3f | ||
|
|
2e32688ad6 | ||
|
|
b24011cd6c | ||
|
|
1cf40a7d7a | ||
|
|
6d8d4b59cc | ||
|
|
f4e329f5de | ||
|
|
0eb86fd4b6 | ||
|
|
a284275ffe | ||
|
|
a8317177c6 | ||
|
|
df7fab7e03 | ||
|
|
7d0d01211b | ||
|
|
8b9ae1c1b8 | ||
|
|
844919cfda | ||
|
|
d01bc09b5b | ||
|
|
e55de4bf8d | ||
|
|
b6b3e7f492 | ||
|
|
4ce0bbe1c0 | ||
|
|
d2b5a7362e | ||
|
|
66e3c90655 | ||
|
|
b508ccab08 | ||
|
|
a138e9bde4 | ||
|
|
822a4310cd | ||
|
|
d0399eb902 | ||
|
|
86b780aa2e | ||
|
|
db6c70215a | ||
|
|
c55e1ac99c | ||
|
|
a60ce80141 | ||
|
|
acb1929b49 | ||
|
|
1ab9170a9b | ||
|
|
0e8e725f59 | ||
|
|
0c63a5aba4 | ||
|
|
831bbef9e1 | ||
|
|
6b7c610676 | ||
|
|
092a6b922e | ||
|
|
f2ced978c9 | ||
|
|
213c70e00d | ||
|
|
dc014e9b1d | ||
|
|
417f5c3f0f | ||
|
|
5365d1d1fd | ||
|
|
9c0777991b | ||
|
|
c61ba5f050 | ||
|
|
e10445807c | ||
|
|
3bc50c1c3b | ||
|
|
15b383886e | ||
|
|
91d1ba6713 | ||
|
|
4cb516d8b1 | ||
|
|
ecee86cc6d | ||
|
|
5500792ed3 | ||
|
|
51b0231f68 | ||
|
|
b55140d50a | ||
|
|
43af999d41 | ||
|
|
64b41ffb8e | ||
|
|
0e8bf56284 | ||
|
|
f2c9719c23 | ||
|
|
fb3ddb035c | ||
|
|
390208d16a | ||
|
|
4963f8401a | ||
|
|
9ce20d50ac | ||
|
|
717781655f | ||
|
|
4ffed5edf2 | ||
|
|
21ab1b7e06 | ||
|
|
e07c98b418 | ||
|
|
20eaf1455d | ||
|
|
6e75a13a90 | ||
|
|
d970bd5830 | ||
|
|
2652ece827 | ||
|
|
5b95ff5ff3 | ||
|
|
50ed22c63c | ||
|
|
e0af565824 | ||
|
|
b96285a56c | ||
|
|
49accf9fc8 | ||
|
|
932aa87d87 | ||
|
|
c1c5c67b36 | ||
|
|
a5a622cf92 | ||
|
|
f04a199f4c | ||
|
|
70da3e287d | ||
|
|
57d0e12b1f | ||
|
|
e2eefe350d | ||
|
|
e5b0094f7d | ||
|
|
3fe872c125 | ||
|
|
3739710fba | ||
|
|
65593c5200 | ||
|
|
46cc218694 | ||
|
|
c08ec3e52a | ||
|
|
9420c22298 | ||
|
|
a05e116ceb | ||
|
|
99b04827d1 | ||
|
|
40027c2266 | ||
|
|
6db2aa5354 | ||
|
|
9d8a4f14e3 | ||
|
|
6b1180caa4 | ||
|
|
e5ea6b242d | ||
|
|
0638de200b | ||
|
|
8dc6500649 | ||
|
|
bb2697a1d8 | ||
|
|
65d9df9c54 | ||
|
|
14d91c6bec | ||
|
|
3bd28b28ab | ||
|
|
f1601d01ea | ||
|
|
c27cc7ddf1 | ||
|
|
f58940d92d | ||
|
|
a1b680b722 | ||
|
|
56364d187a | ||
|
|
0023c5753a | ||
|
|
436c466e2d | ||
|
|
ffe1f9c7a4 | ||
|
|
0b4701f96d | ||
|
|
93ea03972b | ||
|
|
c9fcc22875 | ||
|
|
32e302b1b6 | ||
|
|
844d8fbe08 | ||
|
|
b60a99008e | ||
|
|
4c6959191d | ||
|
|
4a50eb2e05 | ||
|
|
661fd3446d | ||
|
|
02a997e28c | ||
|
|
221b4329c6 | ||
|
|
6930357351 | ||
|
|
28ebace7c6 | ||
|
|
486d0ae25a | ||
|
|
41ba12b1d7 | ||
|
|
b525be4680 | ||
|
|
87389c7ca6 | ||
|
|
f372e7ae11 | ||
|
|
018fe182d3 | ||
|
|
d0b437bc74 | ||
|
|
50a5abe321 | ||
|
|
9dcdf84824 | ||
|
|
880e4dfb3a | ||
|
|
c6e266b08f | ||
|
|
3eed879fd2 | ||
|
|
07b6f54990 | ||
|
|
bd07c5338a | ||
|
|
31279a0cdc | ||
|
|
487bf288e5 | ||
|
|
ad93ad55cc | ||
|
|
9e7ca719d8 | ||
|
|
b7b0ca40b0 | ||
|
|
1dcc6db079 | ||
|
|
10422caa92 | ||
|
|
319127f88a | ||
|
|
b4cf1ba041 | ||
|
|
0a32d13cb0 | ||
|
|
6d34f53e43 | ||
|
|
b3cc75fe46 | ||
|
|
409fab31e6 | ||
|
|
df6ec644e9 | ||
|
|
101cf4db2c | ||
|
|
9c3c239162 | ||
|
|
b22d1837be | ||
|
|
7ed2efd584 | ||
|
|
eaa9003aea | ||
|
|
9c4d4ccb70 | ||
|
|
9f8ca93a57 | ||
|
|
3aa97c7e0f | ||
|
|
38d72bc672 | ||
|
|
229d814866 | ||
|
|
5cc1ef9609 | ||
|
|
8913ef8929 | ||
|
|
71ee5f6b4b | ||
|
|
07000fd86f | ||
|
|
19d26f148f | ||
|
|
47c20911c3 | ||
|
|
a07211253c | ||
|
|
2f85a417d6 | ||
|
|
ee3b7deb07 | ||
|
|
2664e30a8b | ||
|
|
3268081d1f | ||
|
|
88035429bb | ||
|
|
25c7d98d2c | ||
|
|
4422530fe4 | ||
|
|
17b3fc044d | ||
|
|
bbe1b58437 | ||
|
|
546c95bdf6 | ||
|
|
faac82287f | ||
|
|
0fdfd8919f | ||
|
|
55fa6024db | ||
|
|
a1e3feda4f | ||
|
|
46a975b966 | ||
|
|
0508c54799 | ||
|
|
89e776281f | ||
|
|
1188da3c21 | ||
|
|
d81c944e45 | ||
|
|
6750b90671 | ||
|
|
723c9ae556 | ||
|
|
1d38ca64c7 | ||
|
|
547ed38855 | ||
|
|
8da7b0ee91 | ||
|
|
1da9e93241 | ||
|
|
3200f1c6ca | ||
|
|
a25ffe0899 | ||
|
|
7b04023f54 | ||
|
|
1c79db2303 | ||
|
|
b1fc23af08 | ||
|
|
4280c3513a | ||
|
|
9eab840f37 | ||
|
|
ce31353a7a | ||
|
|
f22684b5e8 | ||
|
|
12459aae1e | ||
|
|
fd0a0de4b2 | ||
|
|
0e3ead62fb | ||
|
|
8b36cddd9c | ||
|
|
415e826a73 | ||
|
|
6a0958f9c1 | ||
|
|
2788af6922 | ||
|
|
49e4e8e2fd | ||
|
|
63585dfea6 | ||
|
|
27035770d7 | ||
|
|
f2a70194c8 | ||
|
|
8bdd8258be | ||
|
|
aa0135515c | ||
|
|
9a0fe44acc | ||
|
|
b74b382e30 | ||
|
|
180c42b9d5 | ||
|
|
ec3dcaa287 | ||
|
|
ac6260bf0d | ||
|
|
1f54d5de0e | ||
|
|
2c335e37b0 | ||
|
|
4afed0067e | ||
|
|
49719a2e8c | ||
|
|
197bdf8228 | ||
|
|
9890fbd57a | ||
|
|
72ae296db0 | ||
|
|
51ad4b2b06 | ||
|
|
80bc3e3455 | ||
|
|
0fe9de4f09 | ||
|
|
958d17831c | ||
|
|
6b6991bf6a | ||
|
|
bc9b36165c | ||
|
|
5fdf39edb3 | ||
|
|
00c6a8905d | ||
|
|
6ad037e469 | ||
|
|
aaa56f7c02 | ||
|
|
f6c0d93c47 | ||
|
|
cd3b5b7cea | ||
|
|
2bdb0575ad | ||
|
|
e095b245e2 | ||
|
|
b7f2f315e2 | ||
|
|
0bd543b57b | ||
|
|
7c4b1daed6 | ||
|
|
66203b0685 | ||
|
|
a5f23035ae | ||
|
|
0ff64751e8 | ||
|
|
670ebee06d | ||
|
|
53aadde343 | ||
|
|
699550c879 | ||
|
|
58111b3ba1 | ||
|
|
44d837c1eb | ||
|
|
689dd72363 | ||
|
|
0f34966f43 | ||
|
|
50baf5fd50 | ||
|
|
48fb8f00ca | ||
|
|
a30e91052e | ||
|
|
abd86c676b | ||
|
|
df5f6c692f | ||
|
|
3f410ea688 | ||
|
|
c29b5f2fef | ||
|
|
471f6cb5e5 | ||
|
|
1758e18bd2 | ||
|
|
40def352d9 | ||
|
|
0e965f73b7 | ||
|
|
ecaab49c08 | ||
|
|
f671b89da5 | ||
|
|
e4b2988864 | ||
|
|
8ea3963f62 | ||
|
|
99b192c4bc | ||
|
|
94c838dfde | ||
|
|
a9034de9cd | ||
|
|
69833f3d72 | ||
|
|
6671414da5 | ||
|
|
a4a075655d | ||
|
|
9aad67ae04 | ||
|
|
63f813d8ad | ||
|
|
e35c4b1461 | ||
|
|
9ec781fb41 | ||
|
|
064a554985 | ||
|
|
8aef955c77 | ||
|
|
492f4b6480 | ||
|
|
69cf9b34b1 | ||
|
|
d2b11a9ba4 | ||
|
|
66bbfbbb3c | ||
|
|
9295e2436d | ||
|
|
615383e665 | ||
|
|
d1977f148e | ||
|
|
9d70c6ad9c | ||
|
|
c8101d481e | ||
|
|
3ac6b3c1fb | ||
|
|
d0db80335b | ||
|
|
f0defa1799 | ||
|
|
f73c569f9a | ||
|
|
6819743850 | ||
|
|
f28f2c6e8c | ||
|
|
10b34965bd | ||
|
|
9e7fd7f74f | ||
|
|
b0d5ca2830 | ||
|
|
6273929184 | ||
|
|
9e9266712a | ||
|
|
6acb074993 | ||
|
|
986a8815ff | ||
|
|
add96e9c04 | ||
|
|
289d0cac63 | ||
|
|
90389996d1 | ||
|
|
d28bfee704 | ||
|
|
cc32844a9c | ||
|
|
6f3a0bdc55 | ||
|
|
06601ba5df | ||
|
|
928be8d8e7 | ||
|
|
7fabb182f1 | ||
|
|
815e12be66 | ||
|
|
7c2e6875c6 | ||
|
|
8de321c780 | ||
|
|
99f6492f29 | ||
|
|
bb135f041a | ||
|
|
a45869be49 | ||
|
|
26b7d5fa8b | ||
|
|
9ef4a92613 | ||
|
|
86cc38cd69 | ||
|
|
19a4140f7d | ||
|
|
62284c9218 | ||
|
|
6291804775 | ||
|
|
0b05b4b7d8 | ||
|
|
490ad97f7b | ||
|
|
03ec852804 | ||
|
|
1ef0e1c078 | ||
|
|
b371a73644 | ||
|
|
e859e9efc0 | ||
|
|
3b82ef5fde | ||
|
|
17bb6aac05 | ||
|
|
44e4af0f02 | ||
|
|
9d34f3d4e6 | ||
|
|
e05e8788d6 | ||
|
|
e926aad568 | ||
|
|
0559bac7a9 | ||
|
|
c2ed5b68a1 | ||
|
|
03f1cf8ec4 | ||
|
|
cc1a3ee93f | ||
|
|
869db920a9 | ||
|
|
6d27e5d0f8 | ||
|
|
32372c68cd | ||
|
|
fb3c43ef6a | ||
|
|
0af841cfd4 | ||
|
|
4a24923edb | ||
|
|
33cd18e109 | ||
|
|
84f6564cd2 | ||
|
|
f61a82d0eb | ||
|
|
88f3186839 | ||
|
|
541b73e322 | ||
|
|
89476fb54e | ||
|
|
1a56d10bef | ||
|
|
b3c71fef9e | ||
|
|
6696eca847 | ||
|
|
d942ed415b | ||
|
|
fb7535286c | ||
|
|
2c51252c09 | ||
|
|
6a5bf822ab | ||
|
|
c6529f6bf7 | ||
|
|
481cedb871 | ||
|
|
78b84be3fd | ||
|
|
3235cfd288 | ||
|
|
899db8fe0b | ||
|
|
08ba49ed46 | ||
|
|
0a8b08ebec | ||
|
|
78503a9380 | ||
|
|
67658876d5 | ||
|
|
d361cffc75 | ||
|
|
b9cd78fde9 | ||
|
|
42451ece28 | ||
|
|
f5963ecc30 | ||
|
|
74e3bfe6ce | ||
|
|
2b455ecfc3 | ||
|
|
a1802ff043 | ||
|
|
5b7d6e2e1f | ||
|
|
ac8ecdb7f3 | ||
|
|
0af5bb05fc | ||
|
|
35fc44e183 | ||
|
|
aabe06eb3f | ||
|
|
ef7000a29d | ||
|
|
bec47ec814 | ||
|
|
b29dc6052e | ||
|
|
3afd7a9a69 | ||
|
|
3305385311 | ||
|
|
ce5cfca43d | ||
|
|
08a3b64483 | ||
|
|
9fa56531c6 | ||
|
|
459316f102 | ||
|
|
554aa77707 | ||
|
|
4eb1490d80 | ||
|
|
670efb196e | ||
|
|
8b68729041 | ||
|
|
3b299aa5b4 | ||
|
|
14f267da47 | ||
|
|
059456571d | ||
|
|
f7571f83c8 | ||
|
|
bd42a03cf0 | ||
|
|
66c7997ed0 | ||
|
|
9cbefdda28 | ||
|
|
389a44dfc1 | ||
|
|
bc30541b9c | ||
|
|
8fa5217318 | ||
|
|
7ad1c68249 | ||
|
|
ef0baea3ad | ||
|
|
69b1685280 | ||
|
|
508e8b04a1 | ||
|
|
365a5f182c | ||
|
|
bbd632fa34 | ||
|
|
488078ac04 | ||
|
|
a9b57d4187 | ||
|
|
2d887d0a7e | ||
|
|
cd9a3a68ec | ||
|
|
d73450d8fa | ||
|
|
5eb7be054e | ||
|
|
70e1fe5a4b | ||
|
|
75015ce466 | ||
|
|
c2b9ebd4b9 | ||
|
|
7c117f22a7 | ||
|
|
f278de3a8c | ||
|
|
b584f8bc46 | ||
|
|
b08e98d0ab | ||
|
|
de5d83aedd | ||
|
|
0b48863a51 | ||
|
|
5f66874d3e | ||
|
|
79ba2af699 | ||
|
|
2aacd3e807 | ||
|
|
1d2d37334c | ||
|
|
fbf4f426db | ||
|
|
5ecb83aafa | ||
|
|
77315d0268 | ||
|
|
f29be2f69c | ||
|
|
4a9f09570e | ||
|
|
fd46487c82 | ||
|
|
d92fc8c4d0 | ||
|
|
b87493bd5f | ||
|
|
6925bfd416 | ||
|
|
2d9d9f3e93 | ||
|
|
87f644e5ba | ||
|
|
5c57d241ca | ||
|
|
a966e7520f | ||
|
|
f6b3afa75c | ||
|
|
cfbdb1ce76 | ||
|
|
39ff2b46ab | ||
|
|
29d33193cd | ||
|
|
5babf3708e | ||
|
|
ab40761a6c | ||
|
|
cf5fa034b9 | ||
|
|
9d6ebf79cd | ||
|
|
c992b61ca2 | ||
|
|
3ba2f208d4 | ||
|
|
462bed7d67 | ||
|
|
d938d39929 | ||
|
|
abe2e14af4 | ||
|
|
3516e026d0 | ||
|
|
10db0dab7e | ||
|
|
e0967272e7 | ||
|
|
c0aa78eb76 | ||
|
|
09d03bf270 | ||
|
|
6399ac0c95 | ||
|
|
2639b7beeb | ||
|
|
062ff28a5c | ||
|
|
d59403cc5b | ||
|
|
9565ad2cb2 | ||
|
|
72375fe1cd | ||
|
|
ce696a50f1 | ||
|
|
a7a4740c60 | ||
|
|
d0b52f7118 | ||
|
|
a92243314a | ||
|
|
538adfeb61 | ||
|
|
2dc5458e63 | ||
|
|
043bcc5729 | ||
|
|
2ff51a3dbb | ||
|
|
5d8cea9ed6 | ||
|
|
f3dcfbac52 | ||
|
|
952fe0091b | ||
|
|
6ea22a96ad | ||
|
|
b4b28cda92 | ||
|
|
db542fa905 | ||
|
|
30f905a253 | ||
|
|
cc8e6e5820 | ||
|
|
67ee22c6d9 | ||
|
|
20830ae349 | ||
|
|
44b6f8cfc5 | ||
|
|
1be4ba0075 | ||
|
|
bbeba1e735 | ||
|
|
3b65f460d6 | ||
|
|
fbb3959e66 | ||
|
|
663f28e541 | ||
|
|
9321084d84 | ||
|
|
7e352676c9 | ||
|
|
4c8d7b0f1c | ||
|
|
827e56b23f | ||
|
|
04294cf8b9 | ||
|
|
7f0aa75ba1 | ||
|
|
cb7c8046e4 | ||
|
|
ad2bf51fd9 | ||
|
|
26333c3d2b | ||
|
|
cbf51a6127 | ||
|
|
4dbfe1f23e | ||
|
|
ea5221bfda | ||
|
|
ea0c9af0f8 | ||
|
|
ba43925f22 | ||
|
|
e51b1cb7e1 | ||
|
|
d64e55226d | ||
|
|
311939ecb9 | ||
|
|
e1d57aac3b | ||
|
|
102c741ca3 | ||
|
|
1a8093c314 | ||
|
|
7d8b02dd96 | ||
|
|
6e15ce0c00 | ||
|
|
9e8eff4543 | ||
|
|
eee0338b63 | ||
|
|
75bedef465 | ||
|
|
8adaa2d3c8 | ||
|
|
581a004ed0 | ||
|
|
88fbecb4d3 | ||
|
|
ac5c59f8ba | ||
|
|
add1776125 | ||
|
|
937865ef83 | ||
|
|
bf72b5cd13 | ||
|
|
88f12bad93 | ||
|
|
9b764caf79 | ||
|
|
56c2b9d755 | ||
|
|
814696070d | ||
|
|
0907f287af | ||
|
|
08c1973f11 | ||
|
|
3156de3a5c | ||
|
|
16209b3e3f | ||
|
|
9f9cc4c22c | ||
|
|
9def3ba765 | ||
|
|
5d5aa0a4d2 | ||
|
|
edf463ab31 | ||
|
|
7702fd9929 | ||
|
|
eae1dc2568 | ||
|
|
204739e220 | ||
|
|
41b3d7987a | ||
|
|
f5b0542d35 | ||
|
|
b754d60752 | ||
|
|
edd49f222e | ||
|
|
6cff27c7bb | ||
|
|
54d48e9292 | ||
|
|
07507eac1e | ||
|
|
912895d1ad | ||
|
|
5823db8319 | ||
|
|
b9b109daf0 | ||
|
|
ed5aa5b54c | ||
|
|
1457f5d1c8 | ||
|
|
e8febcda15 | ||
|
|
5c04acf840 | ||
|
|
a3ae869f1d | ||
|
|
7a3fab1d6d | ||
|
|
745df353c4 | ||
|
|
f7dd436b81 | ||
|
|
80f95e4d58 | ||
|
|
8fcf7581e6 | ||
|
|
12b58cd938 | ||
|
|
5144a5a579 | ||
|
|
b5ad11323c | ||
|
|
748d5b84bd | ||
|
|
b7bba7dc7f | ||
|
|
3e6fa883cf | ||
|
|
6f8a6bde62 | ||
|
|
75aa1e591a | ||
|
|
7568d91d28 | ||
|
|
5023588af4 | ||
|
|
98e5c0ed3e | ||
|
|
2e9e38ebaf | ||
|
|
f34265a864 | ||
|
|
cb53db05dc | ||
|
|
88c64c5071 | ||
|
|
abebe2e081 | ||
|
|
fabd1df4dd | ||
|
|
abbbc6e8f0 | ||
|
|
a6e4712524 | ||
|
|
0798e47459 | ||
|
|
5c6b82c172 | ||
|
|
2c7c53084f | ||
|
|
04695597b2 | ||
|
|
956bc7fe14 | ||
|
|
75117d77a9 | ||
|
|
3190b57ad3 | ||
|
|
94ea70f4d4 | ||
|
|
d3a0d90ce7 | ||
|
|
010fe01265 | ||
|
|
f8ee950339 | ||
|
|
f7aa5c15c9 | ||
|
|
17ec9a0cae | ||
|
|
42a68812a9 | ||
|
|
82f26fbdc0 | ||
|
|
f19793f8a3 | ||
|
|
13940ad38f | ||
|
|
abe4dd7abc | ||
|
|
c6a2d55821 | ||
|
|
dd56b75bd6 | ||
|
|
cd2a66a644 | ||
|
|
32fd299d1d | ||
|
|
fc9e5b3a4c | ||
|
|
0ac0a84b18 | ||
|
|
dad6d613e6 | ||
|
|
fd23867c93 | ||
|
|
59b7c55ba2 | ||
|
|
0997042dcd | ||
|
|
847118c35b | ||
|
|
5a086243f5 | ||
|
|
f228934e54 | ||
|
|
5404b0bbfa | ||
|
|
c2b1130e23 |
@@ -0,0 +1,6 @@
|
||||
.DS_Store
|
||||
Frameworks
|
||||
Build
|
||||
Demos
|
||||
./Aristo
|
||||
WebSite
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
[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
|
||||
+61
-41
@@ -20,44 +20,64 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CALayer.j"
|
||||
import "CPAnimation.j"
|
||||
import "CPApplication.j"
|
||||
import "CPButton.j"
|
||||
import "CPClipView.j"
|
||||
import "CPCollectionView.j"
|
||||
import "CPColor.j"
|
||||
import "CPColorPanel.j"
|
||||
import "CPColorWell.j"
|
||||
import "CPCompatibility.j"
|
||||
import "CPControl.j"
|
||||
import "CPCookie.j"
|
||||
import "CPDocument.j"
|
||||
import "CPDocumentController.j"
|
||||
import "CPEvent.j"
|
||||
import "CPFont.j"
|
||||
import "CPFontManager.j"
|
||||
import "CPGeometry.j"
|
||||
import "CPImage.j"
|
||||
import "CPImageView.j"
|
||||
import "CPMenu.j"
|
||||
import "CPMenuItem.j"
|
||||
import "CPPanel.j"
|
||||
import "CPPasteboard.j"
|
||||
import "CPPopUpButton.j"
|
||||
import "CPProgressIndicator.j"
|
||||
import "CPResponder.j"
|
||||
import "CPScroller.j"
|
||||
import "CPScrollView.j"
|
||||
import "CPSecureTextField.j"
|
||||
import "CPSegmentedControl.j"
|
||||
import "CPShadow.j"
|
||||
import "CPSlider.j"
|
||||
import "CPSplitView.j"
|
||||
import "CPTextField.j"
|
||||
import "CPToolbar.j"
|
||||
import "CPToolbarItem.j"
|
||||
import "CPView.j"
|
||||
import "CPWebView.j"
|
||||
import "CPWindow.j"
|
||||
import "CPWindowController.j"
|
||||
@import "CALayer.j"
|
||||
@import "CPAlert.j"
|
||||
@import "CPAnimation.j"
|
||||
@import "CPApplication.j"
|
||||
@import "CPBezierPath.j"
|
||||
@import "CPBox.j"
|
||||
@import "CPButton.j"
|
||||
@import "CPButtonBar.j"
|
||||
@import "CPCheckBox.j"
|
||||
@import "CPCib.j"
|
||||
@import "CPCibConnector.j"
|
||||
@import "CPCibControlConnector.j"
|
||||
@import "CPCibLoading.j"
|
||||
@import "CPCibOutletConnector.j"
|
||||
@import "CPClipView.j"
|
||||
@import "CPCollectionViewItem.j"
|
||||
@import "CPCollectionView.j"
|
||||
@import "CPColor.j"
|
||||
@import "CPColorPanel.j"
|
||||
@import "CPColorWell.j"
|
||||
@import "CPCompatibility.j"
|
||||
@import "CPControl.j"
|
||||
@import "CPCookie.j"
|
||||
@import "CPDocument.j"
|
||||
@import "CPDocumentController.j"
|
||||
@import "CPEvent.j"
|
||||
@import "CPFlashMovie.j"
|
||||
@import "CPFlashView.j"
|
||||
@import "CPFont.j"
|
||||
@import "CPFontManager.j"
|
||||
@import "CPGeometry.j"
|
||||
@import "CPImage.j"
|
||||
@import "CPImageView.j"
|
||||
@import "CPMenu.j"
|
||||
@import "CPMenuItem.j"
|
||||
@import "CPOpenPanel.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 "CPSecureTextField.j"
|
||||
@import "CPSegmentedControl.j"
|
||||
@import "CPShadow.j"
|
||||
@import "CPSlider.j"
|
||||
@import "CPSplitView.j"
|
||||
@import "CPTabView.j"
|
||||
@import "CPTableColumn.j"
|
||||
@import "CPTableView.j"
|
||||
@import "CPTextField.j"
|
||||
@import "CPToolbar.j"
|
||||
@import "CPToolbarItem.j"
|
||||
@import "CPView.j"
|
||||
@import "CPViewController.j"
|
||||
@import "CPWebView.j"
|
||||
@import "CPWindow.j"
|
||||
@import "CPWindowController.j"
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>AppKit</string>
|
||||
<key>Targets</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>AppKit</string>
|
||||
<key>Excluded</key>
|
||||
<array>
|
||||
<string>CPOutlineView.j</string>
|
||||
<string>CPTableColumn.j</string>
|
||||
<string>CPTableView.j</string>
|
||||
</array>
|
||||
<key>Flags</key>
|
||||
<string>-DPLATFORM_DOM</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>AppKit-Rhino</string>
|
||||
<key>Excluded</key>
|
||||
<array>
|
||||
<string>CPOutlineView.j</string>
|
||||
<string>CPTableColumn.j</string>
|
||||
<string>CPTableView.j</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>Configurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>Debug</string>
|
||||
<key>Flags</key>
|
||||
<string>-DDEBUG</string>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
<key>PREPROCESS</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>Release</string>
|
||||
<key>Settings</key>
|
||||
<dict>
|
||||
<key>PREPROCESS</key>
|
||||
<true/>
|
||||
<key>PREINTERPRET</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* CPAccordionView.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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/CPArray.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPKeyValueObserving.j>
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
#import "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
@implementation CPAccordionViewItem : CPObject
|
||||
{
|
||||
CPString _identifier @accessors(property=identifier);
|
||||
CPView _view @accessors(property=view);
|
||||
CPString _label @accessors(property=label);
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithIdentifier:@""];
|
||||
}
|
||||
|
||||
- (id)initWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
[self setIdentifier:anIdentifier];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPAccordionView : CPView
|
||||
{
|
||||
CPInteger _dirtyItemIndex;
|
||||
CPView _itemHeaderPrototype;
|
||||
|
||||
CPMutableArray _items;
|
||||
CPMutableArray _itemViews;
|
||||
CPIndexSet _expandedItemIndexes;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_items = [];
|
||||
_itemViews = [];
|
||||
_expandedItemIndexes = [CPIndexSet indexSet];
|
||||
|
||||
[self setItemHeaderPrototype:[[CPButton alloc] initWithFrame:_CGRectMake(0.0, 0.0, 100.0, 24.0)]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setItemHeaderPrototype:(CPView)aView
|
||||
{
|
||||
_itemHeaderPrototype = aView;
|
||||
}
|
||||
|
||||
- (CPView)itemHeaderPrototype
|
||||
{
|
||||
return _itemHeaderPrototype;
|
||||
}
|
||||
|
||||
- (CPArray)items
|
||||
{
|
||||
return _items;
|
||||
}
|
||||
|
||||
- (void)addItem:(CPAccordionItem)anItem
|
||||
{
|
||||
[self insertItem:anItem atIndex:_items.length];
|
||||
}
|
||||
|
||||
- (void)insertItem:(CPAccordionItem)anItem atIndex:(CPInteger)anIndex
|
||||
{
|
||||
// FIXME: SHIFT ITEMS RIGHT
|
||||
[_expandedItemIndexes addIndex:anIndex];
|
||||
|
||||
var itemView = [[_CPAccordionItemView alloc] initWithAccordionView:self];
|
||||
|
||||
[itemView setIndex:anIndex];
|
||||
[itemView setLabel:[anItem label]];
|
||||
[itemView setContentView:[anItem view]];
|
||||
|
||||
[self addSubview:itemView];
|
||||
|
||||
[_items insertObject:anItem atIndex:anIndex];
|
||||
[_itemViews insertObject:itemView atIndex:anIndex];
|
||||
|
||||
[self _invalidateItemsStartingAtIndex:anIndex];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)removeItem:(CPAccordionItem)anItem
|
||||
{
|
||||
[self removeItemAtIndex:[_items indexOfObjectIdenticalTo:anItem]];
|
||||
}
|
||||
|
||||
- (void)removeItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
// SHIFT ITEMS LEFT
|
||||
[_expandedItemIndexes removeIndex:anIndex];
|
||||
|
||||
[_itemViews[anIndex] removeFromSuperview];
|
||||
|
||||
[_items removeObjectAtIndex:anIndex];
|
||||
[_itemViews removeObjectAtIndex:anIndex];
|
||||
|
||||
[self _invalidateItemsStartingAtIndex:anIndex];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)removeAllItems
|
||||
{
|
||||
var count = _items.length;
|
||||
|
||||
while (count--)
|
||||
[self removeItemAtIndex:count];
|
||||
}
|
||||
|
||||
- (void)expandItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
if (![_itemViews[anIndex] isCollapsed])
|
||||
return;
|
||||
|
||||
[_expandedItemIndexes addIndex:anIndex];
|
||||
[_itemViews[anIndex] setCollapsed:NO];
|
||||
|
||||
[self _invalidateItemsStartingAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (void)collapseItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
if ([_itemViews[anIndex] isCollapsed])
|
||||
return;
|
||||
|
||||
[_expandedItemIndexes removeIndex:anIndex];
|
||||
[_itemViews[anIndex] setCollapsed:YES];
|
||||
|
||||
[self _invalidateItemsStartingAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (void)toggleItemAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
var itemView = _itemViews[anIndex];
|
||||
|
||||
if ([itemView isCollapsed])
|
||||
[self expandItemAtIndex:anIndex];
|
||||
|
||||
else
|
||||
[self collapseItemAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (CPIndexSet)expandedItemIndexes
|
||||
{
|
||||
return _expandedItemIndexes;
|
||||
}
|
||||
|
||||
- (CPIndexSet)collapsedItemIndexes
|
||||
{
|
||||
var indexSet = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, _items.length)];
|
||||
|
||||
[indexSet removeIndexes:_expandedIndexes];
|
||||
|
||||
return indexSet;
|
||||
}
|
||||
|
||||
- (void)_invalidateItemsStartingAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
if (_dirtyItemIndex === CPNotFound)
|
||||
_dirtyItemIndex = anIndex;
|
||||
|
||||
_dirtyItemIndex = MIN(_dirtyItemIndex, anIndex);
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
var width = _CGRectGetWidth([self frame]);
|
||||
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
if (width !== _CGRectGetWidth([self frame]))
|
||||
[self _invalidateItemsStartingAtIndex:0];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
if (_items.length <= 0)
|
||||
return [self setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), 0.0)];
|
||||
|
||||
if (_dirtyItemIndex === CPNotFound)
|
||||
return;
|
||||
|
||||
_dirtyItemIndex = MIN(_dirtyItemIndex, _items.length - 1);
|
||||
|
||||
var index = _dirtyItemIndex,
|
||||
count = _itemViews.length,
|
||||
width = _CGRectGetWidth([self bounds]),
|
||||
y = index > 0 ? CGRectGetMaxY([_itemViews[index - 1] frame]) : 0.0;
|
||||
|
||||
// Do this now (instead of after looping), so that if we are made dirty again in the middle we don't blow this value away.
|
||||
_dirtyItemIndex = CPNotFound;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var itemView = _itemViews[index];
|
||||
|
||||
[itemView setFrameY:y width:width];
|
||||
|
||||
y = CGRectGetMaxY([itemView frame]);
|
||||
}
|
||||
|
||||
[self setFrameSize:_CGSizeMake(_CGRectGetWidth([self frame]), y)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPAccordionItemView : CPView
|
||||
{
|
||||
CPAccordionView _accordionView;
|
||||
|
||||
BOOL _isCollapsed @accessors(getter=isCollapsed, setter=setCollapsed:);
|
||||
CPInteger _index @accessors(property=index);
|
||||
CPView _headerView;
|
||||
CPView _contentView;
|
||||
}
|
||||
|
||||
- (id)initWithAccordionView:(CPAccordionView)anAccordionView
|
||||
{
|
||||
self = [super initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_accordionView = anAccordionView;
|
||||
_isCollapsed = NO;
|
||||
|
||||
var bounds = [self bounds];
|
||||
|
||||
_headerView = [CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:[_accordionView itemHeaderPrototype]]];
|
||||
|
||||
if ([_headerView respondsToSelector:@selector(setTarget:)] && [_headerView respondsToSelector:@selector(setAction:)])
|
||||
{
|
||||
[_headerView setTarget:self];
|
||||
[_headerView setAction:@selector(toggle:)];
|
||||
}
|
||||
|
||||
[self addSubview:_headerView];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)toggle:(id)aSender
|
||||
{
|
||||
[_accordionView toggleItemAtIndex:[self index]];
|
||||
}
|
||||
|
||||
- (void)setLabel:(CPString)aLabel
|
||||
{
|
||||
if ([_headerView respondsToSelector:@selector(setTitle:)])
|
||||
[_headerView setTitle:aLabel];
|
||||
|
||||
else if ([_headerView respondsToSelector:@selector(setLabel:)])
|
||||
[_headerView setLabel:aLabel];
|
||||
|
||||
else if ([_headerView respondsToSelector:@selector(setStringValue:)])
|
||||
[_headerView setStringValue:aLabel];
|
||||
}
|
||||
|
||||
- (void)setContentView:(CPView)aView
|
||||
{
|
||||
if (_contentView === aView)
|
||||
return;
|
||||
|
||||
[_contentView removeObserver:self forKeyPath:@"frame"];
|
||||
|
||||
[_contentView removeFromSuperview];
|
||||
|
||||
_contentView = aView;
|
||||
|
||||
[_contentView addObserver:self forKeyPath:@"frame" options:0 context:NULL];
|
||||
|
||||
[self addSubview:_contentView];
|
||||
|
||||
[_accordionView _invalidateItemsStartingAtIndex:[self index]];
|
||||
}
|
||||
|
||||
- (void)setFrameY:(float)aY width:(float)aWidth
|
||||
{
|
||||
var headerHeight = _CGRectGetHeight([_headerView frame]);
|
||||
|
||||
// Size to fit or something?
|
||||
[_headerView setFrameSize:_CGSizeMake(aWidth, headerHeight)];
|
||||
[_contentView setFrameOrigin:_CGPointMake(0.0, headerHeight)];
|
||||
|
||||
if ([self isCollapsed])
|
||||
[self setFrame:_CGRectMake(0.0, aY, aWidth, headerHeight)];
|
||||
|
||||
else
|
||||
{
|
||||
var contentHeight = _CGRectGetHeight([_contentView frame]);
|
||||
|
||||
[_contentView setFrameSize:_CGSizeMake(aWidth, contentHeight)];
|
||||
[self setFrame:_CGRectMake(0.0, aY, aWidth, contentHeight + headerHeight)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resizeSubviewsWithOldSize:(CGSize)aSize
|
||||
{
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(CPString)aKeyPath
|
||||
ofObject:(id)anObject
|
||||
change:(CPDictionary)aChange
|
||||
context:(id)aContext
|
||||
{
|
||||
if (aKeyPath === "frame" && !CGRectEqualToRect([aChange objectForKey:CPKeyValueChangeOldKey], [aChange objectForKey:CPKeyValueChangeNewKey]))
|
||||
[_accordionView _invalidateItemsStartingAtIndex:[self index]];
|
||||
/*
|
||||
else if (aKeyPath === "itemHeaderPrototype")
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* CPAlert.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Jake MacMullin.
|
||||
* Copyright 2008, Jake MacMullin.
|
||||
*
|
||||
* 11/10/2008 Ross Boucher
|
||||
* - Make it conform to style guidelines, general cleanup and ehancements
|
||||
*
|
||||
* 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/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>
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPAlertStyle
|
||||
*/
|
||||
CPWarningAlertStyle = 0;
|
||||
/*
|
||||
@global
|
||||
@group CPAlertStyle
|
||||
*/
|
||||
CPInformationalAlertStyle = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPAlertStyle
|
||||
*/
|
||||
CPCriticalAlertStyle = 2;
|
||||
|
||||
|
||||
var CPAlertWarningImage,
|
||||
CPAlertInformationImage,
|
||||
CPAlertErrorImage;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
|
||||
CPAlert is an alert panel that can be displayed modally to present the
|
||||
user with a message and one or more options.
|
||||
|
||||
It can be used to display an information message \c CPInformationalAlertStyle,
|
||||
a warning message \c CPWarningAlertStyle (the default), or a critical
|
||||
alert \c CPCriticalAlertStyle. In each case the user can be presented with one
|
||||
or more options by adding buttons using the \c -addButtonWithTitle: method.
|
||||
|
||||
The panel is displayed modally by calling \c -runModal and once the user has
|
||||
dismissed the panel, a message will be sent to the panel's delegate (if set), informing
|
||||
it which button was clicked (see delegate methods).
|
||||
|
||||
@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,
|
||||
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
|
||||
{
|
||||
CPPanel _alertPanel;
|
||||
|
||||
CPTextField _messageLabel;
|
||||
CPImageView _alertImageView;
|
||||
|
||||
CPAlertStyle _alertStyle;
|
||||
CPString _windowTitle;
|
||||
int _windowStyle;
|
||||
int _buttonCount;
|
||||
CPArray _buttons;
|
||||
|
||||
id _delegate;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != CPAlert)
|
||||
return;
|
||||
|
||||
var bundle = [CPBundle bundleForClass:[self class]];
|
||||
|
||||
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)];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes a \c CPAlert panel with the default alert style \c CPWarningAlertStyle.
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_buttonCount = 0;
|
||||
_buttons = [CPArray array];
|
||||
_alertStyle = CPWarningAlertStyle;
|
||||
|
||||
[self setWindowStyle:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the window appearance.
|
||||
@param styleMask - Either CPHUDBackgroundWindowMask or nil for standard.
|
||||
*/
|
||||
- (void)setWindowStyle:(int)styleMask
|
||||
{
|
||||
_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];
|
||||
|
||||
[_messageLabel setTextColor:(styleMask & CPHUDBackgroundWindowMask) ? [CPColor whiteColor] : [CPColor blackColor]];
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
/*!
|
||||
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.
|
||||
*/
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
{
|
||||
_windowTitle = aTitle;
|
||||
}
|
||||
|
||||
/*!
|
||||
Gets the window's title.
|
||||
*/
|
||||
- (CPString)title
|
||||
{
|
||||
return _windowTitle;
|
||||
}
|
||||
|
||||
/*!
|
||||
Gets the window's style.
|
||||
*/
|
||||
- (int)windowStyle
|
||||
{
|
||||
return _windowStyle;
|
||||
}
|
||||
|
||||
/*!
|
||||
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.
|
||||
*/
|
||||
- (CPString)messageText
|
||||
{
|
||||
return [_messageLabel stringValue];
|
||||
}
|
||||
|
||||
/*!
|
||||
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];
|
||||
|
||||
_buttonCount++;
|
||||
[_buttons addObject:button];
|
||||
}
|
||||
|
||||
/*!
|
||||
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
|
||||
by clicking on one of the buttons.
|
||||
*/
|
||||
- (void)runModal
|
||||
{
|
||||
var theTitle;
|
||||
|
||||
switch (_alertStyle)
|
||||
{
|
||||
case CPWarningAlertStyle: [_alertImageView setImage:CPAlertWarningImage];
|
||||
theTitle = @"Warning";
|
||||
break;
|
||||
case CPInformationalAlertStyle: [_alertImageView setImage:CPAlertInformationImage];
|
||||
theTitle = @"Information";
|
||||
break;
|
||||
case CPCriticalAlertStyle: [_alertImageView setImage:CPAlertErrorImage];
|
||||
theTitle = @"Error";
|
||||
break;
|
||||
}
|
||||
|
||||
[_alertPanel setTitle:_windowTitle ? _windowTitle : theTitle];
|
||||
|
||||
[CPApp runModalForWindow:_alertPanel];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_notifyDelegate:(id)button
|
||||
{
|
||||
[CPApp abortModal];
|
||||
[_alertPanel close];
|
||||
|
||||
if (_delegate && [_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)])
|
||||
[_delegate alertDidEnd:self returnCode:[button tag]];
|
||||
}
|
||||
|
||||
@end
|
||||
+95
-53
@@ -20,9 +20,10 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import "CAMediaTimingFunction.j"
|
||||
|
||||
import "CAMediaTimingFunction.j"
|
||||
|
||||
/*
|
||||
@global
|
||||
@@ -47,14 +48,19 @@ 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 <code>startAnimation</code>.
|
||||
Called at the beginning of \c -startAnimation.
|
||||
@param animation the animation that will start
|
||||
@return <code>YES</code> allows the animation to start.
|
||||
<code>NO</code> stops the animation.
|
||||
@return \c YES allows the animation to start.
|
||||
\c NO stops the animation.
|
||||
|
||||
@delegate -(void)animationDidEnd:(CPAnimation)animation;
|
||||
Called when an animation has completed.
|
||||
@@ -65,15 +71,15 @@ ACTUAL_FRAME_RATE = 0;
|
||||
@param animation the animation that was stopped
|
||||
|
||||
@delegate - (float)animation:(CPAnimation)animation valueForProgress:(float)progress;
|
||||
The value from this method will be returned when <objj>CPAnimation</objj>'s
|
||||
<code>currentValue</code> method is called.
|
||||
The value from this method will be returned when CPAnimation's
|
||||
\c currentValue method is called.
|
||||
@param animation the animation to obtain the curve value for
|
||||
@param progress the current animation progress
|
||||
@return the curve value
|
||||
*/
|
||||
@implementation CPAnimation : CPObject
|
||||
{
|
||||
CPTimeInterval _startTime;
|
||||
CPTimeInterval _lastTime;
|
||||
CPTimeInterval _duration;
|
||||
|
||||
CPAnimationCurve _animationCurve;
|
||||
@@ -86,7 +92,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
CPTimer _timer;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the animation with a duration and animation curve.
|
||||
@param aDuration the length of the animation
|
||||
@param anAnimationCurve defines the animation's pace
|
||||
@@ -98,26 +104,24 @@ ACTUAL_FRAME_RATE = 0;
|
||||
|
||||
if (self)
|
||||
{
|
||||
_progress = 0.0;
|
||||
_duration = MAX(0.0, aDuration);
|
||||
_animationCurve = anAnimationCurve;
|
||||
_frameRate = 60.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
|
||||
*/
|
||||
- (void)setAnimationCurve:(CPAnimationCurve)anAnimationCurve
|
||||
{
|
||||
_animationCurve = anAnimationCurve;
|
||||
|
||||
var timingFunctionName = kCAMediaTimingFunctionLinear;
|
||||
|
||||
switch (_animationCurve)
|
||||
switch (anAnimationCurve)
|
||||
{
|
||||
case CPAnimationEaseInOut: timingFunctionName = kCAMediaTimingFunctionEaseInEaseOut;
|
||||
break;
|
||||
@@ -127,16 +131,20 @@ ACTUAL_FRAME_RATE = 0;
|
||||
|
||||
case CPAnimationEaseOut: timingFunctionName = kCAMediaTimingFunctionEaseOut;
|
||||
break;
|
||||
|
||||
case CPAnimationLinear: timingFunctionName = kCAMediaTimingFunctionLinear;
|
||||
break;
|
||||
|
||||
default: [CPException raise:CPInvalidArgumentException
|
||||
reason:"Invalid value provided for animation curve"];
|
||||
break;
|
||||
}
|
||||
|
||||
_animationCurve = anAnimationCurve;
|
||||
_timingFunction = [CAMediaTimingFunction functionWithName:timingFunctionName];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the animation's pace
|
||||
*/
|
||||
- (CPAnimationCurve)animationCurve
|
||||
@@ -144,10 +152,10 @@ ACTUAL_FRAME_RATE = 0;
|
||||
return _animationCurve;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the animation's length.
|
||||
@param aDuration the new animation length
|
||||
@throws CPInvalidArgumentException if <code>aDuration</code> is negative
|
||||
@throws CPInvalidArgumentException if \c aDuration is negative
|
||||
*/
|
||||
- (void)setDuration:(CPTimeInterval)aDuration
|
||||
{
|
||||
@@ -157,7 +165,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
_duration = aDuration;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the length of the animation.
|
||||
*/
|
||||
- (CPTimeInterval)duration
|
||||
@@ -165,10 +173,10 @@ ACTUAL_FRAME_RATE = 0;
|
||||
return _duration;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the animation frame rate. This is not a guaranteed frame rate. 0 means to go as fast as possible.
|
||||
@param frameRate the new desired frame rate
|
||||
@throws CPInvalidArgumentException if <code>frameRate</code> is negative
|
||||
@throws CPInvalidArgumentException if \c frameRate is negative
|
||||
*/
|
||||
- (void)setFrameRate:(float)frameRate
|
||||
{
|
||||
@@ -178,7 +186,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
_frameRate = frameRate;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the desired frame rate.
|
||||
*/
|
||||
- (float)frameRate
|
||||
@@ -186,7 +194,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
return _frameRate;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the animation's delegate
|
||||
*/
|
||||
- (id)delegate
|
||||
@@ -194,7 +202,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the animation's delegate.
|
||||
@param aDelegate the new delegate
|
||||
*/
|
||||
@@ -203,25 +211,24 @@ ACTUAL_FRAME_RATE = 0;
|
||||
_delegate = aDelegate;
|
||||
}
|
||||
|
||||
/*
|
||||
Starts the animation. The method calls <code>animationShouldStart:</code>
|
||||
/*!
|
||||
Starts the animation. The method calls \c -animationShouldStart:
|
||||
on the delegate (if it implements it) to see if the animation
|
||||
should begin.
|
||||
*/
|
||||
- (void)startAnimation
|
||||
{
|
||||
// If we're already animating, or our delegate stops us, animate.
|
||||
if (_timer || _delegate && [_delegate respondsToSelector:@selector(animationShouldStart)] && ![_delegate animationShouldStart:self])
|
||||
if (_timer || _delegate && [_delegate respondsToSelector:@selector(animationShouldStart:)] && ![_delegate animationShouldStart:self])
|
||||
return;
|
||||
|
||||
_progress = 0.0;
|
||||
if (_progress === 1.0)
|
||||
_progress = 0.0;
|
||||
|
||||
ACTUAL_FRAME_RATE = 0;
|
||||
_startTime = new Date();
|
||||
// FIXME: THIS SHOULD BE A CPTIMER!!!
|
||||
_timer = window.setInterval(function() {
|
||||
[self animationTimerDidFire:_timer];
|
||||
[[CPRunLoop currentRunLoop] performSelectors];
|
||||
}, 1); // must be 1ms not 0 for IE. //_duration * 1000 / _frameRate);
|
||||
_lastTime = new Date();
|
||||
|
||||
_timer = [CPTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(animationTimerDidFire:) userInfo:nil repeats:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -229,25 +236,26 @@ ACTUAL_FRAME_RATE = 0;
|
||||
*/
|
||||
- (void)animationTimerDidFire:(CPTimer)aTimer
|
||||
{
|
||||
var elapsed = new Date() - _startTime,
|
||||
progress = MIN(1.0, 1.0 - (_duration - elapsed / 1000.0) / _duration);
|
||||
var currentTime = new Date(),
|
||||
progress = MIN(1.0, [self currentProgress] + (currentTime - _lastTime) / (_duration * 1000.0));
|
||||
|
||||
_lastTime = currentTime;
|
||||
|
||||
++ACTUAL_FRAME_RATE;
|
||||
|
||||
[self setCurrentProgress:progress];
|
||||
|
||||
if (progress == 1.0)
|
||||
if (progress === 1.0)
|
||||
{
|
||||
window.clearTimeout(_timer);
|
||||
[_timer invalidate];
|
||||
_timer = nil;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(animationDidEnd:)])
|
||||
[_delegate animationDidEnd:self];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Stops the animation before it has completed.
|
||||
*/
|
||||
- (void)stopAnimation
|
||||
@@ -255,15 +263,15 @@ ACTUAL_FRAME_RATE = 0;
|
||||
if (!_timer)
|
||||
return;
|
||||
|
||||
window.clearTimeout(_timer);
|
||||
[_timer invalidate];
|
||||
_timer = nil;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(animationDidStop:)])
|
||||
[_delegate animationDidStop:self];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the animation
|
||||
/*!
|
||||
Returns \c YES if the animation
|
||||
is running.
|
||||
*/
|
||||
- (BOOL)isAnimating
|
||||
@@ -271,7 +279,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
return _timer;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the animation's progress.
|
||||
@param aProgress the animation's progress
|
||||
*/
|
||||
@@ -280,7 +288,7 @@ ACTUAL_FRAME_RATE = 0;
|
||||
_progress = aProgress;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the animation's progress
|
||||
*/
|
||||
- (float)currentProgress
|
||||
@@ -288,18 +296,52 @@ ACTUAL_FRAME_RATE = 0;
|
||||
return _progress;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the animation's timing progress.
|
||||
*/
|
||||
- (float)currentValue
|
||||
{
|
||||
var t = [self currentProgress];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(animation:valueForProgress:)])
|
||||
return [_delegate animation:self valueForProgress:_progress];
|
||||
return [_delegate animation:self valueForProgress:t];
|
||||
|
||||
if (_animationCurve == CPAnimationLinear)
|
||||
return _progress;
|
||||
|
||||
alert("IMPLEMENT ANIMATION CURVES!!!");
|
||||
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);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// 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 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;};
|
||||
// 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));};
|
||||
// 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;}};
|
||||
// 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;}
|
||||
// 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;}
|
||||
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;
|
||||
// Convert from input time to parametric value in curve, then from that to output time.
|
||||
return solve(t, solveEpsilon(duration));
|
||||
};
|
||||
|
||||
+363
-102
@@ -20,28 +20,43 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPBundle.j>
|
||||
@import <Foundation/CPBundle.j>
|
||||
|
||||
import "CPCompatibility.j"
|
||||
import "CPEvent.j"
|
||||
import "CPMenu.j"
|
||||
import "CPResponder.j"
|
||||
import "CPDocumentController.j"
|
||||
@import "CPCompatibility.j"
|
||||
@import "CPEvent.j"
|
||||
@import "CPMenu.j"
|
||||
@import "CPResponder.j"
|
||||
@import "CPDocumentController.j"
|
||||
@import "CPThemeBlend.j"
|
||||
@import "CPCibLoading.j"
|
||||
@import "CPPlatform.j"
|
||||
|
||||
|
||||
var CPMainCibFile = @"CPMainCibFile",
|
||||
CPMainCibFileHumanFriendly = @"Main cib file base name";
|
||||
|
||||
CPApp = nil;
|
||||
|
||||
CPApplicationWillFinishLaunchingNotification = @"CPApplicationWillFinishLaunchingNotification";
|
||||
CPApplicationDidFinishLaunchingNotification = @"CPApplicationDidFinishLaunchingNotification";
|
||||
CPApplicationWillTerminateNotification = @"CPApplicationWillTerminateNotification";
|
||||
|
||||
CPTerminateNow = YES;
|
||||
CPTerminateCancel = NO;
|
||||
CPTerminateLater = -1; // not currently supported
|
||||
|
||||
CPRunStoppedResponse = -1000;
|
||||
CPRunAbortedResponse = -1001;
|
||||
CPRunContinuesResponse = -1002;
|
||||
|
||||
/*
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPApplication
|
||||
|
||||
CPApplication is THE way to start up the Cappucino framework for your application to use.
|
||||
Every GUI application has exactly one instance of CPApplication (or of a custom subclass of
|
||||
CPApplication). Your program's main() function can create that instance by calling the
|
||||
<code>CPApplicationMain</code> function. A simple example looks like this:
|
||||
\c CPApplicationMain function. A simple example looks like this:
|
||||
|
||||
<pre>
|
||||
function main(args, namedArgs)
|
||||
@@ -76,12 +91,14 @@ CPRunContinuesResponse = -1002;
|
||||
|
||||
//
|
||||
id _delegate;
|
||||
BOOL _finishedLaunching;
|
||||
|
||||
CPDictionary _namedArgs;
|
||||
CPArray _args;
|
||||
CPString _fullArgsString;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the singleton instance of the running application. If it
|
||||
doesn't exist, it will be created, and then returned.
|
||||
@return the application singleton
|
||||
@@ -94,9 +111,9 @@ CPRunContinuesResponse = -1002;
|
||||
return CPApp;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the Document based application with basic menu functions.
|
||||
Functions are <code>New, Open, Undo, Redo, Save, Cut, Copy, Paste</code>.
|
||||
Functions are \c New, \c Open, \c Undo, \c Redo, \c Save, \c Cut, \c Copy, \c Paste.
|
||||
@return the initialized application
|
||||
*/
|
||||
- (id)init
|
||||
@@ -133,19 +150,19 @@ CPRunContinuesResponse = -1002;
|
||||
[_mainMenu addItem:openMenuItem];
|
||||
|
||||
var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"],
|
||||
saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@""];
|
||||
saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:nil];
|
||||
|
||||
[saveMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Save.png"] size:CGSizeMake(16.0, 16.0)]];
|
||||
[saveMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/SaveHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
|
||||
|
||||
[saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"S"]];
|
||||
[saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:@""]];
|
||||
[saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:nil]];
|
||||
|
||||
[saveMenuItem setSubmenu:saveMenu];
|
||||
|
||||
[_mainMenu addItem:saveMenuItem];
|
||||
|
||||
var editMenuItem = [[CPMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:@""],
|
||||
var editMenuItem = [[CPMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:nil],
|
||||
editMenu = [[CPMenu alloc] initWithTitle:@"Edit"],
|
||||
|
||||
undoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:CPUndoKeyEquivalent],
|
||||
@@ -174,7 +191,7 @@ CPRunContinuesResponse = -1002;
|
||||
|
||||
// Configuring Applications
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the delegate for this application. The delegate will receive various notifications
|
||||
caused by user interactions during the application's run. The delegate can choose to
|
||||
react to these events.
|
||||
@@ -217,7 +234,7 @@ CPRunContinuesResponse = -1002;
|
||||
object:self];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the application's delegate. The app can only have one delegate at a time.
|
||||
*/
|
||||
- (id)delegate
|
||||
@@ -225,11 +242,11 @@ CPRunContinuesResponse = -1002;
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
/*
|
||||
This method is called by <code>run</code> before the event loop begins.
|
||||
/*!
|
||||
This method is called by \c -run before the event loop begins.
|
||||
When it successfully completes, it posts the notification
|
||||
<objj>CPApplicationDidFinishLaunchingNotification</objj>. If you override
|
||||
<code>finishLaunching</code>, the subclass method should invoke the superclass method.
|
||||
CPApplicationDidFinishLaunchingNotification. If you override
|
||||
\c -finishLaunching, the subclass method should invoke the superclass method.
|
||||
*/
|
||||
- (void)finishLaunching
|
||||
{
|
||||
@@ -257,19 +274,72 @@ CPRunContinuesResponse = -1002;
|
||||
[defaultCenter
|
||||
postNotificationName:CPApplicationWillFinishLaunchingNotification
|
||||
object:self];
|
||||
|
||||
if (_documentController)
|
||||
|
||||
var needsUntitled = !!_documentController,
|
||||
URLStrings = window.cpOpeningURLStrings && window.cpOpeningURLStrings(),
|
||||
index = 0,
|
||||
count = [URLStrings count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
needsUntitled = ![self _openURL:[CPURL URLWithString:URLStrings[index]]] || needsUntitled;
|
||||
|
||||
if (needsUntitled && [_delegate respondsToSelector:@selector(applicationShouldOpenUntitledFile:)])
|
||||
needsUntitled = [_delegate applicationShouldOpenUntitledFile:self];
|
||||
|
||||
if (needsUntitled)
|
||||
[_documentController newDocument:self];
|
||||
|
||||
|
||||
[_documentController _updateRecentDocumentsMenu];
|
||||
|
||||
[defaultCenter
|
||||
postNotificationName:CPApplicationDidFinishLaunchingNotification
|
||||
object:self];
|
||||
|
||||
[[CPRunLoop currentRunLoop] performSelectors];
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
_finishedLaunching = YES;
|
||||
}
|
||||
|
||||
/*
|
||||
Calls <code>finishLaunching</code> method which results in starting
|
||||
- (void)terminate:(id)aSender
|
||||
{
|
||||
[[CPDocumentController sharedDocumentController] closeAllDocumentsWithDelegate:self
|
||||
didCloseAllSelector:@selector(_documentController:didCloseAll:context:)
|
||||
contextInfo:nil];
|
||||
}
|
||||
|
||||
- (void)_documentController:(NSDocumentController *)docController didCloseAll:(BOOL)didCloseAll context:(Object)info
|
||||
{
|
||||
// callback method for terminate:
|
||||
if (didCloseAll)
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
|
||||
[self replyToApplicationShouldTerminate:[_delegate applicationShouldTerminate:self]];
|
||||
else
|
||||
[self replyToApplicationShouldTerminate:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)replyToApplicationShouldTerminate:(BOOL)terminate
|
||||
{
|
||||
if (terminate == CPTerminateNow)
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPApplicationWillTerminateNotification object:self];
|
||||
[CPPlatform terminateApplication];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)activateIgnoringOtherApps:(BOOL)shouldIgnoreOtherApps
|
||||
{
|
||||
[CPPlatform activateIgnoringOtherApps:shouldIgnoreOtherApps];
|
||||
}
|
||||
|
||||
- (void)hideOtherApplications:(id)aSender
|
||||
{
|
||||
[CPPlatform hideOtherApplications:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
Calls \c -finishLaunching method which results in starting
|
||||
the main event loop.
|
||||
*/
|
||||
- (void)run
|
||||
@@ -278,8 +348,8 @@ CPRunContinuesResponse = -1002;
|
||||
}
|
||||
|
||||
// Managing the Event Loop
|
||||
/*
|
||||
Starts a modal event loop for <code>aWindow</code>
|
||||
/*!
|
||||
Starts a modal event loop for \c aWindow
|
||||
@param aWindow the window to start the event loop for
|
||||
*/
|
||||
- (void)runModalForWindow:(CPWindow)aWindow
|
||||
@@ -287,9 +357,9 @@ CPRunContinuesResponse = -1002;
|
||||
[self runModalSession:[self beginModalSessionForWindow:aWindow]];
|
||||
}
|
||||
|
||||
/*
|
||||
Stops the event loop started by <code>runModalForWindow:</code> and
|
||||
sets the code that <code>runModalForWindow:</code> will return.
|
||||
/*!
|
||||
Stops the event loop started by \c -runModalForWindow: and
|
||||
sets the code that \c -runModalForWindow: will return.
|
||||
@param aCode the return code for the modal event
|
||||
*/
|
||||
- (void)stopModalWithCode:(int)aCode
|
||||
@@ -303,7 +373,7 @@ CPRunContinuesResponse = -1002;
|
||||
_currentSession._state = aCode;
|
||||
_currentSession = _currentSession._previous;
|
||||
|
||||
if (aCode == CPRunAbortedResponse)
|
||||
// if (aCode == CPRunAbortedResponse)
|
||||
[self _removeRunModalLoop];
|
||||
}
|
||||
|
||||
@@ -313,7 +383,7 @@ CPRunContinuesResponse = -1002;
|
||||
var count = _eventListeners.length;
|
||||
|
||||
while (count--)
|
||||
if (_eventListeners[count]._callback == _CPRunModalLoop)
|
||||
if (_eventListeners[count]._callback === _CPRunModalLoop)
|
||||
{
|
||||
_eventListeners.splice(count, 1);
|
||||
|
||||
@@ -321,7 +391,7 @@ CPRunContinuesResponse = -1002;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Stops the modal event loop
|
||||
*/
|
||||
- (void)stopModal
|
||||
@@ -329,16 +399,16 @@ CPRunContinuesResponse = -1002;
|
||||
[self stopModalWithCode:CPRunStoppedResponse]
|
||||
}
|
||||
|
||||
/*
|
||||
Aborts the event loop started by <code>runModalForWindow:</code>
|
||||
/*!
|
||||
Aborts the event loop started by \c -runModalForWindow:
|
||||
*/
|
||||
- (void)abortModal
|
||||
{
|
||||
[self stopModalWithCode:CPRunAbortedResponse];
|
||||
}
|
||||
|
||||
/*
|
||||
Sets up a modal session with <code>theWindow</code>.
|
||||
/*!
|
||||
Sets up a modal session with \c theWindow.
|
||||
@param aWindow the window to set up the modal session for
|
||||
*/
|
||||
- (CPModalSession)beginModalSessionForWindow:(CPWindow)aWindow
|
||||
@@ -346,7 +416,7 @@ CPRunContinuesResponse = -1002;
|
||||
return _CPModalSessionMake(aWindow, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Runs a modal session
|
||||
@param CPModalSession the session to run
|
||||
*/
|
||||
@@ -365,9 +435,9 @@ CPRunContinuesResponse = -1002;
|
||||
[CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the window for the current modal session. If there is no
|
||||
modal session, it returns <code>nil</code>.
|
||||
modal session, it returns \c nil.
|
||||
*/
|
||||
- (CPWindow)modalWindow
|
||||
{
|
||||
@@ -380,24 +450,24 @@ CPRunContinuesResponse = -1002;
|
||||
/* @ignore */
|
||||
- (BOOL)_handleKeyEquivalent:(CPEvent)anEvent
|
||||
{
|
||||
if ([_mainMenu performKeyEquivalent:anEvent])
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
return [[self keyWindow] performKeyEquivalent:anEvent] ||
|
||||
[_mainMenu performKeyEquivalent:anEvent];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Dispatches events to other objects.
|
||||
@param anEvent the event to dispatch
|
||||
*/
|
||||
- (void)sendEvent:(CPEvent)anEvent
|
||||
{
|
||||
_currentEvent = anEvent;
|
||||
|
||||
// Check if this is a candidate for key equivalent...
|
||||
if ([anEvent type] == CPKeyDown &&
|
||||
[anEvent modifierFlags] & (CPCommandKeyMask | CPControlKeyMask) &&
|
||||
[[anEvent characters] length] > 0 &&
|
||||
[self _handleKeyEquivalent:anEvent])
|
||||
if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent])
|
||||
{
|
||||
[[[anEvent window] platformWindow] _propagateCurrentDOMEvent:NO];
|
||||
return;
|
||||
}
|
||||
|
||||
if (_eventListeners.length)
|
||||
{
|
||||
@@ -418,7 +488,7 @@ CPRunContinuesResponse = -1002;
|
||||
[super doCommandBySelector:aSelector];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the key window.
|
||||
*/
|
||||
- (CPWindow)keyWindow
|
||||
@@ -426,7 +496,7 @@ CPRunContinuesResponse = -1002;
|
||||
return _keyWindow;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the main window.
|
||||
*/
|
||||
- (CPWindow)mainWindow
|
||||
@@ -434,24 +504,29 @@ CPRunContinuesResponse = -1002;
|
||||
return _mainWindow;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the <objj>CPWindow</objj> object corresponding to <code>aWindowNumber</code>.
|
||||
/*!
|
||||
Returns the CPWindow object corresponding to \c aWindowNumber.
|
||||
*/
|
||||
- (CPWindow)windowWithWindowNumber:(int)aWindowNumber
|
||||
{
|
||||
return _windows[aWindowNumber];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns an array of the application's <objj>CPWindow</objj>s
|
||||
/*!
|
||||
Returns an array of the application's CPWindows
|
||||
*/
|
||||
- (CPArray)windows
|
||||
{
|
||||
return _windows;
|
||||
}
|
||||
|
||||
- (void)hide:(id)aSender
|
||||
{
|
||||
[CPPlatform hide:self];
|
||||
}
|
||||
|
||||
// Accessing the Main Menu
|
||||
/*
|
||||
/*!
|
||||
Returns the application's main menu
|
||||
*/
|
||||
- (CPMenu)mainMenu
|
||||
@@ -459,36 +534,62 @@ CPRunContinuesResponse = -1002;
|
||||
return _mainMenu;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the main menu for the application
|
||||
@param aMenu the menu to set for the application
|
||||
*/
|
||||
- (void)setMainMenu:(CPMenu)aMenu
|
||||
{
|
||||
_mainMenu = aMenu;
|
||||
if ([aMenu _menuName] === "CPMainMenu")
|
||||
{
|
||||
if (_mainMenu === aMenu)
|
||||
return;
|
||||
|
||||
_mainMenu = aMenu;
|
||||
|
||||
if ([CPPlatform supportsNativeMainMenu])
|
||||
window.cpSetMainMenu(_mainMenu);
|
||||
}
|
||||
else
|
||||
[aMenu _setMenuName:@"CPMainMenu"];
|
||||
}
|
||||
|
||||
- (void)orderFrontColorPanel:(id)aSender
|
||||
{
|
||||
[[CPColorPanel sharedColorPanel] orderFront:self];
|
||||
}
|
||||
|
||||
- (void)orderFrontStandardAboutPanel:(id)aSender
|
||||
{
|
||||
[self orderFrontStandardAboutPanelWithOptions:nil];
|
||||
}
|
||||
|
||||
- (void)orderFrontStandardAboutPanelWithOptions:(CPDictionary)aDictionary
|
||||
{
|
||||
// FIXME: Implement.
|
||||
}
|
||||
|
||||
// Posting Actions
|
||||
/*
|
||||
/*!
|
||||
Tries to perform the action with an argument. Performs
|
||||
the action on itself (if it responds to it), then
|
||||
tries to perform the action on the delegate.
|
||||
@param anAction the action to perform.
|
||||
@param anObject the argument for the action
|
||||
method
|
||||
@return <code>YES</code> if the action was performed
|
||||
@return \c YES if the action was performed
|
||||
*/
|
||||
- (BOOL)tryToPerform:(SEL)anAction with:(id)anObject
|
||||
{
|
||||
if (!anAction)
|
||||
return NO;
|
||||
|
||||
if ([self tryToPerform:anAction with:anObject])
|
||||
if ([super tryToPerform:anAction with:anObject])
|
||||
return YES;
|
||||
|
||||
if([_delegate respondsToSelector:aSelector])
|
||||
if([_delegate respondsToSelector:anAction])
|
||||
{
|
||||
[_delegate performSelector:aSelector withObject:anObject];
|
||||
[_delegate performSelector:anAction withObject:anObject];
|
||||
|
||||
return YES;
|
||||
}
|
||||
@@ -496,12 +597,12 @@ CPRunContinuesResponse = -1002;
|
||||
return NO;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sends an action to a target.
|
||||
@param anAction the action to send
|
||||
@param aTarget the target for the action
|
||||
@param aSender the action sender
|
||||
@return <code>YES</code>
|
||||
@return \c YES
|
||||
*/
|
||||
- (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)aSender
|
||||
{
|
||||
@@ -515,14 +616,14 @@ CPRunContinuesResponse = -1002;
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Finds a target for the specified action. If the
|
||||
action is <code>nil</code>, returns <code>nil</code>.
|
||||
If the target is not <code>nil</code>, <code>aTarget</code> is
|
||||
returned. Otherwise, it calls <code>targetForAction:</code>
|
||||
action is \c nil, returns \c nil.
|
||||
If the target is not \c nil, \c aTarget is
|
||||
returned. Otherwise, it calls \c -targetForAction:
|
||||
to search for a target.
|
||||
@param anAction the action to find a target for
|
||||
@param aTarget if not <code>nil</code>, this will be returned
|
||||
@param aTarget if not \c nil, this will be returned
|
||||
@aSender not used
|
||||
@return a target for the action
|
||||
*/
|
||||
@@ -537,7 +638,7 @@ CPRunContinuesResponse = -1002;
|
||||
return [self targetForAction:anAction];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Finds an action-target for a specified window.
|
||||
It finds a matching target in the following order:
|
||||
<ol>
|
||||
@@ -550,7 +651,7 @@ CPRunContinuesResponse = -1002;
|
||||
</ol>
|
||||
@param aWindow the window to search for a target
|
||||
@param anAction the action to find a responder to
|
||||
@return the object that responds to the action, or <code>nil</code>
|
||||
@return the object that responds to the action, or \c nil
|
||||
if no matching target was found
|
||||
@ignore
|
||||
*/
|
||||
@@ -591,18 +692,18 @@ CPRunContinuesResponse = -1002;
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Looks for a target that can handle the specified action.
|
||||
Checks for a target in the following order:
|
||||
<ol>
|
||||
<li>a responder from the key window</li>
|
||||
<li>a responder frmo the main window</li>
|
||||
<li>the <objj>CPApplication</objj> instance</li>
|
||||
<li>the CPApplication instance</li>
|
||||
<li>the application delegate</li>
|
||||
<li>the document controller</li>
|
||||
</ol>
|
||||
@param anAction the action to handle
|
||||
@return a target that can respond, or <code>nil</code>
|
||||
@return a target that can respond, or \c nil
|
||||
if no match could be found
|
||||
*/
|
||||
- (id)targetForAction:(SEL)anAction
|
||||
@@ -644,9 +745,14 @@ CPRunContinuesResponse = -1002;
|
||||
_eventListeners.push(_CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }));
|
||||
}
|
||||
|
||||
- (CPEvent)currentEvent
|
||||
{
|
||||
return _currentEvent;
|
||||
}
|
||||
|
||||
// Managing Sheets
|
||||
|
||||
/*
|
||||
/*!
|
||||
Displays a window as a sheet.
|
||||
@param aSheet the window to display as a sheet
|
||||
@param aWindow the window that will hold the sheet as a child
|
||||
@@ -656,19 +762,108 @@ CPRunContinuesResponse = -1002;
|
||||
*/
|
||||
- (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo
|
||||
{
|
||||
var styleMask = [aSheet styleMask];
|
||||
if (!(styleMask & CPDocModalWindowMask))
|
||||
{
|
||||
[CPException raise:CPInternalInconsistencyException reason:@"Currently only CPDocModalWindowMask style mask is supported for attached sheets"];
|
||||
return;
|
||||
}
|
||||
|
||||
[aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo];
|
||||
}
|
||||
|
||||
- (void)endSheet:(CPWindow)sheet returnCode:(int)returnCode
|
||||
{
|
||||
var count = [_windows count];
|
||||
|
||||
while (--count >= 0)
|
||||
{
|
||||
var aWindow = [_windows objectAtIndex:count];
|
||||
var context = aWindow._sheetContext;
|
||||
|
||||
if (context != nil && context["sheet"] === sheet)
|
||||
{
|
||||
context["returnCode"] = returnCode;
|
||||
[aWindow _detachSheetWindow];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)endSheet:(CPWindow)sheet
|
||||
{
|
||||
[self endSheet:sheet returnCode:0];
|
||||
}
|
||||
|
||||
|
||||
- (CPArray)arguments
|
||||
{
|
||||
if(_fullArgsString != window.location.hash)
|
||||
[self _reloadArguments];
|
||||
|
||||
return _args;
|
||||
}
|
||||
|
||||
- (void)setArguments:(CPArray)args
|
||||
{
|
||||
if(!args || args.length == 0)
|
||||
{
|
||||
_args = [];
|
||||
window.location.hash = @"#";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if([args class] != CPArray)
|
||||
args = [CPArray arrayWithObject:args];
|
||||
|
||||
_args = args;
|
||||
|
||||
var toEncode = [_args copy];
|
||||
for(var i=0, count = toEncode.length; i<count; i++)
|
||||
toEncode[i] = encodeURIComponent(toEncode[i]);
|
||||
|
||||
var hash = [toEncode componentsJoinedByString:@"/"];
|
||||
|
||||
window.location.hash = @"#" + hash;
|
||||
}
|
||||
|
||||
- (void)_reloadArguments
|
||||
{
|
||||
_fullArgsString = window.location.hash;
|
||||
var args = _fullArgsString.replace("#", "").split("/").slice(0);
|
||||
|
||||
for(var i=0, count = args.length; i<count; i++)
|
||||
args[i] = decodeURIComponent(args[i]);
|
||||
|
||||
_args = args;
|
||||
}
|
||||
|
||||
- (CPDictionary)namedArguments
|
||||
{
|
||||
return _namedArgs;
|
||||
}
|
||||
|
||||
- (BOOL)_openURL:(CPURL)aURL
|
||||
{
|
||||
if (_delegate && [_delegate respondsToSelector:@selector(application:openFile:)])
|
||||
{
|
||||
CPLog.warn("application:openFile: is deprecated, use application:openURL: instead.");
|
||||
return [_delegate application:self openFile:[aURL absoluteString]];
|
||||
}
|
||||
|
||||
if (_delegate && [_delegate respondsToSelector:@selector(application:openURL:)])
|
||||
return [_delegate application:self openURL:aURL];
|
||||
|
||||
return !![_documentController openDocumentWithContentsOfURL:aURL display:YES error:NULL];
|
||||
}
|
||||
|
||||
- (void)_didResignActive
|
||||
{
|
||||
if (self._activeMenu)
|
||||
[self._activeMenu cancelTracking];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPModalSessionMake = function(aWindow, aStopCode)
|
||||
@@ -684,51 +879,45 @@ var _CPEventListenerMake = function(anEventMask, aCallback)
|
||||
var _CPRunModalLoop = function(anEvent)
|
||||
{
|
||||
[CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
|
||||
|
||||
// FIXME: abortModal from event loop?
|
||||
|
||||
var theWindow = [anEvent window],
|
||||
modalSession = CPApp._currentSession;
|
||||
|
||||
if (theWindow == modalSession._window || [theWindow worksWhenModal])
|
||||
[theWindow sendEvent:anEvent];
|
||||
/*else
|
||||
[[session modalWindow] makeKeyAndOrderFront:]*/
|
||||
|
||||
if (modalSession._state != CPRunContinuesResponse)
|
||||
[CPApp _removeRunModalLoop];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Starts the GUI and Cappuccino frameworks. This function should be
|
||||
called from the <code>main()</code> function of your program.
|
||||
called from the \c main() function of your program.
|
||||
@class CPApplication
|
||||
@return void
|
||||
*/
|
||||
|
||||
function CPApplicationMain(args, namedArgs)
|
||||
{
|
||||
var mainBundle = [CPBundle mainBundle],
|
||||
principalClass = [mainBundle principalClass];
|
||||
|
||||
|
||||
if (!principalClass)
|
||||
principalClass = [CPApplication class];
|
||||
|
||||
[principalClass sharedApplication];
|
||||
|
||||
//[NSBundle loadNibNamed:@"myMain" owner:NSApp];
|
||||
|
||||
|
||||
//FIXME?
|
||||
if (!args && !namedArgs)
|
||||
if (!args)
|
||||
{
|
||||
var args = window.location.hash.replace("#", "").split("/").slice(0),
|
||||
searchParams = window.location.search.substring(1).split("&");
|
||||
namedArgs = [CPDictionary dictionary];
|
||||
|
||||
for(var i=0, count = args.length; i<count; i++)
|
||||
args[i] = decodeURIComponent(args[i]);
|
||||
|
||||
var args = [CPApp arguments];
|
||||
|
||||
if([args containsObject:"debug"])
|
||||
CPLogRegister(CPLogPopup);
|
||||
|
||||
}
|
||||
|
||||
if (!namedArgs)
|
||||
{
|
||||
var searchParams = window.location.search.substring(1).split("&");
|
||||
namedArgs = [CPDictionary dictionary];
|
||||
|
||||
for(var i=0; i<searchParams.length; i++)
|
||||
{
|
||||
var index = searchParams[i].indexOf('=');
|
||||
@@ -738,9 +927,81 @@ function CPApplicationMain(args, namedArgs)
|
||||
[namedArgs setObject: searchParams[i].substring(index+1) forKey: searchParams[i].substring(0, index)];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CPApp._args = args;
|
||||
CPApp._namedArgs = namedArgs;
|
||||
|
||||
|
||||
[_CPAppBootstrapper performActions];
|
||||
}
|
||||
|
||||
var _CPAppBootstrapperActions = nil;
|
||||
|
||||
@implementation _CPAppBootstrapper : CPObject
|
||||
{
|
||||
}
|
||||
|
||||
+ (void)actions
|
||||
{
|
||||
return [@selector(bootstrapPlatform), @selector(loadDefaultTheme), @selector(loadMainCibFile)];
|
||||
}
|
||||
|
||||
+ (void)performActions
|
||||
{
|
||||
if (!_CPAppBootstrapperActions)
|
||||
_CPAppBootstrapperActions = [self actions];
|
||||
|
||||
while (_CPAppBootstrapperActions.length)
|
||||
{
|
||||
var action = _CPAppBootstrapperActions.shift();
|
||||
|
||||
if (objj_msgSend(self, action))
|
||||
return;
|
||||
}
|
||||
|
||||
[CPApp run];
|
||||
}
|
||||
|
||||
+ (BOOL)bootstrapPlatform
|
||||
{
|
||||
return [CPPlatform bootstrap];
|
||||
}
|
||||
|
||||
+ (BOOL)loadDefaultTheme
|
||||
{
|
||||
var blend = [[CPThemeBlend alloc] initWithContentsOfURL:[[CPBundle bundleForClass:[CPApplication class]] pathForResource:@"Aristo.blend"]];
|
||||
|
||||
[blend loadWithDelegate:self];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (void)blendDidFinishLoading:(CPBundle)aBundle
|
||||
{
|
||||
[CPTheme setDefaultTheme:[CPTheme themeNamed:@"Aristo"]];
|
||||
|
||||
[self performActions];
|
||||
}
|
||||
|
||||
+ (BOOL)loadMainCibFile
|
||||
{
|
||||
var mainBundle = [CPBundle mainBundle],
|
||||
mainCibFile = [mainBundle objectForInfoDictionaryKey:CPMainCibFile] || [mainBundle objectForInfoDictionaryKey:CPMainCibFileHumanFriendly];
|
||||
|
||||
if (mainCibFile)
|
||||
{
|
||||
[mainBundle loadCibFile:mainCibFile
|
||||
externalNameTable:[CPDictionary dictionaryWithObject:CPApp forKey:CPCibOwner]
|
||||
loadDelegate:self];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (void)cibDidFinishLoading:(CPCib)aCib
|
||||
{
|
||||
[self performActions];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* CPBezierPath.j
|
||||
*
|
||||
* Created by Ross Boucher.
|
||||
* Copyright 2009, 280 North, Inc.
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
* Copyright 2009 280 North, Inc.
|
||||
|
||||
*/
|
||||
|
||||
@import <AppKit/CPGraphicsContext.j>
|
||||
@import <Foundation/CPObject.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.
|
||||
|
||||
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).
|
||||
*/
|
||||
|
||||
@implementation CPBezierPath : CPObject
|
||||
{
|
||||
CGPath _path;
|
||||
float _lineWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Create a new CPBezierPath object.
|
||||
*/
|
||||
+ (CPBezierPath)bezierPath
|
||||
{
|
||||
return [[self alloc] init];
|
||||
}
|
||||
|
||||
/*!
|
||||
Create a new CPBezierPath object initialized with an oval path drawn within a rectangular path.
|
||||
*/
|
||||
+ (CPBezierPath)bezierPathWithOvalInRect:(CGRect)rect
|
||||
{
|
||||
var path = [self bezierPath];
|
||||
|
||||
[path appendBezierPathWithOvalInRect:rect];
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/*!
|
||||
Create a new CPBezierPath object initialized with a rectangular path.
|
||||
*/
|
||||
+ (CPBezierPath)bezierPathWithRect:(CGRect)rect
|
||||
{
|
||||
var path = [self bezierPath];
|
||||
|
||||
[path appendBezierPathWithRect:rect];
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/*!
|
||||
Get default line width.
|
||||
*/
|
||||
+ (float)defaultLineWidth
|
||||
{
|
||||
return DefaultLineWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set default line width.
|
||||
*/
|
||||
+ (void)setDefaultLineWidth:(float)width
|
||||
{
|
||||
DefaultLineWidth = width;
|
||||
}
|
||||
|
||||
/*!
|
||||
Fill rectangular path with current fill color.
|
||||
*/
|
||||
+ (void)fillRect:(CGRect)aRect
|
||||
{
|
||||
[[self bezierPathWithRect:aRect] fill];
|
||||
}
|
||||
|
||||
/*!
|
||||
Using the current stroke color and default drawing attributes, strokes a counterclockwise path beginning at the rectangle's origin.
|
||||
*/
|
||||
+ (void)strokeRect:(CGRect)aRect
|
||||
{
|
||||
[[self bezierPathWithRect:aRect] stroke];
|
||||
}
|
||||
|
||||
/*!
|
||||
Using the current stroke color and default drawing attributes, strokes a line between two points.
|
||||
*/
|
||||
+ (void)strokeLineFromPoint:(CGPoint)point1 toPoint:(CGPoint)point2
|
||||
{
|
||||
var path = [self bezierPath];
|
||||
|
||||
[path moveToPoint:point1];
|
||||
[path lineToPoint:point2];
|
||||
|
||||
[path stroke];
|
||||
}
|
||||
|
||||
/*!
|
||||
Create a new CPBezierPath object using the default line width.
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_path = CGPathCreateMutable();
|
||||
_lineWidth = [[self class] defaultLineWidth];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Moves the current point to another location.
|
||||
*/
|
||||
- (void)moveToPoint:(CGPoint)point
|
||||
{
|
||||
CGPathMoveToPoint(_path, nil, point.x, point.y);
|
||||
}
|
||||
|
||||
/*!
|
||||
Append a straight line to the path.
|
||||
*/
|
||||
- (void)lineToPoint:(CGPoint)point
|
||||
{
|
||||
CGPathAddLineToPoint(_path, nil, point.x, point.y);
|
||||
}
|
||||
|
||||
/*!
|
||||
Add a cubic Bezier curve to the path.
|
||||
*/
|
||||
- (void)curveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
|
||||
{
|
||||
CGPathAddCurveToPoint(_path, nil, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y);
|
||||
}
|
||||
|
||||
/*!
|
||||
Create a line segment between the first and last points in the subpath, closing it.
|
||||
*/
|
||||
- (void)closePath
|
||||
{
|
||||
CGPathCloseSubpath(_path);
|
||||
}
|
||||
|
||||
/*!
|
||||
Draw a line along the path with the current stroke color and default drawing attributes.
|
||||
*/
|
||||
- (void)stroke
|
||||
{
|
||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextBeginPath(ctx);
|
||||
CGContextAddPath(ctx, _path);
|
||||
CGContextSetLineWidth(ctx, [self lineWidth]);
|
||||
CGContextClosePath(ctx);
|
||||
CGContextStrokePath(ctx);
|
||||
}
|
||||
|
||||
/*!
|
||||
Fill the path with the current fill color.
|
||||
*/
|
||||
- (void)fill
|
||||
{
|
||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextBeginPath(ctx);
|
||||
CGContextAddPath(ctx, _path);
|
||||
CGContextSetLineWidth(ctx, [self lineWidth]);
|
||||
CGContextClosePath(ctx);
|
||||
CGContextFillPath(ctx);
|
||||
}
|
||||
|
||||
/*!
|
||||
Get the line width.
|
||||
*/
|
||||
- (float)lineWidth
|
||||
{
|
||||
return _lineWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the line width.
|
||||
*/
|
||||
- (void)setLineWidth:(float)lineWidth
|
||||
{
|
||||
_lineWidth = lineWidth;
|
||||
}
|
||||
|
||||
/*!
|
||||
Get the total number of elements.
|
||||
*/
|
||||
- (unsigned)elementCount
|
||||
{
|
||||
return _path.count;
|
||||
}
|
||||
|
||||
/*!
|
||||
Check if receiver is empty, returns appropriate Boolean value.
|
||||
*/
|
||||
- (BOOL)isEmpty
|
||||
{
|
||||
return CGPathIsEmpty(_path);
|
||||
}
|
||||
|
||||
/*!
|
||||
Get the current point.
|
||||
*/
|
||||
- (CGPoint)currentPoint
|
||||
{
|
||||
return CGPathGetCurrentPoint(_path);
|
||||
}
|
||||
|
||||
/*!
|
||||
Append a series of line segments.
|
||||
*/
|
||||
- (void)appendBezierPathWithPoints:(CPArray)points count:(unsigned)count
|
||||
{
|
||||
CGPathAddLines(_path, nil, points, count);
|
||||
}
|
||||
|
||||
/*!
|
||||
Append a rectangular path.
|
||||
*/
|
||||
- (void)appendBezierPathWithRect:(CGRect)rect
|
||||
{
|
||||
CGPathAddRect(_path, nil, rect);
|
||||
}
|
||||
|
||||
/*!
|
||||
Append an oval path; oval is drawn within the rectangular path.
|
||||
*/
|
||||
- (void)appendBezierPathWithOvalInRect:(CGRect)rect
|
||||
{
|
||||
CGPathAddPath(_path, nil, CGPathWithEllipseInRect(rect));
|
||||
}
|
||||
|
||||
/*!
|
||||
Append a rounded rectangular path.
|
||||
*/
|
||||
- (void)appendBezierPathWithRoundedRect:(CGRect)rect xRadius:(float)xRadius yRadius:(float)yRadius
|
||||
{
|
||||
CGPathAddPath(_path, nil, CGPathWithRoundedRectangleInRect(rect, xRadius, yRadius, YES, YES, YES, YES));
|
||||
}
|
||||
|
||||
/*!
|
||||
Append the contents of a CPBezierPath object.
|
||||
*/
|
||||
- (void)appendBezierPath:(NSBezierPath *)other
|
||||
{
|
||||
CGPathAddPath(_path, nil, other._path);
|
||||
}
|
||||
/*!
|
||||
Remove all path elements; clears path.
|
||||
*/
|
||||
- (void)removeAllPoints
|
||||
{
|
||||
_path = CGPathCreateMutable();
|
||||
}
|
||||
|
||||
@end
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* CPBox.j
|
||||
* AppKit
|
||||
*
|
||||
* 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 "CPView.j"
|
||||
|
||||
// CPBorderType
|
||||
CPNoBorder = 0;
|
||||
CPLineBorder = 1;
|
||||
CPBezelBorder = 2;
|
||||
CPGrooveBorder = 3;
|
||||
|
||||
@implementation CPBox : CPView
|
||||
{
|
||||
CPBorderType _borderType;
|
||||
|
||||
CPColor _borderColor;
|
||||
CPColor _fillColor;
|
||||
|
||||
float _cornerRadius;
|
||||
float _borderWidth;
|
||||
|
||||
CPSize _contentMargin;
|
||||
CPView _contentView;
|
||||
}
|
||||
|
||||
+ (id)boxEnclosingView:(CPView)aView
|
||||
{
|
||||
var box = [[self alloc] initWithFrame:CGRectMakeZero()],
|
||||
enclosingView = [aView superview];
|
||||
|
||||
[box setFrameFromContentFrame:[aView frame]];
|
||||
|
||||
[enclosingView replaceSubview:aView with:box];
|
||||
|
||||
[box setContentView:aView];
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)frameRect
|
||||
{
|
||||
self = [super initWithFrame:frameRect];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_fillColor = [CPColor clearColor];
|
||||
_borderColor = [CPColor blackColor];
|
||||
|
||||
_borderWidth = 1.0;
|
||||
_contentMargin = CGSizeMake(0.0, 0.0);
|
||||
|
||||
_contentView = [[CPView alloc] initWithFrame:[self bounds]];
|
||||
|
||||
[self addSubview:_contentView];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// Configuring Boxes
|
||||
|
||||
- (CPRect)borderRect
|
||||
{
|
||||
return [self bounds];
|
||||
}
|
||||
|
||||
- (CPBorderType)borderType
|
||||
{
|
||||
return _borderType;
|
||||
}
|
||||
|
||||
- (void)setBorderType:(CPBorderType)value
|
||||
{
|
||||
_borderType = value;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (CPColor)borderColor
|
||||
{
|
||||
return _borderColor;
|
||||
}
|
||||
|
||||
- (void)setBorderColor:(CPColor)color
|
||||
{
|
||||
if ([color isEqual:_borderColor])
|
||||
return;
|
||||
|
||||
_borderColor = color;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (float)borderWidth
|
||||
{
|
||||
return _borderWidth;
|
||||
}
|
||||
|
||||
- (void)setBorderWidth:(float)width
|
||||
{
|
||||
if (width === _borderWidth)
|
||||
return;
|
||||
|
||||
_borderWidth = width;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (float)cornerRadius
|
||||
{
|
||||
return _cornerRadius;
|
||||
}
|
||||
|
||||
- (void)setCornerRadius:(float)radius
|
||||
{
|
||||
if (radius === _cornerRadius)
|
||||
return;
|
||||
|
||||
_cornerRadius = radius;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (CPColor)fillColor
|
||||
{
|
||||
return _fillColor;
|
||||
}
|
||||
|
||||
- (void)setFillColor:(CPColor)color
|
||||
{
|
||||
if ([color isEqual:_fillColor])
|
||||
return;
|
||||
|
||||
_fillColor = color;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (CPView)contentView
|
||||
{
|
||||
return _contentView;
|
||||
}
|
||||
|
||||
- (void)setContentView:(CPView)aView
|
||||
{
|
||||
if (aView === _contentView)
|
||||
return;
|
||||
|
||||
[aView setFrame:CGRectInset([self bounds], _contentMargin.width + _borderWidth, _contentMargin.height + _borderWidth)];
|
||||
[self replaceSubview:_contentView with:aView];
|
||||
|
||||
_contentView = aView;
|
||||
}
|
||||
|
||||
- (CPSize)contentViewMargins
|
||||
{
|
||||
return _contentMargin;
|
||||
}
|
||||
|
||||
- (void)setContentViewMargins:(CPSize)size
|
||||
{
|
||||
if(size.width < 0 || size.height < 0)
|
||||
[CPException raise:CPGenericException reason:@"Margins must be positive"];
|
||||
|
||||
_contentMargin = CGSizeMakeCopy(size);
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)setFrameFromContentFrame:(CPRect)aRect
|
||||
{
|
||||
[self setFrame:CGRectInset(aRect, -(_contentMargin.width + _borderWidth), -(_contentMargin.height + _borderWidth))];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)sizeToFit
|
||||
{
|
||||
var contentFrame = [_contentView frame];
|
||||
|
||||
[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,
|
||||
|
||||
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);
|
||||
|
||||
CGContextSetFillColor(aContext, [self fillColor]);
|
||||
CGContextSetStrokeColor(aContext, [self borderColor]);
|
||||
CGContextSetLineWidth(aContext, _borderWidth);
|
||||
|
||||
switch(_borderType)
|
||||
{
|
||||
case CPLineBorder: CGContextFillRoundedRectangleInRect(aContext, fillRect, _cornerRadius, YES, YES, YES, YES);
|
||||
CGContextStrokeRoundedRectangleInRect(aContext, strokeRect, _cornerRadius, YES, YES, YES, YES);
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
+419
-657
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
@implementation CPButtonBar : CPControl
|
||||
{
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
{
|
||||
return @"button-bar";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[nil]
|
||||
forKeys:[@"bezel-color"]];
|
||||
}
|
||||
|
||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if (aName === "bezel-view")
|
||||
return [self bounds];
|
||||
|
||||
return [super rectForEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if (aName === "bezel-view")
|
||||
{
|
||||
var view = [[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"]];
|
||||
}
|
||||
|
||||
- (void)addSubview:(CPView)aSubview
|
||||
{
|
||||
[super addSubview:aSubview];
|
||||
|
||||
[aSubview setAutoresizingMask:CPViewMinXMargin];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* CPCheckBox.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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 "CPButton.j"
|
||||
|
||||
|
||||
@implementation CPCheckBox : CPButton
|
||||
{
|
||||
}
|
||||
|
||||
+ (id)checkBoxWithTitle:(CPString)aTitle theme:(CPTheme)aTheme
|
||||
{
|
||||
return [self buttonWithTitle:aTitle theme:aTheme];
|
||||
}
|
||||
|
||||
+ (id)checkBoxWithTitle:(CPString)aTitle
|
||||
{
|
||||
return [self buttonWithTitle:aTitle];
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
{
|
||||
return @"check-box";
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setHighlightsBy:CPContentsCellMask];
|
||||
[self setShowsStateBy:CPContentsCellMask];
|
||||
|
||||
// Defaults?
|
||||
[self setImagePosition:CPImageLeft];
|
||||
[self setAlignment:CPLeftTextAlignment];
|
||||
|
||||
[self setBordered:YES];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
+73
-20
@@ -20,22 +20,26 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPView.j"
|
||||
@import "CPView.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
/*
|
||||
<objj>CPClipView</objj> 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 <objj>CPScrollView</objj>.
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPClipView
|
||||
|
||||
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
|
||||
{
|
||||
CPView _documentView;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the document view to be <code>aView</code>.
|
||||
@param aView the new document view. It's frame origin will be changed to <code>(0,0)</code> after calling this method.
|
||||
/*!
|
||||
Sets the document view to be \c aView.
|
||||
@param aView the new document view. It's frame origin will be changed to \c (0,0) after calling this method.
|
||||
*/
|
||||
- (void)setDocumentView:(CPView)aView
|
||||
{
|
||||
@@ -63,9 +67,6 @@ import "CPView.j"
|
||||
|
||||
if (_documentView)
|
||||
{
|
||||
// FIXME: remove when bounds.
|
||||
[_documentView setFrameOrigin:CGPointMake(0.0, 0.0)];
|
||||
|
||||
[self addSubview:_documentView];
|
||||
|
||||
[_documentView setPostsFrameChangedNotifications:YES];
|
||||
@@ -85,7 +86,7 @@ import "CPView.j"
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the document view.
|
||||
*/
|
||||
- (id)documentView
|
||||
@@ -93,14 +94,17 @@ import "CPView.j"
|
||||
return _documentView;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a new point that may be adjusted from <code>aPoint</code>
|
||||
/*!
|
||||
Returns a new point that may be adjusted from \c aPoint
|
||||
to make sure it lies within the document view.
|
||||
@param aPoint
|
||||
@return the adjusted point
|
||||
*/
|
||||
- (CGPoint)constrainScrollPoint:(CGPoint)aPoint
|
||||
{
|
||||
if (!_documentView)
|
||||
return _CGPointMakeZero();
|
||||
|
||||
var documentFrame = [_documentView frame];
|
||||
|
||||
aPoint.x = MAX(0.0, MIN(aPoint.x, MAX(_CGRectGetWidth(documentFrame) - _CGRectGetWidth(_bounds), 0.0)));
|
||||
@@ -122,29 +126,40 @@ import "CPView.j"
|
||||
[superview reflectScrolledClipView:self];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Scrolls the clip view to the specified point. The method
|
||||
sets its bounds origin to <code>aPoint</code>.
|
||||
sets its bounds origin to \c aPoint.
|
||||
*/
|
||||
- (void)scrollToPoint:(CGPoint)aPoint
|
||||
{
|
||||
[self setBoundsOrigin:[self constrainScrollPoint:aPoint]];
|
||||
}
|
||||
|
||||
/*
|
||||
Handles a <objj>CPViewBoundsDidChangeNotification</objj>.
|
||||
/*!
|
||||
Handles a CPViewBoundsDidChangeNotification.
|
||||
@param aNotification the notification event
|
||||
*/
|
||||
- (void)viewBoundsChanged:(CPNotification)aNotification
|
||||
{
|
||||
[self viewFrameChanged:aNotification];
|
||||
[self _constrainScrollPoint];
|
||||
}
|
||||
|
||||
/*
|
||||
Handles a <objj>CPViewFrameDidChangeNotification</objj>.
|
||||
/*!
|
||||
Handles a CPViewFrameDidChangeNotification.
|
||||
@param aNotification the notification event
|
||||
*/
|
||||
- (void)viewFrameChanged:(CPNotification)aNotification
|
||||
{
|
||||
[self _constrainScrollPoint];
|
||||
}
|
||||
|
||||
- (void)resizeSubviewsWithOldSize:(CGSize)aSize
|
||||
{
|
||||
[super resizeSubviewsWithOldSize:aSize];
|
||||
[self _constrainScrollPoint];
|
||||
}
|
||||
|
||||
- (void)_constrainScrollPoint
|
||||
{
|
||||
var oldScrollPoint = [self bounds].origin;
|
||||
|
||||
@@ -164,4 +179,42 @@ import "CPView.j"
|
||||
[superview reflectScrolledClipView:self];
|
||||
}
|
||||
|
||||
- (BOOL)autoscroll:(CPEvent)anEvent
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
eventLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
if (CPRectContainsPoint(bounds, eventLocation))
|
||||
return NO;
|
||||
|
||||
var newRect = CGRectMakeZero();
|
||||
|
||||
newRect.origin = eventLocation;
|
||||
newRect.size = CPSizeMake(10, 10);
|
||||
|
||||
return [_documentView scrollRectToVisible:newRect];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPClipViewDocumentViewKey = @"CPScrollViewDocumentView";
|
||||
|
||||
@implementation CPClipView (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
[self setDocumentView:[aCoder decodeObjectForKey:CPClipViewDocumentViewKey]];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeObject:_documentView forKey:CPClipViewDocumentViewKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+233
-194
@@ -20,16 +20,25 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPArray.j>
|
||||
import <Foundation/CPData.j>
|
||||
import <Foundation/CPIndexSet.j>
|
||||
import <Foundation/CPKeyedArchiver.j>
|
||||
import <Foundation/CPKeyedUnarchiver.j>
|
||||
@import <Foundation/CPArray.j>
|
||||
@import <Foundation/CPData.j>
|
||||
@import <Foundation/CPIndexSet.j>
|
||||
@import <Foundation/CPKeyedArchiver.j>
|
||||
@import <Foundation/CPKeyedUnarchiver.j>
|
||||
|
||||
import <AppKit/CPView.j>
|
||||
@import "CPView.j"
|
||||
@import "CPCollectionViewItem.j"
|
||||
|
||||
/*
|
||||
This class presents an array of content as a grid of views.
|
||||
|
||||
/*!
|
||||
@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
|
||||
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.
|
||||
@@ -51,8 +60,9 @@ import <AppKit/CPView.j>
|
||||
Invoked to obtain the data types supported by the specified indices for placement on the pasteboard.
|
||||
@param collectionView the collection view the items reside in
|
||||
@param indices the indices to obtain drag types
|
||||
@return an array of drag types (<objj>CPString</objj>)
|
||||
@return an array of drag types (CPString)
|
||||
*/
|
||||
|
||||
@implementation CPCollectionView : CPView
|
||||
{
|
||||
CPArray _content;
|
||||
@@ -68,11 +78,14 @@ import <AppKit/CPView.j>
|
||||
|
||||
CGSize _minItemSize;
|
||||
CGSize _maxItemSize;
|
||||
|
||||
|
||||
CPArray _backgroundColors;
|
||||
|
||||
float _tileWidth;
|
||||
|
||||
BOOL _isSelectable;
|
||||
BOOL _allowsMultipleSelection;
|
||||
BOOL _allowsEmptySelection;
|
||||
CPIndexSet _selectionIndexes;
|
||||
|
||||
CGSize _itemSize;
|
||||
@@ -84,6 +97,8 @@ import <AppKit/CPView.j>
|
||||
unsigned _numberOfColumns;
|
||||
|
||||
id _delegate;
|
||||
|
||||
CPEvent _mouseDownEvent;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -100,29 +115,35 @@ import <AppKit/CPView.j>
|
||||
_itemSize = CGSizeMakeZero();
|
||||
_minItemSize = CGSizeMakeZero();
|
||||
_maxItemSize = CGSizeMakeZero();
|
||||
|
||||
|
||||
[self setBackgroundColors:nil];
|
||||
|
||||
_verticalMargin = 5.0;
|
||||
_tileWidth = -1.0;
|
||||
|
||||
_selectionIndexes = [CPIndexSet indexSet];
|
||||
_allowsEmptySelection = YES;
|
||||
_isSelectable = YES;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the item prototype to <code>anItem</code>
|
||||
/*!
|
||||
Sets the item prototype to \c anItem
|
||||
@param anItem the new item prototype
|
||||
*/
|
||||
- (void)setItemPrototype:(CPCollectionViewItem)anItem
|
||||
{
|
||||
_itemData = [CPKeyedArchiver archivedDataWithRootObject:anItem];
|
||||
_itemForDragging = anItem//[CPKeyedUnarchiver unarchiveObjectWithData:_itemData];
|
||||
|
||||
_cachedItems = [];
|
||||
_itemData = nil;
|
||||
_itemForDragging = nil;
|
||||
_itemPrototype = anItem;
|
||||
|
||||
[self reloadContent];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the current item prototype
|
||||
*/
|
||||
- (CPCollectionViewItem)itemPrototype
|
||||
@@ -130,18 +151,25 @@ import <AppKit/CPView.j>
|
||||
return _itemPrototype;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a collection view item for <code>anObject</code>.
|
||||
/*!
|
||||
Returns a collection view item for \c anObject.
|
||||
@param anObject the object to be represented.
|
||||
*/
|
||||
- (CPCollectionViewItem)newItemForRepresentedObject:(id)anObject
|
||||
{
|
||||
var item = nil;
|
||||
|
||||
|
||||
if (_cachedItems.length)
|
||||
item = _cachedItems.pop();
|
||||
|
||||
else
|
||||
{
|
||||
if (!_itemData)
|
||||
if (_itemPrototype)
|
||||
_itemData = [CPKeyedArchiver archivedDataWithRootObject:_itemPrototype];
|
||||
|
||||
item = [CPKeyedUnarchiver unarchiveObjectWithData:_itemData];
|
||||
}
|
||||
|
||||
[item setRepresentedObject:anObject];
|
||||
[[item view] setFrameSize:_itemSize];
|
||||
@@ -150,26 +178,26 @@ import <AppKit/CPView.j>
|
||||
}
|
||||
|
||||
// Working with the Responder Chain
|
||||
/*
|
||||
Returns <code>YES</code> by default.
|
||||
/*!
|
||||
Returns \c YES by default.
|
||||
*/
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns whether the receiver is currently the first responder.
|
||||
*/
|
||||
- (BOOL)isFirstResponder
|
||||
{
|
||||
return [[self window] firstResponder] == self;
|
||||
return [[self window] firstResponder] === self;
|
||||
}
|
||||
|
||||
// Setting the Content
|
||||
/*
|
||||
Sets the content of the collection view to the content in <code>anArray</code>.
|
||||
This array can be of any type, and each element will be passed to the <code>setRepresentedObject:</code> 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
|
||||
*/
|
||||
@@ -183,7 +211,7 @@ import <AppKit/CPView.j>
|
||||
[self reloadContent];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the collection view content array
|
||||
*/
|
||||
- (CPArray)content
|
||||
@@ -191,7 +219,7 @@ import <AppKit/CPView.j>
|
||||
return _content;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the collection view items.
|
||||
*/
|
||||
- (CPArray)items
|
||||
@@ -200,9 +228,9 @@ import <AppKit/CPView.j>
|
||||
}
|
||||
|
||||
// Setting the Selection Mode
|
||||
/*
|
||||
/*!
|
||||
Sets whether the user is allowed to select items
|
||||
@param isSelectable <code>YES</code> allows the user to select items.
|
||||
@param isSelectable \c YES allows the user to select items.
|
||||
*/
|
||||
- (void)setSelectable:(BOOL)isSelectable
|
||||
{
|
||||
@@ -220,58 +248,75 @@ import <AppKit/CPView.j>
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the collection view is
|
||||
selected, and <code>NO</code> otherwise.
|
||||
/*!
|
||||
Returns \c YES if the collection view is
|
||||
selectable, and \c NO otherwise.
|
||||
*/
|
||||
- (BOOL)isSelected
|
||||
- (BOOL)isSelectable
|
||||
{
|
||||
return _isSelected;
|
||||
return _isSelectable;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets whether the user may have no items selected. If YES, mouse clicks not on any item will empty the current selection. The first item will also start off as selected.
|
||||
@param shouldAllowMultipleSelection \c YES allows the user to select multiple items
|
||||
*/
|
||||
- (void)setAllowsEmptySelection:(BOOL)shouldAllowEmptySelection
|
||||
{
|
||||
_allowsEmptySelection = shouldAllowEmptySelection;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c YES if the user can select no items, \c NO otherwise.
|
||||
*/
|
||||
- (BOOL)allowsEmptySelection
|
||||
{
|
||||
return _allowsEmptySelection;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the user can select multiple items.
|
||||
@param shouldAllowMultipleSelection <code>YES</code> allows the user to select multiple items
|
||||
@param shouldAllowMultipleSelection \c YES allows the user to select multiple items
|
||||
*/
|
||||
- (void)setAllowsMultipleSelection:(BOOL)shouldAllowMultipleSelection
|
||||
{
|
||||
_allowsMultipleSelection = shouldAllowMultipleSelection;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the user can select multiple items, <code>NO</code> otherwise.
|
||||
/*!
|
||||
Returns \c YES if the user can select multiple items, \c NO otherwise.
|
||||
*/
|
||||
- (BOOL)allowsMultipleSelection
|
||||
{
|
||||
return _allowsMultipleSelection;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the selected items based on the provided indices.
|
||||
@param anIndexSet the set of items to be selected
|
||||
*/
|
||||
- (void)setSelectionIndexes:(CPIndexSet)anIndexSet
|
||||
{
|
||||
if (_selectionIndexes == anIndexSet)
|
||||
if (_selectionIndexes == 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];
|
||||
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)])
|
||||
[_delegate collectionViewDidChangeSelection:self]
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a set of the selected indices.
|
||||
*/
|
||||
- (CPIndexSet)selectionIndexes
|
||||
@@ -293,13 +338,13 @@ import <AppKit/CPView.j>
|
||||
|
||||
_items = [];
|
||||
|
||||
if (!_itemData || !_content)
|
||||
if (!_itemPrototype || !_content)
|
||||
return;
|
||||
|
||||
|
||||
var index = 0;
|
||||
|
||||
|
||||
count = _content.length;
|
||||
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
_items.push([self newItemForRepresentedObject:_content[index]]);
|
||||
@@ -322,7 +367,7 @@ import <AppKit/CPView.j>
|
||||
// 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)
|
||||
@@ -334,6 +379,10 @@ import <AppKit/CPView.j>
|
||||
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;
|
||||
@@ -382,7 +431,7 @@ import <AppKit/CPView.j>
|
||||
}
|
||||
|
||||
// Laying Out the Collection View
|
||||
/*
|
||||
/*!
|
||||
Sets the maximum number of rows.
|
||||
@param aMaxNumberOfRows the new maximum number of rows
|
||||
*/
|
||||
@@ -396,7 +445,7 @@ import <AppKit/CPView.j>
|
||||
[self tile];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the maximum number of rows.
|
||||
*/
|
||||
- (unsigned)maxNumberOfRows
|
||||
@@ -404,7 +453,7 @@ import <AppKit/CPView.j>
|
||||
return _maxNumberOfRows;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the maximum number of columns.
|
||||
@param aMaxNumberOfColumns the new maximum number of columns
|
||||
*/
|
||||
@@ -418,7 +467,7 @@ import <AppKit/CPView.j>
|
||||
[self tile];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the maximum number of columns
|
||||
*/
|
||||
- (unsigned)maxNumberOfColumns
|
||||
@@ -426,7 +475,24 @@ import <AppKit/CPView.j>
|
||||
return _maxNumberOfColumns;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the current number of rows
|
||||
*/
|
||||
- (unsigned)numberOfRows
|
||||
{
|
||||
return _numberOfRows;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the current number of columns
|
||||
*/
|
||||
|
||||
- (unsigned)numberOfColumns
|
||||
{
|
||||
return _numberOfColumns;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the minimum size for an item
|
||||
@param aSize the new minimum item size
|
||||
*/
|
||||
@@ -440,7 +506,7 @@ import <AppKit/CPView.j>
|
||||
[self tile];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the current minimum item size
|
||||
*/
|
||||
- (CGSize)minItemSize
|
||||
@@ -448,7 +514,7 @@ import <AppKit/CPView.j>
|
||||
return _minItemSize;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the maximum item size.
|
||||
@param aSize the new maximum item size
|
||||
*/
|
||||
@@ -462,7 +528,7 @@ import <AppKit/CPView.j>
|
||||
[self tile];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the current maximum item size.
|
||||
*/
|
||||
- (CGSize)maxItemSize
|
||||
@@ -470,6 +536,30 @@ import <AppKit/CPView.j>
|
||||
return _maxItemSize;
|
||||
}
|
||||
|
||||
- (void)setBackgroundColors:(CPArray)backgroundColors
|
||||
{
|
||||
if (_backgroundColors === backgroundColors)
|
||||
return;
|
||||
|
||||
_backgroundColors = backgroundColors;
|
||||
|
||||
if (!_backgroundColors)
|
||||
_backgroundColors = [CPColor whiteColor];
|
||||
|
||||
if ([_backgroundColors count] === 1)
|
||||
[self setBackgroundColor:_backgroundColors[0]];
|
||||
|
||||
else
|
||||
[self setBackgroundColor:nil];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (CPArray)backgroundColors
|
||||
{
|
||||
return _backgroundColors;
|
||||
}
|
||||
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
{
|
||||
if ([_selectionIndexes count] && [anEvent clickCount] == 2 && [_delegate respondsToSelector:@selector(collectionView:didDoubleClickOnItemAtIndex:)])
|
||||
@@ -478,48 +568,56 @@ import <AppKit/CPView.j>
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
_mouseDownEvent = anEvent;
|
||||
|
||||
var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
row = FLOOR(location.y / (_itemSize.height + _verticalMargin)),
|
||||
column = FLOOR(location.x / (_itemSize.width + _horizontalMargin)),
|
||||
index = row * _numberOfColumns + column;
|
||||
|
||||
|
||||
if (index >= 0 && index < _items.length)
|
||||
[self setSelectionIndexes:[CPIndexSet indexSetWithIndex:index]];
|
||||
|
||||
else if (_allowsEmptySelection)
|
||||
[self setSelectionIndexes:[CPIndexSet indexSet]];
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
if (![_delegate respondsToSelector:@selector(collectionView:dragTypesForItemsAtIndexes:)])
|
||||
return;
|
||||
|
||||
|
||||
// If we don't have any selected items, we've clicked away, and thus the drag is meaningless.
|
||||
if (![_selectionIndexes count])
|
||||
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];
|
||||
|
||||
[_itemForDragging setRepresentedObject:_content[[_selectionIndexes firstIndex]]];
|
||||
if (!_itemForDragging)
|
||||
_itemForDragging = [self newItemForRepresentedObject:_content[[_selectionIndexes firstIndex]]];
|
||||
else
|
||||
[_itemForDragging setRepresentedObject:_content[[_selectionIndexes firstIndex]]];
|
||||
|
||||
var view = [_itemForDragging view];
|
||||
|
||||
var view = [_itemForDragging view],
|
||||
frame = [view frame];
|
||||
|
||||
[view setFrameSize:_itemSize];
|
||||
|
||||
[view setAlphaValue:0.7];
|
||||
|
||||
[self dragView:view
|
||||
at:[[_items[[_selectionIndexes firstIndex]] view] frame].origin
|
||||
offset:CGPointMakeZero()
|
||||
event:anEvent
|
||||
offset:CGSizeMakeZero()
|
||||
event:_mouseDownEvent
|
||||
pasteboard:nil
|
||||
source:self
|
||||
slideBack:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Places the selected items on the specified pasteboard. The items are requested from the collection's delegate.
|
||||
@param aPasteboard the pasteboard to put the items on
|
||||
@param aType the format the pasteboard data
|
||||
@@ -531,6 +629,11 @@ import <AppKit/CPView.j>
|
||||
|
||||
// Cappuccino Additions
|
||||
|
||||
/*!
|
||||
Sets the collection view's vertical spacing between elements.
|
||||
@param aVerticalMargin the number of pixels to place between elements
|
||||
*/
|
||||
|
||||
- (void)setVerticalMargin:(float)aVerticalMargin
|
||||
{
|
||||
if (_verticalMargin == aVerticalMargin)
|
||||
@@ -541,7 +644,16 @@ import <AppKit/CPView.j>
|
||||
[self tile];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Gets the collection view's current vertical spacing between elements.
|
||||
*/
|
||||
|
||||
- (float)verticalMargin
|
||||
{
|
||||
return _verticalMargin;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the collection view's delegate
|
||||
@param aDelegate the new delegate
|
||||
*/
|
||||
@@ -550,7 +662,7 @@ import <AppKit/CPView.j>
|
||||
_delegate = aDelegate;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the collection view's delegate
|
||||
*/
|
||||
- (id)delegate
|
||||
@@ -560,135 +672,62 @@ import <AppKit/CPView.j>
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
Represents an object inside a <objj>CPCollectionView</objj>.
|
||||
*/
|
||||
@implementation CPCollectionViewItem : CPObject
|
||||
{
|
||||
id _representedObject;
|
||||
|
||||
CPView _view;
|
||||
|
||||
BOOL _isSelected;
|
||||
}
|
||||
var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeKey",
|
||||
CPCollectionViewMaxItemSizeKey = @"CPCollectionViewMaxItemSizeKey",
|
||||
CPCollectionViewVerticalMarginKey = @"CPCollectionViewVerticalMarginKey",
|
||||
CPCollectionViewSelectableKey = @"CPCollectionViewSelectableKey",
|
||||
CPCollectionViewBackgroundColorsKey = @"CPCollectionViewBackgroundColorsKey";
|
||||
|
||||
// Setting the Represented Object
|
||||
/*
|
||||
Sets the object to be represented by this item.
|
||||
@param anObject the object to be represented
|
||||
*/
|
||||
- (void)setRepresentedObject:(id)anObject
|
||||
{
|
||||
if (_representedObject == anObject)
|
||||
return;
|
||||
|
||||
_representedObject = anObject;
|
||||
|
||||
// FIXME: This should be set up by bindings
|
||||
[_view setRepresentedObject:anObject];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the object represented by this view item
|
||||
*/
|
||||
- (id)representedObject
|
||||
{
|
||||
return _representedObject;
|
||||
}
|
||||
@implementation CPCollectionView (CPCoding)
|
||||
|
||||
// Modifying the View
|
||||
/*
|
||||
Sets the view that is used represent this object.
|
||||
@param aView the view used to represent this object
|
||||
*/
|
||||
- (void)setView:(CPView)aView
|
||||
{
|
||||
_view = aView;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the view that represents this object.
|
||||
*/
|
||||
- (CPView)view
|
||||
{
|
||||
return _view;
|
||||
}
|
||||
|
||||
// Modifying the Selection
|
||||
/*
|
||||
Sets whether this view item should be selected.
|
||||
@param shouldBeSelected <code>YES</code> makes the item selected. <code>NO</code> deselects it.
|
||||
*/
|
||||
- (void)setSelected:(BOOL)shouldBeSelected
|
||||
{
|
||||
if (_isSelected == shouldBeSelected)
|
||||
return;
|
||||
|
||||
_isSelected = shouldBeSelected;
|
||||
|
||||
// FIXME: This should be set up by bindings
|
||||
[_view setSelected:_isSelected];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the item is currently selected. <code>NO</code> if the item is not selected.
|
||||
*/
|
||||
- (BOOL)isSelected
|
||||
{
|
||||
return _isSelected;
|
||||
}
|
||||
|
||||
// Parent Collection View
|
||||
/*
|
||||
Returns the collection view of which this item is a part.
|
||||
*/
|
||||
- (CPCollectionView)collectionView
|
||||
{
|
||||
return [_view superview];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPCollectionViewItemViewKey = @"CPCollectionViewItemViewKey";
|
||||
|
||||
@implementation CPCollectionViewItem (CPCoding)
|
||||
|
||||
/*
|
||||
FIXME Not yet implemented
|
||||
*/
|
||||
- (id)copy
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPCollectionViewItemViewKey = @"CPCollectionViewItemViewKey";
|
||||
|
||||
@implementation CPCollectionViewItem (CPCoding)
|
||||
|
||||
/*
|
||||
Initializes the view item by unarchiving data from a coder.
|
||||
@param aCoder the coder from which the data will be unarchived
|
||||
@return the initialized collection view item
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
_view = [aCoder decodeObjectForKey:CPCollectionViewItemViewKey];
|
||||
|
||||
{
|
||||
_items = [];
|
||||
_content = [];
|
||||
|
||||
_cachedItems = [];
|
||||
|
||||
_itemSize = CGSizeMakeZero();
|
||||
|
||||
_minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey] || CGSizeMakeZero();
|
||||
_maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey] || CGSizeMakeZero();
|
||||
|
||||
_verticalMargin = [aCoder decodeFloatForKey:CPCollectionViewVerticalMarginKey];
|
||||
|
||||
_isSelectable = [aCoder decodeBoolForKey:CPCollectionViewSelectableKey];
|
||||
|
||||
[self setBackgroundColors:[aCoder decodeObjectForKey:CPCollectionViewBackgroundColorsKey]];
|
||||
|
||||
_tileWidth = -1.0;
|
||||
|
||||
_selectionIndexes = [CPIndexSet indexSet];
|
||||
|
||||
_allowsEmptySelection = YES;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
Archives the colletion view item to the provided coder.
|
||||
@param aCoder the coder to which the view item should be archived
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_view forKey:CPCollectionViewItemViewKey];
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
if (!CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
|
||||
[aCoder encodeSize:_minItemSize forKey:CPCollectionViewMinItemSizeKey];
|
||||
|
||||
if (!CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
|
||||
[aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey];
|
||||
|
||||
[aCoder encodeBool:_isSelectable forKey:CPCollectionViewSelectableKey];
|
||||
|
||||
[aCoder encodeFloat:_verticalMargin forKey:CPCollectionViewVerticalMarginKey];
|
||||
|
||||
[aCoder encodeObject:_backgroundColors forKey:CPCollectionViewBackgroundColorsKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* CPCollectionViewItem.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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 "CPViewController.j"
|
||||
|
||||
/*!
|
||||
Represents an object inside a CPCollectionView.
|
||||
*/
|
||||
@implementation CPCollectionViewItem : CPViewController
|
||||
{
|
||||
BOOL _isSelected;
|
||||
}
|
||||
|
||||
// Setting the Represented Object
|
||||
/*!
|
||||
Sets the object to be represented by this item.
|
||||
@param anObject the object to be represented
|
||||
*/
|
||||
- (void)setRepresentedObject:(id)anObject
|
||||
{
|
||||
[super setRepresentedObject:anObject];
|
||||
|
||||
var view = [self view];
|
||||
|
||||
if ([view respondsToSelector:@selector(setRepresentedObject:)])
|
||||
[view setRepresentedObject:[self representedObject]];
|
||||
}
|
||||
|
||||
// Modifying the Selection
|
||||
/*!
|
||||
Sets whether this view item should be selected.
|
||||
@param shouldBeSelected \c YES makes the item selected. \c NO deselects it.
|
||||
*/
|
||||
- (void)setSelected:(BOOL)shouldBeSelected
|
||||
{
|
||||
shouldBeSelected = !!shouldBeSelected;
|
||||
|
||||
if (_isSelected === shouldBeSelected)
|
||||
return;
|
||||
|
||||
_isSelected = shouldBeSelected;
|
||||
|
||||
var view = [self view];
|
||||
|
||||
if ([view respondsToSelector:@selector(setSelected:)])
|
||||
[view setSelected:[self isSelected]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c YES if the item is currently selected. \c NO if the item is not selected.
|
||||
*/
|
||||
- (BOOL)isSelected
|
||||
{
|
||||
return _isSelected;
|
||||
}
|
||||
|
||||
// Parent Collection View
|
||||
/*!
|
||||
Returns the collection view of which this item is a part.
|
||||
*/
|
||||
- (CPCollectionView)collectionView
|
||||
{
|
||||
return [_view superview];
|
||||
}
|
||||
|
||||
@end
|
||||
+291
-62
@@ -20,12 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
import "CGColor.j"
|
||||
@import "CGColor.j"
|
||||
|
||||
@import "CPCompatibility.j"
|
||||
@import "CPImage.j"
|
||||
|
||||
import "CPCompatibility.j"
|
||||
import "CPImage.j"
|
||||
|
||||
var _redComponent = 0,
|
||||
_greenComponent = 1,
|
||||
@@ -36,17 +37,37 @@ var _hueComponent = 0,
|
||||
_saturationComponent = 1,
|
||||
_brightnessComponent = 2;
|
||||
|
||||
/*
|
||||
<code>CPColor</code> can be used to represent color
|
||||
var cachedBlackColor,
|
||||
cachedRedColor,
|
||||
cachedGreenColor,
|
||||
cachedBlueColor,
|
||||
cachedYellowColor,
|
||||
cachedGrayColor,
|
||||
cachedLightGrayColor,
|
||||
cachedDarkGrayColor,
|
||||
cachedWhiteColor,
|
||||
cachedBrownColor,
|
||||
cachedCyanColor,
|
||||
cachedMagentaColor,
|
||||
cachedOrangeColor,
|
||||
cachedPurpleColor,
|
||||
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 <code>set:</code> method
|
||||
<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 <objj>CGContextSetFillColor()</objj>.
|
||||
context, use CGContextSetFillColor().
|
||||
*/
|
||||
@implementation CPColor : CPObject
|
||||
{
|
||||
@@ -56,7 +77,27 @@ var _hueComponent = 0,
|
||||
CPString _cssString;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
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)colorWithRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[red, green, blue, alpha]];
|
||||
}
|
||||
|
||||
/*!
|
||||
@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
|
||||
@@ -71,11 +112,28 @@ var _hueComponent = 0,
|
||||
*/
|
||||
+ (CPColor)colorWithCalibratedRed:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[red, green, blue, alpha]];
|
||||
return [self colorWithRed:red green:green blue:blue alpha:alpha];
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a new color object with <code>white</code> for the RGB components.
|
||||
|
||||
/*!
|
||||
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
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[white, white, white, alpha]];
|
||||
}
|
||||
|
||||
/*!
|
||||
@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
|
||||
@@ -85,10 +143,10 @@ var _hueComponent = 0,
|
||||
*/
|
||||
+ (CPColor)colorWithCalibratedWhite:(float)white alpha:(float)alpha
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[white, white, white, alpha]];
|
||||
return [self colorWithWhite:white alpha:alpha];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates a new color in HSB space.
|
||||
|
||||
@param hue the hue value
|
||||
@@ -99,8 +157,13 @@ var _hueComponent = 0,
|
||||
*/
|
||||
+ (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness
|
||||
{
|
||||
if(saturation == 0.0)
|
||||
return [CPColor colorWithCalibratedWhite: brightness/100.0 alpha: 1.0];
|
||||
return [self colorWithHue:hue saturation:saturation brightness:brightness alpha:1.0];
|
||||
}
|
||||
|
||||
+ (CPColor)colorWithHue:(float)hue saturation:(float)saturation brightness:(float)brightness alpha:(float)alpha
|
||||
{
|
||||
if(saturation === 0.0)
|
||||
return [CPColor colorWithCalibratedWhite:brightness / 100.0 alpha:alpha];
|
||||
|
||||
var f = hue % 60,
|
||||
p = (brightness * (100 - saturation)) / 10000,
|
||||
@@ -110,16 +173,16 @@ var _hueComponent = 0,
|
||||
|
||||
switch(FLOOR(hue / 60))
|
||||
{
|
||||
case 0: return [CPColor colorWithCalibratedRed: b green: t blue: p alpha: 1.0];
|
||||
case 1: return [CPColor colorWithCalibratedRed: q green: b blue: p alpha: 1.0];
|
||||
case 2: return [CPColor colorWithCalibratedRed: p green: b blue: t alpha: 1.0];
|
||||
case 3: return [CPColor colorWithCalibratedRed: p green: q blue: b alpha: 1.0];
|
||||
case 4: return [CPColor colorWithCalibratedRed: t green: p blue: b alpha: 1.0];
|
||||
case 5: return [CPColor colorWithCalibratedRed: b green: p blue: q alpha: 1.0];
|
||||
case 0: return [CPColor colorWithCalibratedRed: b green: t blue: p alpha: alpha];
|
||||
case 1: return [CPColor colorWithCalibratedRed: q green: b blue: p alpha: alpha];
|
||||
case 2: return [CPColor colorWithCalibratedRed: p green: b blue: t alpha: alpha];
|
||||
case 3: return [CPColor colorWithCalibratedRed: p green: q blue: b alpha: alpha];
|
||||
case 4: return [CPColor colorWithCalibratedRed: t green: p blue: b alpha: alpha];
|
||||
case 5: return [CPColor colorWithCalibratedRed: b green: p blue: q alpha: alpha];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
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
|
||||
@@ -134,88 +197,196 @@ var _hueComponent = 0,
|
||||
return [[CPColor alloc] _initWithRGBA: hexToRGB(hex)];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a black color object. (RGBA=[0.0, 0.0, 0.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)blackColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0]];
|
||||
if (!cachedBlackColor)
|
||||
cachedBlackColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0]];
|
||||
|
||||
return cachedBlackColor;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a blue color object. (RGBA=[0.0, 0.0, 1.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)blueColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 1.0, 1.0]];
|
||||
if (!cachedBlueColor)
|
||||
cachedBlueColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 1.0, 1.0]];
|
||||
|
||||
return cachedBlueColor;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a dark gray color object. (RGBA=[0.33 ,0.33, 0.33, 1.0])
|
||||
*/
|
||||
+ (CPColor)darkGrayColor
|
||||
{
|
||||
return [CPColor colorWithCalibratedWhite:1.0 / 3.0 alpha:1.0];
|
||||
if (!cachedDarkGrayColor)
|
||||
cachedDarkGrayColor = [CPColor colorWithCalibratedWhite:1.0 / 3.0 alpha:1.0];
|
||||
|
||||
return cachedDarkGrayColor;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a gray color object. (RGBA=[0.5, 0.5, 0.5, 1.0])
|
||||
*/
|
||||
+ (CPColor)grayColor
|
||||
{
|
||||
return [CPColor colorWithCalibratedWhite:0.5 alpha: 1.0];
|
||||
if (!cachedGrayColor)
|
||||
cachedGrayColor = [CPColor colorWithCalibratedWhite:0.5 alpha: 1.0];
|
||||
|
||||
return cachedGrayColor;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a green color object. (RGBA=[0.0, 1.0, 0.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)greenColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.0, 1.0, 0.0, 1.0]];
|
||||
if (!cachedGreenColor)
|
||||
cachedGreenColor = [[CPColor alloc] _initWithRGBA:[0.0, 1.0, 0.0, 1.0]];
|
||||
|
||||
return cachedGreenColor;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a light gray color object (RGBA=[0.66, 0.66, 0.66, 1.0])
|
||||
*/
|
||||
+ (CPColor)lightGrayColor
|
||||
{
|
||||
return [CPColor colorWithCalibratedWhite:2.0 / 3.0 alpha:1.0];
|
||||
if (!cachedLightGrayColor)
|
||||
cachedLightGrayColor = [CPColor colorWithCalibratedWhite:2.0 / 3.0 alpha:1.0];
|
||||
|
||||
return cachedLightGrayColor;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a red color object (RGBA=[1.0, 0.0, 0.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)redColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[1.0, 0.0, 0.0, 1.0]];
|
||||
if (!cachedRedColor)
|
||||
cachedRedColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.0, 0.0, 1.0]];
|
||||
|
||||
return cachedRedColor;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a white color object (RGBA=[1.0, 1.0, 1.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)whiteColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 1.0, 1.0]];
|
||||
if (!cachedWhiteColor)
|
||||
cachedWhiteColor = [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 1.0, 1.0]];
|
||||
|
||||
return cachedWhiteColor;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a yellow color object (RGBA=[1.0, 1.0, 0.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)yellowColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 0.0, 1.0]];
|
||||
if (!cachedYellowColor)
|
||||
cachedYellowColor = [[CPColor alloc] _initWithRGBA:[1.0, 1.0, 0.0, 1.0]];
|
||||
|
||||
return cachedYellowColor;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a brown color object (RGBA=[0.6, 0.4, 0.2, 1.0])
|
||||
*/
|
||||
+ (CPColor)brownColor
|
||||
{
|
||||
if (!cachedBrownColor)
|
||||
cachedBrownColor = [[CPColor alloc] _initWithRGBA:[0.6, 0.4, 0.2, 1.0]];
|
||||
|
||||
return cachedBrownColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a cyan color object (RGBA=[0.0, 1.0, 1.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)cyanColor
|
||||
{
|
||||
if (!cachedCyanColor)
|
||||
cachedCyanColor = [[CPColor alloc] _initWithRGBA:[0.0, 1.0, 1.0, 1.0]];
|
||||
|
||||
return cachedCyanColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a magenta color object (RGBA=[1.0, 0.0, 1.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)magentaColor
|
||||
{
|
||||
if (!cachedMagentaColor)
|
||||
cachedMagentaColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.0, 1.0, 1.0]];
|
||||
|
||||
return cachedMagentaColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a orange color object (RGBA=[1.0, 0.5, 0.0, 1.0])
|
||||
*/
|
||||
+ (CPColor)orangeColor
|
||||
{
|
||||
if (!cachedOrangeColor)
|
||||
cachedOrangeColor = [[CPColor alloc] _initWithRGBA:[1.0, 0.5, 0.0, 1.0]];
|
||||
|
||||
return cachedOrangeColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a purple color object (RGBA=[0.5, 0.0, 0.5, 1.0])
|
||||
*/
|
||||
+ (CPColor)purpleColor
|
||||
{
|
||||
if (!cachedPurpleColor)
|
||||
cachedPurpleColor = [[CPColor alloc] _initWithRGBA:[0.5, 0.0, 0.5, 1.0]];
|
||||
|
||||
return cachedPurpleColor;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a shadow looking color (RGBA=[0.0, 0.0, 0.0, 0.33])
|
||||
*/
|
||||
|
||||
+ (CPColor)shadowColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0 / 3.0]];
|
||||
if (!cachedShadowColor)
|
||||
cachedShadowColor = [[CPColor alloc] _initWithRGBA:[0.0, 0.0, 0.0, 1.0 / 3.0]];
|
||||
|
||||
return cachedShadowColor;
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a color using a tile pattern with <code>anImage</code>
|
||||
/*!
|
||||
Returns a clear color (RGBA=[0.0, 0.0, 0.0, 0.0])
|
||||
*/
|
||||
|
||||
+ (CPColor)clearColor
|
||||
{
|
||||
if (!cachedClearColor)
|
||||
cachedClearColor = [self colorWithCalibratedWhite:0.0 alpha:0.0];
|
||||
|
||||
return cachedClearColor;
|
||||
}
|
||||
|
||||
+ (CPColor)alternateSelectedControlColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]];
|
||||
}
|
||||
|
||||
+ (CPColor)secondarySelectedControlColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a color using a tile pattern with \c anImage
|
||||
@param the image to tile
|
||||
@return a tiled image color object
|
||||
*/
|
||||
@@ -224,8 +395,8 @@ var _hueComponent = 0,
|
||||
return [[CPColor alloc] _initWithPatternImage:anImage];
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a <objj>CPColor</objj> from a valid CSS RGB string. Example, "rgb(32,64,129)".
|
||||
/*!
|
||||
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
|
||||
@@ -322,13 +493,13 @@ var _hueComponent = 0,
|
||||
if (self)
|
||||
{
|
||||
_patternImage = anImage;
|
||||
_cssString = "url(\"" + _patternImage._filename + "\")";
|
||||
_cssString = "url(\"" + [_patternImage filename] + "\")";
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the image being used as the pattern for the tile in this color.
|
||||
*/
|
||||
- (CPImage)patternImage
|
||||
@@ -336,7 +507,7 @@ var _hueComponent = 0,
|
||||
return _patternImage;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the alpha component of this color.
|
||||
*/
|
||||
- (float)alphaComponent
|
||||
@@ -344,7 +515,7 @@ var _hueComponent = 0,
|
||||
return _components[3];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the blue component of this color.
|
||||
*/
|
||||
- (float)blueComponent
|
||||
@@ -352,7 +523,7 @@ var _hueComponent = 0,
|
||||
return _components[2];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the green component of this color.
|
||||
*/
|
||||
- (float)greenComponent
|
||||
@@ -360,7 +531,7 @@ var _hueComponent = 0,
|
||||
return _components[1];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Return the red component of this color.
|
||||
*/
|
||||
- (float)redComponent
|
||||
@@ -368,7 +539,7 @@ var _hueComponent = 0,
|
||||
return _components[0];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the RGBA components of this color in an array.
|
||||
The index values are ordered as:
|
||||
<pre>
|
||||
@@ -384,7 +555,7 @@ var _hueComponent = 0,
|
||||
return _components;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
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
|
||||
@@ -400,7 +571,7 @@ var _hueComponent = 0,
|
||||
return [[[self class] alloc] _initWithRGBA:components];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns an array with the HSB values for this color.
|
||||
The index values are ordered as:
|
||||
<pre>
|
||||
@@ -451,7 +622,7 @@ var _hueComponent = 0,
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the CSS representation of this color. The color will
|
||||
be in one of the following forms:
|
||||
<pre>
|
||||
@@ -465,7 +636,7 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
||||
return _cssString;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a 6 character long hex string of this color.
|
||||
*/
|
||||
- (CPString)hexString
|
||||
@@ -473,6 +644,62 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
||||
return rgbToHex([self redComponent], [self greenComponent], [self blueComponent])
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(CPColor)aColor
|
||||
{
|
||||
if (!aColor)
|
||||
return NO;
|
||||
|
||||
if (aColor === self)
|
||||
return YES;
|
||||
|
||||
return [aColor isKindOfClass:CPColor] && [aColor cssString] === [self cssString];
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [super description]+" "+[self cssString];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPColor (CoreGraphicsExtensions)
|
||||
|
||||
/*!
|
||||
Set's the receiver to be the fill and stroke color in the current graphics context
|
||||
*/
|
||||
- (void)set
|
||||
{
|
||||
[self setFill];
|
||||
[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);
|
||||
}
|
||||
|
||||
/*!
|
||||
Set's the receiver to be the stroke color in the current graphics context
|
||||
*/
|
||||
- (void)setStroke
|
||||
{
|
||||
var ctx = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
CGContextSetStrokeColor(ctx, self);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPColor (Debugging)
|
||||
|
||||
+ (CPColor)randomColor
|
||||
{
|
||||
return [CPColor colorWithRed:RAND() green:RAND() blue:RAND() alpha:1.0];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
@@ -480,7 +707,7 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
|
||||
@implementation CPColor (CPCoding)
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes this color from the data archived in a coder.
|
||||
@param aCoder the coder from which the color will be loaded
|
||||
*/
|
||||
@@ -492,7 +719,7 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
return [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Archives this color into a coder.
|
||||
@param aCoder the coder into which the color will be archived.
|
||||
*/
|
||||
@@ -508,14 +735,16 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
|
||||
var hexCharacters = "0123456789ABCDEF";
|
||||
|
||||
/*
|
||||
Used for the <objj>CPColor</objj> <code>colorWithHexString:</code> implementation
|
||||
/*!
|
||||
Used for the CPColor \c +colorWithHexString: implementation
|
||||
@ignore
|
||||
@class CPColor
|
||||
@return an array of rgb components
|
||||
*/
|
||||
function hexToRGB(hex)
|
||||
{
|
||||
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)
|
||||
return null;
|
||||
|
||||
|
||||
+200
-222
@@ -20,75 +20,76 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPButton.j"
|
||||
import "CPColorPicker.j"
|
||||
import "CPCookie.j"
|
||||
import "CPKulerColorPicker.j"
|
||||
import "CPPanel.j"
|
||||
import "CPSliderColorPicker.j"
|
||||
import "CPView.j"
|
||||
|
||||
@import "CPButton.j"
|
||||
@import "CPCookie.j"
|
||||
@import "CPPanel.j"
|
||||
@import "CPView.j"
|
||||
|
||||
CPColorPanelColorDidChangeNotification = @"CPColorPanelColorDidChangeNotification";
|
||||
|
||||
var PREVIEW_HEIGHT = 20.0,
|
||||
TOOLBAR_HEIGHT = 32.0,
|
||||
SWATCH_HEIGHT = 14.0;
|
||||
|
||||
var SharedColorPanel = nil;
|
||||
SWATCH_HEIGHT = 14.0,
|
||||
ICON_WIDTH = 32.0,
|
||||
ICON_PADDING = 12.0;
|
||||
|
||||
var SharedColorPanel = nil,
|
||||
ColorPickerClasses = [];
|
||||
|
||||
/*
|
||||
A color wheel
|
||||
@global
|
||||
@group CPColorPanelMode
|
||||
*/
|
||||
CPWheelColorPickerMode = 1;
|
||||
/*
|
||||
Kuler color picker
|
||||
@global
|
||||
@group CPColorPanelMode
|
||||
*/
|
||||
CPKulerColorPickerMode = 2;
|
||||
/*
|
||||
Slider based picker
|
||||
@global
|
||||
@group CPColorPanelMode
|
||||
*/
|
||||
CPSliderColorPickerMode = 3;
|
||||
CPSliderColorPickerMode = 2;
|
||||
|
||||
CPColorPickerViewWidth = 265,
|
||||
CPColorPickerViewHeight = 370;
|
||||
|
||||
/*
|
||||
<objj>CPColorPanel</objj> provides a reusable panel that can be used
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPColorPanel
|
||||
|
||||
CPColorPanel provides a reusable panel that can be used
|
||||
displayed on screen to prompt the user for a color selection. To
|
||||
obtain the panel, call the <code>sharedColorPanel</code> method.
|
||||
obtain the panel, call the \c +sharedColorPanel method.
|
||||
*/
|
||||
@implementation CPColorPanel : CPPanel
|
||||
{
|
||||
_CPColorPanelToolbar _toolbar;
|
||||
_CPColorPanelSwatches _swatchView;
|
||||
_CPColorPanelPreview _previewView;
|
||||
|
||||
CPTextField _previewLabel;
|
||||
CPTextField _swatchLabel;
|
||||
|
||||
CPView _activeView;
|
||||
|
||||
CPColorPicker _activePicker;
|
||||
CPColorPicker _wheelPicker;
|
||||
CPColorPicker _kulerPicker;
|
||||
CPColorPicker _sliderPicker;
|
||||
|
||||
|
||||
CPSlider _opacitySlider;
|
||||
|
||||
CPArray _colorPickers;
|
||||
CPView _currentView;
|
||||
id _activePicker;
|
||||
|
||||
CPColor _color;
|
||||
|
||||
|
||||
id _target;
|
||||
SEL _action;
|
||||
|
||||
int _mode;
|
||||
SEL _action;
|
||||
|
||||
int _mode;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
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
|
||||
{
|
||||
ColorPickerClasses.push(aColorPickerSubclass);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns (and if necessary, creates) the shared color panel.
|
||||
*/
|
||||
+ (CPColorPanel)sharedColorPanel
|
||||
@@ -99,7 +100,7 @@ CPColorPickerViewHeight = 370;
|
||||
return SharedColorPanel;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the mode for the shared color panel.
|
||||
@param mode the mode to which the color panel will be set
|
||||
*/
|
||||
@@ -110,62 +111,66 @@ CPColorPickerViewHeight = 370;
|
||||
}
|
||||
|
||||
/*
|
||||
To obtain the color panel, use <code>sharedColorPanel</code>.
|
||||
To obtain the color panel, use \c +sharedColorPanel.
|
||||
@ignore
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
self = [super initWithContentRect:CGRectMake(500.0, 50.0, 218.0, 360.0)
|
||||
styleMask:(CPHUDBackgroundWindowMask | CPTitledWindowMask | CPClosableWindowMask | CPResizableWindowMask)];
|
||||
|
||||
self = [super initWithContentRect:CGRectMake(500.0, 50.0, 219.0, 370.0)
|
||||
styleMask:(CPTitledWindowMask | CPClosableWindowMask | CPResizableWindowMask)];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[[self contentView] setBackgroundColor:[CPColor colorWithWhite:0.95 alpha:1.0]];
|
||||
|
||||
[self setTitle:@"Color Panel"];
|
||||
[self setLevel:CPFloatingWindowLevel];
|
||||
|
||||
[self setFloatingPanel:YES];
|
||||
[self setBecomesKeyOnlyIfNeeded:YES];
|
||||
|
||||
[self setMinSize:CGSizeMake(218.0, 360.0)];
|
||||
[self setMaxSize:CGSizeMake(327.0, 540.0)];
|
||||
[self setMinSize:CGSizeMake(219.0, 342.0)];
|
||||
[self setMaxSize:CGSizeMake(323.0, 537.0)];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the color of the panel, and updates the picker. Also posts a <code>CPColorPanelDidChangeNotification</code>.
|
||||
/*!
|
||||
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)
|
||||
objj_msgSend(_target, _action, self);
|
||||
[CPApp sendAction:_action to:_target from:self];
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPColorPanelColorDidChangeNotification
|
||||
object:self];
|
||||
|
||||
[_activePicker setColor:_color];
|
||||
[_opacitySlider setFloatValue:[_color alphaComponent]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the selected color of the panel and optionally updates the picker.
|
||||
@param bool whether or not to update the picker
|
||||
@ignore
|
||||
*/
|
||||
-(void)setColor:(CPColor)aColor updatePicker:(BOOL)bool
|
||||
{
|
||||
[self setColor: aColor];
|
||||
- (void)setColor:(CPColor)aColor updatePicker:(BOOL)bool
|
||||
{
|
||||
[self setColor:aColor];
|
||||
|
||||
if (bool)
|
||||
[_activePicker setColor:_color];
|
||||
}
|
||||
|
||||
if(bool)
|
||||
[_activePicker setColor: _color];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the panel's currently selected color.
|
||||
*/
|
||||
- (CPColor)color
|
||||
@@ -173,7 +178,12 @@ CPColorPickerViewHeight = 370;
|
||||
return _color;
|
||||
}
|
||||
|
||||
/*
|
||||
- (float)opacity
|
||||
{
|
||||
return [_opacitySlider floatValue];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the target for the color panel. Messages are sent
|
||||
to the target when colors are selected in the panel.
|
||||
*/
|
||||
@@ -182,7 +192,7 @@ CPColorPickerViewHeight = 370;
|
||||
_target = aTarget;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the current target. The target receives messages
|
||||
when colors are selected in the panel.
|
||||
*/
|
||||
@@ -191,7 +201,7 @@ CPColorPickerViewHeight = 370;
|
||||
return _target;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the action that gets sent to the target.
|
||||
This action is sent whenever a color is selected in the panel.
|
||||
@param anAction the action that will be sent
|
||||
@@ -201,7 +211,7 @@ CPColorPickerViewHeight = 370;
|
||||
_action = anAction;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the current target action.
|
||||
*/
|
||||
- (selector)action
|
||||
@@ -209,36 +219,47 @@ CPColorPickerViewHeight = 370;
|
||||
return _action;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the mode (look) of the color panel.
|
||||
@param mode the mode in which to display the color panel
|
||||
*/
|
||||
- (void)setMode:(CPColorPanelMode)mode
|
||||
{
|
||||
if(mode == _mode)
|
||||
return;
|
||||
|
||||
var frame = CPRectCreateCopy([_currentView frame]);
|
||||
[_currentView removeFromSuperview];
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case CPWheelColorPickerMode: _activePicker = _wheelPicker; break;
|
||||
case CPKulerColorPickerMode: _activePicker = _kulerPicker; break;
|
||||
case CPSliderColorPickerMode: _activePicker = _sliderPicker; break;
|
||||
}
|
||||
|
||||
_currentView = [_activePicker provideNewView: NO];
|
||||
[_activePicker setColor: _color];
|
||||
|
||||
_mode = mode;
|
||||
|
||||
[_currentView setFrame: frame];
|
||||
[_currentView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)];
|
||||
[[self contentView] addSubview: _currentView];
|
||||
}
|
||||
|
||||
/*
|
||||
- (void)_setPicker:(id)sender
|
||||
{
|
||||
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),
|
||||
bounds = [[self contentView] bounds];
|
||||
|
||||
[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]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the color panel's current display mode.
|
||||
*/
|
||||
- (CPColorPanelMode)mode
|
||||
@@ -249,7 +270,6 @@ CPColorPickerViewHeight = 370;
|
||||
- (void)orderFront:(id)aSender
|
||||
{
|
||||
[self _loadContentsIfNecessary];
|
||||
|
||||
[super orderFront:aSender];
|
||||
}
|
||||
|
||||
@@ -258,178 +278,130 @@ CPColorPickerViewHeight = 370;
|
||||
{
|
||||
if (_toolbar)
|
||||
return;
|
||||
|
||||
|
||||
if (!_color)
|
||||
_color = [CPColor whiteColor];
|
||||
|
||||
_colorPickers = [];
|
||||
|
||||
var count = [ColorPickerClasses count];
|
||||
for (var i=0; i<count; i++)
|
||||
{
|
||||
var currentPickerClass = ColorPickerClasses[i],
|
||||
currentPicker = [[currentPickerClass alloc] initWithPickerMask:0 colorPanel:self];
|
||||
|
||||
_colorPickers.push(currentPicker);
|
||||
}
|
||||
|
||||
var contentView = [self contentView],
|
||||
bounds = [contentView bounds];
|
||||
|
||||
_toolbar = [[_CPColorPanelToolbar alloc] initWithFrame: CPRectMake(0, 0, CGRectGetWidth(bounds), TOOLBAR_HEIGHT)];
|
||||
|
||||
_toolbar = [[CPView alloc] initWithFrame:CGRectMake(0, 6, CGRectGetWidth(bounds), TOOLBAR_HEIGHT)];
|
||||
[_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++)
|
||||
{
|
||||
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 setTag:i];
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(_setPicker:)];
|
||||
[button setBordered:NO];
|
||||
[button setAutoresizingMask:CPViewMinXMargin|CPViewMaxXMargin];
|
||||
|
||||
[button setImage:image];
|
||||
[button setAlternateImage:highlightImage];
|
||||
|
||||
[_toolbar addSubview:button];
|
||||
|
||||
if (!buttonForLater)
|
||||
buttonForLater = button;
|
||||
}
|
||||
|
||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||
var previewBox = [[CPView alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + 10, CGRectGetWidth(bounds) - 86, PREVIEW_HEIGHT)];
|
||||
|
||||
|
||||
_previewView = [[_CPColorPanelPreview alloc] initWithFrame:CGRectInset([previewBox bounds], 2.0, 2.0)];
|
||||
|
||||
|
||||
[_previewView setColorPanel:self];
|
||||
[_previewView setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[previewBox setBackgroundColor:[CPColor grayColor]];
|
||||
|
||||
[previewBox setBackgroundColor:[CPColor colorWithWhite:0.8 alpha:1.0]];
|
||||
[previewBox setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
|
||||
[previewBox addSubview:_previewView];
|
||||
|
||||
_previewLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 14, 60, 15)];
|
||||
|
||||
var _previewLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 10, 60, 15)];
|
||||
[_previewLabel setStringValue: "Preview:"];
|
||||
[_previewLabel setTextColor:[CPColor whiteColor]];
|
||||
[_previewLabel setTextColor:[CPColor blackColor]];
|
||||
[_previewLabel setAlignment:CPRightTextAlignment];
|
||||
|
||||
// FIXME: http://280north.lighthouseapp.com/projects/13294-cappuccino/tickets/25-implement-cpbox
|
||||
var swatchBox = [[CPView alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + 10 + PREVIEW_HEIGHT + 5, CGRectGetWidth(bounds) - 86, SWATCH_HEIGHT + 2.0)];
|
||||
|
||||
[swatchBox setBackgroundColor:[CPColor grayColor]];
|
||||
[swatchBox setBackgroundColor:[CPColor colorWithWhite:0.8 alpha:1.0]];
|
||||
[swatchBox setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
|
||||
_swatchView = [[_CPColorPanelSwatches alloc] initWithFrame:CGRectInset([swatchBox bounds], 1.0, 1.0)];
|
||||
|
||||
|
||||
[_swatchView setColorPanel: self];
|
||||
[_swatchView setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
[swatchBox addSubview:_swatchView];
|
||||
|
||||
_swatchLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 8 + PREVIEW_HEIGHT + 5, 60, 15)];
|
||||
var _swatchLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + 8 + PREVIEW_HEIGHT + 6, 60, 15)];
|
||||
[_swatchLabel setStringValue: "Swatches:"];
|
||||
[_swatchLabel setTextColor:[CPColor whiteColor]];
|
||||
[_swatchLabel setTextColor:[CPColor blackColor]];
|
||||
[_swatchLabel setAlignment:CPRightTextAlignment];
|
||||
|
||||
_wheelPicker = [[CPColorWheelColorPicker alloc] initWithPickerMask: 1|2|3 colorPanel: self];
|
||||
_currentView = [_wheelPicker provideNewView: YES];
|
||||
|
||||
var height = (TOOLBAR_HEIGHT+10+PREVIEW_HEIGHT+5+SWATCH_HEIGHT+10);
|
||||
[_currentView setFrameSize: CPSizeMake(bounds.size.width - 10, bounds.size.height - height)];
|
||||
[_currentView setFrameOrigin: CPPointMake(5, TOOLBAR_HEIGHT+10+PREVIEW_HEIGHT+5+SWATCH_HEIGHT+10)];
|
||||
[_currentView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)];
|
||||
var opacityLabel = [[CPTextField alloc] initWithFrame: CPRectMake(10, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 35, 60, 20)];
|
||||
[opacityLabel setStringValue: "Opacity:"];
|
||||
[opacityLabel setTextColor:[CPColor blackColor]];
|
||||
[opacityLabel setAlignment:CPRightTextAlignment];
|
||||
|
||||
_kulerPicker = [[CPKulerColorPicker alloc] initWithPickerMask: 1|2|3 colorPanel: self];
|
||||
[_kulerPicker provideNewView: YES];
|
||||
|
||||
_sliderPicker = [[CPSliderColorPicker alloc] initWithPickerMask: 1|2|3 colorPanel: self];
|
||||
[_sliderPicker provideNewView: YES];
|
||||
|
||||
[contentView addSubview: _toolbar];
|
||||
[contentView addSubview: previewBox];
|
||||
[contentView addSubview: _previewLabel];
|
||||
[contentView addSubview: swatchBox];
|
||||
[contentView addSubview: _swatchLabel];
|
||||
[contentView addSubview: _currentView];
|
||||
_opacitySlider = [[CPSlider alloc] initWithFrame:CGRectMake(76, TOOLBAR_HEIGHT + PREVIEW_HEIGHT + 34, CGRectGetWidth(bounds) - 86, 20.0)];
|
||||
|
||||
[_opacitySlider setMinValue:0.0];
|
||||
[_opacitySlider setMaxValue:1.0];
|
||||
|
||||
[_opacitySlider setTarget:self];
|
||||
[_opacitySlider setAction:@selector(setOpacity:)];
|
||||
|
||||
[contentView addSubview:_toolbar];
|
||||
[contentView addSubview:previewBox];
|
||||
[contentView addSubview:_previewLabel];
|
||||
[contentView addSubview:swatchBox];
|
||||
[contentView addSubview:_swatchLabel];
|
||||
[contentView addSubview:opacityLabel];
|
||||
[contentView addSubview:_opacitySlider];
|
||||
|
||||
_target = nil;
|
||||
_action = nil;
|
||||
|
||||
_activePicker = _wheelPicker;
|
||||
|
||||
[self setColor:[CPColor whiteColor]];
|
||||
[_activePicker setColor:[CPColor whiteColor]];
|
||||
_activePicker = nil;
|
||||
|
||||
[_previewView setBackgroundColor: _color];
|
||||
|
||||
if (buttonForLater)
|
||||
[self _setPicker:buttonForLater];
|
||||
}
|
||||
|
||||
- (void)setOpacity:(id)sender
|
||||
{
|
||||
var components = [[self color] components],
|
||||
alpha = [sender floatValue];
|
||||
|
||||
[self setColor:[_color colorWithAlphaComponent:alpha] updatePicker:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var iconSize = 32,
|
||||
totalIcons = 3;
|
||||
|
||||
/* @ignore */
|
||||
@implementation _CPColorPanelToolbar : CPView
|
||||
{
|
||||
CPImage _wheelImage;
|
||||
CPImage _wheelAlternateImage;
|
||||
CPButton _wheelButton;
|
||||
|
||||
CPImage _sliderImage;
|
||||
CPImage _sliderAlternateImage;
|
||||
CPButton _sliderButton;
|
||||
|
||||
CPImage _kulerImage;
|
||||
CPImage _kulerAlternateImage;
|
||||
CPButton _kulerButton;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
var width = aFrame.size.width;
|
||||
var center = width / 2.0;
|
||||
var start = center - ((totalIcons * iconSize) + (totalIcons - 1) * 8.0) / 2.0;
|
||||
|
||||
_wheelButton = [[CPButton alloc] initWithFrame:CPRectMake(start, 0, iconSize, iconSize)];
|
||||
|
||||
start += iconSize + 8;
|
||||
|
||||
var path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"wheel_button.png"];
|
||||
_wheelImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)];
|
||||
|
||||
path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"wheel_button_h.png"];
|
||||
_wheelAlternateImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)];
|
||||
|
||||
[_wheelButton setBordered:NO];
|
||||
[_wheelButton setImage: _wheelImage];
|
||||
[_wheelButton setAlternateImage: _wheelAlternateImage];
|
||||
[_wheelButton setTarget: self];
|
||||
[_wheelButton setAction: @selector(setMode:)];
|
||||
[_wheelButton setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
|
||||
|
||||
[self addSubview: _wheelButton];
|
||||
|
||||
_sliderButton = [[CPButton alloc] initWithFrame:CPRectMake(start, 0, iconSize, iconSize)];
|
||||
|
||||
start += iconSize + 8;
|
||||
|
||||
path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"slider_button.png"];
|
||||
_sliderImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)];
|
||||
|
||||
path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"slider_button_h.png"];
|
||||
_sliderAlternateImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)];
|
||||
|
||||
[_sliderButton setBordered:NO];
|
||||
[_sliderButton setImage: _sliderImage];
|
||||
[_sliderButton setAlternateImage: _sliderAlternateImage];
|
||||
[_sliderButton setTarget: self];
|
||||
[_sliderButton setAction: @selector(setMode:)];
|
||||
[_sliderButton setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
|
||||
|
||||
[self addSubview: _sliderButton];
|
||||
|
||||
_kulerButton = [[CPButton alloc] initWithFrame:CPRectMake(start, 0, iconSize, iconSize)];
|
||||
start += iconSize + 8;
|
||||
|
||||
path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"kuler_button.png"];
|
||||
_kulerImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)];
|
||||
|
||||
path = [[CPBundle bundleForClass: _CPColorPanelToolbar] pathForResource:@"kuler_button_h.png"];
|
||||
_kulerAlternateImage = [[CPImage alloc] initWithContentsOfFile:path size: CPSizeMake(iconSize, iconSize)];
|
||||
|
||||
[_kulerButton setBordered:NO];
|
||||
[_kulerButton setImage: _kulerImage];
|
||||
[_kulerButton setAlternateImage: _kulerAlternateImage];
|
||||
[_kulerButton setTarget: self];
|
||||
[_kulerButton setAction: @selector(setMode:)];
|
||||
[_kulerButton setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin];
|
||||
|
||||
[self addSubview: _kulerButton];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setMode:(id)sender
|
||||
{
|
||||
if(sender == _kulerButton)
|
||||
[[CPColorPanel sharedColorPanel] setMode: CPKulerColorPickerMode];
|
||||
else if(sender == _wheelButton)
|
||||
[[CPColorPanel sharedColorPanel] setMode: CPWheelColorPickerMode];
|
||||
else
|
||||
[[CPColorPanel sharedColorPanel] setMode: CPSliderColorPickerMode];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
CPColorDragType = "CPColorDragType";
|
||||
var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
@@ -519,7 +491,7 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
var future = new Date();
|
||||
future.setYear(2019);
|
||||
|
||||
[_swatchCookie setValue: CPJSObjectCreateJSON(result) expires:future domain: nil];
|
||||
[_swatchCookie setValue: JSON.stringify(result) expires:future domain: nil];
|
||||
}
|
||||
|
||||
- (void)setColorPanel:(CPColorPanel)panel
|
||||
@@ -682,3 +654,9 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@import "CPColorPicker.j"
|
||||
@import "CPSliderColorPicker.j"
|
||||
|
||||
[CPColorPanel provideColorPickerClass:CPColorWheelColorPicker];
|
||||
[CPColorPanel provideColorPickerClass:CPSliderColorPicker];
|
||||
|
||||
+110
-99
@@ -20,13 +20,14 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/Foundation.j>
|
||||
import <AppKit/CPView.j>
|
||||
import <AppKit/CPImage.j>
|
||||
import <AppKit/CPImageView.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import "CPColorPanel.j"
|
||||
|
||||
/*
|
||||
<objj>CPColorPicker</objj> is an abstract superclass for all color picker subclasses. If you want a particular color picker, use <objj>CPColorPanel</objj>'s <code>setPickerMode:</code> method. The simplest way to implement your own color picker is to create a subclass of CPColorPicker.
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPColorPicker
|
||||
|
||||
CPColorPicker is an abstract superclass for all color picker subclasses. If you want a particular color picker, use CPColorPanel's \c +setPickerMode: method. The simplest way to implement your own color picker is to create a subclass of CPColorPicker.
|
||||
*/
|
||||
@implementation CPColorPicker : CPObject
|
||||
{
|
||||
@@ -34,7 +35,7 @@ import <AppKit/CPImageView.j>
|
||||
int _mask;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the color picker.
|
||||
@param aMask a unique unsigned int identifying your color picker
|
||||
@param aPanel the color panel that owns this picker
|
||||
@@ -42,14 +43,14 @@ import <AppKit/CPImageView.j>
|
||||
- (id)initWithPickerMask:(int)aMask colorPanel:(CPColorPanel)aPanel
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
_panel = aPanel;
|
||||
_mask = aMask;
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the color panel that owns this picker
|
||||
*/
|
||||
- (CPColorPanel)colorPanel
|
||||
@@ -59,7 +60,7 @@ import <AppKit/CPImageView.j>
|
||||
|
||||
/*
|
||||
FIXME Not implemented.
|
||||
@return <code>nil</code>
|
||||
@return \c nil
|
||||
@ignore
|
||||
*/
|
||||
- (CPImage)provideNewButtonImage
|
||||
@@ -67,7 +68,7 @@ import <AppKit/CPImageView.j>
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the color picker's mode.
|
||||
@param mode the color panel mode
|
||||
*/
|
||||
@@ -76,7 +77,7 @@ import <AppKit/CPImageView.j>
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the picker's color.
|
||||
@param aColor the new color for the picker
|
||||
*/
|
||||
@@ -93,52 +94,44 @@ import <AppKit/CPImageView.j>
|
||||
*/
|
||||
@implementation CPColorWheelColorPicker : CPColorPicker
|
||||
{
|
||||
CPView _pickerView;
|
||||
CPView _pickerView;
|
||||
CPView _brightnessSlider;
|
||||
DOMElement _brightnessBarImage;
|
||||
__CPColorWheel _hueSaturationView;
|
||||
|
||||
CPColor _cachedColor;
|
||||
}
|
||||
|
||||
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
|
||||
{
|
||||
return [super initWithPickerMask:mask colorPanel: owningColorPanel];
|
||||
}
|
||||
|
||||
|
||||
-(id)initView
|
||||
{
|
||||
aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight);
|
||||
|
||||
_pickerView = [[CPView alloc] initWithFrame:aFrame];
|
||||
|
||||
var path = [[CPBundle bundleForClass: CPColorPicker] pathForResource:@"brightness_bar.png"];
|
||||
|
||||
_brightnessBarImage = new Image();
|
||||
_brightnessBarImage.src = path;
|
||||
_brightnessBarImage.style.width = "100%";
|
||||
_brightnessBarImage.style.height = "100%";
|
||||
_brightnessBarImage.style.position = "absolute";
|
||||
_brightnessBarImage.style.top = "0px";
|
||||
_brightnessBarImage.style.left = "0px";
|
||||
[_pickerView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||
|
||||
var brightnessBarView = [[CPView alloc] initWithFrame: CPRectMake(0, (aFrame.size.height - 34), aFrame.size.width, 15)];
|
||||
[brightnessBarView setAutoresizingMask: (CPViewWidthSizable | CPViewMinYMargin)];
|
||||
brightnessBarView._DOMElement.appendChild(_brightnessBarImage);
|
||||
|
||||
_brightnessSlider = [[CPSlider alloc] initWithFrame: CPRectMake(0, aFrame.size.height - 22, aFrame.size.width, 12)];
|
||||
[_brightnessSlider setMaxValue: 100.0];
|
||||
[_brightnessSlider setMinValue: 0.0];
|
||||
[_brightnessSlider setValue: 100.0];
|
||||
_brightnessSlider = [[CPSlider alloc] initWithFrame:CGRectMake(0, (aFrame.size.height - 34), aFrame.size.width, 15)];
|
||||
|
||||
[_brightnessSlider setTarget: self];
|
||||
[_brightnessSlider setAction: @selector(brightnessSliderDidChange:)];
|
||||
[_brightnessSlider setAutoresizingMask: (CPViewWidthSizable | CPViewMinYMargin)];
|
||||
[_brightnessSlider setValue:15.0 forThemeAttribute:@"track-width"];
|
||||
[_brightnessSlider setValue:[CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPColorPicker class]] pathForResource:@"brightness_bar.png"]]] forThemeAttribute:@"track-color"];
|
||||
|
||||
[_brightnessSlider setMinValue:0.0];
|
||||
[_brightnessSlider setMaxValue:100.0];
|
||||
[_brightnessSlider setFloatValue:100.0];
|
||||
|
||||
[_brightnessSlider setTarget:self];
|
||||
[_brightnessSlider setAction:@selector(brightnessSliderDidChange:)];
|
||||
[_brightnessSlider setAutoresizingMask:CPViewWidthSizable | CPViewMinYMargin];
|
||||
|
||||
_hueSaturationView = [[__CPColorWheel alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, aFrame.size.height - 38)];
|
||||
[_hueSaturationView setDelegate: self];
|
||||
[_hueSaturationView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)];
|
||||
|
||||
[_pickerView addSubview: brightnessBarView];
|
||||
[_pickerView addSubview: _hueSaturationView];
|
||||
[_pickerView addSubview: _brightnessSlider];
|
||||
[_pickerView addSubview:_hueSaturationView];
|
||||
[_pickerView addSubview:_brightnessSlider];
|
||||
}
|
||||
|
||||
-(void)brightnessSliderDidChange:(id)sender
|
||||
@@ -155,41 +148,59 @@ import <AppKit/CPImageView.j>
|
||||
{
|
||||
var hue = [_hueSaturationView angle],
|
||||
saturation = [_hueSaturationView distance],
|
||||
brightness = [_brightnessSlider value];
|
||||
brightness = [_brightnessSlider floatValue];
|
||||
|
||||
[_hueSaturationView setWheelBrightness: brightness / 100.0];
|
||||
_brightnessBarImage.style.backgroundColor = "#"+[[CPColor colorWithHue: hue saturation: saturation brightness: 100] hexString];
|
||||
[_hueSaturationView setWheelBrightness:brightness / 100.0];
|
||||
[_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hue saturation:saturation brightness:100]];
|
||||
|
||||
[[self colorPanel] setColor:[CPColor colorWithHue: hue saturation: saturation brightness: brightness]];
|
||||
var colorPanel = [self colorPanel],
|
||||
opacity = [colorPanel opacity];
|
||||
|
||||
_cachedColor = [CPColor colorWithHue:hue saturation:saturation brightness:brightness alpha:opacity];
|
||||
|
||||
[[self colorPanel] setColor:_cachedColor];
|
||||
}
|
||||
|
||||
- (BOOL)supportsMode:(int)mode
|
||||
|
||||
- (BOOL)supportsMode:(int)mode
|
||||
{
|
||||
return (mode == CPWheelColorPickerMode) ? YES : NO;
|
||||
}
|
||||
|
||||
- (int)currentMode
|
||||
|
||||
- (int)currentMode
|
||||
{
|
||||
return CPWheelColorPickerMode;
|
||||
}
|
||||
|
||||
- (CPView)provideNewView:(BOOL)initialRequest
|
||||
|
||||
- (CPView)provideNewView:(BOOL)initialRequest
|
||||
{
|
||||
if (initialRequest)
|
||||
if (initialRequest)
|
||||
[self initView];
|
||||
|
||||
|
||||
return _pickerView;
|
||||
}
|
||||
|
||||
- (void)setColor:(CPColor)newColor
|
||||
{
|
||||
var hsb = [newColor hsbComponents];
|
||||
|
||||
[_hueSaturationView setPositionToColor: newColor];
|
||||
[_brightnessSlider setValue: hsb[2]];
|
||||
[_hueSaturationView setWheelBrightness: hsb[2] / 100.0];
|
||||
|
||||
_brightnessBarImage.style.backgroundColor = "#"+[[CPColor colorWithHue: hsb[0] saturation: hsb[1] brightness: 100] hexString];
|
||||
- (void)setColor:(CPColor)newColor
|
||||
{
|
||||
if ([newColor isEqual:_cachedColor])
|
||||
return;
|
||||
|
||||
var hsb = [newColor hsbComponents];
|
||||
|
||||
[_hueSaturationView setPositionToColor:newColor];
|
||||
[_brightnessSlider setFloatValue:hsb[2]];
|
||||
[_hueSaturationView setWheelBrightness:hsb[2] / 100.0];
|
||||
|
||||
[_brightnessSlider setBackgroundColor:[CPColor colorWithHue:hsb[0] saturation:hsb[1] brightness:100]];
|
||||
}
|
||||
|
||||
- (CPImage)provideNewButtonImage
|
||||
{
|
||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:CPColorPicker] pathForResource:"wheel_button.png"] size:CGSizeMake(32, 32)];
|
||||
}
|
||||
|
||||
- (CPImage)provideNewAlternateButtonImage
|
||||
{
|
||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:CPColorPicker] pathForResource:"wheel_button_h.png"] size:CGSizeMake(32, 32)];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -199,28 +210,28 @@ import <AppKit/CPImageView.j>
|
||||
{
|
||||
DOMElement _wheelImage;
|
||||
DOMElement _blackWheelImage;
|
||||
|
||||
|
||||
CPView _crosshair;
|
||||
|
||||
|
||||
id _delegate;
|
||||
|
||||
|
||||
float _angle;
|
||||
float _distance;
|
||||
|
||||
|
||||
float _radius;
|
||||
}
|
||||
|
||||
-(id)initWithFrame:(CPRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame: aFrame];
|
||||
|
||||
var path = [[CPBundle bundleForClass: CPColorPicker] pathForResource:@"wheel.png"];
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
var path = [[CPBundle bundleForClass:CPColorPicker] pathForResource:@"wheel.png"];
|
||||
|
||||
_wheelImage = new Image();
|
||||
_wheelImage.src = path;
|
||||
_wheelImage.style.position = "absolute";
|
||||
|
||||
path = [[CPBundle bundleForClass: CPColorPicker] pathForResource:@"wheel_black.png"];
|
||||
|
||||
path = [[CPBundle bundleForClass:CPColorPicker] pathForResource:@"wheel_black.png"];
|
||||
|
||||
_blackWheelImage = new Image();
|
||||
_blackWheelImage.src = path;
|
||||
@@ -230,19 +241,19 @@ import <AppKit/CPImageView.j>
|
||||
|
||||
_DOMElement.appendChild(_wheelImage);
|
||||
_DOMElement.appendChild(_blackWheelImage);
|
||||
|
||||
[self setWheelSize: aFrame.size];
|
||||
|
||||
[self setWheelSize:aFrame.size];
|
||||
|
||||
_crosshair = [[CPView alloc] initWithFrame:CPRectMake(_radius - 2, _radius - 2, 4, 4)];
|
||||
[_crosshair setBackgroundColor:[CPColor blackColor]];
|
||||
|
||||
|
||||
var view = [[CPView alloc] initWithFrame:CGRectInset([_crosshair bounds], 1.0, 1.0)];
|
||||
[view setBackgroundColor:[CPColor whiteColor]];
|
||||
|
||||
|
||||
[_crosshair addSubview:view];
|
||||
|
||||
[self addSubview: _crosshair];
|
||||
|
||||
|
||||
[self addSubview:_crosshair];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -254,8 +265,8 @@ import <AppKit/CPImageView.j>
|
||||
|
||||
-(void)setFrameSize:(CPSize)aSize
|
||||
{
|
||||
[super setFrameSize: aSize];
|
||||
[self setWheelSize: aSize];
|
||||
[super setFrameSize:aSize];
|
||||
[self setWheelSize:aSize];
|
||||
}
|
||||
|
||||
-(void)setWheelSize:(CPSize)aSize
|
||||
@@ -277,8 +288,8 @@ import <AppKit/CPImageView.j>
|
||||
_wheelImage.style.left = (aSize.width - min) / 2.0 + "px";
|
||||
|
||||
_radius = min / 2.0;
|
||||
|
||||
[self setAngle: [self degreesToRadians: _angle] distance: (_distance / 100.0) * _radius];
|
||||
|
||||
[self setAngle:[self degreesToRadians:_angle] distance:(_distance / 100.0) * _radius];
|
||||
}
|
||||
|
||||
-(void)setDelegate:(id)aDelegate
|
||||
@@ -303,29 +314,28 @@ import <AppKit/CPImageView.j>
|
||||
|
||||
-(void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
[self reposition: anEvent];
|
||||
[self reposition:anEvent];
|
||||
}
|
||||
|
||||
-(void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
[self reposition: anEvent];
|
||||
[self reposition:anEvent];
|
||||
}
|
||||
|
||||
-(void)reposition:(CPEvent)anEvent
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
location = [self convertPoint: [anEvent locationInWindow] fromView: nil];
|
||||
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);
|
||||
|
||||
[self setAngle: angle distance: distance];
|
||||
|
||||
if(_delegate)
|
||||
[_delegate colorWheelDidChange: self];
|
||||
[self setAngle:angle distance:distance];
|
||||
|
||||
[_delegate colorWheelDidChange:self];
|
||||
}
|
||||
|
||||
-(void)setAngle:(int)angle distance:(float)distance
|
||||
@@ -333,10 +343,10 @@ import <AppKit/CPImageView.j>
|
||||
var bounds = [self bounds];
|
||||
var midX = CGRectGetMidX(bounds);
|
||||
var midY = CGRectGetMidY(bounds);
|
||||
|
||||
_angle = [self radiansToDegrees: angle];
|
||||
_distance = (distance / _radius) * 100.0;
|
||||
|
||||
|
||||
_angle = [self radiansToDegrees:angle];
|
||||
_distance = (distance / _radius) * 100.0;
|
||||
|
||||
[_crosshair setFrameOrigin:CPPointMake(COS(angle) * distance + midX - 2.0, SIN(angle) * distance + midY - 2.0)];
|
||||
}
|
||||
|
||||
@@ -344,11 +354,11 @@ import <AppKit/CPImageView.j>
|
||||
{
|
||||
var hsb = [aColor hsbComponents],
|
||||
bounds = [self bounds];
|
||||
|
||||
var angle = [self degreesToRadians: hsb[0]],
|
||||
|
||||
var angle = [self degreesToRadians:hsb[0]],
|
||||
distance = (hsb[1] / 100.0) * _radius;
|
||||
|
||||
[self setAngle: angle distance: distance];
|
||||
|
||||
[self setAngle:angle distance:distance];
|
||||
}
|
||||
|
||||
-(int)radiansToDegrees:(float)radians
|
||||
@@ -361,4 +371,5 @@ import <AppKit/CPImageView.j>
|
||||
return -(((degrees - 360) / 180) * PI);
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
|
||||
+113
-32
@@ -20,23 +20,27 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPString.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
import "CPView.j"
|
||||
import "CPColor.j"
|
||||
import "CPColorPanel.j"
|
||||
@import "CPView.j"
|
||||
@import "CPColor.j"
|
||||
@import "CPColorPanel.j"
|
||||
|
||||
|
||||
var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiveNotification";
|
||||
|
||||
/*
|
||||
<objj>CPColorWell</objj> is a <objj>CPControl</objj> for selecting and displaying a single color value. An example of a <objj>CPColorWell</objj> object (or simply color well) is found in <objj>CPColorPanel</objj>, which uses a color well to display the current color selection.</p>
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPColorWell
|
||||
|
||||
<p>An application can have one or more active <objj>CPColorWell</objj>s. You can activate multiple <objj>CPColorWell</objj>s by invoking the <code>activate:</code> method with <code>NO</code> as its argument. When a mouse-down event occurs on an <objj>CPColorWell</objj>'s border, it becomes the only active color well. When a color well becomes active, it brings up the color panel also.
|
||||
CPColorWell is a CPControl for selecting and displaying a single color value. An example of a CPColorWell object (or simply color well) is found in CPColorPanel, which uses a color well to display the current color selection.</p>
|
||||
|
||||
<p>An application can have one or more active CPColorWells. You can activate multiple CPColorWells by invoking the \c -activate: method with \c NO as its argument. When a mouse-down event occurs on an CPColorWell's border, it becomes the only active color well. When a color well becomes active, it brings up the color panel also.
|
||||
*/
|
||||
@implementation CPColorWell : CPControl
|
||||
{
|
||||
BOOL _active;
|
||||
BOOL _bordered;
|
||||
|
||||
CPColor _color;
|
||||
CPView _wellView;
|
||||
@@ -49,33 +53,59 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
if (self)
|
||||
{
|
||||
_active = NO;
|
||||
|
||||
_bordered = YES;
|
||||
_color = [CPColor whiteColor];
|
||||
|
||||
[self drawBezelWithHighlight:NO];
|
||||
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(colorWellDidBecomeExclusive:)
|
||||
name:_CPColorWellDidBecomeExclusiveNotification
|
||||
object:nil];
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(colorPanelWillClose:)
|
||||
name:CPWindowWillCloseNotification
|
||||
object:[CPColorPanel sharedColorPanel]];
|
||||
[self _registerForNotifications];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_registerForNotifications
|
||||
{
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(colorWellDidBecomeExclusive:)
|
||||
name:_CPColorWellDidBecomeExclusiveNotification
|
||||
object:nil];
|
||||
|
||||
[defaultCenter
|
||||
addObserver:self
|
||||
selector:@selector(colorPanelWillClose:)
|
||||
name:CPWindowWillCloseNotification
|
||||
object:[CPColorPanel sharedColorPanel]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns whether the color well is bordered
|
||||
*/
|
||||
- (BOOL)isBordered
|
||||
{
|
||||
return _bordered;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the color well's current color.
|
||||
*/
|
||||
- (void)setBordered:(BOOL)bordered
|
||||
{
|
||||
if (_bordered == bordered)
|
||||
return;
|
||||
|
||||
_bordered = bordered;
|
||||
|
||||
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||
}
|
||||
|
||||
// Managing Color From Color Wells
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the color well's current color.
|
||||
*/
|
||||
- (CPColor)color
|
||||
@@ -83,7 +113,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
return _color;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the color well's current color.
|
||||
*/
|
||||
- (void)setColor:(CPColor)aColor
|
||||
@@ -96,8 +126,8 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||
}
|
||||
|
||||
/*
|
||||
Changes the color of the well to that of <code>aSender</code>.
|
||||
/*!
|
||||
Changes the color of the well to that of \c aSender.
|
||||
@param aSender the object from which to retrieve the color
|
||||
*/
|
||||
- (void)takeColorFrom:(id)aSender
|
||||
@@ -106,9 +136,9 @@ 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.
|
||||
If exclusive is <code>YES</code>, deactivates any other <objj>CPColorWell</objj>s. <code>NO</code>, keeps them active.
|
||||
If exclusive is \c YES, deactivates any other CPColorWells. \c NO, keeps them active.
|
||||
@param shouldBeExclusive whether other color wells should be deactivated.
|
||||
*/
|
||||
- (void)activate:(BOOL)shouldBeExclusive
|
||||
@@ -132,7 +162,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
object:[CPColorPanel sharedColorPanel]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Deactivates the color well.
|
||||
*/
|
||||
- (void)deactivate
|
||||
@@ -148,8 +178,8 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
object:[CPColorPanel sharedColorPanel]];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the color well is active.
|
||||
/*!
|
||||
Returns \c YES if the color well is active.
|
||||
*/
|
||||
- (BOOL)isActive
|
||||
{
|
||||
@@ -162,7 +192,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Draws the colored area inside the color well without borders.
|
||||
@param aRect the location at which to draw
|
||||
*/
|
||||
@@ -171,6 +201,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
if (!_wellView)
|
||||
{
|
||||
_wellView = [[CPView alloc] initWithFrame:aRect];
|
||||
[_wellView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
[self addSubview:_wellView];
|
||||
}
|
||||
@@ -220,8 +251,58 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
var colorPanel = [CPColorPanel sharedColorPanel];
|
||||
|
||||
[colorPanel setColor:_color];
|
||||
|
||||
[colorPanel orderFront:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPColorWellColorKey = "CPColorWellColorKey",
|
||||
CPColorWellBorderedKey = "CPColorWellBorderedKey";
|
||||
|
||||
@implementation CPColorWell (CPCoding)
|
||||
|
||||
/*!
|
||||
Initializes the color well by unarchiving data from \c aCoder.
|
||||
@param aCoder the coder containing the archived CPColorWell.
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_active = NO;
|
||||
_bordered = [aCoder decodeObjectForKey:CPColorWellBorderedKey];
|
||||
_color = [aCoder decodeObjectForKey:CPColorWellColorKey];
|
||||
|
||||
[self drawBezelWithHighlight:NO];
|
||||
[self drawWellInside:CGRectInset([self bounds], 3.0, 3.0)];
|
||||
|
||||
[self _registerForNotifications];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Archives this button into the provided coder.
|
||||
@param aCoder the coder to which the color well's instance data will be written.
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
// 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];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+63
-19
@@ -20,7 +20,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPEvent.j"
|
||||
@import "CPEvent.j"
|
||||
@import "CPPlatform.j"
|
||||
|
||||
// Browser Engines
|
||||
CPUnknownBrowserEngine = 0;
|
||||
@@ -35,25 +36,40 @@ CPCSSRGBAFeature = 1 << 5;
|
||||
|
||||
CPHTMLCanvasFeature = 1 << 6;
|
||||
CPHTMLContentEditableFeature = 1 << 7;
|
||||
CPHTMLDragAndDropFeature = 1 << 8;
|
||||
|
||||
CPJavascriptInnerTextFeature = 1 << 8;
|
||||
CPJavascriptTextContentFeature = 1 << 9;
|
||||
CPJavascriptClipboardEventsFeature = 1 << 10;
|
||||
CPJavascriptClipboardAccessFeature = 1 << 11;
|
||||
CPJavaScriptCanvasDrawFeature = 1 << 12;
|
||||
CPJavascriptInnerTextFeature = 1 << 9;
|
||||
CPJavascriptTextContentFeature = 1 << 10;
|
||||
CPJavascriptClipboardEventsFeature = 1 << 11;
|
||||
CPJavascriptClipboardAccessFeature = 1 << 12;
|
||||
CPJavaScriptCanvasDrawFeature = 1 << 13;
|
||||
CPJavaScriptCanvasTransformFeature = 1 << 14;
|
||||
|
||||
CPVMLFeature = 1 << 14;
|
||||
CPVMLFeature = 1 << 15;
|
||||
|
||||
CPJavascriptRemedialKeySupport = 1 << 15;
|
||||
CPJavascriptRemedialKeySupport = 1 << 16;
|
||||
CPJavaScriptShadowFeature = 1 << 20;
|
||||
|
||||
CPJavaScriptNegativeMouseWheelValues = 1 << 22;
|
||||
CPJavaScriptMouseWheelValues_8_15 = 1 << 23
|
||||
|
||||
var USER_AGENT = "";
|
||||
|
||||
CPOpacityRequiresFilterFeature = 1 << 24;
|
||||
|
||||
//Internet explorer does not allow dynamically changing the type of an input element
|
||||
CPInputTypeCanBeChangedFeature = 1 << 25;
|
||||
CPHTML5DragAndDropSourceYOffBy1 = 1 << 26;
|
||||
|
||||
|
||||
|
||||
|
||||
var USER_AGENT = "",
|
||||
PLATFORM_ENGINE = CPUnknownBrowserEngine,
|
||||
PLATFORM_FEATURES = 0;
|
||||
|
||||
// default these features to true
|
||||
|
||||
PLATFORM_FEATURES |= CPInputTypeCanBeChangedFeature;
|
||||
|
||||
if (typeof window != "undfined" && typeof window.navigator != "undefined")
|
||||
USER_AGENT = window.navigator.userAgent;
|
||||
|
||||
@@ -74,6 +90,10 @@ else if (window.attachEvent) // Must follow Opera check.
|
||||
PLATFORM_FEATURES |= CPVMLFeature;
|
||||
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||
PLATFORM_FEATURES |= CPJavaScriptShadowFeature;
|
||||
|
||||
PLATFORM_FEATURES |= CPOpacityRequiresFilterFeature;
|
||||
|
||||
PLATFORM_FEATURES &= ~CPInputTypeCanBeChangedFeature;
|
||||
}
|
||||
|
||||
// WebKit
|
||||
@@ -84,45 +104,69 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
// Features we can only be sure of with WebKit (no known independent tests)
|
||||
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
||||
PLATFORM_FEATURES |= CPHTMLContentEditableFeature;
|
||||
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),
|
||||
version = parseFloat(USER_AGENT.substring(versionStart, versionEnd), 10);
|
||||
versionString = USER_AGENT.substring(versionStart, versionEnd),
|
||||
versionDivision = versionString.indexOf('.'),
|
||||
majorVersion = parseInt(versionString.substring(0, versionDivision)),
|
||||
minorVersion = parseInt(versionString.substr(versionDivision + 1));
|
||||
|
||||
if(USER_AGENT.indexOf("Plainview") == -1 && version >= 525.14 || USER_AGENT.indexOf("Chrome") != -1)
|
||||
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:
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=21548
|
||||
if (![CPPlatform isBrowser])
|
||||
PLATFORM_FEATURES |= CPJavascriptRemedialKeySupport;
|
||||
|
||||
if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6))
|
||||
PLATFORM_FEATURES |= CPHTML5DragAndDropSourceYOffBy1;
|
||||
}
|
||||
|
||||
// KHTML
|
||||
else if (USER_AGENT.indexOf('KHTML') != -1) // Must follow WebKit check.
|
||||
else if (USER_AGENT.indexOf("KHTML") != -1) // Must follow WebKit check.
|
||||
{
|
||||
PLATFORM_ENGINE = CPKHTMLBrowserEngine;
|
||||
}
|
||||
|
||||
// Gecko
|
||||
else if (USER_AGENT.indexOf('Gecko') != -1) // Must follow KHTML 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));
|
||||
version = (index === -1) ? 2.0 : parseFloat(USER_AGENT.substring(index + "Firefox".length + 1));
|
||||
|
||||
if (version >= 3.0)
|
||||
PLATFORM_FEATURES |= CPCSSRGBAFeature;
|
||||
|
||||
if (version < 3.0)
|
||||
PLATFORM_FEATURES |= CPJavaScriptMouseWheelValues_8_15;
|
||||
}
|
||||
|
||||
// Feature Specific Checks
|
||||
if (typeof document != "undefined")
|
||||
{
|
||||
var canvasElement = document.createElement("canvas");
|
||||
// Detect Canvas Support
|
||||
if (document.createElement("canvas").getContext)
|
||||
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)
|
||||
@@ -145,7 +189,7 @@ function CPBrowserIsEngine(anEngine)
|
||||
if (USER_AGENT.indexOf("Mac") != -1)
|
||||
{
|
||||
CPPlatformActionKeyMask = CPCommandKeyMask;
|
||||
|
||||
|
||||
CPUndoKeyEquivalent = @"Z";
|
||||
CPRedoKeyEquivalent = @"Z";
|
||||
|
||||
@@ -155,10 +199,10 @@ if (USER_AGENT.indexOf("Mac") != -1)
|
||||
else
|
||||
{
|
||||
CPPlatformActionKeyMask = CPControlKeyMask;
|
||||
|
||||
|
||||
CPUndoKeyEquivalent = @"Z";
|
||||
CPRedoKeyEquivalent = @"Y";
|
||||
|
||||
|
||||
CPUndoKeyEquivalentModifierMask = CPControlKeyMask;
|
||||
CPRedoKeyEquivalentModifierMask = CPControlKeyMask;
|
||||
}
|
||||
|
||||
+412
-282
@@ -20,10 +20,11 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPFont.j"
|
||||
import "CPShadow.j"
|
||||
import "CPView.j"
|
||||
@import "CPFont.j"
|
||||
@import "CPShadow.j"
|
||||
@import "CPView.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
#include "Platform/Platform.h"
|
||||
|
||||
/*
|
||||
@@ -68,36 +69,66 @@ CPSmallControlSize = 1;
|
||||
*/
|
||||
CPMiniControlSize = 2;
|
||||
|
||||
CPControlNormalBackgroundColor = @"CPControlNormalBackgroundColor";
|
||||
CPControlSelectedBackgroundColor = @"CPControlSelectedBackgroundColor";
|
||||
CPControlHighlightedBackgroundColor = @"CPControlHighlightedBackgroundColor";
|
||||
CPControlDisabledBackgroundColor = @"CPControlDisabledBackgroundColor";
|
||||
CPControlNormalBackgroundColor = "CPControlNormalBackgroundColor";
|
||||
CPControlSelectedBackgroundColor = "CPControlSelectedBackgroundColor";
|
||||
CPControlHighlightedBackgroundColor = "CPControlHighlightedBackgroundColor";
|
||||
CPControlDisabledBackgroundColor = "CPControlDisabledBackgroundColor";
|
||||
|
||||
CPControlTextDidBeginEditingNotification = "CPControlTextDidBeginEditingNotification";
|
||||
CPControlTextDidChangeNotification = "CPControlTextDidChangeNotification";
|
||||
CPControlTextDidEndEditingNotification = "CPControlTextDidEndEditingNotification";
|
||||
|
||||
var CPControlBlackColor = [CPColor blackColor];
|
||||
|
||||
/*
|
||||
<objj>CPControl</objj> is an abstract superclass used to implement user interface elements. As a subclass of <objj>CPView</objj> and <objj>CPResponder</objj> it has the ability to handle screen drawing and handling user input.
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPControl
|
||||
|
||||
CPControl is an abstract superclass used to implement user interface elements. As a subclass of CPView and CPResponder it has the ability to handle screen drawing and handling user input.
|
||||
*/
|
||||
@implementation CPControl : CPView
|
||||
{
|
||||
id _value;
|
||||
id _value;
|
||||
|
||||
// Target-Action Support
|
||||
id _target;
|
||||
SEL _action;
|
||||
int _sendActionOn;
|
||||
BOOL _sendsActionOnEndEditing @accessors(property=sendsActionOnEndEditing);
|
||||
|
||||
BOOL _isEnabled;
|
||||
|
||||
int _alignment;
|
||||
CPFont _font;
|
||||
CPColor _textColor;
|
||||
CPShadow _textShadow;
|
||||
|
||||
id _target;
|
||||
SEL _action;
|
||||
int _sendActionOn;
|
||||
|
||||
CPColor _backgroundColor;
|
||||
CPColor _highlightedBackgroundColor;
|
||||
|
||||
CPDictionary _backgroundColors;
|
||||
CPString _currentBackgroundColorName;
|
||||
// Mouse Tracking Support
|
||||
BOOL _continuousTracking;
|
||||
BOOL _trackingWasWithinFrame;
|
||||
unsigned _trackingMouseDownFlags;
|
||||
CGPoint _previousTrackingLocation;
|
||||
|
||||
CPString _toolTip;
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[CPLeftTextAlignment,
|
||||
CPTopVerticalTextAlignment,
|
||||
CPLineBreakByClipping,
|
||||
[CPColor blackColor],
|
||||
[CPFont systemFontOfSize:12.0],
|
||||
nil,
|
||||
_CGSizeMakeZero(),
|
||||
CPImageLeft,
|
||||
CPScaleToFit,
|
||||
_CGSizeMakeZero(),
|
||||
_CGSizeMake(-1.0, -1.0)]
|
||||
forKeys:[@"alignment",
|
||||
@"vertical-alignment",
|
||||
@"line-break-mode",
|
||||
@"text-color",
|
||||
@"font",
|
||||
@"text-shadow-color",
|
||||
@"text-shadow-offset",
|
||||
@"image-position",
|
||||
@"image-scaling",
|
||||
@"min-size",
|
||||
@"max-size"]];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -107,125 +138,13 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
if (self)
|
||||
{
|
||||
_sendActionOn = CPLeftMouseUpMask;
|
||||
_isEnabled = YES;
|
||||
|
||||
[self setFont:[CPFont systemFontOfSize:12.0]];
|
||||
[self setTextColor:CPControlBlackColor];
|
||||
|
||||
_backgroundColors = [CPDictionary dictionary];
|
||||
_trackingMouseDownFlags = 0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets whether the receiver responds to mouse events.
|
||||
@param isEnabled whether the receiver will respond to mouse events
|
||||
*/
|
||||
- (void)setEnabled:(BOOL)isEnabled
|
||||
{
|
||||
[self setAlphaValue:(_isEnabled = isEnabled) ? 1.0 : 0.3];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the receiver responds to mouse events.
|
||||
*/
|
||||
- (BOOL)isEnabled
|
||||
{
|
||||
return _isEnabled;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Sets the color of the receiver's text.
|
||||
*/
|
||||
- (void)setTextColor:(CPColor)aColor
|
||||
{
|
||||
if (_textColor == aColor)
|
||||
return;
|
||||
|
||||
_textColor = aColor;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.style.color = [aColor cssString];
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the color of the receiver's text
|
||||
*/
|
||||
- (CPColor)textColor
|
||||
{
|
||||
return _textColor;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the receiver's alignment
|
||||
*/
|
||||
- (CPTextAlignment)alignment
|
||||
{
|
||||
return _alignment;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the receiver's alignment
|
||||
@param anAlignment the receiver's alignment
|
||||
*/
|
||||
- (void)setAlignment:(CPTextAlignment)anAlignment
|
||||
{
|
||||
_alignment = anAlignment;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the receiver's font
|
||||
@param aFont the font for the receiver
|
||||
*/
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
if (_font == aFont)
|
||||
return;
|
||||
|
||||
_font = aFont;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.style.font = [_font ? _font : [CPFont systemFontOfSize:12.0] cssString];
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the receiver's font
|
||||
*/
|
||||
- (CPFont)font
|
||||
{
|
||||
return _font;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the shadow for the receiver's text.
|
||||
@param aTextShadow the text shadow
|
||||
*/
|
||||
- (void)setTextShadow:(CPShadow)aTextShadow
|
||||
{
|
||||
_DOMElement.style.textShadow = [_textShadow = aTextShadow cssString];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the receiver's text shadow
|
||||
*/
|
||||
- (CPShadow)textShadow
|
||||
{
|
||||
return _textShadow;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the receiver's target action
|
||||
*/
|
||||
- (SEL)action
|
||||
{
|
||||
return _action;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the receiver's target action
|
||||
@param anAction Sets the action message that gets sent to the target.
|
||||
*/
|
||||
@@ -234,15 +153,15 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
_action = anAction;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the receiver's target. The target receives action messages from the receiver.
|
||||
/*!
|
||||
Returns the receiver's target action
|
||||
*/
|
||||
- (id)target
|
||||
- (SEL)action
|
||||
{
|
||||
return _target;
|
||||
return _action;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the receiver's target. The target receives action messages from the receiver.
|
||||
@param aTarget the object that will receive the message specified by action
|
||||
*/
|
||||
@@ -251,16 +170,16 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
_target = aTarget;
|
||||
}
|
||||
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
/*!
|
||||
Returns the receiver's target. The target receives action messages from the receiver.
|
||||
*/
|
||||
- (id)target
|
||||
{
|
||||
if (_sendActionOn & CPLeftMouseUpMask && CPRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil]))
|
||||
[self sendAction:_action to:_target];
|
||||
|
||||
[super mouseUp:anEvent];
|
||||
return _target;
|
||||
}
|
||||
|
||||
/*
|
||||
Causes <code>anAction</code> to be sent to <code>anObject</code>.
|
||||
/*!
|
||||
Causes \c anAction to be sent to \c anObject.
|
||||
@param anAction the action to send
|
||||
@param anObject the object to which the action will be sent
|
||||
*/
|
||||
@@ -269,23 +188,160 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
[CPApp sendAction:anAction to:anObject from:self];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the receiver's float value
|
||||
*/
|
||||
- (float)floatValue
|
||||
- (int)sendActionOn:(int)mask
|
||||
{
|
||||
return _value ? parseFloat(_value, 10) : 0.0;
|
||||
var previousMask = _sendActionOn;
|
||||
|
||||
_sendActionOn = mask;
|
||||
|
||||
return previousMask;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the receiver's float value
|
||||
/*!
|
||||
Sets the tooltip for the receiver.
|
||||
@param aToolTip the tooltip
|
||||
*/
|
||||
- (void)setFloatValue:(float)aValue
|
||||
/*
|
||||
-(void)setToolTip:(CPString)aToolTip
|
||||
{
|
||||
[self setObjectValue:aValue];
|
||||
if (_toolTip == aToolTip)
|
||||
return;
|
||||
|
||||
_toolTip = aToolTip;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.title = aToolTip;
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
/*!
|
||||
Returns the receiver's tooltip
|
||||
*/
|
||||
/*
|
||||
-(CPString)toolTip
|
||||
{
|
||||
return _toolTip;
|
||||
}
|
||||
*/
|
||||
|
||||
/*!
|
||||
Returns whether the control can continuously send its action messages.
|
||||
*/
|
||||
- (BOOL)isContinuous
|
||||
{
|
||||
// Some subclasses should redefine this with CPLeftMouseDraggedMask
|
||||
return (_sendActionOn & CPPeriodicMask) !== 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets whether the cell can continuously send its action messages.
|
||||
*/
|
||||
- (void)setContinuous:(BOOL)flag
|
||||
{
|
||||
// Some subclasses should redefine this with CPLeftMouseDraggedMask
|
||||
if (flag)
|
||||
_sendActionOn |= CPPeriodicMask;
|
||||
else
|
||||
_sendActionOn &= ~CPPeriodicMask;
|
||||
}
|
||||
|
||||
- (BOOL)tracksMouseOutsideOfFrame
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)trackMouse:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type],
|
||||
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;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (type === CPLeftMouseDown)
|
||||
{
|
||||
_trackingMouseDownFlags = [anEvent modifierFlags];
|
||||
_continuousTracking = [self startTrackingAt:currentLocation];
|
||||
}
|
||||
else if (type === CPLeftMouseDragged)
|
||||
{
|
||||
if (isWithinFrame)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
- (void)setState:(int)state
|
||||
{
|
||||
}
|
||||
|
||||
- (int)nextState
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)performClick:(id)sender
|
||||
{
|
||||
[self highlight:YES];
|
||||
[self setState:[self nextState]];
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
[self highlight:NO];
|
||||
}
|
||||
|
||||
- (unsigned)mouseDownFlags
|
||||
{
|
||||
return _trackingMouseDownFlags;
|
||||
}
|
||||
|
||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
||||
{
|
||||
[self highlight:YES];
|
||||
|
||||
return (_sendActionOn & CPPeriodicMask) || (_sendActionOn & CPLeftMouseDraggedMask);
|
||||
}
|
||||
|
||||
- (BOOL)continueTracking:(CGPoint)lastPoint at:(CGPoint)aPoint
|
||||
{
|
||||
return (_sendActionOn & CPPeriodicMask) || (_sendActionOn & CPLeftMouseDraggedMask);
|
||||
}
|
||||
|
||||
- (void)stopTracking:(CGPoint)lastPoint at:(CGPoint)aPoint mouseIsUp:(BOOL)mouseIsUp
|
||||
{
|
||||
[self highlight:NO];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
[self trackMouse:anEvent];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the receiver's object value
|
||||
*/
|
||||
- (id)objectValue
|
||||
@@ -293,122 +349,232 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
return _value;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Set's the receiver's object value
|
||||
*/
|
||||
- (void)setObjectValue:(id)anObject
|
||||
{
|
||||
_value = anObject;
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the receiver's float value
|
||||
*/
|
||||
- (float)floatValue
|
||||
{
|
||||
var floatValue = parseFloat(_value, 10);
|
||||
return isNaN(floatValue) ? 0.0 : floatValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the receiver's float value
|
||||
*/
|
||||
- (void)setFloatValue:(float)aValue
|
||||
{
|
||||
[self setObjectValue:aValue];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the receiver's double value
|
||||
*/
|
||||
- (id)doubleValue
|
||||
- (double)doubleValue
|
||||
{
|
||||
return [self floatValue];
|
||||
var doubleValue = parseFloat(_value, 10);
|
||||
return isNaN(doubleValue) ? 0.0 : doubleValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Set's the receiver's double value
|
||||
*/
|
||||
- (void)setDoubleValue:(id)anObject
|
||||
- (void)setDoubleValue:(double)anObject
|
||||
{
|
||||
[self setObjectValue:anObject];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the receiver's int value
|
||||
*/
|
||||
- (id)intValue
|
||||
- (int)intValue
|
||||
{
|
||||
return _value ? parseInt(_value, 10) : 0;
|
||||
var intValue = parseInt(_value, 10);
|
||||
return isNaN(intValue) ? 0.0 : intValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Set's the receiver's int value
|
||||
*/
|
||||
- (void)setIntValue:(id)anObject
|
||||
- (void)setIntValue:(int)anObject
|
||||
{
|
||||
[self setObjectValue:anObject];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the receiver's int value
|
||||
*/
|
||||
- (id)stringValue
|
||||
- (int)integerValue
|
||||
{
|
||||
return _value ? ""+_value : "";
|
||||
var intValue = parseInt(_value, 10);
|
||||
return isNaN(intValue) ? 0.0 : intValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Set's the receiver's int value
|
||||
*/
|
||||
- (void)setStringValue:(id)anObject
|
||||
- (void)setIntegerValue:(int)anObject
|
||||
{
|
||||
[self setObjectValue:anObject];
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(CPColor)aColor
|
||||
/*!
|
||||
Returns the receiver's int value
|
||||
*/
|
||||
- (CPString)stringValue
|
||||
{
|
||||
_backgroundColors = [CPDictionary dictionary];
|
||||
|
||||
[self setBackgroundColor:aColor forName:CPControlNormalBackgroundColor];
|
||||
|
||||
[super setBackgroundColor:aColor];
|
||||
return (_value === undefined || _value === nil) ? "" : String(_value);
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(CPColor)aColor forName:(CPString)aName
|
||||
/*!
|
||||
Set's the receiver's int value
|
||||
*/
|
||||
- (void)setStringValue:(CPString)anObject
|
||||
{
|
||||
if (!aColor)
|
||||
[_backgroundColors removeObjectForKey:aName];
|
||||
[self setObjectValue:anObject];
|
||||
}
|
||||
|
||||
- (void)takeDoubleValueFrom:(id)sender
|
||||
{
|
||||
if ([sender respondsToSelector:@selector(doubleValue)])
|
||||
[self setDoubleValue:[sender doubleValue]];
|
||||
}
|
||||
|
||||
|
||||
- (void)takeFloatValueFrom:(id)sender
|
||||
{
|
||||
if ([sender respondsToSelector:@selector(floatValue)])
|
||||
[self setFloatValue:[sender floatValue]];
|
||||
}
|
||||
|
||||
|
||||
- (void)takeIntegerValueFrom:(id)sender
|
||||
{
|
||||
if ([sender respondsToSelector:@selector(integerValue)])
|
||||
[self setIntegerValue:[sender integerValue]];
|
||||
}
|
||||
|
||||
|
||||
- (void)takeIntValueFrom:(id)sender
|
||||
{
|
||||
if ([sender respondsToSelector:@selector(intValue)])
|
||||
[self setIntValue:[sender intValue]];
|
||||
}
|
||||
|
||||
|
||||
- (void)takeObjectValueFrom:(id)sender
|
||||
{
|
||||
if ([sender respondsToSelector:@selector(objectValue)])
|
||||
[self setObjectValue:[sender objectValue]];
|
||||
}
|
||||
|
||||
- (void)takeStringValueFrom:(id)sender
|
||||
{
|
||||
if ([sender respondsToSelector:@selector(stringValue)])
|
||||
[self setStringValue:[sender stringValue]];
|
||||
}
|
||||
|
||||
- (void)textDidBeginEditing:(CPNotification)note
|
||||
{
|
||||
//this looks to prevent false propagation of notifications for other objects
|
||||
if([note object] != self)
|
||||
return;
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidBeginEditingNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||
}
|
||||
|
||||
- (void)textDidChange:(CPNotification)note
|
||||
{
|
||||
//this looks to prevent false propagation of notifications for other objects
|
||||
if([note object] != self)
|
||||
return;
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPControlTextDidChangeNotification object:self userInfo:[CPDictionary dictionaryWithObject:[note object] forKey:"CPFieldEditor"]];
|
||||
}
|
||||
|
||||
- (void)textDidEndEditing:(CPNotification)note
|
||||
{
|
||||
//this looks to prevent false propagation of notifications for other objects
|
||||
if([note object] != self)
|
||||
return;
|
||||
|
||||
[[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];\
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
- (void)setEnabled:(BOOL)isEnabled
|
||||
{
|
||||
if (isEnabled)
|
||||
[self unsetThemeState:CPThemeStateDisabled];
|
||||
else
|
||||
[_backgroundColors setObject:aColor forKey:aName];
|
||||
|
||||
if (_currentBackgroundColorName == aName)
|
||||
[self setBackgroundColorWithName:_currentBackgroundColorName];
|
||||
[self setThemeState:CPThemeStateDisabled];
|
||||
}
|
||||
|
||||
- (CPColor)backgroundColorForName:(CPString)aName
|
||||
- (BOOL)isEnabled
|
||||
{
|
||||
var backgroundColor = [_backgroundColors objectForKey:aName];
|
||||
|
||||
if (!backgroundColor && aName != CPControlNormalBackgroundColor)
|
||||
return [_backgroundColors objectForKey:CPControlNormalBackgroundColor];
|
||||
|
||||
return backgroundColor;
|
||||
return ![self hasThemeState:CPThemeStateDisabled];
|
||||
}
|
||||
|
||||
- (void)setBackgroundColorWithName:(CPString)aName
|
||||
- (void)highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
_currentBackgroundColorName = aName;
|
||||
|
||||
[super setBackgroundColor:[self backgroundColorForName:aName]];
|
||||
[self setHighlighted:shouldHighlight];
|
||||
}
|
||||
|
||||
/*
|
||||
Ð doubleValue
|
||||
Ð setDoubleValue:
|
||||
Ð intValue
|
||||
Ð setIntValue:
|
||||
Ð objectValue
|
||||
Ð setObjectValue:
|
||||
Ð stringValue
|
||||
Ð setStringValue:
|
||||
Ð setNeedsDisplay
|
||||
Ð attributedStringValue
|
||||
Ð setAttributedStringValue:
|
||||
*/
|
||||
- (void)setHighlighted:(BOOL)isHighlighted
|
||||
{
|
||||
if (isHighlighted)
|
||||
[self setThemeState:CPThemeStateHighlighted];
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateHighlighted];
|
||||
}
|
||||
|
||||
- (BOOL)isHighlighted
|
||||
{
|
||||
return [self hasThemeState:CPThemeStateHighlighted];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPControlIsEnabledKey = @"CPControlIsEnabledKey",
|
||||
CPControlAlignmentKey = @"CPControlAlignmentKey",
|
||||
CPControlFontKey = @"CPControlFontKey",
|
||||
CPControlTextColorKey = @"CPControlTextColorKey",
|
||||
CPControlTargetKey = @"CPControlTargetKey",
|
||||
CPControlActionKey = @"CPControlActionKey",
|
||||
CPControlSendActionOnKey = @"CPControlSendActionOnKey";
|
||||
var CPControlValueKey = "CPControlValueKey",
|
||||
CPControlControlStateKey = @"CPControlControlStateKey",
|
||||
CPControlIsEnabledKey = "CPControlIsEnabledKey",
|
||||
|
||||
CPControlTargetKey = "CPControlTargetKey",
|
||||
CPControlActionKey = "CPControlActionKey",
|
||||
CPControlSendActionOnKey = "CPControlSendActionOnKey";
|
||||
|
||||
var __Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||
|
||||
@implementation CPControl (CPCoding)
|
||||
|
||||
@@ -420,19 +586,15 @@ var CPControlIsEnabledKey = @"CPControlIsEnabledKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setEnabled:[aCoder decodeIntForKey:CPControlIsEnabledKey]];
|
||||
|
||||
[self setAlignment:[aCoder decodeIntForKey:CPControlAlignmentKey]];
|
||||
[self setFont:[aCoder decodeObjectForKey:CPControlFontKey]];
|
||||
[self setTextColor:[aCoder decodeObjectForKey:CPControlTextColorKey]];
|
||||
|
||||
[self setObjectValue:[aCoder decodeObjectForKey:CPControlValueKey]];
|
||||
|
||||
[self setTarget:[aCoder decodeObjectForKey:CPControlTargetKey]];
|
||||
[self setAction:[aCoder decodeObjectForKey:CPControlActionKey]];
|
||||
|
||||
_sendActionOn = [aCoder decodeIntForKey:CPControlSendActionOnKey];
|
||||
|
||||
[self sendActionOn:[aCoder decodeIntForKey:CPControlSendActionOnKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -445,29 +607,28 @@ var CPControlIsEnabledKey = @"CPControlIsEnabledKey",
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeInt:_isEnabled forKey:CPControlIsEnabledKey];
|
||||
|
||||
[aCoder encodeInt:_alignment forKey:CPControlAlignmentKey];
|
||||
[aCoder encodeObject:_font forKey:CPControlFontKey];
|
||||
[aCoder encodeObject:_textColor forKey:CPControlTextColorKey];
|
||||
|
||||
[aCoder encodeConditionalObject:_target forKey:CPControlTargetKey];
|
||||
[aCoder encodeObject:_action forKey:CPControlActionKey];
|
||||
|
||||
|
||||
if (_value !== nil)
|
||||
[aCoder encodeObject:_value forKey:CPControlValueKey];
|
||||
|
||||
if (_target !== nil)
|
||||
[aCoder encodeConditionalObject:_target forKey:CPControlTargetKey];
|
||||
|
||||
if (_action !== NULL)
|
||||
[aCoder encodeObject:_action forKey:CPControlActionKey];
|
||||
|
||||
[aCoder encodeInt:_sendActionOn forKey:CPControlSendActionOnKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPControlSizeIdentifiers = [],
|
||||
_CPControlCachedThreePartImages = {},
|
||||
_CPControlCachedColorWithPatternImages = {},
|
||||
_CPControlCachedThreePartImagePattern = {};
|
||||
|
||||
_CPControlSizeIdentifiers[CPRegularControlSize] = @"Regular";
|
||||
_CPControlSizeIdentifiers[CPSmallControlSize] = @"Small";
|
||||
_CPControlSizeIdentifiers[CPMiniControlSize] = @"Mini";
|
||||
_CPControlSizeIdentifiers[CPRegularControlSize] = "Regular";
|
||||
_CPControlSizeIdentifiers[CPSmallControlSize] = "Small";
|
||||
_CPControlSizeIdentifiers[CPMiniControlSize] = "Mini";
|
||||
|
||||
function _CPControlIdentifierForControlSize(aControlSize)
|
||||
{
|
||||
@@ -478,7 +639,7 @@ function _CPControlColorWithPatternImage(sizes, aClassName)
|
||||
{
|
||||
var index = 1,
|
||||
count = arguments.length,
|
||||
identifier = @"";
|
||||
identifier = "";
|
||||
|
||||
for (; index < count; ++index)
|
||||
identifier += arguments[index];
|
||||
@@ -489,7 +650,7 @@ function _CPControlColorWithPatternImage(sizes, aClassName)
|
||||
{
|
||||
var bundle = [CPBundle bundleForClass:[CPControl class]];
|
||||
|
||||
color = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:aClassName + "/" + identifier + @".png"] size:sizes[identifier]]];
|
||||
color = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:aClassName + "/" + identifier + ".png"] size:sizes[identifier]]];
|
||||
|
||||
_CPControlCachedColorWithPatternImages[identifier] = color;
|
||||
}
|
||||
@@ -497,41 +658,11 @@ function _CPControlColorWithPatternImage(sizes, aClassName)
|
||||
return color;
|
||||
}
|
||||
|
||||
function _CPControlThreePartImages(sizes, aClassName)
|
||||
{
|
||||
var index = 1,
|
||||
count = arguments.length,
|
||||
identifier = @"";
|
||||
|
||||
for (; index < count; ++index)
|
||||
identifier += arguments[index];
|
||||
|
||||
var images = _CPControlCachedThreePartImages[identifier];
|
||||
|
||||
if (!images)
|
||||
{
|
||||
var bundle = [CPBundle bundleForClass:[CPControl class]],
|
||||
path = aClassName + "/" + identifier;
|
||||
|
||||
sizes = sizes[identifier];
|
||||
|
||||
images = [
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"0.png"] size:sizes[0]],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"1.png"] size:sizes[1]],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"2.png"] size:sizes[2]]
|
||||
];
|
||||
|
||||
_CPControlCachedThreePartImages[identifier] = images;
|
||||
}
|
||||
|
||||
return images;
|
||||
}
|
||||
|
||||
function _CPControlThreePartImagePattern(isVertical, sizes, aClassName)
|
||||
{
|
||||
var index = 2,
|
||||
count = arguments.length,
|
||||
identifier = @"";
|
||||
identifier = "";
|
||||
|
||||
for (; index < count; ++index)
|
||||
identifier += arguments[index];
|
||||
@@ -546,9 +677,9 @@ function _CPControlThreePartImagePattern(isVertical, sizes, aClassName)
|
||||
sizes = sizes[identifier];
|
||||
|
||||
color = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:[
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"0.png"] size:sizes[0]],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"1.png"] size:sizes[1]],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + @"2.png"] size:sizes[2]]
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:path + "0.png"] size:sizes[0]],
|
||||
[[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;
|
||||
@@ -556,4 +687,3 @@ function _CPControlThreePartImagePattern(isVertical, sizes, aClassName)
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
|
||||
+12
-10
@@ -20,12 +20,14 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
|
||||
/*
|
||||
<objj>CPCookie</objj> is the Cappuccino interface to a web browser cookie. You can set the name
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPCookie
|
||||
CPCookie is the Cappuccino interface to a web browser cookie. You can set the name
|
||||
*/
|
||||
@implementation CPCookie : CPObject
|
||||
{
|
||||
@@ -35,8 +37,8 @@ import <Foundation/CPString.j>
|
||||
CPString _expires;
|
||||
}
|
||||
|
||||
/*
|
||||
Initializes a cookie with a given name <code>aName</code>.
|
||||
/*!
|
||||
Initializes a cookie with a given name \c aName.
|
||||
@param the name for the cookie
|
||||
*/
|
||||
- (id)initWithName:(CPString)aName
|
||||
@@ -49,7 +51,7 @@ import <Foundation/CPString.j>
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the cookie's data value
|
||||
*/
|
||||
- (CPString)value
|
||||
@@ -57,7 +59,7 @@ import <Foundation/CPString.j>
|
||||
return _cookieValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the cookie's name
|
||||
*/
|
||||
- (CPString)name
|
||||
@@ -65,7 +67,7 @@ import <Foundation/CPString.j>
|
||||
return _cookieName;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the cookie's expiration date
|
||||
*/
|
||||
- (CPString)expires
|
||||
@@ -73,7 +75,7 @@ import <Foundation/CPString.j>
|
||||
return _expires;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets a value, expiration date, and domain for the cookie
|
||||
@param value the cookie's value
|
||||
@param date the cookie's expiration date
|
||||
|
||||
+294
-90
@@ -20,11 +20,14 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPString.j>
|
||||
import <Foundation/CPArray.j>
|
||||
@import <Foundation/CPString.j>
|
||||
@import <Foundation/CPArray.j>
|
||||
|
||||
@import "CPResponder.j"
|
||||
@import "CPSavePanel.j"
|
||||
@import "CPViewController.j"
|
||||
@import "CPWindowController.j"
|
||||
|
||||
import "CPResponder.j"
|
||||
import "CPWindowController.j"
|
||||
|
||||
/*
|
||||
@global
|
||||
@@ -79,29 +82,38 @@ CPDocumentDidFailToSaveNotification = @"CPDocumentDidFailToSaveNotification";
|
||||
|
||||
var CPDocumentUntitledCount = 0;
|
||||
|
||||
/*
|
||||
<objj>CPDocument</objj> is used to represent a document/file in a Cappuccino application.
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPDocument
|
||||
|
||||
CPDocument is used to represent a document/file in a Cappuccino application.
|
||||
In a document-based application, generally multiple documents are open simutaneously
|
||||
(multiple text documents, slide presentations, spreadsheets, etc.), and multiple
|
||||
<objj>CPDocument</objj>s should be used to represent this.
|
||||
CPDocuments should be used to represent this.
|
||||
*/
|
||||
@implementation CPDocument : CPResponder
|
||||
{
|
||||
CPURL _fileURL;
|
||||
CPString _fileType;
|
||||
CPArray _windowControllers;
|
||||
unsigned _untitledDocumentIndex;
|
||||
{
|
||||
CPWindow _window; // For outlet purposes.
|
||||
CPView _view; // For outlet purposes
|
||||
CPDictionary _viewControllersForWindowControllers;
|
||||
|
||||
BOOL _hasUndoManager;
|
||||
CPUndoManager _undoManager;
|
||||
|
||||
int _changeCount;
|
||||
|
||||
CPURLConnection _readConnection;
|
||||
CPURLRequest _writeRequest;
|
||||
CPURL _fileURL;
|
||||
CPString _fileType;
|
||||
CPArray _windowControllers;
|
||||
unsigned _untitledDocumentIndex;
|
||||
|
||||
BOOL _hasUndoManager;
|
||||
CPUndoManager _undoManager;
|
||||
|
||||
int _changeCount;
|
||||
|
||||
CPURLConnection _readConnection;
|
||||
CPURLRequest _writeRequest;
|
||||
|
||||
CPAlert _canCloseAlert;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes an empty document.
|
||||
@return the initialized document
|
||||
*/
|
||||
@@ -112,17 +124,18 @@ var CPDocumentUntitledCount = 0;
|
||||
if (self)
|
||||
{
|
||||
_windowControllers = [];
|
||||
|
||||
_viewControllersForWindowControllers = [CPDictionary dictionary];
|
||||
|
||||
_hasUndoManager = YES;
|
||||
_changeCount = 0;
|
||||
|
||||
|
||||
[self setNextResponder:CPApp];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the document with a specific data type.
|
||||
@param aType the type of document to initialize
|
||||
@param anError not used
|
||||
@@ -138,7 +151,7 @@ var CPDocumentUntitledCount = 0;
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes a document of a specific type located at a URL. Notifies
|
||||
the provided delegate after initialization.
|
||||
@param anAbsoluteURL the url of the document content
|
||||
@@ -155,23 +168,23 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self readFromURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||
|
||||
[self setFileURL:anAbsoluteURL];
|
||||
[self setFileType:aType];
|
||||
|
||||
[self readFromURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the document from a URL.
|
||||
@param anAbsoluteURL the document location
|
||||
@param absoluteContentsURL the location of the document's contents
|
||||
@param aType the type of the contents
|
||||
@param aDelegate this object will receive a callback after the document's contents are loaded
|
||||
@param aDidReadSelector the message selector that will be sent to <code>aDelegate</code>
|
||||
@param aContextInfo passed as the argument to the message sent to the <code>aDelegate</code>
|
||||
@param aDidReadSelector the message selector that will be sent to \c aDelegate
|
||||
@param aContextInfo passed as the argument to the message sent to the \c aDelegate
|
||||
@return the initialized document
|
||||
*/
|
||||
- (id)initForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aDidReadSelector contextInfo:(id)aContextInfo
|
||||
@@ -180,16 +193,16 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self readFromURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||
|
||||
[self setFileURL:anAbsoluteURL];
|
||||
[self setFileType:aType];
|
||||
|
||||
[self readFromURL:absoluteContentsURL ofType:aType delegate:aDelegate didReadSelector:aDidReadSelector contextInfo:aContextInfo];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the receiver's data in a specified type. The default implementation just
|
||||
throws an exception.
|
||||
@param aType the format of the data
|
||||
@@ -203,7 +216,7 @@ var CPDocumentUntitledCount = 0;
|
||||
reason:"dataOfType:error: must be overridden by the document subclass."];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the content of the document by reading the provided
|
||||
data. The default implementation just throws an exception.
|
||||
@param aData the document's data
|
||||
@@ -218,18 +231,72 @@ var CPDocumentUntitledCount = 0;
|
||||
reason:"readFromData:ofType: must be overridden by the document subclass."];
|
||||
}
|
||||
|
||||
- (void)viewControllerWillLoadCib:(CPViewController)aViewController
|
||||
{
|
||||
}
|
||||
|
||||
- (void)viewControllerDidLoadCib:(CPViewController)aViewController
|
||||
{
|
||||
}
|
||||
|
||||
- (CPWindowController)firstEligibleExistingWindowController
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Creating and managing window controllers
|
||||
/*
|
||||
/*!
|
||||
Creates the window controller for this document.
|
||||
*/
|
||||
- (void)makeWindowControllers
|
||||
{
|
||||
var controller = [[CPWindowController alloc] initWithWindowCibName:nil];
|
||||
|
||||
[self addWindowController:controller];
|
||||
[self makeViewAndWindowControllers];
|
||||
}
|
||||
|
||||
/*
|
||||
- (void)makeViewAndWindowControllers
|
||||
{
|
||||
var viewCibName = [self viewCibName],
|
||||
viewController = nil,
|
||||
windowController = nil;
|
||||
|
||||
// Create our view controller if we have a cib for it.
|
||||
if ([viewCibName length])
|
||||
viewController = [[CPViewController alloc] initWithCibName:viewCibName bundle:nil owner:self];
|
||||
|
||||
// If we have a view controller, check if we have a free window for it.
|
||||
if (viewController)
|
||||
windowController = [self firstEligibleExistingWindowController];
|
||||
|
||||
// If not, create one.
|
||||
if (!windowController)
|
||||
{
|
||||
var windowCibName = [self windowCibName];
|
||||
|
||||
// From a cib if we have one.
|
||||
if ([windowCibName length])
|
||||
windowController = [[CPWindowController alloc] initWithWindowCibName:windowCibName owner:self];
|
||||
|
||||
// If not you get a standard window capable of displaying multiple documents and view
|
||||
else if (viewController)
|
||||
{
|
||||
var view = [viewController view],
|
||||
theWindow = [[CPWindow alloc] initWithContentRect:[view frame] styleMask:CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask];
|
||||
|
||||
windowController = [[CPWindowController alloc] initWithWindow:theWindow];
|
||||
}
|
||||
}
|
||||
|
||||
if (windowController && viewController)
|
||||
[windowController setSupportsMultipleDocuments:YES];
|
||||
|
||||
if (windowController)
|
||||
[self addWindowController:windowController];
|
||||
|
||||
if (viewController)
|
||||
[self addViewController:viewController forWindowController:windowController];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the document's window controllers
|
||||
*/
|
||||
- (CPArray)windowControllers
|
||||
@@ -237,7 +304,7 @@ var CPDocumentUntitledCount = 0;
|
||||
return _windowControllers;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Add a controller to the document's list of controllers. This should
|
||||
be called after making a new window controller.
|
||||
@param aWindowController the controller to add
|
||||
@@ -245,24 +312,65 @@ var CPDocumentUntitledCount = 0;
|
||||
- (void)addWindowController:(CPWindowController)aWindowController
|
||||
{
|
||||
[_windowControllers addObject:aWindowController];
|
||||
|
||||
if ([aWindowController document] != self)
|
||||
{
|
||||
[aWindowController setNextResponder:self];
|
||||
|
||||
if ([aWindowController document] !== self)
|
||||
[aWindowController setDocument:self];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Remove a controller to the document's list of controllers. This should
|
||||
be called after closing the controller's window.
|
||||
@param aWindowController the controller to remove
|
||||
*/
|
||||
- (void)removeWindowController:(CPWindowController)aWindowController
|
||||
{
|
||||
if (aWindowController)
|
||||
[_windowControllers removeObject:aWindowController];
|
||||
|
||||
if ([aWindowController document] === self)
|
||||
[aWindowController setDocument:nil];
|
||||
}
|
||||
|
||||
- (CPView)view
|
||||
{
|
||||
return _view;
|
||||
}
|
||||
|
||||
- (CPArray)viewControllers
|
||||
{
|
||||
return [_viewControllersForWindowControllers allValues];
|
||||
}
|
||||
|
||||
- (void)addViewController:(CPViewController)aViewController forWindowController:(CPWindowController)aWindowController
|
||||
{
|
||||
// FIXME: exception if we don't own the window controller?
|
||||
[_viewControllersForWindowControllers setObject:aViewController forKey:[aWindowController UID]];
|
||||
|
||||
if ([aWindowController document] === self)
|
||||
[aWindowController setViewController:aViewController];
|
||||
}
|
||||
|
||||
- (void)removeViewController:(CPViewController)aViewController
|
||||
{
|
||||
[_viewControllersForWindowControllers removeObject:aViewController];
|
||||
}
|
||||
|
||||
- (CPViewController)viewControllerForWindowController:(CPWindowController)aWindowController
|
||||
{
|
||||
return [_viewControllersForWindowControllers objectForKey:[aWindowController UID]];
|
||||
}
|
||||
|
||||
// Managing Document Windows
|
||||
/*
|
||||
/*!
|
||||
Shows all the document's windows.
|
||||
*/
|
||||
- (void)showWindows
|
||||
{
|
||||
[_windowControllers makeObjectsPerformSelector:@selector(setDocument:) withObject:self];
|
||||
[_windowControllers makeObjectsPerformSelector:@selector(showWindow:) withObject:self];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the name of the document as displayed in the title bar.
|
||||
*/
|
||||
- (CPString)displayName
|
||||
@@ -279,7 +387,12 @@ var CPDocumentUntitledCount = 0;
|
||||
return @"Untitled " + _untitledDocumentIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
- (CPString)viewCibName
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the document's Cib name
|
||||
*/
|
||||
- (CPString)windowCibName
|
||||
@@ -287,24 +400,24 @@ var CPDocumentUntitledCount = 0;
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*
|
||||
Called after <code>aWindowController<code> loads the document's Nib file.
|
||||
/*!
|
||||
Called after \c aWindowController loads the document's Nib file.
|
||||
@param aWindowController the controller that loaded the Nib file
|
||||
*/
|
||||
- (void)windowControllerDidLoadNib:(CPWindowController)aWindowController
|
||||
- (void)windowControllerDidLoadCib:(CPWindowController)aWindowController
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
Called before <code>aWindowController</code> will load the document's Nib file.
|
||||
/*!
|
||||
Called before \c aWindowController will load the document's Nib file.
|
||||
@param aWindowController the controller that will load the Nib file
|
||||
*/
|
||||
- (void)windowControllerWillLoadNib:(CPWindowController)aWindowController
|
||||
- (void)windowControllerWillLoadCib:(CPWindowController)aWindowController
|
||||
{
|
||||
}
|
||||
|
||||
// Reading from and Writing to URLs
|
||||
/*
|
||||
/*!
|
||||
Set the document's data from a URL. Notifies the provided delegate afterwards.
|
||||
@param anAbsoluteURL the URL to the document's content
|
||||
@param aType the document type
|
||||
@@ -318,11 +431,11 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
// FIXME: Oh man is this every looking for trouble, we need to handle login at the Cappuccino level, with HTTP Errors.
|
||||
_readConnection = [CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:anAbsoluteURL] delegate:self];
|
||||
|
||||
|
||||
_readConnection.session = _CPReadSessionMake(aType, aDelegate, aDidReadSelector, aContextInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the path to the document's file.
|
||||
*/
|
||||
- (CPURL)fileURL
|
||||
@@ -330,13 +443,13 @@ var CPDocumentUntitledCount = 0;
|
||||
return _fileURL;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the path to the document's file.
|
||||
@param aFileURL the path to the document's file
|
||||
*/
|
||||
- (void)setFileURL:(CPURL)aFileURL
|
||||
{
|
||||
if (_fileURL == aFileURL)
|
||||
if (_fileURL === aFileURL)
|
||||
return;
|
||||
|
||||
_fileURL = aFileURL;
|
||||
@@ -344,7 +457,7 @@ var CPDocumentUntitledCount = 0;
|
||||
[_windowControllers makeObjectsPerformSelector:@selector(synchronizeWindowTitleWithDocumentName)];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Saves the document to the specified URL. Notifies the provided delegate
|
||||
with the provided selector and context info afterwards.
|
||||
@param anAbsoluteURL the url to write the document data to
|
||||
@@ -361,17 +474,22 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
_writeRequest = [CPURLRequest requestWithURL:anAbsoluteURL];
|
||||
|
||||
[_writeRequest setHTTPMethod:@"POST"];
|
||||
// FIXME: THIS IS WRONG! We need a way to decide
|
||||
if ([CPPlatform isBrowser])
|
||||
[_writeRequest setHTTPMethod:@"POST"];
|
||||
else
|
||||
[_writeRequest setHTTPMethod:@"PUT"];
|
||||
|
||||
[_writeRequest setHTTPBody:[data string]];
|
||||
|
||||
|
||||
[_writeRequest setValue:@"close" forHTTPHeaderField:@"Connection"];
|
||||
|
||||
if (aSaveOperation == CPSaveOperation)
|
||||
[_writeRequest setValue:@"true" forHTTPHeaderField:@"x-cappuccino-overwrite"];
|
||||
|
||||
|
||||
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.
|
||||
var connection = [CPURLConnection connectionWithRequest:_writeRequest delegate:self];
|
||||
|
||||
@@ -384,10 +502,14 @@ var CPDocumentUntitledCount = 0;
|
||||
*/
|
||||
- (void)connection:(CPURLConnection)aConnection didReceiveResponse:(CPURLResponse)aResponse
|
||||
{
|
||||
// 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)
|
||||
if (statusCode === 200)
|
||||
return;
|
||||
|
||||
var session = aConnection.session;
|
||||
@@ -424,6 +546,7 @@ var CPDocumentUntitledCount = 0;
|
||||
_writeRequest = nil;
|
||||
|
||||
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||
[self _sendDocumentSavedNotification:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -452,6 +575,7 @@ var CPDocumentUntitledCount = 0;
|
||||
_writeRequest = nil;
|
||||
|
||||
objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
|
||||
[self _sendDocumentSavedNotification:YES];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,6 +603,7 @@ var CPDocumentUntitledCount = 0;
|
||||
alert("There was an error saving the document.");
|
||||
|
||||
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||
[self _sendDocumentSavedNotification:NO];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,15 +618,15 @@ var CPDocumentUntitledCount = 0;
|
||||
}
|
||||
|
||||
// Managing Document Status
|
||||
/*
|
||||
Returns <code>YES</code> if there are any unsaved changes.
|
||||
/*!
|
||||
Returns \c YES if there are any unsaved changes.
|
||||
*/
|
||||
- (BOOL)isDocumentEdited
|
||||
{
|
||||
return _changeCount != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Updates the number of unsaved changes to the document.
|
||||
@param aChangeType a new document change to apply
|
||||
*/
|
||||
@@ -521,7 +646,7 @@ var CPDocumentUntitledCount = 0;
|
||||
}
|
||||
|
||||
// Managing File Types
|
||||
/*
|
||||
/*!
|
||||
Sets the document's file type
|
||||
@param aType the document's type
|
||||
*/
|
||||
@@ -530,7 +655,7 @@ var CPDocumentUntitledCount = 0;
|
||||
_fileType = aType;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the document's file type
|
||||
*/
|
||||
- (CPString)fileType
|
||||
@@ -539,18 +664,18 @@ var CPDocumentUntitledCount = 0;
|
||||
}
|
||||
|
||||
// Working with Undo Manager
|
||||
/*
|
||||
Returns <code>YES</code> if the document has a
|
||||
<objj>CPUndoManager</objj>.
|
||||
/*!
|
||||
Returns \c YES if the document has a
|
||||
CPUndoManager.
|
||||
*/
|
||||
- (BOOL)hasUndoManager
|
||||
{
|
||||
return _hasUndoManager;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets whether the document should have a <objj>CPUndoManager</objj>.
|
||||
@param aFlag <code>YES</code> makes the document have an undo manager
|
||||
/*!
|
||||
Sets whether the document should have a CPUndoManager.
|
||||
@param aFlag \c YES makes the document have an undo manager
|
||||
*/
|
||||
- (void)setHasUndoManager:(BOOL)aFlag
|
||||
{
|
||||
@@ -632,9 +757,9 @@ var CPDocumentUntitledCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the document's undo manager. If the document
|
||||
should have one, but the manager is <code>nil</code>, it
|
||||
should have one, but the manager is \c nil, it
|
||||
will be created and then returned.
|
||||
@return the document's undo manager
|
||||
*/
|
||||
@@ -647,7 +772,7 @@ var CPDocumentUntitledCount = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Implemented as a delegate of a <objj>CPWindow</objj>
|
||||
Implemented as a delegate of a CPWindow
|
||||
@ignore
|
||||
*/
|
||||
- (CPUndoManager)windowWillReturnUndoManager:(CPWindow)aWindow
|
||||
@@ -656,13 +781,18 @@ var CPDocumentUntitledCount = 0;
|
||||
}
|
||||
|
||||
// Handling User Actions
|
||||
/*
|
||||
/*!
|
||||
Saves the document. If the document does not
|
||||
have a file path to save to (<code>fileURL</code>)
|
||||
then <code>saveDocumentAs:</code> will be called.
|
||||
have a file path to save to (\c fileURL)
|
||||
then \c -saveDocumentAs: will be called.
|
||||
@param aSender the object requesting the save
|
||||
*/
|
||||
- (void)saveDocument:(id)aSender
|
||||
{
|
||||
[self saveDocumentWithDelegate:nil didSaveSelector:nil contextInfo:nil];
|
||||
}
|
||||
|
||||
- (void)saveDocumentWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(Object)contextInfo
|
||||
{
|
||||
if (_fileURL)
|
||||
{
|
||||
@@ -670,34 +800,42 @@ var CPDocumentUntitledCount = 0;
|
||||
postNotificationName:CPDocumentWillSaveNotification
|
||||
object:self];
|
||||
|
||||
[self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL];
|
||||
[self saveToURL:_fileURL ofType:[self fileType] forSaveOperation:CPSaveOperation delegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
|
||||
}
|
||||
else
|
||||
[self saveDocumentAs:self];
|
||||
[self _saveDocumentAsWithDelegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Saves the document to a user specified path.
|
||||
@param aSender the object requesting the operation
|
||||
*/
|
||||
- (void)saveDocumentAs:(id)aSender
|
||||
{
|
||||
_documentName = window.prompt("Document Name:");
|
||||
[self _saveDocumentAsWithDelegate:nil didSaveSelector:nil contextInfo:nil];
|
||||
}
|
||||
|
||||
if (!_documentName)
|
||||
- (void)_saveDocumentAsWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(Object)contextInfo
|
||||
{
|
||||
var savePanel = [CPSavePanel savePanel],
|
||||
response = [savePanel runModal];
|
||||
|
||||
if (!response)
|
||||
return;
|
||||
|
||||
|
||||
var saveURL = [savePanel URL];
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPDocumentWillSaveNotification
|
||||
object:self];
|
||||
|
||||
[self saveToURL:[self proposedFileURL] ofType:[self fileType] forSaveOperation:CPSaveAsOperation delegate:self didSaveSelector:@selector(document:didSave:contextInfo:) contextInfo:NULL];
|
||||
|
||||
[self saveToURL:saveURL ofType:[self fileType] forSaveOperation:CPSaveAsOperation delegate:delegate didSaveSelector:didSaveSelector contextInfo:contextInfo];
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (void)document:(id)aDocument didSave:(BOOL)didSave contextInfo:(id)aContextInfo
|
||||
- (void)_sendDocumentSavedNotification:(BOOL)didSave
|
||||
{
|
||||
if (didSave)
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
@@ -711,6 +849,72 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPDocument (ClosingDocuments)
|
||||
|
||||
- (void)close
|
||||
{
|
||||
[_windowControllers makeObjectsPerformSelector:@selector(removeDocumentAndCloseIfNecessary:) withObject:self];
|
||||
[[CPDocumentController sharedDocumentController] removeDocument:self];
|
||||
}
|
||||
|
||||
- (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}];
|
||||
|
||||
else if ([delegate respondsToSelector:selector])
|
||||
objj_msgSend(delegate, selector, self, YES, info);
|
||||
}
|
||||
|
||||
- (void)_document:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
|
||||
{
|
||||
if (aDocument === self && shouldClose)
|
||||
[self close];
|
||||
|
||||
objj_msgSend(context.delegate, context.selector, aDocument, shouldClose, context.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);
|
||||
|
||||
_canCloseAlert = [[CPAlert alloc] init];
|
||||
|
||||
[_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 addButtonWithTitle:@"Save"];
|
||||
[_canCloseAlert addButtonWithTitle:@"Cancel"];
|
||||
[_canCloseAlert addButtonWithTitle:@"Don't Save"];
|
||||
|
||||
_canCloseAlert._context = {delegate:aDelegate, selector:aSelector, context:context};
|
||||
|
||||
[_canCloseAlert runModal];
|
||||
}
|
||||
|
||||
- (void)alertDidEnd:(CPAlert)alert returnCode:(int)returnCode
|
||||
{
|
||||
if (alert !== _canCloseAlert)
|
||||
return;
|
||||
|
||||
var delegate = alert._context.delegate,
|
||||
selector = alert._context.selector,
|
||||
context = alert._context.context;
|
||||
|
||||
if (returnCode === 0)
|
||||
[self saveDocumentWithDelegate:delegate didSaveSelector:selector contextInfo:context];
|
||||
else
|
||||
objj_msgSend(delegate, selector, self, returnCode === 2, context);
|
||||
|
||||
_canCloseAlert = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPReadSessionMake = function(aType, aDelegate, aDidReadSelector, aContextInfo)
|
||||
{
|
||||
return { fileType:aType, delegate:aDelegate, didReadSelector:aDidReadSelector, contextInfo:aContextInfo };
|
||||
|
||||
+184
-34
@@ -20,14 +20,18 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
import <Foundation/CPBundle.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPBundle.j>
|
||||
|
||||
@import "CPDocument.j"
|
||||
@import "CPOpenPanel.j";
|
||||
|
||||
import "CPDocument.j"
|
||||
|
||||
var CPSharedDocumentController = nil;
|
||||
|
||||
/*
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPDocumentController
|
||||
This class is responsible for managing an application's open documents.
|
||||
*/
|
||||
@implementation CPDocumentController : CPObject
|
||||
@@ -36,10 +40,10 @@ var CPSharedDocumentController = nil;
|
||||
CPArray _documentTypes;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
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 <objj>CPDocumentController</objj>
|
||||
@return a CPDocumentController
|
||||
*/
|
||||
+ (id)sharedDocumentController
|
||||
{
|
||||
@@ -70,12 +74,12 @@ var CPSharedDocumentController = nil;
|
||||
|
||||
// Creating and Opening Documents
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the document matching the specified URL. This
|
||||
method searches documents already open. It does not
|
||||
open the document at the URL if it is not already open.
|
||||
@param aURL the url of the document
|
||||
@return the document, or <code>nil</code> if such a document is not open
|
||||
@return the document, or \c nil if such a document is not open
|
||||
*/
|
||||
- (CPDocument)documentForURL:(CPURL)aURL
|
||||
{
|
||||
@@ -93,7 +97,7 @@ var CPSharedDocumentController = nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates a new document of the specified type.
|
||||
@param aType the type of the new document
|
||||
@param shouldDisplay whether to display the document on screen
|
||||
@@ -110,11 +114,11 @@ var CPSharedDocumentController = nil;
|
||||
[theDocument makeWindowControllers];
|
||||
[theDocument showWindows];
|
||||
}
|
||||
|
||||
|
||||
return theDocument;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates a document of the specified type.
|
||||
@param aType the document type
|
||||
@param anError not used
|
||||
@@ -125,7 +129,7 @@ var CPSharedDocumentController = nil;
|
||||
return [[[self documentClassForType:aType] alloc] initWithType:aType error:anError];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Opens the document at the specified URL.
|
||||
@param anAbsoluteURL the path to the document's file
|
||||
@param shouldDisplay whether to display the document on screen
|
||||
@@ -135,33 +139,38 @@ var CPSharedDocumentController = nil;
|
||||
- (CPDocument)openDocumentWithContentsOfURL:(CPURL)anAbsoluteURL display:(BOOL)shouldDisplay error:(CPError)anError
|
||||
{
|
||||
var result = [self documentForURL:anAbsoluteURL];
|
||||
|
||||
|
||||
if (!result)
|
||||
{
|
||||
var type = [self typeForContentsOfURL:anAbsoluteURL error:anError];
|
||||
|
||||
|
||||
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];
|
||||
}
|
||||
else if (shouldDisplay)
|
||||
[result showWindows];
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Loads a document for a specified URL with it's content
|
||||
retrieved from another URL.
|
||||
@param anAbsoluteURL the document URL
|
||||
@param absoluteContentsURL the location of the document's contents
|
||||
@param anError not used
|
||||
@return the loaded document or <code>nil</code> if there was an error
|
||||
@return the loaded document or \c nil if there was an error
|
||||
*/
|
||||
- (CPDocument)reopenDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL error:(CPError)anError
|
||||
{
|
||||
return [self makeDocumentForURL:anAbsoluteURL withContentsOfURL:absoluteContentsURL ofType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] delegate:self didReadSelector:@selector(document:didRead:contextInfo:) contextInfo:nil];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates a document from the contents at the specified URL.
|
||||
Notifies the provided delegate with the provided selector afterwards.
|
||||
@param anAbsoluteURL the location of the document data
|
||||
@@ -175,7 +184,7 @@ var CPSharedDocumentController = nil;
|
||||
return [[[self documentClassForType:aType] alloc] initWithContentsOfURL:anAbsoluteURL ofType:aType delegate:aDelegate didReadSelector:aSelector contextInfo:aContextInfo];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates a document from the contents of a URL, and sets
|
||||
the document's URL location as another URL.
|
||||
@param anAbsoluteURL the document's location
|
||||
@@ -184,7 +193,7 @@ var CPSharedDocumentController = nil;
|
||||
@param aDelegate receives a callback after the load has completed
|
||||
@param aSelector the selector to invoke for the callback
|
||||
@param aContextInfo an object passed as an argument for the callback
|
||||
@return a new document or <code>nil</code> if there was an error
|
||||
@return a new document or \c nil if there was an error
|
||||
*/
|
||||
- (CPDocument)makeDocumentForURL:(CPURL)anAbsoluteURL withContentsOfURL:(CPURL)absoluteContentsURL ofType:(CPString)aType delegate:(id)aDelegate didReadSelector:(SEL)aSelector contextInfo:(id)aContextInfo
|
||||
{
|
||||
@@ -200,14 +209,13 @@ var CPSharedDocumentController = nil;
|
||||
if (!didRead)
|
||||
return;
|
||||
|
||||
[self addDocument:aDocument];
|
||||
[aDocument makeWindowControllers];
|
||||
|
||||
if ([aContextInfo objectForKey:@"shouldDisplay"])
|
||||
[aDocument showWindows];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Opens a new document in the application.
|
||||
@param aSender the requesting object
|
||||
*/
|
||||
@@ -216,9 +224,23 @@ var CPSharedDocumentController = nil;
|
||||
[self openUntitledDocumentOfType:[[_documentTypes objectAtIndex:0] objectForKey:@"CPBundleTypeName"] display:YES];
|
||||
}
|
||||
|
||||
-(void)openDocument:(id)aSender
|
||||
{
|
||||
var openPanel = [CPOpenPanel openPanel];
|
||||
|
||||
[openPanel runModal];
|
||||
|
||||
var URLs = [openPanel URLs],
|
||||
index = 0,
|
||||
count = [URLs count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
[self openDocumentWithContentsOfURL:[CPURL URLWithString:URLs[index]] display:YES error:nil];
|
||||
}
|
||||
|
||||
// Managing Documents
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the array of all documents being managed. This is
|
||||
the same as all open documents in the application.
|
||||
*/
|
||||
@@ -227,8 +249,8 @@ var CPSharedDocumentController = nil;
|
||||
return _documents;
|
||||
}
|
||||
|
||||
/*
|
||||
Adds <code>aDocument</code> under the control of the receiver.
|
||||
/*!
|
||||
Adds \c aDocument under the control of the receiver.
|
||||
@param aDocument the document to add
|
||||
*/
|
||||
- (void)addDocument:(CPDocument)aDocument
|
||||
@@ -236,8 +258,8 @@ var CPSharedDocumentController = nil;
|
||||
[_documents addObject:aDocument];
|
||||
}
|
||||
|
||||
/*
|
||||
Removes <code>aDocument</code> from the control of the receiver.
|
||||
/*!
|
||||
Removes \c aDocument from the control of the receiver.
|
||||
@param aDocument the document to remove
|
||||
*/
|
||||
- (void)removeDocument:(CPDocument)aDocument
|
||||
@@ -255,7 +277,8 @@ var CPSharedDocumentController = nil;
|
||||
var index = 0,
|
||||
count = _documentTypes.length,
|
||||
|
||||
extension = [[anAbsoluteURL pathExtension] lowercaseString];
|
||||
extension = [[anAbsoluteURL pathExtension] lowercaseString],
|
||||
starType = nil;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
@@ -265,12 +288,17 @@ var CPSharedDocumentController = nil;
|
||||
extensionCount = extensions.length;
|
||||
|
||||
for (; extensionIndex < extensionCount; ++extensionIndex)
|
||||
if ([extensions[extensionIndex] lowercaseString] == extension)
|
||||
{
|
||||
var thisExtension = [extensions[extensionIndex] lowercaseString];
|
||||
if (thisExtension === extension)
|
||||
return [documentType objectForKey:@"CPBundleTypeName"];
|
||||
|
||||
if (thisExtension === "****")
|
||||
starType = [documentType objectForKey:@"CPBundleTypeName"];
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME?
|
||||
return [self defaultType];//nil;
|
||||
return starType || [self defaultType];
|
||||
}
|
||||
|
||||
// Managing Document Types
|
||||
@@ -292,10 +320,10 @@ var CPSharedDocumentController = nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the <objj>CPDocument</objj> subclass associated with <code>aType</code>.
|
||||
/*!
|
||||
Returns the CPDocument subclass associated with \c aType.
|
||||
@param aType the type of document
|
||||
@return a Cappuccino Class object, or <code>nil</code> if no match was found
|
||||
@return a Cappuccino Class object, or \c nil if no match was found
|
||||
*/
|
||||
- (Class)documentClassForType:(CPString)aType
|
||||
{
|
||||
@@ -305,3 +333,125 @@ var CPSharedDocumentController = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPDocumentController (Closing)
|
||||
|
||||
- (void)closeAllDocumentsWithDelegate:(id)aDelegate didCloseAllSelector:(SEL)didCloseSelector contextInfo:(Object)info
|
||||
{
|
||||
var context = {
|
||||
delegate: aDelegate,
|
||||
selector: didCloseSelector,
|
||||
context: info
|
||||
};
|
||||
|
||||
[self _closeDocumentsStartingWith:nil shouldClose:YES context:context];
|
||||
}
|
||||
|
||||
// Recursive callback method. Start it by passing in a document of nil.
|
||||
- (void)_closeDocumentsStartingWith:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
|
||||
{
|
||||
if (shouldClose)
|
||||
{
|
||||
[aDocument close];
|
||||
|
||||
if ([[self documents] count] > 0)
|
||||
{
|
||||
[[[self documents] lastObject] canCloseDocumentWithDelegate:self
|
||||
shouldCloseSelector:@selector(_closeDocumentsStartingWith:shouldClose:context:)
|
||||
contextInfo:context];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ([context.delegate respondsToSelector:context.selector])
|
||||
objj_msgSend(context.delegate, context.selector, self, [[self documents] count] === 0, context.context);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPDocumentController (Recents)
|
||||
|
||||
- (CPArray)recentDocumentURLs
|
||||
{
|
||||
// FIXME move this to CP land
|
||||
if (typeof window["cpRecentDocumentURLs"] === 'function')
|
||||
return window.cpRecentDocumentURLs();
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
- (void)clearRecentDocuments:(id)sender
|
||||
{
|
||||
if (typeof window["cpClearRecentDocuments"] === 'function')
|
||||
window.cpClearRecentDocuments();
|
||||
|
||||
[self _updateRecentDocumentsMenu];
|
||||
}
|
||||
|
||||
- (void)noteNewRecentDocument:(CPDocument)aDocument
|
||||
{
|
||||
[self noteNewRecentDocumentURL:[[aDocument fileURL] absoluteString]];
|
||||
}
|
||||
|
||||
- (void)noteNewRecentDocumentURL:(CPString)aURL
|
||||
{
|
||||
if (typeof window["cpNoteNewRecentDocumentPath"] === 'function')
|
||||
window.cpNoteNewRecentDocumentPath(aURL);
|
||||
|
||||
[self _updateRecentDocumentsMenu];
|
||||
}
|
||||
|
||||
- (void)_removeAllRecentDocumentsFromMenu:(CPMenu)aMenu
|
||||
{
|
||||
var items = [aMenu itemArray],
|
||||
count = [items count];
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var item = items[count];
|
||||
|
||||
if ([item action] === @selector(_openRecentDocument:))
|
||||
[aMenu removeItemAtIndex:count];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_updateRecentDocumentsMenu
|
||||
{
|
||||
var menu = [[CPApp mainMenu] _menuWithName:@"_CPRecentDocumentsMenu"],
|
||||
recentDocuments = [self recentDocumentURLs],
|
||||
menuItems = [menu itemArray],
|
||||
documentCount = [recentDocuments count],
|
||||
menuItemCount = [menuItems count];
|
||||
|
||||
[self _removeAllRecentDocumentsFromMenu:menu];
|
||||
|
||||
if (menuItemCount)
|
||||
{
|
||||
if (!documentCount)
|
||||
{
|
||||
if ([menuItems[0] isSeparatorItem])
|
||||
[menu removeItemAtIndex:0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (![menuItems[0] isSeparatorItem])
|
||||
[menu insertItem:[CPMenuItem separatorItem] atIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
while (documentCount--)
|
||||
{
|
||||
var path = recentDocuments[documentCount],
|
||||
item = [[CPMenuItem alloc] initWithTitle:[path lastPathComponent] action:@selector(_openRecentDocument:) keyEquivalent:nil];
|
||||
|
||||
[item setTag:path];
|
||||
[menu insertItem:item atIndex:0];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_openRecentDocument:(id)sender
|
||||
{
|
||||
[self openDocumentWithContentsOfURL:[sender tag] display:YES error:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+311
-177
@@ -20,143 +20,136 @@
|
||||
* 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 <AppKit/CPView.j>
|
||||
@import <AppKit/CPEvent.j>
|
||||
@import <AppKit/CPPasteboard.j>
|
||||
@import <AppKit/CPImageView.j>
|
||||
|
||||
#import "CoreGraphics/CGGeometry.h"
|
||||
#import "Platform/Platform.h"
|
||||
|
||||
|
||||
CPDragOperationNone = 0,
|
||||
CPDragOperationCopy = 1 << 1,
|
||||
CPDragOperationLink = 1 << 1,
|
||||
CPDragOperationGeneric = 1 << 2,
|
||||
CPDragOperationPrivate = 1 << 3,
|
||||
CPDragOperationMove = 1 << 4,
|
||||
CPDragOperationDelete = 1 << 5,
|
||||
CPDragOperationEvery = -1;
|
||||
|
||||
#define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window])
|
||||
|
||||
var CPDragServerPreviousEvent = nil,
|
||||
CPDragServerAutoscrollInterval = nil;
|
||||
/*
|
||||
var CPDragServerAutoscroll = function()
|
||||
{
|
||||
[CPDragServerSource autoscroll:CPDragServerPreviousEvent];
|
||||
}
|
||||
|
||||
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 CPDragServerView = nil,
|
||||
CPDragServerSource = nil,
|
||||
CPDragServerWindow = nil,
|
||||
CPDragServerOffset = nil,
|
||||
CPDragServerLocation = nil,
|
||||
CPDragServerPasteboard = nil,
|
||||
CPDragServerDestination = nil,
|
||||
CPDragServerDraggingInfo = nil;
|
||||
|
||||
var CPDragServerIsDraggingImage = NO,
|
||||
|
||||
CPDragServerShouldSendDraggedViewMovedTo = NO,
|
||||
CPDragServerShouldSendDraggedImageMovedTo = NO,
|
||||
|
||||
CPDragServerShouldSendDraggedViewEndedAtOperation = NO,
|
||||
CPDragServerShouldSendDraggedImageEndedAtOperation = NO;
|
||||
|
||||
var CPDragServerStartDragging = function(anEvent)
|
||||
{
|
||||
CPDragServerUpdateDragging(anEvent);
|
||||
}
|
||||
|
||||
var CPDragServerUpdateDragging = function(anEvent)
|
||||
{
|
||||
// If this is a mouse up, then complete the drag.
|
||||
if([anEvent type] == CPLeftMouseUp)
|
||||
{
|
||||
CPDragServerLocation = [[CPDragServerDestination window] convertBridgeToBase:[[anEvent window] convertBaseToBridge:[anEvent locationInWindow]]];
|
||||
|
||||
[CPDragServerView removeFromSuperview];
|
||||
[CPSharedDragServer._dragWindow orderOut:nil];
|
||||
|
||||
if (CPDragServerDestination &&
|
||||
(![CPDragServerDestination respondsToSelector:@selector(prepareForDragOperation:)] || [CPDragServerDestination prepareForDragOperation:CPDragServerDraggingInfo]) &&
|
||||
(![CPDragServerDestination respondsToSelector:@selector(performDragOperation:)] || [CPDragServerDestination performDragOperation:CPDragServerDraggingInfo]) &&
|
||||
[CPDragServerDestination respondsToSelector:@selector(concludeDragOperation:)])
|
||||
[CPDragServerDestination concludeDragOperation:CPDragServerDraggingInfo];
|
||||
|
||||
if (CPDragServerShouldSendDraggedImageEndedAtOperation)
|
||||
[CPDragServerSource draggedImage:[CPDragServerView image] endedAt:CPDragServerLocation operation:NO];
|
||||
else if (CPDragServerShouldSendDraggedViewEndedAtOperation)
|
||||
[CPDragServerSource draggedView:CPDragServerView endedAt:CPDragServerLocation operation:NO];
|
||||
|
||||
CPDragServerIsDraggingImage = NO;
|
||||
CPDragServerDestination = nil;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're not a mouse up, then we're going to want to grab the next event.
|
||||
[CPApp setCallback:CPDragServerUpdateDragging
|
||||
forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask
|
||||
untilDate:nil inMode:0 dequeue:NO];
|
||||
|
||||
var location = [anEvent locationInWindow],
|
||||
operation =
|
||||
bridgeLocation = [[anEvent window] convertBaseToBridge:location];
|
||||
|
||||
// We have to convert base to bridge since the drag event comes from the source window, not the drag window.
|
||||
var draggingDestination = [[CPDOMWindowBridge sharedDOMWindowBridge] _dragHitTest:bridgeLocation pasteboard:CPDragServerPasteboard];
|
||||
|
||||
CPDragServerLocation = [[CPDragServerDestination window] convertBridgeToBase:bridgeLocation];
|
||||
|
||||
if(draggingDestination != CPDragServerDestination)
|
||||
{
|
||||
if (CPDragServerDestination && [CPDragServerDestination respondsToSelector:@selector(draggingExited:)])
|
||||
[CPDragServerDestination draggingExited:CPDragServerDraggingInfo];
|
||||
|
||||
CPDragServerDestination = draggingDestination;
|
||||
|
||||
if (CPDragServerDestination && [CPDragServerDestination respondsToSelector:@selector(draggingEntered:)])
|
||||
[CPDragServerDestination draggingEntered:CPDragServerDraggingInfo];
|
||||
}
|
||||
else if (CPDragServerDestination && [CPDragServerDestination respondsToSelector:@selector(draggingUpdated:)])
|
||||
[CPDragServerDestination draggingUpdated:CPDragServerDraggingInfo];
|
||||
|
||||
location.x -= CPDragServerOffset.x;
|
||||
location.y -= CPDragServerOffset.y;
|
||||
|
||||
[CPDragServerView setFrameOrigin:location];
|
||||
|
||||
if (CPDragServerShouldSendDraggedImageMovedTo)
|
||||
[CPDragServerSource draggedImage:[CPDragServerView image] movedTo:location];
|
||||
else if (CPDragServerShouldSendDraggedViewMovedTo)
|
||||
[CPDragServerSource draggedView:CPDragServerView movedTo:location];
|
||||
}
|
||||
var CPDragServerSource = nil;
|
||||
var CPDragServerDraggingInfo = nil;
|
||||
|
||||
/*
|
||||
<objj>CPDraggingInfo</objj> is a container of information about a specific dragging session.
|
||||
CPDraggingInfo is a container of information about a specific dragging session.
|
||||
@ignore
|
||||
*/
|
||||
@implementation CPDraggingInfo : CPObject
|
||||
{
|
||||
CPWindow _window;
|
||||
}
|
||||
|
||||
/*
|
||||
Initializes the receiver with the window
|
||||
*/
|
||||
- (id)initWithWindow:(CPWindow)aWindow
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_window = aWindow;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
- (id)draggingSource
|
||||
{
|
||||
return CPDragServerSource;
|
||||
}
|
||||
|
||||
- (CPPoint)draggingLocation
|
||||
{
|
||||
return CPDragServerLocation;
|
||||
}
|
||||
|
||||
- (CPPasteboard)draggingPasteboard
|
||||
{
|
||||
return CPDragServerPasteboard;
|
||||
if ([CPPlatform supportsDragAndDrop])
|
||||
return [_CPDOMDataTransferPasteboard DOMDataTransferPasteboard];
|
||||
|
||||
return [[CPDragServer sharedDragServer] draggingPasteboard];
|
||||
}
|
||||
|
||||
- (id)draggingSource
|
||||
{
|
||||
return [[CPDragServer sharedDragServer] draggingSource];
|
||||
}
|
||||
|
||||
/*
|
||||
- (unsigned)draggingSourceOperationMask
|
||||
*/
|
||||
|
||||
- (CPPoint)draggingLocation
|
||||
{
|
||||
return [[CPDragServer sharedDragServer] draggingLocation];
|
||||
}
|
||||
|
||||
- (CPWindow)draggingDestinationWindow
|
||||
{
|
||||
return DRAGGING_WINDOW([[CPDragServer sharedDragServer] draggingDestination]);
|
||||
}
|
||||
|
||||
- (CPImage)draggedImage
|
||||
{
|
||||
return [[self draggedView] image];
|
||||
}
|
||||
|
||||
- (CGPoint)draggedImageLocation
|
||||
{
|
||||
return [self draggedViewLocation];
|
||||
}
|
||||
|
||||
- (CPView)draggedView
|
||||
{
|
||||
return [[CPDragServer sharedDragServer] draggedView];
|
||||
}
|
||||
|
||||
- (CGPoint)draggedViewLocation
|
||||
{
|
||||
var dragServer = [CPDragServer sharedDragServer];
|
||||
|
||||
return [DRAGGING_WINDOW([dragServer draggingDestination]) convertPlatformWindowToBase:[[dragServer draggedView] frame].origin];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPDraggingSource_draggedImage_movedTo_ = 1 << 0,
|
||||
CPDraggingSource_draggedImage_endAt_operation_ = 1 << 1,
|
||||
CPDraggingSource_draggedView_movedTo_ = 1 << 2,
|
||||
CPDraggingSource_draggedView_endedAt_operation_ = 1 << 3;
|
||||
|
||||
@implementation CPDragServer : CPObject
|
||||
{
|
||||
CPWindow _dragWindow;
|
||||
CPImageView _imageView;
|
||||
BOOL _isDragging @accessors(readonly, getter=isDragging);
|
||||
|
||||
CPWindow _draggedWindow @accessors(readonly, getter=draggedWindow);
|
||||
CPView _draggedView @accessors(readonly, getter=draggedView);
|
||||
CPImageView _imageView;
|
||||
|
||||
BOOL _isDraggingImage;
|
||||
|
||||
CGSize _draggingOffset @accessors(readonly, getter=draggingOffset);
|
||||
|
||||
CPPasteboard _draggingPasteboard @accessors(readonly, getter=draggingPasteboard);
|
||||
|
||||
id _draggingSource @accessors(readonly, getter=draggingSource);
|
||||
unsigned _implementedDraggingSourceMethods;
|
||||
|
||||
CGPoint _draggingLocation;
|
||||
id _draggingDestination;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -165,9 +158,9 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
*/
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [CPDragServer class])
|
||||
if (self !== [CPDragServer class])
|
||||
return;
|
||||
|
||||
|
||||
CPDragServerDraggingInfo = [[CPDraggingInfo alloc] init];
|
||||
}
|
||||
|
||||
@@ -175,7 +168,7 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
{
|
||||
if (!CPSharedDragServer)
|
||||
CPSharedDragServer = [[CPDragServer alloc] init];
|
||||
|
||||
|
||||
return CPSharedDragServer;
|
||||
}
|
||||
|
||||
@@ -185,17 +178,105 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_dragWindow = [[CPWindow alloc] initWithContentRect:CPRectMakeZero() styleMask:CPBorderlessWindowMask];
|
||||
[_dragWindow setLevel:CPDraggingWindowLevel];
|
||||
_draggedWindow = [[CPWindow alloc] initWithContentRect:_CGRectMakeZero() styleMask:CPBorderlessWindowMask];
|
||||
|
||||
[_draggedWindow setLevel:CPDraggingWindowLevel];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
- (CGPoint)draggingLocation
|
||||
{
|
||||
return _draggingLocation
|
||||
}
|
||||
|
||||
- (void)draggingStartedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation
|
||||
{
|
||||
if (_isDraggingImage)
|
||||
{
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedImage:beganAt:)])
|
||||
[_draggingSource draggedImage:[_draggedView image] beganAt:aLocation];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedView:beganAt:)])
|
||||
[_draggingSource draggedView:_draggedView beganAt:aLocation];
|
||||
}
|
||||
|
||||
if (![CPPlatform supportsDragAndDrop])
|
||||
[_draggedWindow orderFront:self];
|
||||
}
|
||||
|
||||
- (void)draggingSourceUpdatedWithGlobalLocation:(CGPoint)aGlobalLocation
|
||||
{
|
||||
if (![CPPlatform supportsDragAndDrop])
|
||||
[_draggedWindow setFrameOrigin:_CGPointMake(aGlobalLocation.x - _draggingOffset.width, aGlobalLocation.y - _draggingOffset.height)];
|
||||
|
||||
if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_movedTo_)
|
||||
[_draggingSource draggedImage:[_draggedView image] movedTo:aGlobalLocation];
|
||||
|
||||
else if (_implementedDraggingSourceMethods & CPDraggingSource_draggedView_movedTo_)
|
||||
[_draggingSource draggedView:_draggedView movedTo:aGlobalLocation];
|
||||
}
|
||||
|
||||
- (CPDragOperation)draggingUpdatedInPlatformWindow:(CPPlatformWindow)aPlatformWindow location:(CGPoint)aLocation
|
||||
{
|
||||
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]];
|
||||
|
||||
if (draggingDestination)
|
||||
_draggingLocation = [DRAGGING_WINDOW(draggingDestination) convertPlatformWindowToBase:aLocation];
|
||||
|
||||
if(draggingDestination !== _draggingDestination)
|
||||
{
|
||||
if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingExited:)])
|
||||
[_draggingDestination draggingExited:CPDragServerDraggingInfo];
|
||||
|
||||
_draggingDestination = draggingDestination;
|
||||
|
||||
if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingEntered:)])
|
||||
dragOperation = [_draggingDestination draggingEntered:CPDragServerDraggingInfo];
|
||||
}
|
||||
else if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingUpdated:)])
|
||||
dragOperation = [_draggingDestination draggingUpdated:CPDragServerDraggingInfo];
|
||||
|
||||
if (!_draggingDestination)
|
||||
dragOperation = CPDragOperationNone;
|
||||
|
||||
return dragOperation;
|
||||
}
|
||||
|
||||
- (void)draggingEndedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation operation:(CPDragOperation)anOperation
|
||||
{
|
||||
[_draggedView removeFromSuperview];
|
||||
|
||||
if (![CPPlatform supportsDragAndDrop])
|
||||
[_draggedWindow orderOut:self];
|
||||
|
||||
if (_implementedDraggingSourceMethods & CPDraggingSource_draggedImage_endAt_operation_)
|
||||
[_draggingSource draggedImage:[_draggedView image] endedAt:aLocation operation:anOperation];
|
||||
|
||||
else if (_implementedDraggingSourceMethods & CPDraggingSource_draggedView_endedAt_operation_)
|
||||
[_draggingSource draggedView:_draggedView endedAt:aLocation operation:anOperation];
|
||||
|
||||
_isDragging = NO;
|
||||
}
|
||||
|
||||
- (void)performDragOperationInPlatformWindow:(CPPlatformWindow)aPlatformWindow
|
||||
{
|
||||
if (_draggingDestination &&
|
||||
(![_draggingDestination respondsToSelector:@selector(prepareForDragOperation:)] || [_draggingDestination prepareForDragOperation:CPDragServerDraggingInfo]) &&
|
||||
(![_draggingDestination respondsToSelector:@selector(performDragOperation:)] || [_draggingDestination performDragOperation:CPDragServerDraggingInfo]) &&
|
||||
[_draggingDestination respondsToSelector:@selector(concludeDragOperation:)])
|
||||
[_draggingDestination concludeDragOperation:CPDragServerDraggingInfo];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initiates a drag session.
|
||||
@param aView the view being dragged
|
||||
@param aWindow the window where the drag source is
|
||||
@@ -204,53 +285,73 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
@param anEvent
|
||||
@param aPasteboard the pasteboard that contains the drag data
|
||||
@param aSourceObject the object where the drag started
|
||||
@param slideBack if <code>YES</code>, <code>aView</code> slides back to
|
||||
@param slideBack if \c YES, \c aView slides back to
|
||||
its origin on a failed drop
|
||||
*/
|
||||
- (void)dragView:(CPView)aView fromWindow:(CPWindow)aWindow at:(CGPoint)viewLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
|
||||
- (void)dragView:(CPView)aView fromWindow:(CPWindow)aWindow at:(CGPoint)viewLocation offset:(CGSize)mouseOffset event:(CPEvent)mouseDownEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
|
||||
{
|
||||
var eventLocation = [anEvent locationInWindow];
|
||||
|
||||
CPDragServerView = aView;
|
||||
CPDragServerSource = aSourceObject;
|
||||
CPDragServerWindow = aWindow;
|
||||
CPDragServerOffset = CPPointMake(eventLocation.x - viewLocation.x, eventLocation.y - viewLocation.y);
|
||||
CPDragServerPasteboard = [CPPasteboard pasteboardWithName:CPDragPboard];//aPasteboard;
|
||||
_isDragging = YES;
|
||||
|
||||
[_dragWindow setFrameSize:CGSizeMakeCopy([[CPDOMWindowBridge sharedDOMWindowBridge] frame].size)];
|
||||
[_dragWindow orderFront:self];
|
||||
_draggedView = aView;
|
||||
_draggingPasteboard = aPasteboard || [CPPasteboard pasteboardWithName:CPDragPboard];
|
||||
_draggingSource = aSourceObject;
|
||||
_draggingDestination = nil;
|
||||
|
||||
[aView setFrameOrigin:viewLocation];
|
||||
[[_dragWindow contentView] addSubview:aView];
|
||||
// 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.
|
||||
var mouseDownWindow = [mouseDownEvent window],
|
||||
mouseDownEventLocation = [mouseDownEvent locationInWindow];
|
||||
|
||||
if (CPDragServerIsDraggingImage)
|
||||
if (mouseDownEventLocation)
|
||||
{
|
||||
if ([CPDragServerSource respondsToSelector:@selector(draggedImage:beganAt:)])
|
||||
[CPDragServerSource draggedImage:[aView image] beganAt:viewLocation];
|
||||
|
||||
CPDragServerShouldSendDraggedImageMovedTo = [CPDragServerSource respondsToSelector:@selector(draggedImage:movedTo:)];
|
||||
CPDragServerShouldSendDraggedImageEndedAtOperation = [CPDragServerSource respondsToSelector:@selector(draggedImage:endAt:operation:)];
|
||||
|
||||
CPDragServerShouldSendDraggedViewMovedTo = NO;
|
||||
CPDragServerShouldSendDraggedViewEndedAtOperation = NO;
|
||||
if (mouseDownWindow)
|
||||
mouseDownEventLocation = [mouseDownWindow convertBaseToGlobal:mouseDownEventLocation];
|
||||
|
||||
_draggingOffset = _CGSizeMake(mouseDownEventLocation.x - viewLocation.x, mouseDownEventLocation.y - viewLocation.y);
|
||||
}
|
||||
else
|
||||
_draggingOffset = _CGSizeMakerZero();
|
||||
|
||||
if ([CPPlatform isBrowser])
|
||||
[_draggedWindow setPlatformWindow:[aWindow platformWindow]];
|
||||
|
||||
[aView setFrameOrigin:_CGPointMakeZero()];
|
||||
|
||||
var mouseLocation = [CPEvent mouseLocation];
|
||||
|
||||
// Place it where the mouse pointer is.
|
||||
[_draggedWindow setFrameOrigin:_CGPointMake(mouseLocation.x - _draggingOffset.width, mouseLocation.y - _draggingOffset.height)];
|
||||
[_draggedWindow setFrameSize:[aView frame].size];
|
||||
|
||||
[[_draggedWindow contentView] addSubview:aView];
|
||||
|
||||
_implementedDraggingSourceMethods = 0;
|
||||
|
||||
if (_draggedView === _imageView)
|
||||
{
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedImage:movedTo:)])
|
||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_movedTo_;
|
||||
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedImage:endAt:operation:)])
|
||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedImage_endAt_operation_;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([CPDragServerSource respondsToSelector:@selector(draggedView:beganAt:)])
|
||||
[CPDragServerSource draggedView:aView beganAt:viewLocation];
|
||||
|
||||
CPDragServerShouldSendDraggedViewMovedTo = [CPDragServerSource respondsToSelector:@selector(draggedView:movedTo:)];
|
||||
CPDragServerShouldSendDraggedViewEndedAtOperation = [CPDragServerSource respondsToSelector:@selector(draggedView:endedAt:operation:)];
|
||||
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedView:movedTo:)])
|
||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedView_movedTo_;
|
||||
|
||||
CPDragServerShouldSendDraggedImageMovedTo = NO;
|
||||
CPDragServerShouldSendDraggedImageEndedAtOperation = NO;
|
||||
if ([_draggingSource respondsToSelector:@selector(draggedView:endedAt:operation:)])
|
||||
_implementedDraggingSourceMethods |= CPDraggingSource_draggedView_endedAt_operation_;
|
||||
}
|
||||
|
||||
CPDragServerStartDragging(anEvent);
|
||||
if (![CPPlatform supportsDragAndDrop])
|
||||
{
|
||||
[self draggingStartedInPlatformWindow:[aWindow platformWindow] globalLocation:mouseLocation];
|
||||
[self trackDragging:mouseDownEvent];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initiates a drag session.
|
||||
@param anImage the image to be dragged
|
||||
@param aWindow the source window of the drag session
|
||||
@@ -259,43 +360,76 @@ var CPDragServerUpdateDragging = function(anEvent)
|
||||
@param anEvent
|
||||
@param aPasteboard the pasteboard where the drag data is located
|
||||
@param aSourceObject the object where the drag started
|
||||
@param slideBack if <code>YES</code>, <code>aView</code> slides back to
|
||||
@param slideBack if \c YES, \c aView slides back to
|
||||
its origin on a failed drop
|
||||
*/
|
||||
- (void)dragImage:(CPImage)anImage fromWindow:(CPWindow)aWindow at:(CGPoint)imageLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack
|
||||
{
|
||||
CPDragServerIsDraggingImage = YES;
|
||||
|
||||
_isDraggingImage = YES;
|
||||
|
||||
var imageSize = [anImage size];
|
||||
|
||||
if (!_imageView)
|
||||
_imageView = [[CPImageView alloc] initWithFrame:CPRectMakeZero()];
|
||||
|
||||
_imageView = [[CPImageView alloc] initWithFrame:_CGRectMake(0.0, 0.0, imageSize.width, imageSize.height)];
|
||||
|
||||
[_imageView setImage:anImage];
|
||||
[_imageView setFrameSize:CGSizeMakeCopy([anImage size])];
|
||||
|
||||
|
||||
[self dragView:_imageView fromWindow:aWindow at:imageLocation offset:mouseOffset event:anEvent pasteboard:aPasteboard source:aSourceObject slideBack:slideBack];
|
||||
}
|
||||
|
||||
- (void)trackDragging:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type],
|
||||
platformWindow = [_draggedWindow platformWindow],
|
||||
platformWindowLocation = [[anEvent window] convertBaseToPlatformWindow:[anEvent locationInWindow]];
|
||||
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
[self performDragOperationInPlatformWindow:platformWindow];
|
||||
[self draggingEndedInPlatformWindow:platformWindow globalLocation:platformWindowLocation operation:CPDragOperationNone];
|
||||
|
||||
// Stop tracking events.
|
||||
return;
|
||||
}
|
||||
|
||||
[self draggingSourceUpdatedWithGlobalLocation:platformWindowLocation];
|
||||
[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
|
||||
untilDate:nil inMode:0 dequeue:NO];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPView (CPDraggingAdditions)
|
||||
@implementation CPWindow (CPDraggingAdditions)
|
||||
|
||||
/* @ignore */
|
||||
- (CPView)_dragHitTest:(CPPoint)aPoint pasteboard:(CPPasteboard)aPasteboard
|
||||
- (id)_dragHitTest:(CGPoint)aPoint pasteboard:(CPPasteboard)aPasteboard
|
||||
{
|
||||
if(!CPRectContainsPoint(_frame, aPoint) || self == CPDragServerView)
|
||||
// If none of our views or ourselves has registered for drag events...
|
||||
if (!_inclusiveRegisteredDraggedTypes)
|
||||
return nil;
|
||||
|
||||
var view = nil,
|
||||
i = [_subviews count],
|
||||
adjustedPoint = CPPointMake(aPoint.x - CPRectGetMinX(_frame), aPoint.y - CPRectGetMinY(_frame));
|
||||
|
||||
while (i--)
|
||||
if (view = [_subviews[i] _dragHitTest:adjustedPoint pasteboard:aPasteboard])
|
||||
return view;
|
||||
// We don't need to do this because the only place this gets called
|
||||
// -_dragHitTest: in CPPlatformWindow does this already. Perhaps to
|
||||
// be safe?
|
||||
// if (![self containsPoint:aPoint])
|
||||
// return nil;
|
||||
|
||||
if ([aPasteboard availableTypeFromArray:_registeredDraggedTypes])
|
||||
var adjustedPoint = [self convertPlatformWindowToBase:aPoint],
|
||||
hitView = [_windowView hitTest:adjustedPoint];
|
||||
|
||||
while (hitView && ![aPasteboard availableTypeFromArray:[hitView registeredDraggedTypes]])
|
||||
hitView = [hitView superview];
|
||||
|
||||
if (hitView)
|
||||
return hitView;
|
||||
|
||||
if ([aPasteboard availableTypeFromArray:[self registeredDraggedTypes]])
|
||||
return self;
|
||||
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
+102
-130
@@ -20,115 +20,39 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
|
||||
CPLeftMouseDown = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPLeftMouseUp = 2;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPRightMouseDown = 3;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPRightMouseUp = 4;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPMouseMoved = 5;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPLeftMouseDragged = 6;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPRightMouseDragged = 7;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPMouseEntered = 8;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPMouseExited = 9;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPKeyDown = 10;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPKeyUp = 11;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPFlagsChanged = 12;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPAppKitDefined = 13;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPSystemDefined = 14;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPApplicationDefined = 15;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPPeriodic = 16;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPCursorUpdate = 17;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPScrollWheel = 22;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPOtherMouseDown = 25;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPOtherMouseUp = 26;
|
||||
/*
|
||||
@global
|
||||
@group CPEventType
|
||||
*/
|
||||
CPOtherMouseDragged = 27;
|
||||
|
||||
// iPhone Event Types
|
||||
CPTouchStart = 28;
|
||||
CPTouchMove = 29;
|
||||
CPTouchEnd = 30;
|
||||
CPTouchCancel = 31;
|
||||
|
||||
|
||||
CPAlphaShiftKeyMask = 1 << 16;
|
||||
CPShiftKeyMask = 1 << 17;
|
||||
@@ -174,12 +98,18 @@ CPDOMEventKeyPress = "keypress";
|
||||
CPDOMEventCopy = "copy";
|
||||
CPDOMEventPaste = "paste";
|
||||
CPDOMEventScrollWheel = "mousewheel";
|
||||
CPDOMEventTouchStart = "touchstart";
|
||||
CPDOMEventTouchMove = "touchmove";
|
||||
CPDOMEventTouchEnd = "touchend";
|
||||
CPDOMEventTouchCancel = "touchcancel";
|
||||
|
||||
var _CPEventPeriodicEventPeriod = 0,
|
||||
_CPEventPeriodicEventTimer = nil;
|
||||
|
||||
/*
|
||||
<objj>CPEvent</objj> encapsulates the details of a Cappuccino keyboard or mouse event.
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPEvent
|
||||
CPEvent encapsulates the details of a Cappuccino keyboard or mouse event.
|
||||
*/
|
||||
@implementation CPEvent : CPObject
|
||||
{
|
||||
@@ -192,6 +122,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
unsigned _clickCount;
|
||||
float _pressure;
|
||||
CPWindow _window;
|
||||
Number _windowNumber;
|
||||
CPString _characters;
|
||||
CPString _charactersIgnoringModifiers
|
||||
BOOL _isARepeat;
|
||||
@@ -203,20 +134,20 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
float _deltaZ;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates a new keyboard event.
|
||||
@param anEventType the event type. Must be one of <objj>CPKeyDown</objj>, <objj>CPKeyUp</objj> or <objj>CPFlagsChanged</objj>
|
||||
@param aPoint the location of the cursor in the window specified by <code>aWindowNumber</code>
|
||||
@param modifierFlags a bitwise combination of the modifiers specified in the <objj>CPEvent</objj> globals
|
||||
@param anEventType the event type. Must be one of CPKeyDown, CPKeyUp or CPFlagsChanged
|
||||
@param aPoint the location of the cursor in the window specified by \c aWindowNumber
|
||||
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
||||
@param aTimestamp the time the event occurred
|
||||
@param aWindowNumber the number of the <objj>CPWindow</objj> where the event occurred
|
||||
@param aWindowNumber the number of the CPWindow where the event occurred
|
||||
@param aGraphicsContext the graphics context where the event occurred
|
||||
@param characters the characters associated with the event
|
||||
@param unmodCharacters the string of keys pressed without the presence of any modifiers other than Shift
|
||||
@param repeatKey <code>YES</code> if this is caused by the system repeat as opposed to the user pressing the key again
|
||||
@param repeatKey \c YES if this is caused by the system repeat as opposed to the user pressing the key again
|
||||
@param code a number associated with the keyboard key of this event
|
||||
@throws CPInternalInconsistencyException if <code>anEventType</code> is not a <objj>CPKeyDown</objj>,
|
||||
<objj>CPKeyUp</objj> or <objj>CPFlagsChanged</objj>
|
||||
@throws CPInternalInconsistencyException if \c anEventType is not a CPKeyDown,
|
||||
CPKeyUp or CPFlagsChanged
|
||||
@return the keyboard event
|
||||
*/
|
||||
+ (CPEvent)keyEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned int)modifierFlags
|
||||
@@ -228,13 +159,13 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
characters:characters charactersIgnoringModifiers:unmodCharacters isARepeat:repeatKey keyCode:code];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates a new mouse event
|
||||
@param anEventType the event type
|
||||
@param aPoint the location of the cursor in the window specified by <code>aWindowNumber</code>
|
||||
@param modifierFlags a bitwise combination of the modifiers specified in the <objj>CPEvent</objj> globals
|
||||
@param aPoint the location of the cursor in the window specified by \c aWindowNumber
|
||||
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
||||
@param aTimestamp the time the event occurred
|
||||
@param aWindowNumber the number of the <objj>CPWindow</objj> where the event occurred
|
||||
@param aWindowNumber the number of the CPWindow where the event occurred
|
||||
@param aGraphicsContext the graphics context where the event occurred
|
||||
@param anEventNumber a number for this event
|
||||
@param aClickCount the number of clicks that caused this event
|
||||
@@ -250,13 +181,13 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber clickCount:aClickCount pressure:aPressure];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates a new custom event
|
||||
@param anEventType the event type. Must be one of <objj>CPAppKitDefined</objj>, <objj>CPSystemDefined</objj>, <objj>CPApplicationDefined</objj> or <objj>CPPeriodic</objj>
|
||||
@param aLocation the location of the cursor in the window specified by <code>aWindowNumber</code>
|
||||
@param modifierFlags a bitwise combination of the modifiers specified in the <objj>CPEvent</objj> globals
|
||||
@param anEventType the event type. Must be one of CPAppKitDefined, CPSystemDefined, CPApplicationDefined or CPPeriodic
|
||||
@param aLocation the location of the cursor in the window specified by \c aWindowNumber
|
||||
@param modifierFlags a bitwise combination of the modifiers specified in the CPEvent globals
|
||||
@param aTimestamp the time the event occurred
|
||||
@param aWindowNumber the number of the <objj>CPWindow</objj> where the event occurred
|
||||
@param aWindowNumber the number of the CPWindow where the event occurred
|
||||
@param aGraphicsContext the graphics context where the event occurred
|
||||
@param aSubtype a numeric identifier to differentiate this event from other custom events
|
||||
@param aData1 more data that describes the event
|
||||
@@ -313,7 +244,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
_charactersIgnoringModifiers = unmodCharacters;
|
||||
_isARepeat = isARepeat;
|
||||
_keyCode = code;
|
||||
_window = [CPApp windowWithWindowNumber:aWindowNumber];
|
||||
_windowNumber = aWindowNumber;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -341,20 +272,20 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the location of the mouse (for mouse events).
|
||||
If this is not a mouse event, it returns <code>nil</code>.
|
||||
If <code>window</code> returns <code>nil</code>, then
|
||||
If this is not a mouse event, it returns \c nil.
|
||||
If \c window returns \c nil, then
|
||||
the mouse coordinates will be based on the screen coordinates.
|
||||
Otherwise, the coordinates are relative to the window's coordinates.
|
||||
@return the location of the mouse, or <code>nil</code> for non-mouse events.
|
||||
@return the location of the mouse, or \c nil for non-mouse events.
|
||||
*/
|
||||
- (CGPoint)locationInWindow
|
||||
{
|
||||
return _location;
|
||||
return _CGPointMakeCopy(_location);
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns event information as a bit mask
|
||||
*/
|
||||
- (unsigned)modifierFlags
|
||||
@@ -362,7 +293,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _modifierFlags;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the time the event occurred
|
||||
*/
|
||||
- (CPTimeInterval)timestamp
|
||||
@@ -370,7 +301,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _timestamp;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the type of event.
|
||||
*/
|
||||
- (CPEventType)type
|
||||
@@ -378,15 +309,18 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _type;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the event's associated window
|
||||
*/
|
||||
- (CPWindow)window
|
||||
{
|
||||
if (!_window)
|
||||
_window = [CPApp windowWithWindowNumber:_windowNumber];
|
||||
|
||||
return _window;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
The number of the window associated with the event.
|
||||
*/
|
||||
- (int)windowNumber
|
||||
@@ -395,7 +329,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
}
|
||||
|
||||
// Mouse Event Information
|
||||
/*
|
||||
/*!
|
||||
Returns the button number for the mouse that generated the event.
|
||||
*/
|
||||
- (int)buttonNumber
|
||||
@@ -403,7 +337,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _buttonNumber;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the number of clicks that caused this event. (mouse only)
|
||||
*/
|
||||
- (int)clickCount
|
||||
@@ -411,7 +345,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _clickCount;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the characters associated with this event (keyboard only)
|
||||
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
||||
*/
|
||||
@@ -420,7 +354,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _characters;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the character ignoring any modifiers (except shift).
|
||||
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
||||
*/
|
||||
@@ -429,8 +363,8 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _charactersIgnoringModifiers;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the keyboard event was caused by the key being held down.
|
||||
/*!
|
||||
Returns \c YES if the keyboard event was caused by the key being held down.
|
||||
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
||||
*/
|
||||
- (BOOL)isARepeat
|
||||
@@ -438,7 +372,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _isARepeat;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the key's key code.
|
||||
@throws CPInternalInconsistencyException if this method is called on a non-key event
|
||||
*/
|
||||
@@ -447,6 +381,18 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _keyCode;
|
||||
}
|
||||
|
||||
+ (CGPoint)mouseLocation
|
||||
{
|
||||
// FIXME: this is incorrect, we shouldn't depend on the current event.
|
||||
var event = [CPApp currentEvent],
|
||||
eventWindow = [event window];
|
||||
|
||||
if (eventWindow)
|
||||
return [eventWindow convertBaseToGlobal:[event locationInWindow]];
|
||||
|
||||
return [event locationInWindow];
|
||||
}
|
||||
|
||||
- (float)pressure
|
||||
{
|
||||
return _pressure;
|
||||
@@ -461,7 +407,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
}
|
||||
|
||||
// Getting Scroll Wheel Event Infomration
|
||||
/*
|
||||
/*!
|
||||
Returns the change in the x-axis for a mouse event.
|
||||
*/
|
||||
- (float)deltaX
|
||||
@@ -469,7 +415,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _deltaX;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the change in the y-axis for a mouse event.
|
||||
*/
|
||||
- (float)deltaY
|
||||
@@ -477,7 +423,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _deltaY;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the change in the x-axis for a mouse event.
|
||||
*/
|
||||
- (float)deltaZ
|
||||
@@ -485,8 +431,16 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _deltaZ;
|
||||
}
|
||||
|
||||
/*
|
||||
Generates periodic events every <code>aPeriod</code> seconds.
|
||||
- (BOOL)_couldBeKeyEquivalent
|
||||
{
|
||||
// FIXME: More cases? Space?
|
||||
return _type === CPKeyDown &&
|
||||
_modifierFlags & (CPCommandKeyMask | CPControlKeyMask) &&
|
||||
[_characters length] > 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Generates 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
|
||||
*/
|
||||
@@ -498,7 +452,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
_CPEventPeriodicEventTimer = window.setTimeout(function() { _CPEventPeriodicEventTimer = window.setInterval(_CPEventFirePeriodEvent, aPeriod * 1000.0); }, aDelay * 1000.0);
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Stops the periodic events from being generated
|
||||
*/
|
||||
+ (void)stopPeriodicEvents
|
||||
@@ -516,7 +470,25 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
function _CPEventFirePeriodEvent()
|
||||
{
|
||||
[CPApp sendEvent:[CPEvent otherEventWithType:CPPeriodic location:_CGPointMakeZero() modifierFlags:0 timestamp:0 windowNumber:0 context:nil subtype:0 data1:0 data2:0]];
|
||||
|
||||
[[CPRunLoop currentRunLoop] performSelectors];
|
||||
}
|
||||
|
||||
var CPEventClass = [CPEvent class];
|
||||
|
||||
function _CPEventFromNativeMouseEvent(aNativeEvent, anEventType, aPoint, modifierFlags, aTimestamp, aWindowNumber, aGraphicsContext, anEventNumber, aClickCount, aPressure)
|
||||
{
|
||||
aNativeEvent.isa = CPEventClass;
|
||||
|
||||
aNativeEvent._type = anEventType;
|
||||
aNativeEvent._location = aPoint;
|
||||
aNativeEvent._modifierFlags = modifierFlags;
|
||||
aNativeEvent._timestamp = aTimestamp;
|
||||
aNativeEvent._windowNumber = aWindowNumber;
|
||||
aNativeEvent._window = nil;
|
||||
aNativeEvent._context = aGraphicsContext;
|
||||
aNativeEvent._eventNumber = anEventNumber;
|
||||
aNativeEvent._clickCount = aClickCount;
|
||||
aNativeEvent._pressure = aPressure;
|
||||
|
||||
return aNativeEvent;
|
||||
}
|
||||
|
||||
|
||||
+16
-1
@@ -20,19 +20,34 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPFlashMovie
|
||||
|
||||
CPFlashMovie is used to represent a Flash movie in the Cappuccino framework.
|
||||
*/
|
||||
@implementation CPFlashMovie : CPObject
|
||||
{
|
||||
CPString _fileName;
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a new Flash movie with the swf at \c aFileName.
|
||||
@param aFilename the swf to load
|
||||
@return the initialized CPFlashMovie
|
||||
*/
|
||||
+ (id)flashMovieWithFile:(CPString)aFileName
|
||||
{
|
||||
return [[self alloc] initWithFile:aFileName];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the Flash movie.
|
||||
@param aFilename the swf to load
|
||||
@return the initialized CPFlashMovie
|
||||
*/
|
||||
- (id)initWithFile:(CPString)aFileName
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPDOMWindowBridge.j"
|
||||
import "CPFlashMovie.j"
|
||||
import "CPView.j"
|
||||
@import "CPFlashMovie.j"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
@implementation CPFlashView : CPView
|
||||
{
|
||||
CPFlashMovie _flashMovie;
|
||||
@@ -61,7 +63,7 @@ import "CPView.j"
|
||||
_DOMEmbedElement = document.createElement("embed");
|
||||
|
||||
_DOMEmbedElement.type = "application/x-shockwave-flash";
|
||||
_DOMEmbedElement.wmode = "transparent";
|
||||
_DOMEmbedElement.setAttribute("wmode", "transparent");
|
||||
_DOMEmbedElement.width = "100%";
|
||||
_DOMEmbedElement.height = "100%";
|
||||
|
||||
@@ -94,17 +96,17 @@ import "CPView.j"
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
[[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES];
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
[[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES];
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
}
|
||||
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
{
|
||||
[[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES];
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+15
-12
@@ -25,8 +25,11 @@ var _CPFonts = {};
|
||||
|
||||
#define _CPCachedFont(aName, aSize, isBold) _CPFonts[(isBold ? @"bold " : @"") + ROUND(aSize) + @"px '" + aName + @"'"]
|
||||
|
||||
/*
|
||||
The <objj>CPFont</objj> class allows control of the fonts used for displaying text anywhere on the screen. The primary method for getting a particular font is through one of the class methods that take a name and/or size as arguments, and return the appropriate <objj>CPFont</objj>.
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPFont
|
||||
|
||||
The CPFont class allows control of the fonts used for displaying text anywhere on the screen. The primary method for getting a particular font is through one of the class methods that take a name and/or size as arguments, and return the appropriate CPFont.
|
||||
*/
|
||||
@implementation CPFont : CPObject
|
||||
{
|
||||
@@ -37,7 +40,7 @@ var _CPFonts = {};
|
||||
CPString _cssString;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a font with the specified name and size.
|
||||
@param aName the name of the font
|
||||
@param aSize the size of the font (in points)
|
||||
@@ -48,7 +51,7 @@ var _CPFonts = {};
|
||||
return _CPCachedFont(aName, aSize, NO) || [[CPFont alloc] _initWithName:aName size:aSize bold:NO];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a bold font with the specified name and size.
|
||||
@param aName the name of the font
|
||||
@param aSize the size of the font (in points)
|
||||
@@ -59,7 +62,7 @@ var _CPFonts = {};
|
||||
return _CPCachedFont(aName, aSize, YES) || [[CPFont alloc] _initWithName:aName size:aSize bold:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the system font scaled to the specified size
|
||||
@param aSize the size of the font (in points)
|
||||
@return the requested system font
|
||||
@@ -69,7 +72,7 @@ var _CPFonts = {};
|
||||
return _CPCachedFont(_CPFontSystemFontFace, aSize, NO) || [[CPFont alloc] _initWithName:_CPFontSystemFontFace size:aSize bold:NO];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the bold system font scaled to the specified size
|
||||
@param aSize the size of the font (in points)
|
||||
@return the requested bold system font
|
||||
@@ -79,7 +82,7 @@ var _CPFonts = {};
|
||||
return _CPCachedFont(_CPFontSystemFontFace, aSize, YES) || [[CPFont alloc] _initWithName:_CPFontSystemFontFace size:aSize bold:YES];
|
||||
}
|
||||
|
||||
/** FIXME Font Descriptor
|
||||
/* FIXME Font Descriptor
|
||||
@ignore
|
||||
*/
|
||||
- (id)_initWithName:(CPString)aName size:(float)aSize bold:(BOOL)isBold
|
||||
@@ -100,7 +103,7 @@ var _CPFonts = {};
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the font size (in points)
|
||||
*/
|
||||
- (float)size
|
||||
@@ -108,7 +111,7 @@ var _CPFonts = {};
|
||||
return _size;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the font as a CSS string
|
||||
*/
|
||||
- (CPString)cssString
|
||||
@@ -116,7 +119,7 @@ var _CPFonts = {};
|
||||
return _cssString;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the font's family name
|
||||
*/
|
||||
- (CPString)familyName
|
||||
@@ -132,7 +135,7 @@ var CPFontNameKey = @"CPFontNameKey",
|
||||
|
||||
@implementation CPFont (CPCoding)
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the font from a coder.
|
||||
@param aCoder the coder from which to read the font data
|
||||
@return the initialized font
|
||||
@@ -144,7 +147,7 @@ var CPFontNameKey = @"CPFontNameKey",
|
||||
bold:[aCoder decodeBoolForKey:CPFontIsBoldKey]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Writes the font information out to a coder.
|
||||
@param aCoder the coder to which the data will be written
|
||||
*/
|
||||
|
||||
@@ -20,21 +20,24 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
import <AppKit/CPFont.j>
|
||||
@import <AppKit/CPFont.j>
|
||||
|
||||
|
||||
var CPSharedFontManager = nil,
|
||||
CPFontManagerFactory = Nil;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
@implementation CPFontManager : CPObject
|
||||
{
|
||||
CPArray _availableFonts;
|
||||
}
|
||||
|
||||
// Getting the Shared Font Manager
|
||||
/*
|
||||
/*!
|
||||
Returns the application's font manager. If the font
|
||||
manager does not exist yet, it will be created.
|
||||
*/
|
||||
@@ -47,7 +50,7 @@ var CPSharedFontManager = nil,
|
||||
}
|
||||
|
||||
// Changing the Default Font Conversion Classes
|
||||
/*
|
||||
/*!
|
||||
Sets the class that will be used to create the applcation's
|
||||
font manager.
|
||||
*/
|
||||
@@ -56,7 +59,7 @@ var CPSharedFontManager = nil,
|
||||
CPFontManagerFactory = aClass;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns an array of the available fonts
|
||||
*/
|
||||
- (CPArray)availableFonts
|
||||
@@ -84,7 +87,7 @@ var CPSharedFontManager = nil,
|
||||
return _availableFonts;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the available fonts matching the provided name.
|
||||
@param aFontName the name of the font
|
||||
*/
|
||||
|
||||
+107
-97
@@ -20,7 +20,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CGGeometry.j"
|
||||
@import "CGGeometry.j"
|
||||
|
||||
|
||||
CPMinXEdge = 0;
|
||||
CPMinYEdge = 1;
|
||||
@@ -32,7 +33,12 @@ CPMakePoint = CGPointMake;
|
||||
CPMakeSize = CGSizeMake;
|
||||
CPMakeRect = CGRectMake;
|
||||
|
||||
/**
|
||||
/*!
|
||||
@addtogroup appkit
|
||||
@{
|
||||
*/
|
||||
|
||||
/*!
|
||||
Creates a copy of a specified point and returns the copy
|
||||
@group CGPoint
|
||||
@param the point to be copied
|
||||
@@ -43,7 +49,7 @@ function CPPointCreateCopy(aPoint)
|
||||
return { x: aPoint.x, y: aPoint.y };
|
||||
}
|
||||
|
||||
/**
|
||||
/*!
|
||||
Makes a CGPoint object out of two numbers provided as arguments
|
||||
@group CGPoint
|
||||
@param x the x-coordinate of the CGPoint
|
||||
@@ -55,12 +61,12 @@ function CPPointMake(x, y)
|
||||
return { x: x, y: y };
|
||||
}
|
||||
|
||||
/*
|
||||
Makes a CGRect with an origin and size equal to <code>aRect</code> less the <code>dX/dY</code> insets specified.
|
||||
/*!
|
||||
Makes a CGRect with an origin and size equal to \c aRect less the \c dX/dY insets specified.
|
||||
@param dX the size of the inset in the x-axis
|
||||
@param dY the size of the inset in the y-axis
|
||||
@group CGRect
|
||||
@return CGRect a rectangle like <code>aRect</code> with an inset
|
||||
@return CGRect a rectangle like \c aRect with an inset
|
||||
*/
|
||||
function CPRectInset(aRect, dX, dY)
|
||||
{
|
||||
@@ -68,7 +74,7 @@ function CPRectInset(aRect, dX, dY)
|
||||
aRect.size.width - 2 * dX, aRect.size.height - 2*dY);
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
@group CGRect
|
||||
@ignore
|
||||
@return void
|
||||
@@ -80,7 +86,7 @@ function CPRectIntegral(aRect)
|
||||
alert("CPRectIntegral unimplemented");
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the intersection of the two provided rectangles as a new rectangle
|
||||
@group CGRect
|
||||
@param lhsRect the first rectangle used for calculation
|
||||
@@ -100,7 +106,7 @@ function CPRectIntersection(lhsRect, rhsRect)
|
||||
return CPRectIsEmpty(intersection) ? CPRectMakeZero() : intersection;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates a copy of the provided rectangle
|
||||
@group CGRect
|
||||
@param aRect the CGRect that will be copied
|
||||
@@ -111,7 +117,7 @@ function CPRectCreateCopy(aRect)
|
||||
return { origin: CPPointCreateCopy(aRect.origin), size: CPSizeCreateCopy(aRect.size) };
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a CGRect made of the specified arguments
|
||||
@group CGRect
|
||||
@param x the x-coordinate of the rectangle's origin
|
||||
@@ -125,8 +131,8 @@ function CPRectMake(x, y, width, height)
|
||||
return { origin: CPPointMake(x, y), size: CPSizeMake(width, height) };
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a new rectangle with its origin offset by <code>dX</code> and <code>dY</code>.
|
||||
/*!
|
||||
Creates a new rectangle with its origin offset by \c dX and \c dY.
|
||||
@group CGRect
|
||||
@param aRect the rectangle to copy the origin and size from
|
||||
@param dX the amount added to the x-size of the new rectangle
|
||||
@@ -138,7 +144,7 @@ function CPRectOffset(aRect, dX, dY)
|
||||
return CPRectMake(aRect.origin.x + dX, aRect.origin.y + dY, aRect.size.width, aRect.size.height);
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
@group CGRect
|
||||
@param aRect a CGRect
|
||||
@return CGRect
|
||||
@@ -164,8 +170,8 @@ function CPRectStandardize(aRect)
|
||||
return standardized;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the smallest rectangle that can contain the two argument <code>CGRect</code>s.
|
||||
/*!
|
||||
Returns the smallest rectangle that can contain the two argument \c CGRects.
|
||||
@group CGRect
|
||||
@param lhsRect the first CGRect to use for the union calculation
|
||||
@param rhsRect the second CGRect to use for the union calculation
|
||||
@@ -181,7 +187,7 @@ function CPRectUnion(lhsRect, rhsRect)
|
||||
return CPRectMake(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates and returns a copy of the provided CGSize
|
||||
@group CGSize
|
||||
@param aSize the CGSize to copy
|
||||
@@ -192,7 +198,7 @@ function CPSizeCreateCopy(aSize)
|
||||
return { width: aSize.width, height: aSize.height };
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates and returns a new CGSize object from the provided dimensions.
|
||||
@group CGSize
|
||||
@param width the width for the new CGSize
|
||||
@@ -204,13 +210,13 @@ function CPSizeMake(width, height)
|
||||
return { width: width, height: height };
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the <objj>CGRect</objj>, <code>aRect</code>, contains
|
||||
the <objj>CGPoint</objj>, <code>aPoint</code>.
|
||||
/*!
|
||||
Returns \c YES if the CGRect, \c aRect, contains
|
||||
the CGPoint, \c aPoint.
|
||||
@param aRect the rectangle to test with
|
||||
@param aPoint the point to test with
|
||||
@group CGRect
|
||||
@return BOOL <code>YES</code> if the rectangle contains the point, <code>NO</code> otherwise.
|
||||
@return BOOL \c YES if the rectangle contains the point, \c NO otherwise.
|
||||
*/
|
||||
function CPRectContainsPoint(aRect, aPoint)
|
||||
{
|
||||
@@ -220,38 +226,38 @@ function CPRectContainsPoint(aRect, aPoint)
|
||||
aPoint.y < CPRectGetMaxY(aRect);
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a <code>BOOL</code> indicating whether <objj>CGRect</objj> <code>possibleOuter</code>
|
||||
contains <objj>CGRect</objj> <code>possibleInner</code>.
|
||||
/*!
|
||||
Returns a \c BOOL indicating whether CGRect \c possibleOuter
|
||||
contains CGRect \c possibleInner.
|
||||
@group CGRect
|
||||
@param possibleOuter the <objj>CGRect</objj> to test if <code>possibleInner</code> is inside of
|
||||
@param possibleInner the <objj>CGRect</objj> to test if it fits inside <code>possibleOuter</code>.
|
||||
@return BOOL <code>YES</code> if <code>possibleInner</code> fits inside <code>possibleOuter</code>.
|
||||
@param possibleOuter the CGRect to test if \c possibleInner is inside of
|
||||
@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)
|
||||
{
|
||||
return CPRectEqualToRect(CPUnionRect(lhsRect, rhsRect), rhsRect);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests whether the two <objj>CGPoint</objj>s are equal to each other by comparing their
|
||||
<code>x</code> and <code>y</code> members.
|
||||
/*!
|
||||
Tests whether the two CGPoints are equal to each other by comparing their
|
||||
\c x and \c y members.
|
||||
@group @CGPoint
|
||||
@param lhsPoint the first <objj>CGPoint</objj> to check
|
||||
@param rhsPoint the second <objj>CGPoint</objj> to check
|
||||
@return BOOL <code>YES</code> if the two points have the same x's, and the same y's.
|
||||
@param lhsPoint the first CGPoint to check
|
||||
@param rhsPoint the second CGPoint to check
|
||||
@return BOOL \c YES if the two points have the same x's, and the same y's.
|
||||
*/
|
||||
function CPPointEqualToPoint(lhsPoint, rhsPoint)
|
||||
{
|
||||
return lhsPoint.x == rhsPoint.x && lhsPoint.y == rhsPoint.y;
|
||||
}
|
||||
|
||||
/*
|
||||
Test whether the two <objj>CGRect</objj>s have the same origin and size
|
||||
/*!
|
||||
Test whether the two CGRects have the same origin and size
|
||||
@group CGRect
|
||||
@param lhsRect the first <objj>CGRect</objj> to compare
|
||||
@param rhsRect the second <objj>CGRect</objj> to compare
|
||||
@return BOOL <code>YES</code> if the two rectangles have the same origin and size. <code>NO</code>, otherwise.
|
||||
@param lhsRect the first CGRect to compare
|
||||
@param rhsRect the second CGRect to compare
|
||||
@return BOOL \c YES if the two rectangles have the same origin and size. \c NO, otherwise.
|
||||
*/
|
||||
function CPRectEqualToRect(lhsRect, rhsRect)
|
||||
{
|
||||
@@ -259,9 +265,9 @@ function CPRectEqualToRect(lhsRect, rhsRect)
|
||||
CPSizeEqualToSize(lhsRect.size, rhsRect.size);
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
@group CGRect
|
||||
@param aRect a <objj>CGRect</objj>
|
||||
@param aRect a CGRect
|
||||
@return int
|
||||
*/
|
||||
function CPRectGetHeight(aRect)
|
||||
@@ -269,9 +275,9 @@ function CPRectGetHeight(aRect)
|
||||
return aRect.size.height;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
@group CGRect
|
||||
@param aRect a <objj>CGRect</objj>
|
||||
@param aRect a CGRect
|
||||
@return int
|
||||
*/
|
||||
function CPRectGetMaxX(aRect)
|
||||
@@ -279,9 +285,9 @@ function CPRectGetMaxX(aRect)
|
||||
return aRect.origin.x + aRect.size.width;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
@group CGRect
|
||||
@param aRect a <objj>CGRect</objj>
|
||||
@param aRect a CGRect
|
||||
@return int
|
||||
*/
|
||||
function CPRectGetMaxY(aRect)
|
||||
@@ -289,9 +295,9 @@ function CPRectGetMaxY(aRect)
|
||||
return aRect.origin.y + aRect.size.height;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
@group CGRect
|
||||
@param aRect a <objj>CGRect</objj>
|
||||
@param aRect a CGRect
|
||||
@return float
|
||||
*/
|
||||
function CPRectGetMidX(aRect)
|
||||
@@ -299,9 +305,9 @@ function CPRectGetMidX(aRect)
|
||||
return aRect.origin.x + (aRect.size.width) / 2.0;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
@group CGRect
|
||||
@param aRect a <objj>CGRect</objj>
|
||||
@param aRect a CGRect
|
||||
@return float
|
||||
*/
|
||||
function CPRectGetMidY(aRect)
|
||||
@@ -309,9 +315,9 @@ function CPRectGetMidY(aRect)
|
||||
return aRect.origin.y + (aRect.size.height) / 2.0;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
@group CGRect
|
||||
@param aRect a <objj>CGRect</objj>
|
||||
@param aRect a CGRect
|
||||
@return int
|
||||
*/
|
||||
function CPRectGetMinX(aRect)
|
||||
@@ -319,9 +325,9 @@ function CPRectGetMinX(aRect)
|
||||
return aRect.origin.x;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
@group CGRect
|
||||
@param aRect a <objj>CGRect</objj>
|
||||
@param aRect a CGRect
|
||||
@return int
|
||||
*/
|
||||
function CPRectGetMinY(aRect)
|
||||
@@ -329,9 +335,9 @@ function CPRectGetMinY(aRect)
|
||||
return aRect.origin.y;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
@group CGRect
|
||||
@param aRect a <objj>CGRect</objj>
|
||||
@param aRect a CGRect
|
||||
@return int
|
||||
*/
|
||||
function CPRectGetWidth(aRect)
|
||||
@@ -339,77 +345,77 @@ function CPRectGetWidth(aRect)
|
||||
return aRect.size.width;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the two rectangles intersect
|
||||
/*!
|
||||
Returns \c YES if the two rectangles intersect
|
||||
@group CGRect
|
||||
@param lhsRect the first <objj>CGRect</objj>
|
||||
@param rhsRect the second <objj>CGRect</objj>
|
||||
@return BOOL <code>YES</code> if the two rectangles have any common spaces, and <code>NO</code>, otherwise.
|
||||
@param lhsRect the first CGRect
|
||||
@param rhsRect the second CGRect
|
||||
@return BOOL \c YES if the two rectangles have any common spaces, and \c NO, otherwise.
|
||||
*/
|
||||
function CPRectIntersectsRect(lhsRect, rhsRect)
|
||||
{
|
||||
return !CPRectIsEmpty(CPRectIntersection(lhsRect, rhsRect));
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the <objj>CGRect</objj> has no area.
|
||||
/*!
|
||||
Returns \c YES if the CGRect has no area.
|
||||
The test is performed by checking if the width and height are both zero.
|
||||
@group CGRect
|
||||
@param aRect the <objj>CGRect</objj> to test
|
||||
@return BOOL <code>YES</code> if the <objj>CGRect</objj> has no area, and <code>NO</code>, otherwise.
|
||||
@param aRect the CGRect to test
|
||||
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
|
||||
*/
|
||||
function CPRectIsEmpty(aRect)
|
||||
{
|
||||
return aRect.size.width <= 0.0 || aRect.size.height <= 0.0;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the <objj>CGRect</objj> has no area.
|
||||
/*!
|
||||
Returns \c YES if the CGRect has no area.
|
||||
The test is performed by checking if the width and height are both zero.
|
||||
@group CGRect
|
||||
@return BOOL <code>YES</code> if the <objj>CGRect</objj> has no area, and <code>NO</code>, otherwise.
|
||||
@return BOOL \c YES if the CGRect has no area, and \c NO, otherwise.
|
||||
*/
|
||||
function CPRectIsNull(aRect)
|
||||
{
|
||||
return aRect.size.width <= 0.0 || aRect.size.height <= 0.0;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the two <objj>CGSize</objj>s are identical.
|
||||
/*!
|
||||
Returns \c YES if the two CGSizes are identical.
|
||||
@group CGSize
|
||||
@param lhsSize the first <objj>CGSize</objj> to compare
|
||||
@param rhsSize the second <objj>CGSize</objj> to compare
|
||||
@return BOOL <code>YES</code> if the two sizes are identical. <code>NO</code>, otherwise.
|
||||
@param lhsSize the first CGSize to compare
|
||||
@param rhsSize the second CGSize to compare
|
||||
@return BOOL \c YES if the two sizes are identical. \c NO, otherwise.
|
||||
*/
|
||||
function CPSizeEqualToSize(lhsSize, rhsSize)
|
||||
{
|
||||
return lhsSize.width == rhsSize.width && lhsSize.height == rhsSize.height;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a human readable string of the provided <objj>CGPoint</objj>.
|
||||
/*!
|
||||
Returns a human readable string of the provided CGPoint.
|
||||
@group CGPoint
|
||||
@param aPoint the point to represent
|
||||
@return CGPoint a string representation of the <objj>CGPoint</objj>
|
||||
@return CGPoint a string representation of the CGPoint
|
||||
*/
|
||||
function CPStringFromPoint(aPoint)
|
||||
{
|
||||
return "{" + aPoint.x + ", " + aPoint.y + "}";
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a human readable string of the provided <objj>CGSize</objj>.
|
||||
/*!
|
||||
Returns a human readable string of the provided CGSize.
|
||||
@group CGSize
|
||||
@param aSize the size to represent
|
||||
@return CGSize a string representation of the <objj>CGSize</objj>
|
||||
@return CGSize a string representation of the CGSize
|
||||
*/
|
||||
function CPStringFromSize(aSize)
|
||||
{
|
||||
return "{" + aSize.width + ", " + aSize.height + "}";
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a human readable string of the provided <objj>CGRect</objj>.
|
||||
/*!
|
||||
Returns a human readable string of the provided CGRect.
|
||||
@group CGRect
|
||||
@param aRect the rectangle to represent
|
||||
@return CGString the string representation of the rectangle
|
||||
@@ -419,8 +425,8 @@ function CPStringFromRect(aRect)
|
||||
return "{" + CPStringFromPoint(aRect.origin) + ", " + CPStringFromSize(aRect.size) + "}";
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a <objj>CGPoint</objj> from a string with a comma separated pair of integers.
|
||||
/*!
|
||||
Returns a CGPoint from a string with a comma separated pair of integers.
|
||||
@group CGPoint
|
||||
@param aString a string containing two comma separated integers
|
||||
@return CGPoint the point object created from the string
|
||||
@@ -429,11 +435,11 @@ function CPPointFromString(aString)
|
||||
{
|
||||
var comma = aString.indexOf(',');
|
||||
|
||||
return { x:parseInt(aString.substr(1, comma - 1)), y:parseInt(aString.substring(comma + 1, aString.length)) };
|
||||
return { x:parseFloat(aString.substr(1, comma - 1), 10), y:parseFloat(aString.substring(comma + 1, aString.length), 10) };
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a <objj>CGSize</objj> from a string containing a pair of comma separated integers.
|
||||
/*!
|
||||
Returns a CGSize from a string containing a pair of comma separated integers.
|
||||
@group CGSize
|
||||
@param aString a string containing two comma separated integers
|
||||
@return CGSize the size object created from the string
|
||||
@@ -442,13 +448,13 @@ function CPSizeFromString(aString)
|
||||
{
|
||||
var comma = aString.indexOf(',');
|
||||
|
||||
return { width:parseInt(aString.substr(1, comma - 1)), height:parseInt(aString.substring(comma + 1, aString.length)) };
|
||||
return { width:parseFloat(aString.substr(1, comma - 1), 10), height:parseFloat(aString.substring(comma + 1, aString.length), 10) };
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a <objj>CGRect</objj> created from a string.
|
||||
/*!
|
||||
Returns a CGRect created from a string.
|
||||
@group CGRect
|
||||
@param aString a string in the form generated by <code>CPStringFromRect</code>
|
||||
@param aString a string in the form generated by \c CPStringFromRect
|
||||
@return CGRect the rectangle created from the string
|
||||
*/
|
||||
function CPRectFromString(aString)
|
||||
@@ -458,7 +464,7 @@ function CPRectFromString(aString)
|
||||
return { origin:CPPointFromString(aString.substr(1, comma - 1)), size:CPSizeFromString(aString.substring(comma + 2, aString.length)) };
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
@group CGPoint
|
||||
@param anEvent
|
||||
@return CGPoint
|
||||
@@ -468,18 +474,18 @@ function CPPointFromEvent(anEvent)
|
||||
return CPPointMake(anEvent.clientX, anEvent.clientY, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a zero sized <objj>CGSize</objj>.
|
||||
/*!
|
||||
Returns a zero sized CGSize.
|
||||
@group CGSize
|
||||
@return CGSize a size object with zeros for <code>width</code> and <code>height</code>
|
||||
@return CGSize a size object with zeros for \c width and \c height
|
||||
*/
|
||||
function CPSizeMakeZero()
|
||||
{
|
||||
return CPSizeMake(0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a rectangle at origin <code>(0,0)</code> and size of <code>(0,0)</code>.
|
||||
/*!
|
||||
Returns a rectangle at origin \c (0,0) and size of \c (0,0).
|
||||
@group CGRect
|
||||
@return CGRect a zeroed out CGRect
|
||||
*/
|
||||
@@ -488,12 +494,16 @@ function CPRectMakeZero()
|
||||
return CPRectMake(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a point located at <code>(0, 0)</code>.
|
||||
/*!
|
||||
Returns a point located at \c (0, 0).
|
||||
@group CGPoint
|
||||
@return CGPoint a point located at <code>(0, 0)</code>
|
||||
@return CGPoint a point located at \c (0, 0)
|
||||
*/
|
||||
function CPPointMakeZero()
|
||||
{
|
||||
return CPPointMake(0, 0, 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
@}
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
|
||||
var CPGraphicsContextCurrent = nil;
|
||||
|
||||
/*
|
||||
/*!
|
||||
@ingroup appkit
|
||||
Provides an interface for drawing to the screen.
|
||||
*/
|
||||
@implementation CPGraphicsContext : CPObject
|
||||
@@ -30,7 +31,7 @@ var CPGraphicsContextCurrent = nil;
|
||||
CPContext _graphicsPort;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the current graphics context.
|
||||
*/
|
||||
+ (CPGraphicsContext)currentContext
|
||||
@@ -38,7 +39,7 @@ var CPGraphicsContextCurrent = nil;
|
||||
return CPGraphicsContextCurrent;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the current graphics context
|
||||
*/
|
||||
+ (void)setCurrentContext:(CPGraphicsContext)aGraphicsContext
|
||||
@@ -46,7 +47,7 @@ var CPGraphicsContextCurrent = nil;
|
||||
CPGraphicsContextCurrent = aGraphicsContext;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates a graphics context with a provided port.
|
||||
@param aContext the context to initialize with
|
||||
@param aFlag whether the context should be flipped
|
||||
@@ -57,7 +58,7 @@ var CPGraphicsContextCurrent = nil;
|
||||
return [[self alloc] initWithGraphicsPort:aContext];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the context with a graphics port.
|
||||
@param aGraphicsPort the graphics port to initialize with
|
||||
@return the initialized context
|
||||
@@ -72,7 +73,7 @@ var CPGraphicsContextCurrent = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the graphics context's port.
|
||||
*/
|
||||
- (CGContext)graphicsPort
|
||||
|
||||
+98
-41
@@ -20,13 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPBundle.j>
|
||||
import <Foundation/CPNotificationCenter.j>
|
||||
import <Foundation/CPObject.j>
|
||||
import <Foundation/CPRunLoop.j>
|
||||
import <Foundation/CPString.j>
|
||||
@import <Foundation/CPBundle.j>
|
||||
@import <Foundation/CPNotificationCenter.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPRunLoop.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
import "CPGeometry.j"
|
||||
@import "CPGeometry.j"
|
||||
|
||||
CPImageLoadStatusInitialized = 0;
|
||||
CPImageLoadStatusLoading = 1;
|
||||
@@ -38,19 +38,26 @@ CPImageLoadStatusReadError = 6;
|
||||
|
||||
CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
|
||||
@implementation CPBundle (CPImageAdditions)
|
||||
|
||||
- (CPString)pathForResource:(CPString)aFilename
|
||||
function CPImageInBundle(aFilename, aSize, aBundle)
|
||||
{
|
||||
return [self bundlePath] + "Resources/" + aFilename;
|
||||
if (!aBundle)
|
||||
aBundle = [CPBundle mainBundle];
|
||||
|
||||
if (aSize)
|
||||
return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename] size:aSize];
|
||||
|
||||
return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:aFilename]];
|
||||
}
|
||||
|
||||
@end
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPImage
|
||||
|
||||
/*
|
||||
<objj>CPImage</objj> is used to represent images in the Cappuccino framework. It supports loading
|
||||
CPImage is used to represent images in the Cappuccino framework. It supports loading
|
||||
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,15 +81,20 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
Image _image;
|
||||
}
|
||||
|
||||
/*
|
||||
- (id)init
|
||||
{
|
||||
return [self initByReferencingFile:@"" size:CGSizeMake(-1, -1)];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the image, by associating it with a filename. The image
|
||||
denoted in <code>aFilename</code> is not actually loaded. It will
|
||||
denoted in \c aFilename is not actually loaded. It will
|
||||
be loaded once needed.
|
||||
@param aFilename the file containing the image
|
||||
@param aSize the image's size
|
||||
@return the initialized image
|
||||
*/
|
||||
- (CPImage)initByReferencingFile:(CPString)aFilename size:(CGSize)aSize
|
||||
- (id)initByReferencingFile:(CPString)aFilename size:(CGSize)aSize
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
@@ -96,13 +108,13 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the image. Loads the specified image into memory.
|
||||
@param aFilename the image to load
|
||||
@param aSize the size of the image
|
||||
@return the initialized image.
|
||||
*/
|
||||
- (CPImage)initWithContentsOfFile:(CPString)aFilename size:(CGSize)aSize
|
||||
- (id)initWithContentsOfFile:(CPString)aFilename size:(CGSize)aSize
|
||||
{
|
||||
self = [self initByReferencingFile:aFilename size:aSize];
|
||||
|
||||
@@ -112,15 +124,15 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the receiver with the contents of the specified
|
||||
image file. The method loads the data into memory.
|
||||
@param aFilename the file name of the image
|
||||
@return the initialized image
|
||||
*/
|
||||
- (CPImage)initWithContentsOfFile:(CPString)aFilename
|
||||
- (id)initWithContentsOfFile:(CPString)aFilename
|
||||
{
|
||||
self = [self initByReferencingFile:aFilename size: CGSizeMake(-1, -1)];
|
||||
self = [self initByReferencingFile:aFilename size:CGSizeMake(-1, -1)];
|
||||
|
||||
if (self)
|
||||
[self load];
|
||||
@@ -128,7 +140,7 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the path of the file associated with this image.
|
||||
*/
|
||||
- (CPString)filename
|
||||
@@ -136,7 +148,7 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
return _filename;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the size of the image.
|
||||
@param the size of the image
|
||||
*/
|
||||
@@ -145,7 +157,7 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
_size = CGSizeMakeCopy(aSize);
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the size of the image
|
||||
*/
|
||||
- (CGSize)size
|
||||
@@ -153,7 +165,7 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
return _size;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the receiver's delegate.
|
||||
@param the delegate
|
||||
*/
|
||||
@@ -162,7 +174,7 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
_delegate = aDelegate;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the receiver's delegate
|
||||
*/
|
||||
- (id)delegate
|
||||
@@ -170,19 +182,20 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the image data has already been loaded.
|
||||
/*!
|
||||
Returns the load status, which will be CPImageLoadStatusCompleted if the image data has already been loaded.
|
||||
*/
|
||||
- (BOOL)loadStatus
|
||||
- (unsigned)loadStatus
|
||||
{
|
||||
return _loadStatus;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Loads the image data from the file into memory. You
|
||||
should not call this method directly. Instead use
|
||||
one of the initializers.
|
||||
*/
|
||||
|
||||
- (void)load
|
||||
{
|
||||
if (_loadStatus == CPImageLoadStatusLoading || _loadStatus == CPImageLoadStatusCompleted)
|
||||
@@ -195,9 +208,41 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
var isSynchronous = YES;
|
||||
|
||||
// FIXME: We need a better/performance way of doing this.
|
||||
_image.onload = function () { if (isSynchronous) window.setTimeout(function() { [self _imageDidLoad] }, 0); else [self _imageDidLoad]; }
|
||||
_image.onerror = function () { if (isSynchronous) window.setTimeout(function() { [self _imageDidError] }, 0); else [self _imageDidError]; }
|
||||
_image.onabort = function () { if (isSynchronous) window.setTimeout(function() { [self _imageDidAbort] }, 0); else [self _imageDidAbort]; }
|
||||
_image.onload = function ()
|
||||
{
|
||||
if (isSynchronous)
|
||||
window.setTimeout(function() { [self _imageDidLoad]; }, 0);
|
||||
else
|
||||
{
|
||||
[self _imageDidLoad];
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
[self _derefFromImage];
|
||||
}
|
||||
|
||||
_image.onerror = function ()
|
||||
{
|
||||
if (isSynchronous)
|
||||
window.setTimeout(function() { [self _imageDidError]; }, 0);
|
||||
else
|
||||
{
|
||||
[self _imageDidError];
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
[self _derefFromImage];
|
||||
}
|
||||
|
||||
_image.onabort = function ()
|
||||
{
|
||||
if (isSynchronous)
|
||||
window.setTimeout(function() { [self _imageDidAbort]; }, 0);
|
||||
else
|
||||
{
|
||||
[self _imageDidAbort];
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
[self _derefFromImage];
|
||||
}
|
||||
|
||||
_image.src = _filename;
|
||||
|
||||
@@ -214,6 +259,14 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_derefFromImage
|
||||
{
|
||||
_image.onload = null;
|
||||
_image.onerror = null;
|
||||
_image.onabort = null;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_imageDidLoad
|
||||
{
|
||||
@@ -229,8 +282,6 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(imageDidLoad:)])
|
||||
[_delegate imageDidLoad:self];
|
||||
|
||||
[[CPRunLoop currentRunLoop] performSelectors];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
@@ -240,8 +291,6 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(imageDidError:)])
|
||||
[_delegate imageDidError:self];
|
||||
|
||||
[[CPRunLoop currentRunLoop] performSelectors];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
@@ -251,15 +300,13 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(imageDidAbort:)])
|
||||
[_delegate imageDidAbort:self];
|
||||
|
||||
[[CPRunLoop currentRunLoop] performSelectors];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPImage (CPCoding)
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the image with data from a coder.
|
||||
@param aCoder the coder from which to read the image data
|
||||
@return the initialized image
|
||||
@@ -269,7 +316,7 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Writes the image data from memory into the coder.
|
||||
@param aCoder the coder to which the data will be written
|
||||
*/
|
||||
@@ -300,6 +347,11 @@ CPImageDidLoadNotification = @"CPImageDidLoadNotification";
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPString)filename
|
||||
{
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (CPArray)imageSlices
|
||||
{
|
||||
return _imageSlices;
|
||||
@@ -364,6 +416,11 @@ var CPThreePartImageImageSlicesKey = @"CPThreePartImageImageSlicesKey",
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPString)filename
|
||||
{
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (CPArray)imageSlices
|
||||
{
|
||||
return _imageSlices;
|
||||
|
||||
+161
-88
@@ -20,11 +20,11 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPNotificationCenter.j>
|
||||
@import <Foundation/CPNotificationCenter.j>
|
||||
|
||||
import "CPControl.j"
|
||||
import "CPImage.j"
|
||||
import "CPShadowView.j"
|
||||
@import "CPControl.j"
|
||||
@import "CPImage.j"
|
||||
@import "CPShadowView.j"
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
#include "Platform/DOM/CPDOMDisplayServer.h"
|
||||
@@ -58,19 +58,21 @@ 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
|
||||
|
||||
This class is a control that displays an image.
|
||||
*/
|
||||
@implementation CPImageView : CPControl
|
||||
{
|
||||
CPImage _image;
|
||||
DOMElement _DOMImageElement;
|
||||
|
||||
CPImageScaling _imageScaling;
|
||||
|
||||
BOOL _hasShadow;
|
||||
CPView _shadowView;
|
||||
|
||||
|
||||
BOOL _isEditable;
|
||||
|
||||
CGRect _imageRect;
|
||||
}
|
||||
|
||||
@@ -85,7 +87,13 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
_DOMImageElement.style.position = "absolute";
|
||||
_DOMImageElement.style.left = "0px";
|
||||
_DOMImageElement.style.top = "0px";
|
||||
|
||||
|
||||
if ([CPPlatform supportsDragAndDrop])
|
||||
{
|
||||
_DOMImageElement.setAttribute("draggable", "true");
|
||||
_DOMImageElement.style["-khtml-user-drag"] = "element";
|
||||
}
|
||||
|
||||
CPDOMDisplayServerAppendChild(_DOMElement, _DOMImageElement);
|
||||
|
||||
_DOMImageElement.style.visibility = "hidden";
|
||||
@@ -95,65 +103,79 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the view's image.
|
||||
*/
|
||||
- (CPImage)image
|
||||
{
|
||||
return _image;
|
||||
return [self objectValue];
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the image for the view.
|
||||
@param anImage the view's image
|
||||
*/
|
||||
- (void)setImage:(CPImage)anImage
|
||||
{
|
||||
if (_image == anImage)
|
||||
[self setObjectValue:anImage];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (void)setObjectValue:(CPImage)anImage
|
||||
{
|
||||
var oldImage = [self objectValue];
|
||||
|
||||
if (oldImage === anImage)
|
||||
return;
|
||||
|
||||
[super setObjectValue:anImage];
|
||||
|
||||
var center = [CPNotificationCenter defaultCenter];
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
if (_image)
|
||||
[center removeObserver:self name:CPImageDidLoadNotification object:_image];
|
||||
if (oldImage)
|
||||
[defaultCenter removeObserver:self name:CPImageDidLoadNotification object:oldImage];
|
||||
|
||||
_image = anImage;
|
||||
_DOMImageElement.src = [anImage filename];
|
||||
|
||||
var size = [_image size];
|
||||
var newImage = [self objectValue];
|
||||
|
||||
if (size && size.width == -1 && size.height == -1)
|
||||
#if PLATFORM(DOM)
|
||||
_DOMImageElement.src = newImage ? [newImage filename] : "";
|
||||
#endif
|
||||
|
||||
var size = [newImage size];
|
||||
|
||||
if (size && size.width === -1 && size.height === -1)
|
||||
{
|
||||
[center addObserver:self selector:@selector(imageDidLoad:) name:CPImageDidLoadNotification object:_image];
|
||||
|
||||
[defaultCenter addObserver:self selector:@selector(imageDidLoad:) name:CPImageDidLoadNotification object:newImage];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMImageElement.width = 0;
|
||||
_DOMImageElement.height = 0;
|
||||
|
||||
#endif
|
||||
|
||||
[_shadowView setHidden:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self hideOrDisplayContents];
|
||||
[self tile];
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)imageDidLoad:(CPNotification)aNotification
|
||||
{
|
||||
[self hideOrDisplayContents];
|
||||
[self tile];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the image view draws with
|
||||
a drop shadow. The default is <code>NO</code>.
|
||||
/*!
|
||||
Returns \c YES if the image view draws with
|
||||
a drop shadow. The default is \c NO.
|
||||
*/
|
||||
- (BOOL)hasShadow
|
||||
{
|
||||
return _hasShadow;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets whether the image view should draw with a drop shadow.
|
||||
@param shouldHaveShadow whether the image view should have a shadow
|
||||
*/
|
||||
@@ -170,7 +192,8 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
|
||||
[self addSubview:_shadowView];
|
||||
|
||||
[self tile];
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -182,60 +205,48 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
[self hideOrDisplayContents];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the type of image scaling that should be used to
|
||||
render the image.
|
||||
@param anImageScaling the type of scaling to use
|
||||
*/
|
||||
- (void)setImageScaling:(CPImageScaling)anImageScaling
|
||||
{
|
||||
if (_imageScaling == anImageScaling)
|
||||
return;
|
||||
[super setImageScaling:anImageScaling];
|
||||
|
||||
_imageScaling = anImageScaling;
|
||||
|
||||
if (_imageScaling == CPScaleToFit)
|
||||
#if PLATFORM(DOM)
|
||||
if ([self currentValueForThemeAttribute:@"image-scaling"] === CPScaleToFit)
|
||||
{
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, 0.0, 0.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
[self tile];
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the image scaling method used to
|
||||
render this image.
|
||||
*/
|
||||
- (CPImageScaling)imageScaling
|
||||
{
|
||||
return _imageScaling;
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Toggles the display of the image view.
|
||||
*/
|
||||
- (void)hideOrDisplayContents
|
||||
{
|
||||
if (!_image)
|
||||
if (![self image])
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
_DOMImageElement.style.visibility = "hidden";
|
||||
#endif
|
||||
[_shadowView setHidden:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
_DOMImageElement.style.visibility = "visible";
|
||||
#endif
|
||||
[_shadowView setHidden:NO];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the view's image rectangle
|
||||
*/
|
||||
- (CGRect)imageRect
|
||||
@@ -243,15 +254,17 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
return _imageRect;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Add a description
|
||||
*/
|
||||
- (void)tile
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
if (!_image)
|
||||
if (![self image])
|
||||
return;
|
||||
|
||||
var bounds = [self bounds],
|
||||
image = [self image],
|
||||
imageScaling = [self currentValueForThemeAttribute:@"image-scaling"],
|
||||
x = 0.0,
|
||||
y = 0.0,
|
||||
insetWidth = (_hasShadow ? HORIZONTAL_SHADOW_INSET : 0.0),
|
||||
@@ -260,20 +273,22 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
boundsHeight = _CGRectGetHeight(bounds),
|
||||
width = boundsWidth - insetWidth,
|
||||
height = boundsHeight - insetHeight;
|
||||
|
||||
if (_imageScaling == CPScaleToFit)
|
||||
|
||||
if (imageScaling === CPScaleToFit)
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
_DOMImageElement.width = ROUND(width);
|
||||
_DOMImageElement.height = ROUND(height);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
var size = [_image size];
|
||||
var size = [image size];
|
||||
|
||||
if (size.width == -1 && size.height == -1)
|
||||
return;
|
||||
|
||||
if (_imageScaling == CPScaleProportionally)
|
||||
if (imageScaling === CPScaleProportionally)
|
||||
{
|
||||
// The max size it can be is size.width x size.height, so only
|
||||
// only proportion otherwise.
|
||||
@@ -292,9 +307,11 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
else
|
||||
height = width / imageRatio;
|
||||
}
|
||||
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMImageElement.width = ROUND(width);
|
||||
_DOMImageElement.height = ROUND(height);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -302,16 +319,20 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
height = size.height;
|
||||
}
|
||||
|
||||
if (_imageScaling == CPScaleNone)
|
||||
if (imageScaling == CPScaleNone)
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
_DOMImageElement.width = ROUND(size.width);
|
||||
_DOMImageElement.height = ROUND(size.height);
|
||||
#endif
|
||||
}
|
||||
|
||||
var x = (boundsWidth - width) / 2.0,
|
||||
y = (boundsHeight - height) / 2.0;
|
||||
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMImageElement, NULL, x, y);
|
||||
#endif
|
||||
}
|
||||
|
||||
_imageRect = _CGRectMake(x, y, width, height);
|
||||
@@ -320,45 +341,97 @@ var LEFT_SHADOW_INSET = 3.0,
|
||||
[_shadowView setFrame:_CGRectMake(x - LEFT_SHADOW_INSET, y - TOP_SHADOW_INSET, width + insetWidth, height + insetHeight)];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
// Should we do something with this event?
|
||||
[[self nextResponder] mouseDown:anEvent];
|
||||
}
|
||||
|
||||
- (void)setEditable:(BOOL)shouldBeEditable
|
||||
{
|
||||
if (_isEditable === shouldBeEditable)
|
||||
return;
|
||||
|
||||
_isEditable = shouldBeEditable;
|
||||
|
||||
if (_isEditable)
|
||||
[self registerForDraggedTypes:[CPImagesPboardType]];
|
||||
|
||||
else
|
||||
{
|
||||
var draggedTypes = [self registeredDraggedTypes];
|
||||
|
||||
[self unregisterDraggedTypes];
|
||||
|
||||
[draggedTypes removeObjectIdenticalTo:CPImagesPboardType];
|
||||
|
||||
[self registerForDraggedTypes:draggedTypes];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isEditable
|
||||
{
|
||||
return _isEditable;
|
||||
}
|
||||
|
||||
- (void)performDragOperation:(CPDraggingInfo)aSender
|
||||
{
|
||||
var images = [CPKeyedUnarchiver unarchiveObjectWithData:[[aSender draggingPasteboard] dataForType:CPImagesPboardType]];
|
||||
|
||||
if ([images count])
|
||||
[self setImage:images[0]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||
CPImageViewImageScalingKey = @"CPImageViewImageScalingKey",
|
||||
CPImageViewHasShadowKey = @"CPImageViewHasShadowKey";
|
||||
CPImageViewHasShadowKey = @"CPImageViewHasShadowKey",
|
||||
CPImageViewIsEditableKey = @"CPImageViewIsEditableKey";
|
||||
|
||||
@implementation CPImageView (CPCoding)
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the image view with the provided coder.
|
||||
@param aCoder the coder from which data will be read.
|
||||
@return the initialized image view
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
_DOMImageElement = document.createElement("img");
|
||||
_DOMImageElement.style.position = "absolute";
|
||||
_DOMImageElement.style.left = "0px";
|
||||
_DOMImageElement.style.top = "0px";
|
||||
_DOMImageElement.style.visibility = "hidden";
|
||||
if ([CPPlatform supportsDragAndDrop])
|
||||
{
|
||||
_DOMImageElement.setAttribute("draggable", "true");
|
||||
_DOMImageElement.style["-khtml-user-drag"] = "element";
|
||||
}
|
||||
#endif
|
||||
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_DOMImageElement = document.createElement("img");
|
||||
_DOMImageElement.style.position = "absolute";
|
||||
_DOMImageElement.style.left = "0px";
|
||||
_DOMImageElement.style.top = "0px";
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.appendChild(_DOMImageElement);
|
||||
_DOMImageElement.style.visibility = "hidden";
|
||||
|
||||
[self setImage:[aCoder decodeObjectForKey:CPImageViewImageKey]];
|
||||
|
||||
[self setImageScaling:[aCoder decodeIntForKey:CPImageViewImageScalingKey]];
|
||||
#endif
|
||||
|
||||
[self setHasShadow:[aCoder decodeBoolForKey:CPImageViewHasShadowKey]];
|
||||
|
||||
[self tile];
|
||||
if ([aCoder decodeBoolForKey:CPImageViewIsEditableKey] || NO)
|
||||
[self setEditable:YES];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Writes the image view out to the coder.
|
||||
@param aCoder the coder to which the image
|
||||
view will be written
|
||||
@@ -380,10 +453,10 @@ var CPImageViewImageKey = @"CPImageViewImageKey",
|
||||
if (_shadowView)
|
||||
_subviews = actualSubviews;
|
||||
|
||||
[aCoder encodeObject:_image forKey:CPImageViewImageKey];
|
||||
|
||||
[aCoder encodeInt:_imageScaling forKey:CPImageViewImageScalingKey];
|
||||
[aCoder encodeBool:_hasShadow forKey:CPImageViewHasShadowKey];
|
||||
|
||||
if (_isEditable)
|
||||
[aCoder encodeBool:_isEditable forKey:CPImageViewIsEditableKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,372 +0,0 @@
|
||||
/*
|
||||
* CPApplication.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Ross Boucher.
|
||||
* 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 <Foundation/Foundation.j>
|
||||
|
||||
import "CPView.j"
|
||||
import "CPButton.j"
|
||||
import "CPImage.j"
|
||||
import "CPImageView.j"
|
||||
import "CPColorPicker.j"
|
||||
import "CPColorPanel.j"
|
||||
import "CPTabView.j"
|
||||
import "CPTabViewItem.j"
|
||||
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
@implementation CPKulerColorPicker : CPColorPicker
|
||||
{
|
||||
CPView _contentView;
|
||||
CPTabView _tabView;
|
||||
|
||||
CPScrollView _searchView;
|
||||
CPScrollView _popularView;
|
||||
|
||||
CPView _searchView;
|
||||
DOMElement _FIXME_searchField;
|
||||
|
||||
CPURLConnection _searchConnection;
|
||||
CPURLConnection _popularConnection
|
||||
}
|
||||
|
||||
- (id)initWithPickerMask:(int)mask colorPanel:(CPColorPanel)owningColorPanel
|
||||
{
|
||||
return [super initWithPickerMask:mask colorPanel: owningColorPanel];
|
||||
}
|
||||
|
||||
-(id)initView
|
||||
{
|
||||
aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight);
|
||||
_contentView = [[CPView alloc] initWithFrame:aFrame];
|
||||
|
||||
_tabView = [[CPTabView alloc] initWithFrame:CGRectMake(0.0, 0.0, CGRectGetWidth(aFrame), CGRectGetHeight(aFrame) - 20)];
|
||||
[_tabView setAutoresizingMask: CPViewWidthSizable | CPViewHeightSizable];
|
||||
[_tabView setDelegate: self];
|
||||
[_contentView addSubview: _tabView];
|
||||
|
||||
var label = [[CPButton alloc] initWithFrame:CPRectMake((CPColorPickerViewWidth-150)/2, CPColorPickerViewHeight-20, 150, 20)];
|
||||
[label setAutoresizingMask: CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin];
|
||||
[label setFont:[CPFont boldSystemFontOfSize:10.0]];
|
||||
[label setTextColor: [CPColor whiteColor]];
|
||||
[label setTitle: @"Powered by Adobe Kuler"];
|
||||
[label setTarget: self];
|
||||
[label setAction: @selector(openKulerLink:)];
|
||||
[label setBordered: NO];
|
||||
[_contentView addSubview: label];
|
||||
|
||||
var searchThemeView = [[_CPKulerThemeView alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, 0)];
|
||||
[searchThemeView setDelegate: self];
|
||||
[searchThemeView setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
_searchView = [[CPScrollView alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, CPColorPickerViewHeight - 10)];
|
||||
[_searchView setDocumentView: searchThemeView];
|
||||
[_searchView setHasHorizontalScroller: NO];
|
||||
[_searchView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)];
|
||||
[[_searchView verticalScroller] setControlSize:CPSmallControlSize];
|
||||
[_searchView setAutohidesScrollers:YES];
|
||||
|
||||
var auxiliarySearchView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, CPColorPickerViewWidth, 26.0)];
|
||||
[auxiliarySearchView setAutoresizingMask: (CPViewMinYMargin | CPViewWidthSizable)];
|
||||
|
||||
_FIXME_searchField = document.createElement("input");
|
||||
|
||||
if(!window.addEventListener)
|
||||
_FIXME_searchField.type = "text";
|
||||
else
|
||||
_FIXME_searchField.type = "search";
|
||||
|
||||
_FIXME_searchField.style.position = "absolute";
|
||||
_FIXME_searchField.style.width = "96%";
|
||||
_FIXME_searchField.style.left = "2%";
|
||||
_FIXME_searchField.style.top = "2px";
|
||||
_FIXME_searchField.onkeypress = function(aDOMEvent)
|
||||
{
|
||||
aDOMEvent = aDOMEvent || window.event;
|
||||
if (aDOMEvent.keyCode == 13)
|
||||
{
|
||||
[self search];
|
||||
|
||||
if(aDOMEvent.preventDefault)
|
||||
aDOMEvent.preventDefault();
|
||||
else if(aDOMEvent.stopPropagation)
|
||||
aDOMEvent.stopPropagation();
|
||||
|
||||
_FIXME_searchField.blur();
|
||||
}
|
||||
};
|
||||
|
||||
auxiliarySearchView._DOMElement.appendChild(_FIXME_searchField);
|
||||
|
||||
var popularThemeView = [[_CPKulerThemeView alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, 0)];
|
||||
[popularThemeView setDelegate: self];
|
||||
[popularThemeView setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
_popularView = [[CPScrollView alloc] initWithFrame: CPRectMake(0, 0, aFrame.size.width, CPColorPickerViewHeight)];
|
||||
[_popularView setDocumentView: popularThemeView];
|
||||
[_popularView setHasHorizontalScroller: NO];
|
||||
[_popularView setAutoresizingMask: (CPViewWidthSizable | CPViewHeightSizable)];
|
||||
[[_popularView verticalScroller] setControlSize:CPSmallControlSize];
|
||||
[_popularView setAutohidesScrollers:YES];
|
||||
|
||||
var mostPopularItem = [[CPTabViewItem alloc] initWithIdentifier:@"Popular"];
|
||||
var searchItem = [[CPTabViewItem alloc] initWithIdentifier:@"Search"];
|
||||
|
||||
[searchItem setLabel:@"Search"];
|
||||
[searchItem setView:_searchView];
|
||||
[searchItem setAuxiliaryView: auxiliarySearchView];
|
||||
|
||||
[mostPopularItem setLabel:@"Popular"];
|
||||
[mostPopularItem setView: _popularView];
|
||||
|
||||
[_tabView addTabViewItem: mostPopularItem];
|
||||
[_tabView addTabViewItem: searchItem];
|
||||
|
||||
[self popularThemes];
|
||||
}
|
||||
|
||||
- (void)openKulerLink:(id)sender
|
||||
{
|
||||
window.open("http://kuler.adobe.com");
|
||||
}
|
||||
|
||||
- (CPView)provideNewView:(BOOL)initialRequest
|
||||
{
|
||||
if (initialRequest)
|
||||
[self initView];
|
||||
|
||||
return _contentView;
|
||||
}
|
||||
|
||||
- (int)currentMode
|
||||
{
|
||||
return CPKulerColorPickerMode;
|
||||
}
|
||||
|
||||
- (BOOL)supportsMode:(int)mode
|
||||
{
|
||||
return (mode == CPKulerColorPickerMode) ? YES : NO;
|
||||
}
|
||||
|
||||
-(void)search
|
||||
{
|
||||
var query = escape(_FIXME_searchField.value);
|
||||
|
||||
if(query.replace("%20", "") == "")
|
||||
return;
|
||||
|
||||
[_searchConnection cancel];
|
||||
|
||||
_searchConnection = nil;
|
||||
_searchConnection = [CPURLConnection connectionWithRequest:
|
||||
[CPURLRequest requestWithURL: BASE_URL + "kuler.php?mode=search&query="+query]
|
||||
delegate: self];
|
||||
}
|
||||
|
||||
-(void)popularThemes
|
||||
{
|
||||
[_popularConnection cancel];
|
||||
_popularConnection = nil;
|
||||
_popularConnection = [CPURLConnection connectionWithRequest:
|
||||
[CPURLRequest requestWithURL: BASE_URL + "kuler.php?mode=popular"] delegate: self];
|
||||
}
|
||||
|
||||
/*
|
||||
-(void)topRatedThemes
|
||||
{
|
||||
_connection = [CPURLConnection connectionWithRequest:
|
||||
[CPURLRequest requestWithURL: BASE_URL + "kuler.php?mode=rating"] delegate:self];
|
||||
}
|
||||
*/
|
||||
|
||||
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)data
|
||||
{
|
||||
var data = CPJSObjectCreateWithJSON(data);
|
||||
|
||||
if(!data)
|
||||
var themes = [];
|
||||
else
|
||||
var themes = data.result;
|
||||
|
||||
if(aConnection == _popularConnection)
|
||||
[[_popularView documentView] setThemes: themes];
|
||||
|
||||
if(aConnection == _searchConnection)
|
||||
[[_searchView documentView] setThemes: themes];
|
||||
}
|
||||
|
||||
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
|
||||
{
|
||||
[self connectionDidFinishLoading:aConnection];
|
||||
}
|
||||
|
||||
-(void)connectionDidFinishLoading:(CPURLConnection)aConnection
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
-(void)chooseColor:(CPColor)aColor
|
||||
{
|
||||
[[self colorPanel] setColor: aColor];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/* @ignore */
|
||||
@implementation _CPKulerThemeView : CPView
|
||||
{
|
||||
CPArray _themes;
|
||||
id _delegate;
|
||||
|
||||
CPColor _alternateBGColor;
|
||||
}
|
||||
|
||||
-(id)initWithFrame:(CPRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
_alternateBGColor = [CPColor colorWithCalibratedRed: 241.0/255.0 green: 245.0/255.0 blue: 250.0/255.0 alpha: 1.0];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(id)delegate
|
||||
{
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
-(void)setDelegate:(id)delegate
|
||||
{
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
-(CPArray)themes
|
||||
{
|
||||
return _themes;
|
||||
}
|
||||
|
||||
-(void)setThemes:(CPArray)themes
|
||||
{
|
||||
_themes = themes;
|
||||
[self updateDisplay];
|
||||
}
|
||||
|
||||
-(void)updateDisplay
|
||||
{
|
||||
var width = [self frame].size.width;
|
||||
|
||||
var subviews = [self subviews];
|
||||
for(var i = [subviews count]-1; i >= 0; --i)
|
||||
[subviews[i] removeFromSuperview];
|
||||
|
||||
for(var i=0, count = [_themes count]; i<count; i++)
|
||||
{
|
||||
var swatches = _themes[i].swatches;
|
||||
var containerView = [[CPView alloc] initWithFrame: CPRectMake(0, 1+ 20*i, width, 20)];
|
||||
[containerView setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
for(var j=0; j<swatches.length; j++)
|
||||
{
|
||||
var outerView = [[CPView alloc] initWithFrame: CGRectMake(2+20*j, 1, 18, 18)],
|
||||
innerView = [[_CPColorView alloc] initWithFrame: CGRectInset([outerView bounds], 1.0, 1.0)];
|
||||
|
||||
[innerView setTarget: self];
|
||||
[innerView setAction: @selector(selectedColor:)];
|
||||
[innerView setBackgroundColor: [CPColor colorWithHexString: swatches[j].hexColor]];
|
||||
|
||||
[outerView setBackgroundColor: [CPColor blackColor]];
|
||||
[outerView addSubview: innerView];
|
||||
|
||||
[containerView addSubview: outerView];
|
||||
}
|
||||
|
||||
var label = [[CPTextField alloc] initWithFrame: CPRectMake(102, 0, width - 102, 20)];
|
||||
[label setStringValue: _themes[i].title];
|
||||
[label setFont: [CPFont systemFontOfSize: 11.0]];
|
||||
[label setLineBreakMode: CPLineBreakByTruncatingTail];
|
||||
[label setAutoresizingMask: CPViewWidthSizable];
|
||||
|
||||
[containerView addSubview: label];
|
||||
|
||||
if(i%2 == 1)
|
||||
[containerView setBackgroundColor: _alternateBGColor];
|
||||
|
||||
[self addSubview: containerView];
|
||||
}
|
||||
|
||||
[self setFrameSize: CPSizeMake(width, (count)*20 + 2)];
|
||||
}
|
||||
|
||||
-(void)selectedColor:(id)sender
|
||||
{
|
||||
[_delegate chooseColor:[sender backgroundColor]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/* @ignore */
|
||||
@implementation _CPColorView : CPControl
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
-(id)initWithFrame:(CPRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame: aFrame];
|
||||
|
||||
[self registerForDraggedTypes:[CPArray arrayWithObjects:CPColorDragType]];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
[[CPPasteboard pasteboardWithName:CPDragPboard] declareTypes:[CPArray arrayWithObject:CPColorDragType] owner:self];
|
||||
|
||||
var bounds = CPRectMake(0, 0, 15, 15),
|
||||
point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
var outerView = [[CPView alloc] initWithFrame: bounds],
|
||||
innerView = [[CPView alloc] initWithFrame: CGRectInset([outerView bounds], 1.0, 1.0)];
|
||||
|
||||
[innerView setBackgroundColor: [self backgroundColor]];
|
||||
[outerView setBackgroundColor: [CPColor blackColor]];
|
||||
|
||||
[outerView addSubview: innerView];
|
||||
|
||||
[self dragView: outerView
|
||||
at: CPPointMake(point.x - bounds.size.width / 2.0, point.y - bounds.size.height / 2.0)
|
||||
offset: CPPointMake(0.0, 0.0)
|
||||
event: anEvent
|
||||
pasteboard: nil
|
||||
source: self
|
||||
slideBack: YES];
|
||||
}
|
||||
|
||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||
{
|
||||
if(aType == CPColorDragType)
|
||||
[aPasteboard setData:[self backgroundColor] forType:aType];
|
||||
}
|
||||
|
||||
@end
|
||||
+511
-225
File diff suppressed because it is too large
Load Diff
+362
-207
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
||||
|
||||
@import <AppKit/CPPanel.j>
|
||||
|
||||
|
||||
@implementation CPOpenPanel : CPPanel
|
||||
{
|
||||
BOOL _canChooseFiles @accessors(property=canChooseFiles);
|
||||
BOOL _canChooseDirectories @accessors(property=canChooseDirectories);
|
||||
BOOL _allowsMultipleSelection @accessors(property=allowsMultipleSelection);
|
||||
CPURL _directoryURL @accessors(property=directoryURL);
|
||||
CPArray _URLs;
|
||||
}
|
||||
|
||||
+ (id)openPanel
|
||||
{
|
||||
return [[CPOpenPanel alloc] init];
|
||||
}
|
||||
|
||||
- (CPInteger)runModal
|
||||
{
|
||||
if (typeof window["cpOpenPanel"] === "function")
|
||||
{
|
||||
// FIXME: Is this correct???
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
var options = { directoryURL: [self directoryURL],
|
||||
canChooseFiles: [self canChooseFiles],
|
||||
canChooseDirectories: [self canChooseDirectories],
|
||||
allowsMultipleSelection: [self allowsMultipleSelection] };
|
||||
|
||||
var result = window.cpOpenPanel(options);
|
||||
|
||||
_URLs = result.URLs;
|
||||
|
||||
return result.button;
|
||||
}
|
||||
|
||||
throw "-runModal is unimplemented.";
|
||||
}
|
||||
|
||||
- (CPArray)URLs
|
||||
{
|
||||
return _URLs;
|
||||
}
|
||||
|
||||
@end
|
||||
+799
-58
@@ -3,7 +3,7 @@
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2008, 280 North, Inc.
|
||||
* 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
|
||||
@@ -20,93 +20,834 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPTableView.j"
|
||||
@import "CPTableColumn.j"
|
||||
@import "CPTableView.j"
|
||||
|
||||
/*
|
||||
@ignore
|
||||
This class is a subclass of <objj>CPTableView</objj> which provides the user with a way to display
|
||||
tree structured data in an outline format. It is particularly useful for displaying hierarchical data
|
||||
such as a class inheritance tree or any other set of relationships.
|
||||
*/
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
CPOutlineViewColumnDidMoveNotification = @"CPOutlineViewColumnDidMoveNotification";
|
||||
CPOutlineViewColumnDidResizeNotification = @"CPOutlineViewColumnDidResizeNotification";
|
||||
CPOutlineViewItemDidCollapseNotification = @"CPOutlineViewItemDidCollapseNotification";
|
||||
CPOutlineViewItemDidExpandNotification = @"CPOutlineViewItemDidExpandNotification";
|
||||
CPOutlineViewItemWillCollapseNotification = @"CPOutlineViewItemWillCollapseNotification";
|
||||
CPOutlineViewItemWillExpandNotification = @"CPOutlineViewItemWillExpandNotification";
|
||||
CPOutlineViewSelectionDidChangeNotification = @"CPOutlineViewSelectionDidChangeNotification";
|
||||
CPOutlineViewSelectionIsChangingNotification = @"CPOutlineViewSelectionIsChangingNotification";
|
||||
|
||||
var CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_ = 1 << 1,
|
||||
CPOutlineViewDataSource_outlineView_shouldDeferDisplayingChildrenOfItem_ = 1 << 2,
|
||||
|
||||
CPOutlineViewDataSource_outlineView_acceptDrop_item_childIndex_ = 1 << 3,
|
||||
CPOutlineViewDataSource_outlineView_validateDrop_proposedItem_proposedChildIndex_ = 1 << 4,
|
||||
CPOutlineViewDataSource_outlineView_validateDrop_proposedRow_proposedDropOperation_ = 1 << 5,
|
||||
CPOutlineViewDataSource_outlineView_namesOfPromisedFilesDroppedAtDestination_forDraggedItems_ = 1 << 6,
|
||||
|
||||
CPOutlineViewDataSource_outlineView_itemForPersistentObject_ = 1 << 7,
|
||||
CPOutlineViewDataSource_outlineView_persistentObjectForItem_ = 1 << 8,
|
||||
|
||||
CPOutlineViewDataSource_outlineView_writeItems_toPasteboard_ = 1 << 9,
|
||||
|
||||
CPOutlineViewDataSource_outlineView_sortDescriptorsDidChange_ = 1 << 10;
|
||||
|
||||
@implementation CPOutlineView : CPTableView
|
||||
{
|
||||
id _outlineDataSource;
|
||||
CPArray _itemsByRow;
|
||||
id _outlineViewDataSource;
|
||||
id _outlineViewDelegate;
|
||||
CPTableColumn _outlineTableColumn;
|
||||
|
||||
float _indentationPerLevel;
|
||||
BOOL _indentationMarkerFollowsDataView;
|
||||
|
||||
CPInteger _implementedOutlineViewDataSourceMethods;
|
||||
|
||||
Object _rootItemInfo;
|
||||
CPMutableArray _itemsForRows;
|
||||
Object _itemInfosForItems;
|
||||
|
||||
CPControl _disclosureControlPrototype;
|
||||
CPArray _disclosureControlsForRows;
|
||||
CPData _disclosureControlData;
|
||||
CPArray _disclosureControlQueue;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
[super setDataSource:self];
|
||||
_itemsByRow = [[CPArray alloc] init];
|
||||
// The root item has weight "0", thus represents the weight solely of its descendants.
|
||||
_rootItemInfo = { isExpanded:YES, isExpandable:NO, level:-1, row:-1, children:[], weight:0 };
|
||||
|
||||
_itemsForRows = [];
|
||||
_itemInfosForItems = { };
|
||||
_disclosureControlsForRows = [];
|
||||
|
||||
[self setIndentationPerLevel:16.0];
|
||||
[self setIndentationMarkerFollowsDataView:YES];
|
||||
|
||||
[super setDataSource:[[_CPOutlineViewTableViewDataSource alloc] initWithOutlineView:self]];
|
||||
|
||||
[self setDisclosureControlPrototype:[[CPDisclosureButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 10.0)]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
Sets the outline's data source. The data source must implement the following methods:
|
||||
<pre>
|
||||
- (id)outlineView:(<objj>CPOutlineView</objj>)outlineView child:(int)index ofItem:(id)item
|
||||
- (BOOL)outlineView:(<objj>CPOutlineView</objj>)outlineView isItemExpandable:(id)item
|
||||
- (int)outlineView:(<objj>CPOutlineView</objj>)outlineView numberOfChildrenOfItem:(id)item
|
||||
- (id)outlineView:(<objj>CPOutlineView</objj>)outlineView objectValueForTableColumn:(<objj>CPTableColumn</objj>)tableColumn byItem:(id)item
|
||||
</pre>
|
||||
@param aDataSource the outline's data source
|
||||
@throws CPInternalInconsistencyException if the data source does not implement all the required methods
|
||||
*/
|
||||
- (void)setDataSource:(id)aDataSource
|
||||
{
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:child:ofItem)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:child:ofItem'"];
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:isItemExpandable)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:isItemExpandable'"];
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:numberOfChildrenOfItem)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:numberOfChildrenOfItem'"];
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:objectValueForTableColumn:byItem)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:objectValueForTableColumn:byItem'"];
|
||||
if (_outlineViewDataSource === aDataSource)
|
||||
return;
|
||||
|
||||
_outlineDataSource = aDataSource;
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:child:ofItem:)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:child:ofItem:'"];
|
||||
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:isItemExpandable:)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:isItemExpandable:'"];
|
||||
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:numberOfChildrenOfItem:)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:numberOfChildrenOfItem:'"];
|
||||
|
||||
if (![aDataSource respondsToSelector:@selector(outlineView:objectValueForTableColumn:byItem:)])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Data source must implement 'outlineView:objectValueForTableColumn:byItem:'"];
|
||||
|
||||
_outlineViewDataSource = aDataSource;
|
||||
_implementedOutlineViewDataSourceMethods = 0;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:setObjectValue:forTableColumn:byItem:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:shouldDeferDisplayingChildrenOfItem:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_shouldDeferDisplayingChildrenOfItem_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:acceptDrop:item:childIndex:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_acceptDrop_item_childIndex_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:validateDrop:proposedItem:proposedChildIndex:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_validateDrop_proposedItem_proposedChildIndex_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:validateDrop:proposedRow:proposedDropOperation:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_validateDrop_proposedRow_proposedDropOperation_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:namesOfPromisedFilesDroppedAtDestination:forDraggedItems:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_namesOfPromisedFilesDroppedAtDestination_forDraggedItems_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:itemForPersistentObject:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_itemForPersistentObject_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:persistentObjectForItem:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_persistentObjectForItem_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:writeItems:toPasteboard:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_writeItems_toPasteboard_;
|
||||
|
||||
if ([_outlineViewDataSource respondsToSelector:@selector(outlineView:sortDescriptorsDidChange:)])
|
||||
_implementedOutlineViewDataSourceMethods |= CPOutlineViewDataSource_outlineView_sortDescriptorsDidChange_;
|
||||
|
||||
[self reloadData];
|
||||
}
|
||||
/* @ignore */
|
||||
|
||||
- (id)dataSource
|
||||
{
|
||||
return _outlineViewDataSource;
|
||||
}
|
||||
|
||||
- (BOOL)isExpandable:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return YES;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return NO;
|
||||
|
||||
return itemInfo.isExpandable;
|
||||
}
|
||||
|
||||
- (void)isItemExpanded:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return YES;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return NO;
|
||||
|
||||
return itemInfo.isExpanded;
|
||||
}
|
||||
|
||||
- (void)expandItem:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return;
|
||||
|
||||
if (itemInfo.isExpanded)
|
||||
return;
|
||||
|
||||
itemInfo.isExpanded = YES;
|
||||
|
||||
[self reloadItem:anItem reloadChildren:YES];
|
||||
}
|
||||
|
||||
- (void)collapseItem:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return;
|
||||
|
||||
if (!itemInfo.isExpanded)
|
||||
return;
|
||||
|
||||
itemInfo.isExpanded = NO;
|
||||
|
||||
[self reloadItem:anItem reloadChildren:YES];
|
||||
}
|
||||
|
||||
- (void)reloadItem:(id)anItem
|
||||
{
|
||||
[self reloadItem:anItem reloadChildren:NO];
|
||||
}
|
||||
|
||||
- (void)reloadItem:(id)anItem reloadChildren:(BOOL)shouldReloadChildren
|
||||
{
|
||||
if (!!shouldReloadChildren || !anItem)
|
||||
_loadItemInfoForItem(self, anItem);
|
||||
else
|
||||
_reloadItem(self, anItem);
|
||||
|
||||
[super reloadData];
|
||||
}
|
||||
|
||||
- (id)itemAtRow:(CPInteger)aRow
|
||||
{
|
||||
return _itemsForRows[aRow] || nil;
|
||||
}
|
||||
|
||||
- (CPInteger)rowForItem:(id)aItem
|
||||
{
|
||||
if (!anItem)
|
||||
return _rootItemInfo.row;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return CPNotFound;
|
||||
|
||||
return itemInfo.row;
|
||||
}
|
||||
|
||||
- (void)setOutlineTableColumn:(CPTableColumn)aTableColumn
|
||||
{
|
||||
if (_outlineTableColumn === aTableColumn)
|
||||
return;
|
||||
|
||||
_outlineTableColumn = aTableColumn;
|
||||
|
||||
// FIXME: efficiency.
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (CPTableColumn)outlineTableColumn
|
||||
{
|
||||
return _outlineTableColumn;
|
||||
}
|
||||
|
||||
- (CPInteger)levelForItem:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return _rootItemInfo.level;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return CPNotFound;
|
||||
|
||||
return itemInfo.level;
|
||||
}
|
||||
|
||||
- (CPInteger)levelForRow:(CPInteger)aRow
|
||||
{
|
||||
return [self levelForItem:[self itemAtRow:aRow]];
|
||||
}
|
||||
|
||||
- (void)setIndentationPerLevel:(float)anIndentationWidth
|
||||
{
|
||||
if (_indentationPerLevel === anIndentationWidth)
|
||||
return;
|
||||
|
||||
_indentationPerLevel = anIndentationWidth;
|
||||
|
||||
// FIXME: efficiency!!!!
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (float)indentationPerLevel
|
||||
{
|
||||
return _indentationPerLevel;
|
||||
}
|
||||
|
||||
- (void)setIndentationMarkerFollowsDataView:(BOOL)indentationMarkerShouldFollowDataView
|
||||
{
|
||||
if (_indentationMarkerFollowsDataView === indentationMarkerShouldFollowDataView)
|
||||
return;
|
||||
|
||||
_indentationMarkerFollowsDataView = indentationMarkerShouldFollowDataView;
|
||||
|
||||
// !!!!
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (BOOL)indentationMarkerFollowsDataView
|
||||
{
|
||||
return _indentationMarkerFollowsDataView;
|
||||
}
|
||||
|
||||
- (id)parentForItem:(id)anItem
|
||||
{
|
||||
if (!anItem)
|
||||
return nil;
|
||||
|
||||
var itemInfo = _itemInfosForItems[[anItem UID]];
|
||||
|
||||
if (!itemInfo)
|
||||
return nil;
|
||||
|
||||
return itemInfo.parent;
|
||||
}
|
||||
|
||||
- (CGRect)frameOfOutlineDataViewAtColumn:(CPInteger)aColumn row:(CPInteger)aRow
|
||||
{
|
||||
var frame = [super frameOfDataViewAtColumn:aColumn row:aRow],
|
||||
indentationWidth = ([self levelForRow:aRow] + 1) * [self indentationPerLevel];
|
||||
|
||||
frame.origin.x += indentationWidth;
|
||||
frame.size.width -= indentationWidth;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id)aDelegate
|
||||
{
|
||||
if (_outlineViewDelegate === aDelegate)
|
||||
return;
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
if (_outlineViewDelegate)
|
||||
{
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewColumnDidMove:)])
|
||||
[defaultCenter
|
||||
removeObserver:_outlineViewDelegate
|
||||
name:CPOutlineViewColumnDidMoveNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewColumnDidResize:)])
|
||||
[defaultCenter
|
||||
removeObserver:_outlineViewDelegate
|
||||
name:CPOutlineViewColumnDidResizeNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewSelectionDidChange:)])
|
||||
[defaultCenter
|
||||
removeObserver:_outlineViewDelegate
|
||||
name:CPOutlineViewSelectionDidChangeNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewSelectionIsChanging:)])
|
||||
[defaultCenter
|
||||
removeObserver:_outlineViewDelegate
|
||||
name:CPOutlineViewSelectionIsChangingNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
_outlineViewDelegate = aDelegate;/*
|
||||
_implementedDelegateMethods = 0;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(selectionShouldChangeInTableView:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_selectionShouldChangeInTableView_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:dataViewForTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_dataViewForTableColumn_row_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:didClickTableColumn:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_didClickTableColumn_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:didDragTableColumn:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_didDragTableColumn_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:heightOfRow:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_heightOfRow_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:isGroupRow:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_isGroupRow_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:mouseDownInHeaderOfTableColumn:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_mouseDownInHeaderOfTableColumn_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:nextTypeSelectMatchFromRow:toRow:forString:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_nextTypeSelectMatchFromRow_toRow_forString_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:selectionIndexesForProposedSelection:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_selectionIndexesForProposedSelection_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldEditTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldEditTableColumn_row_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldSelectRow:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldSelectRow_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldSelectTableColumn:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldSelectTableColumn_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldShowViewExpansionForTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldShowViewExpansionForTableColumn_row_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldTrackView:forTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldTrackView_forTableColumn_row_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:shouldTypeSelectForEvent:withCurrentSearchString:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_shouldTypeSelectForEvent_withCurrentSearchString_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:toolTipForView:rect:tableColumn:row:mouseLocation:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_toolTipForView_rect_tableColumn_row_mouseLocation_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:typeSelectStringForTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_typeSelectStringForTableColumn_row_;
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(tableView:willDisplayView:forTableColumn:row:)])
|
||||
_implementedDelegateMethods |= CPTableViewDelegate_tableView_willDisplayView_forTableColumn_row_;
|
||||
*/
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewColumnDidMove:)])
|
||||
[defaultCenter
|
||||
addObserver:_outlineViewDelegate
|
||||
selector:@selector(outlineViewColumnDidMove:)
|
||||
name:CPOutlineViewColumnDidMoveNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewColumnDidResize:)])
|
||||
[defaultCenter
|
||||
addObserver:_outlineViewDelegate
|
||||
selector:@selector(outlineViewColumnDidMove:)
|
||||
name:CPOutlineViewColumnDidResizeNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewSelectionDidChange:)])
|
||||
[defaultCenter
|
||||
addObserver:_outlineViewDelegate
|
||||
selector:@selector(outlineViewSelectionDidChange:)
|
||||
name:CPOutlineViewSelectionDidChangeNotification
|
||||
object:self];
|
||||
|
||||
if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewSelectionIsChanging:)])
|
||||
[defaultCenter
|
||||
addObserver:_outlineViewDelegate
|
||||
selector:@selector(outlineViewSelectionIsChanging:)
|
||||
name:CPOutlineViewSelectionIsChangingNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
- (id)delegate
|
||||
{
|
||||
return _outlineViewDelegate;
|
||||
}
|
||||
|
||||
- (void)setDisclosureControlPrototype:(CPControl)aControl
|
||||
{
|
||||
_disclosureControlPrototype = aControl;
|
||||
_disclosureControlData = nil;
|
||||
_disclosureControlQueue = [];
|
||||
|
||||
// fIXME: reall?
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
_numberOfVisibleItems = [_outlineDataSource outlineView:self numberOfChildrenOfItem:nil];
|
||||
_numberOfRows = _numberOfVisibleItems;
|
||||
|
||||
var i = 0;
|
||||
|
||||
for (; i < _numberOfVisibleItems; ++i)
|
||||
_itemsByRow[i] = [_outlineDataSource outlineView:self child:i ofItem:nil];
|
||||
|
||||
[self loadTableCellsInRect:[self bounds]];
|
||||
[self reloadItem:nil reloadChildren:YES];
|
||||
}
|
||||
|
||||
- (CGRect)frameOfDataViewAtColumn:(CPInteger)aColumn row:(CPInteger)aRow
|
||||
{
|
||||
var tableColumn = [self tableColumns][aColumn];
|
||||
|
||||
if (tableColumn === _outlineTableColumn)
|
||||
return [self frameOfOutlineDataViewAtColumn:aColumn row:aRow];
|
||||
|
||||
return [super frameOfDataViewAtColumn:aColumn row:aRow];
|
||||
}
|
||||
|
||||
- (void)_loadDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns
|
||||
{
|
||||
[super _loadDataViewsInRows:rows columns:columns];
|
||||
|
||||
var outlineColumn = [[self tableColumns] indexOfObjectIdenticalTo:[self outlineTableColumn]];
|
||||
|
||||
if (![columns containsIndex:outlineColumn])
|
||||
return;
|
||||
|
||||
var rowArray = [];
|
||||
|
||||
[rows getIndexes:rowArray maxCount:-1 inIndexRange:nil];
|
||||
|
||||
var rowIndex = 0,
|
||||
rowsCount = rowArray.length;
|
||||
|
||||
for (; rowIndex < rowsCount; ++rowIndex)
|
||||
{
|
||||
var row = rowArray[rowIndex],
|
||||
item = _itemsForRows[row],
|
||||
isExpandable = [self isExpandable:item];
|
||||
|
||||
if (!isExpandable)
|
||||
continue;
|
||||
|
||||
var control = [self _dequeueDisclosureControl],
|
||||
frame = [control frame],
|
||||
dataViewFrame = [self frameOfDataViewAtColumn:outlineColumn row:row];
|
||||
|
||||
frame.origin.x = _indentationMarkerFollowsDataView ? _CGRectGetMinX(dataViewFrame) - _CGRectGetWidth(frame) : 0.0;
|
||||
frame.origin.y = _CGRectGetMinY(dataViewFrame);
|
||||
frame.size.height = _CGRectGetHeight(dataViewFrame);
|
||||
// FIXME: center instead?
|
||||
//frame.origin.y = _CGRectGetMidY(dataViewFrame) - _CGRectGetHeight(frame) / 2.0;
|
||||
|
||||
_disclosureControlsForRows[row] = control;
|
||||
|
||||
[control setState:[self isItemExpanded:item] ? CPOnState : CPOffState];
|
||||
[control setFrame:frame];
|
||||
|
||||
[self addSubview:control];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_unloadDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns
|
||||
{
|
||||
[super _unloadDataViewsInRows:rows columns:columns];
|
||||
|
||||
var outlineColumn = [[self tableColumns] indexOfObjectIdenticalTo:[self outlineTableColumn]];
|
||||
|
||||
if (![columns containsIndex:outlineColumn])
|
||||
return;
|
||||
|
||||
var rowArray = [];
|
||||
|
||||
[rows getIndexes:rowArray maxCount:-1 inIndexRange:nil];
|
||||
|
||||
var rowIndex = 0,
|
||||
rowsCount = rowArray.length;
|
||||
|
||||
for (; rowIndex < rowsCount; ++rowIndex)
|
||||
{
|
||||
var row = rowArray[rowIndex],
|
||||
control = _disclosureControlsForRows[row];
|
||||
|
||||
if (!control)
|
||||
continue;
|
||||
|
||||
[control removeFromSuperview];
|
||||
|
||||
[self _enqueueDisclosureControl:control];
|
||||
|
||||
_disclosureControlsForRows[row] = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_toggleFromDisclosureControl:(CPControl)aControl
|
||||
{
|
||||
var controlFrame = [aControl frame],
|
||||
item = [self itemAtRow:[self rowAtPoint:_CGPointMake(_CGRectGetMinX(controlFrame), _CGRectGetMidY(controlFrame))]];
|
||||
|
||||
if ([self isItemExpanded:item])
|
||||
[self collapseItem:item];
|
||||
|
||||
else
|
||||
[self expandItem:item];
|
||||
}
|
||||
|
||||
- (void)_enqueueDisclosureControl:(CPControl)aControl
|
||||
{
|
||||
_disclosureControlQueue.push(aControl);
|
||||
}
|
||||
|
||||
- (CPControl)_dequeueDisclosureControl
|
||||
{
|
||||
if (_disclosureControlQueue.length)
|
||||
return _disclosureControlQueue.pop();
|
||||
|
||||
if (!_disclosureControlData)
|
||||
if (!_disclosureControlPrototype)
|
||||
return nil;
|
||||
else
|
||||
_disclosureControlData = [CPKeyedArchiver archivedDataWithRootObject:_disclosureControlPrototype];
|
||||
|
||||
var disclosureControl = [CPKeyedUnarchiver unarchiveObjectWithData:_disclosureControlData];
|
||||
|
||||
[disclosureControl setTarget:self];
|
||||
[disclosureControl setAction:@selector(_toggleFromDisclosureControl:)];
|
||||
|
||||
return disclosureControl;
|
||||
}
|
||||
|
||||
- (void)_noteSelectionIsChanging
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPOutlineViewSelectionIsChangingNotification
|
||||
object:self
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
- (void)_noteSelectionDidChange
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPOutlineViewSelectionDidChangeNotification
|
||||
object:self
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/* @ignore */
|
||||
@implementation CPOutlineView (CPTableDataSource)
|
||||
|
||||
/*
|
||||
FIXME
|
||||
*/
|
||||
/* @ignore */
|
||||
- (void)numberOfRowsInTableView:(CPTableView)aTableView
|
||||
var _reloadItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anItem)
|
||||
{
|
||||
return _numberOfVisibleItems;
|
||||
if (!anItem)
|
||||
return;
|
||||
|
||||
// Get the existing info if it exists.
|
||||
var itemInfosForItems = anOutlineView._itemInfosForItems,
|
||||
dataSource = anOutlineView._outlineViewDataSource,
|
||||
itemUID = [anItem UID],
|
||||
itemInfo = itemInfosForItems[itemUID];
|
||||
|
||||
// If we're not in the tree, then just bail.
|
||||
if (!itemInfo)
|
||||
return [];
|
||||
|
||||
// See if the item itself can be swapped out.
|
||||
var parent = itemInfo.parent,
|
||||
parentItemInfo = parent ? itemInfosForItems[[parent UID]] : anOutlineView._rootItemInfo,
|
||||
parentChildren = parentItemInfo.children,
|
||||
index = [parentChildren indexOfObjectIdenticalTo:anItem],
|
||||
newItem = [dataSource outlineView:anOutlineView child:index ofItem:parent];
|
||||
|
||||
if (anItem !== newItem)
|
||||
{
|
||||
itemInfosForItems[[anItem UID]] = nil;
|
||||
itemInfosForItems[[newItem UID]] = itemInfo;
|
||||
|
||||
parentChildren[index] = newItem;
|
||||
anOutlineView._itemsForRows[itemInfo.row] = newItem;
|
||||
}
|
||||
|
||||
itemInfo.isExpandable = [dataSource outlineView:anOutlineView isItemExpandable:newItem];
|
||||
itemInfo.isExpanded = itemInfo.isExpandable && itemInfo.isExpanded;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)aTableColumn row:(int)aRowIndex
|
||||
var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anItem, /*BOOL*/ isIntermediate)
|
||||
{
|
||||
return [_outlineDataSource outlineView:self objectValueForTableColumn:aTableColumn byItem:_itemsByRow[aRowIndex]];
|
||||
var itemInfosForItems = anOutlineView._itemInfosForItems,
|
||||
dataSource = anOutlineView._outlineViewDataSource;
|
||||
|
||||
if (!anItem)
|
||||
var itemInfo = anOutlineView._rootItemInfo;
|
||||
|
||||
else
|
||||
{
|
||||
// Get the existing info if it exists.
|
||||
var itemUID = [anItem UID],
|
||||
itemInfo = itemInfosForItems[itemUID];
|
||||
|
||||
// If we're not in the tree, then just bail.
|
||||
if (!itemInfo)
|
||||
return [];
|
||||
|
||||
itemInfo.isExpandable = [dataSource outlineView:anOutlineView isItemExpandable:anItem];
|
||||
|
||||
// If we were previously expanded, but now no longer expandable, "de-expand".
|
||||
// NOTE: we are *not* collapsing, thus no notification is posted.
|
||||
if (!itemInfo.isExpandable && itemInfo.isExpanded)
|
||||
{
|
||||
itemInfo.isExpanded = NO;
|
||||
itemInfo.children = [];
|
||||
}
|
||||
}
|
||||
|
||||
// The root item does not count as a descendant.
|
||||
var weight = itemInfo.weight,
|
||||
descendants = anItem ? [anItem] : [];
|
||||
|
||||
if (itemInfo.isExpanded && (!(anOutlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_shouldDeferDisplayingChildrenOfItem_) ||
|
||||
![dataSource outlineView:anOutlineView shouldDeferDisplayingChildrenOfItem:anItem]))
|
||||
{
|
||||
var index = 0,
|
||||
count = [dataSource outlineView:anOutlineView numberOfChildrenOfItem:anItem],
|
||||
level = itemInfo.level + 1;
|
||||
|
||||
itemInfo.children = [];
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var childItem = [dataSource outlineView:anOutlineView child:index ofItem:anItem],
|
||||
childItemInfo = itemInfosForItems[[childItem UID]];
|
||||
|
||||
if (!childItemInfo)
|
||||
{
|
||||
childItemInfo = { isExpanded:NO, isExpandable:NO, children:[], weight:1 };
|
||||
itemInfosForItems[[childItem UID]] = childItemInfo;
|
||||
}
|
||||
|
||||
itemInfo.children[index] = childItem;
|
||||
|
||||
var childDescendants = _loadItemInfoForItem(anOutlineView, childItem, YES);
|
||||
|
||||
childItemInfo.parent = anItem;
|
||||
childItemInfo.level = level;
|
||||
descendants = descendants.concat(childDescendants);
|
||||
}
|
||||
}
|
||||
|
||||
itemInfo.weight = descendants.length;
|
||||
|
||||
if (!isIntermediate)
|
||||
{
|
||||
// row = -1 is the root item, so just go to row 0 since it is ignored.
|
||||
var index = MAX(itemInfo.row, 0),
|
||||
itemsForRows = anOutlineView._itemsForRows;
|
||||
|
||||
descendants.unshift(index, weight);
|
||||
|
||||
itemsForRows.splice.apply(itemsForRows, descendants);
|
||||
|
||||
var count = itemsForRows.length;
|
||||
|
||||
for (; index < count; ++index)
|
||||
itemInfosForItems[[itemsForRows[index] UID]].row = index;
|
||||
|
||||
var deltaWeight = itemInfo.weight - weight;
|
||||
|
||||
if (deltaWeight !== 0)
|
||||
{
|
||||
var parent = itemInfo.parent;
|
||||
|
||||
while (parent)
|
||||
{
|
||||
var parentItemInfo = itemInfosForItems[[parent UID]];
|
||||
|
||||
parentItemInfo.weight += deltaWeight;
|
||||
parent = parentItemInfo.parent;
|
||||
}
|
||||
|
||||
if (anItem)
|
||||
anOutlineView._rootItemInfo.weight += deltaWeight;
|
||||
}
|
||||
}
|
||||
|
||||
return descendants;
|
||||
}
|
||||
|
||||
@end
|
||||
@implementation _CPOutlineViewTableViewDataSource : CPObject
|
||||
{
|
||||
CPObject _outlineView;
|
||||
}
|
||||
|
||||
- (id)initWithOutlineView:(CPOutlineView)anOutlineView
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_outlineView = anOutlineView;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPInteger)numberOfRowsInTableView:(CPTableView)anOutlineView
|
||||
{
|
||||
return _outlineView._itemsForRows.length;
|
||||
}
|
||||
|
||||
- (id)tableView:(CPTableView)aTableView objectValueForTableColumn:(CPTableColumn)aTableColumn row:(CPInteger)aRow
|
||||
{
|
||||
return [_outlineView._outlineViewDataSource outlineView:_outlineView objectValueForTableColumn:aTableColumn byItem:_outlineView._itemsForRows[aRow]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPOutlineViewTableViewDelegate : CPObject
|
||||
{
|
||||
CPOutlineView _outlineView;
|
||||
}
|
||||
|
||||
- (id)initWithOutlineView:(CPOutlineView)anOutlineView
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_outlineView = anOutlineView;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPDisclosureButton : CPButton
|
||||
{
|
||||
float _angle;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
[self setBordered:NO];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setState:(CPState)aState
|
||||
{
|
||||
[super setState:aState];
|
||||
|
||||
if ([self state] === CPOnState)
|
||||
_angle = 0.0;
|
||||
|
||||
else
|
||||
_angle = -PI_2;
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
context = [[CPGraphicsContext currentContext] graphicsPort];
|
||||
|
||||
CGContextBeginPath(context);
|
||||
|
||||
CGContextTranslateCTM(context, _CGRectGetWidth(bounds) / 2.0, _CGRectGetHeight(bounds) / 2.0);
|
||||
CGContextRotateCTM(context, _angle);
|
||||
CGContextTranslateCTM(context, -_CGRectGetWidth(bounds) / 2.0, -_CGRectGetHeight(bounds) / 2.0);
|
||||
|
||||
// Center, but crisp.
|
||||
CGContextTranslateCTM(context, FLOOR((_CGRectGetWidth(bounds) - 9.0) / 2.0), FLOOR((_CGRectGetHeight(bounds) - 8.0) / 2.0));
|
||||
|
||||
CGContextMoveToPoint(context, 0.0, 0.0);
|
||||
CGContextAddLineToPoint(context, 9.0, 0.0);
|
||||
CGContextAddLineToPoint(context, 4.5, 8.0);
|
||||
CGContextAddLineToPoint(context, 0.0, 0.0);
|
||||
|
||||
CGContextClosePath(context);
|
||||
|
||||
CGContextSetFillColor(context, ([self themeState] & CPThemeState("highlighted")) ? [CPColor blackColor] : [CPColor grayColor]);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+24
-14
@@ -20,14 +20,17 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPWindow.j"
|
||||
@import "CPWindow.j"
|
||||
|
||||
|
||||
CPOKButton = 1;
|
||||
CPCancelButton = 0;
|
||||
|
||||
/*
|
||||
The <objj>CPPanel</objj> class defines objects that manage the panels of the Cappuccino user interface. A panel is a window that serves an auxiliary function within an application. It generally displays controls that the user can act on to give instructions to the application or to modify the contents of a standard window.</p>
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPPanel
|
||||
|
||||
The CPPanel class defines objects that manage the panels of the Cappuccino user interface. A panel is a window that serves an auxiliary function within an application. It generally displays controls that the user can act on to give instructions to the application or to modify the contents of a standard window.</p>
|
||||
|
||||
<p>Panels behave differently from standard windows in only a small number of ways, but the ways are important to the user interface:</p>
|
||||
|
||||
@@ -43,23 +46,30 @@ CPCancelButton = 0;
|
||||
<li>A panel can be made to work even when there's an attention panel on-screen. This permits actions within the panel to affect the attention panel.</li>
|
||||
</ul>
|
||||
*/
|
||||
|
||||
/*
|
||||
@global
|
||||
@class CPWindow
|
||||
*/
|
||||
CPDocModalWindowMask = 1 << 6;
|
||||
|
||||
@implementation CPPanel : CPWindow
|
||||
{
|
||||
BOOL _becomesKeyOnlyIfNeeded;
|
||||
BOOL _worksWhenModal;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the receiver is a floating panel (like a palette).
|
||||
/*!
|
||||
Returns \c YES if the receiver is a floating panel (like a palette).
|
||||
*/
|
||||
- (BOOL)isFloatingPanel
|
||||
{
|
||||
return [self level] == CPFloatingWindowLevel;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the receiver to be a floating panel. <code>YES</code>
|
||||
makes the window a floating panel. <code>NO</code> makes it a normal window.
|
||||
/*!
|
||||
Sets the receiver to be a floating panel. \c YES
|
||||
makes the window a floating panel. \c NO makes it a normal window.
|
||||
@param isFloatingPanel specifies whether to make it floating
|
||||
*/
|
||||
- (void)setFloatingPanel:(BOOL)isFloatingPanel
|
||||
@@ -67,18 +77,18 @@ CPCancelButton = 0;
|
||||
[self setLevel:isFloatingPanel ? CPFloatingWindowLevel : CPNormalWindowLevel];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the window only becomes key
|
||||
if needed. <code>NO</code> means it behaves just like other windows.
|
||||
/*!
|
||||
Returns \c YES if the window only becomes key
|
||||
if needed. \c NO means it behaves just like other windows.
|
||||
*/
|
||||
- (BOOL)becomesKeyOnlyIfNeeded
|
||||
{
|
||||
return _becomesKeyOnlyIfNeeded;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets whether the the window becomes key only if needed.
|
||||
@param shouldBecomeKeyOnlyIfNeeded <code>YES</code> makes the window become key only if needed
|
||||
@param shouldBecomeKeyOnlyIfNeeded \c YES makes the window become key only if needed
|
||||
*/
|
||||
- (void)setBecomesKeyOnlyIfNeeded:(BOOL)shouldBecomeKeyOnlyIfNeeded
|
||||
{
|
||||
@@ -90,7 +100,7 @@ CPCancelButton = 0;
|
||||
return _worksWhenModal;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets whether this window can receive input while another window is running modally.
|
||||
@param shouldWorkWhenModal whether to receive input while another window is modal
|
||||
*/
|
||||
|
||||
+104
-27
@@ -20,10 +20,12 @@
|
||||
* 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/CPPropertyListSerialization.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPArray.j>
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPPropertyListSerialization.j>
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
|
||||
|
||||
CPGeneralPboard = @"CPGeneralPboard";
|
||||
@@ -38,12 +40,20 @@ CPFontPboardType = @"CPFontPboardType";
|
||||
CPHTMLPboardType = @"CPHTMLPboardType";
|
||||
CPStringPboardType = @"CPStringPboardType";
|
||||
CPURLPboardType = @"CPURLPboardType";
|
||||
CPImagesPboardType = @"CPImagesPboardType";
|
||||
CPVideosPboardType = @"CPVideosPboardType";
|
||||
|
||||
// Deprecated
|
||||
CPImagePboardType = @"CPImagePboardType";
|
||||
|
||||
|
||||
var CPPasteboards = nil;
|
||||
|
||||
/*
|
||||
<objj>CPPasteBoard</objj> is the object responsible for cut/copy/paste and drag&drop operations.
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPPasteboard
|
||||
|
||||
CPPasteBoard is the object responsible for cut/copy/paste and drag&drop operations.
|
||||
*/
|
||||
@implementation CPPasteboard : CPObject
|
||||
{
|
||||
@@ -68,7 +78,7 @@ var CPPasteboards = nil;
|
||||
CPPasteboards = [CPDictionary dictionary];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a new instance of a pasteboard
|
||||
*/
|
||||
+ (id)generalPasteboard
|
||||
@@ -76,7 +86,7 @@ var CPPasteboards = nil;
|
||||
return [CPPasteboard pasteboardWithName:CPGeneralPboard];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a pasteboard with the specified name. If the pasteboard doesn't exist, it will be created.
|
||||
@param aName the name of the pasteboard
|
||||
@return the requested pasteboard
|
||||
@@ -113,7 +123,7 @@ var CPPasteboards = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Adds supported data types to the pasteboard
|
||||
@param types the data types
|
||||
@param anOwner the object that contains the data types
|
||||
@@ -140,7 +150,7 @@ var CPPasteboards = nil;
|
||||
return ++_changeCount;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the data types that this pasteboard will contain.
|
||||
@param type the data types it will support
|
||||
@param anOwner the object that contains the the data
|
||||
@@ -161,11 +171,11 @@ var CPPasteboards = nil;
|
||||
return ++_changeCount;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the pasteboard data for the specified type
|
||||
@param aData the data
|
||||
@param aType the data type being set
|
||||
@return <code>YES</code> if the data was successfully written to the pasteboard
|
||||
@return \c YES if the data was successfully written to the pasteboard
|
||||
*/
|
||||
- (BOOL)setData:(CPData)aData forType:(CPString)aType
|
||||
{
|
||||
@@ -174,22 +184,22 @@ var CPPasteboards = nil;
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Writes the specified property list as data for the specified type
|
||||
@param aPropertyList the property list to write
|
||||
@param aType the data type
|
||||
@return <code>YES</code> if the property list was successfully written to the pasteboard
|
||||
@return \c YES if the property list was successfully written to the pasteboard
|
||||
*/
|
||||
- (BOOL)setPropertyList:(id)aPropertyList forType:(CPString)aType
|
||||
{
|
||||
return [self setData:[CPPropertyListSerialization dataFromPropertyList:aPropertyList format:CPPropertyListXMLFormat_v1_0 errorDescription:nil] forType:aType];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the specified string as data for the specified type
|
||||
@param aString the string to write
|
||||
@param aType the data type
|
||||
@return <code>YES</code> if the string was successfully written to the pasteboard
|
||||
@return \c YES if the string was successfully written to the pasteboard
|
||||
*/
|
||||
- (void)setString:(CPString)aString forType:(CPString)aType
|
||||
{
|
||||
@@ -197,18 +207,18 @@ var CPPasteboards = nil;
|
||||
}
|
||||
|
||||
// Determining Types
|
||||
/*
|
||||
/*!
|
||||
Checks the pasteboard's types for a match with the types listen in the specified array. The array should
|
||||
be ordered by the requestor's most preferred data type first.
|
||||
@param anArray an array of requested types ordered by preference
|
||||
@return the highest match with the pasteboard's supported types or <code>nil</code> if no match was found
|
||||
@return the highest match with the pasteboard's supported types or \c nil if no match was found
|
||||
*/
|
||||
- (CPString)availableTypeFromArray:(CPArray)anArray
|
||||
{
|
||||
return [_types firstObjectCommonWithArray:anArray];
|
||||
return [[self types] firstObjectCommonWithArray:anArray];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the pasteboards supported types
|
||||
*/
|
||||
- (CPArray)types
|
||||
@@ -217,7 +227,7 @@ var CPPasteboards = nil;
|
||||
}
|
||||
|
||||
// Reading data
|
||||
/*
|
||||
/*!
|
||||
Returns the number of changes that have occurred to this pasteboard
|
||||
*/
|
||||
- (unsigned)changeCount
|
||||
@@ -225,10 +235,10 @@ var CPPasteboards = nil;
|
||||
return _changeCount;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the pasteboard data for the specified data type
|
||||
@param aType the requested data type
|
||||
@return the requested data or <code>nil</code> if the data doesn't exist
|
||||
@return the requested data or \c nil if the data doesn't exist
|
||||
*/
|
||||
- (CPData)dataForType:(CPString)aType
|
||||
{
|
||||
@@ -251,10 +261,10 @@ var CPPasteboards = nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the property list for the specified data type
|
||||
@param aType the requested data type
|
||||
@return the property list or <code>nil</code> if the list was not found
|
||||
@return the property list or \c nil if the list was not found
|
||||
*/
|
||||
- (id)propertyListForType:(CPString)aType
|
||||
{
|
||||
@@ -266,10 +276,10 @@ var CPPasteboards = nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the string for the specified data type
|
||||
@param aType the requested data type
|
||||
@return the string or <code>nil</code> if the string was not found
|
||||
@return the string or \c nil if the string was not found
|
||||
*/
|
||||
- (CPString)stringForType:(CPString)aType
|
||||
{
|
||||
@@ -296,3 +306,70 @@ var CPPasteboards = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
var DOMDataTransferPasteboard = nil;
|
||||
|
||||
@implementation _CPDOMDataTransferPasteboard : CPPasteboard
|
||||
{
|
||||
DataTransfer _dataTransfer;
|
||||
}
|
||||
|
||||
+ (_CPDOMDataTransferPasteboard)DOMDataTransferPasteboard
|
||||
{
|
||||
if (!DOMDataTransferPasteboard)
|
||||
DOMDataTransferPasteboard = [[_CPDOMDataTransferPasteboard alloc] init];
|
||||
|
||||
return DOMDataTransferPasteboard;
|
||||
}
|
||||
|
||||
- (void)_setDataTransfer:(DataTransfer)aDataTransfer
|
||||
{
|
||||
_dataTransfer = aDataTransfer;
|
||||
}
|
||||
|
||||
- (void)_setPasteboard:(CPPasteboard)aPasteboard
|
||||
{
|
||||
_dataTransfer.clearData();
|
||||
|
||||
var types = [aPasteboard types],
|
||||
count = types.length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var type = types[count];
|
||||
|
||||
if (type === CPStringPboardType)
|
||||
_dataTransfer.setData(type, [aPasteboard stringForType:type]);
|
||||
else
|
||||
_dataTransfer.setData(type, [[aPasteboard dataForType:type] string]);
|
||||
}
|
||||
}
|
||||
|
||||
- (CPArray)types
|
||||
{
|
||||
return Array.prototype.slice.apply(_dataTransfer.types);
|
||||
}
|
||||
|
||||
- (CPData)dataForType:(CPString)aType
|
||||
{
|
||||
var dataString = _dataTransfer.getData(aType);
|
||||
|
||||
if (aType === CPStringPboardType)
|
||||
return [CPData dataFromPropertyList:dataString format:kCFPropertyList280NorthFormat_v1_0 errorDescription:0];
|
||||
|
||||
return [CPData dataWithString:dataString];
|
||||
}
|
||||
|
||||
- (id)propertyListForType:(CPString)aType
|
||||
{
|
||||
if (aType === CPStringPboardType)
|
||||
return _dataTransfer.getData(aType);
|
||||
|
||||
return [CPPropertyListSerialization propertyListFromData:[self dataForType:aType] format:CPPropertyListUnknownFormat errorDescription:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
+168
-162
@@ -20,34 +20,41 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPButton.j"
|
||||
import "CPGeometry.j"
|
||||
import "CPMenu.j"
|
||||
import "CPMenuItem.j"
|
||||
@import "CPButton.j"
|
||||
@import "CPGeometry.j"
|
||||
@import "CPMenu.j"
|
||||
@import "CPMenuItem.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
var VISIBLE_MARGIN = 7.0;
|
||||
|
||||
var CPPopUpButtonArrowsImage = nil;
|
||||
CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
|
||||
/*
|
||||
A <objj>CPPopUpButton</objj> contains a pop-up menu of items that a user can select from.
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPPopUpButton
|
||||
|
||||
A CPPopUpButton contains a pop-up menu of items that a user can select from.
|
||||
*/
|
||||
@implementation CPPopUpButton : CPButton
|
||||
{
|
||||
BOOL _pullsDown;
|
||||
int _selectedIndex;
|
||||
CPRectEdge _preferredEdge;
|
||||
|
||||
CPImageView _arrowsView;
|
||||
|
||||
CPMenu _menu;
|
||||
}
|
||||
|
||||
/*
|
||||
+ (CPString)themeClass
|
||||
{
|
||||
return "popup-button";
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the pop-up button to the specified size.
|
||||
@param aFrame the size for the button
|
||||
@param shouldPullDown <code>YES</code> makes this a pull-down menu, <code>NO</code> makes it a pop-up menu.
|
||||
@param shouldPullDown \c YES makes this a pull-down menu, \c NO makes it a pop-up menu.
|
||||
@return the initialized pop-up button
|
||||
*/
|
||||
- (id)initWithFrame:(CGRect)aFrame pullsDown:(BOOL)shouldPullDown
|
||||
@@ -56,16 +63,16 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
|
||||
if (self)
|
||||
{
|
||||
_pullsDown = shouldPullDown;
|
||||
_selectedIndex = CPNotFound;
|
||||
_preferredEdge = CPMaxYEdge;
|
||||
|
||||
[self setBezelStyle:CPTexturedRoundedBezelStyle];
|
||||
|
||||
[self setImagePosition:CPImageLeft];
|
||||
[self setAlignment:CPLeftTextAlignment];
|
||||
[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;
|
||||
@@ -76,76 +83,61 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
return [self initWithFrame:aFrame pullsDown:NO];
|
||||
}
|
||||
|
||||
- (void)setBordered:(BOOL)shouldBeBordered
|
||||
{
|
||||
if (shouldBeBordered)
|
||||
{
|
||||
var bounds = [self bounds];
|
||||
|
||||
_arrowsView = [[CPImageView alloc] initWithFrame:CGRectMake(CGRectGetWidth(bounds) - 10.0, (CGRectGetHeight(bounds) - 8.0) / 2.0, 5.0, 8.0)];
|
||||
|
||||
if (!CPPopUpButtonArrowsImage)
|
||||
CPPopUpButtonArrowsImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPPopUpButton class]] pathForResource:@"CPPopUpButton/CPPopUpButtonArrows.png"] size:CGSizeMake(5.0, 8.0)];
|
||||
|
||||
[_arrowsView setImage:CPPopUpButtonArrowsImage];
|
||||
[_arrowsView setAutoresizingMask:CPViewMaxXMargin | CPViewMinYMargin | CPViewMaxYMargin];
|
||||
|
||||
|
||||
[self addSubview:_arrowsView];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_arrowsView removeFromSuperview];
|
||||
|
||||
_arrowsView = nil;
|
||||
}
|
||||
|
||||
[super setBordered:shouldBeBordered];
|
||||
}
|
||||
|
||||
// Setting the Type of Menu
|
||||
|
||||
/*
|
||||
/*!
|
||||
Specifies whether the object is a pull-down or a pop-up menu.
|
||||
@param shouldPullDown <code>YES</code> makes the pop-up button
|
||||
a pull-down menu. <code>NO</code> makes it a pop-up menu.
|
||||
@param shouldPullDown \c YES makes the pop-up button
|
||||
a pull-down menu. \c NO makes it a pop-up menu.
|
||||
*/
|
||||
- (void)setPullsDown:(BOOL)shouldPullDown
|
||||
{
|
||||
if (_pullsDown == shouldPullDown)
|
||||
if (shouldPullDown)
|
||||
var changed = [self setThemeState:CPPopUpButtonStatePullsDown];
|
||||
else
|
||||
var changed = [self unsetThemeState:CPPopUpButtonStatePullsDown];
|
||||
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
_pullsDown = shouldPullDown;
|
||||
|
||||
var items = [_menu itemArray];
|
||||
|
||||
if (items.length <= 0)
|
||||
|
||||
if ([items count] <= 0)
|
||||
return;
|
||||
|
||||
[items[0] setHidden:_pullsDown];
|
||||
|
||||
|
||||
[items[0] setHidden:[self pullsDown]];
|
||||
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the button is a pull-down menu. <code>NO</code> if the button is a pop-up menu.
|
||||
/*!
|
||||
Returns \c YES if the button is a pull-down menu. \c NO if the button is a pop-up menu.
|
||||
*/
|
||||
- (BOOL)pullsDown
|
||||
{
|
||||
return _pullsDown;
|
||||
return [self hasThemeState:CPPopUpButtonStatePullsDown];
|
||||
}
|
||||
|
||||
// Inserting and Deleting Items
|
||||
/*
|
||||
|
||||
/*!
|
||||
Adds a new menu item using a CPMenuItem object.
|
||||
*/
|
||||
- (void)addItem:(CPMenuItem)anItem
|
||||
{
|
||||
[_menu addItem:anItem];
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds a new menu item with the specified title.
|
||||
@param the new menu item's tite
|
||||
*/
|
||||
- (void)addItemWithTitle:(CPString)aTitle
|
||||
{
|
||||
[_menu addItemWithTitle:aTitle action:NULL keyEquivalent:NULL];
|
||||
[_menu addItemWithTitle:aTitle action:NULL keyEquivalent:nil];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Adds multiple new menu items with the titles specified in the provided array.
|
||||
@param titles an arry of names for the new items
|
||||
*/
|
||||
@@ -158,7 +150,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
[self addItemWithTitle:titles[index]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Inserts a new item with the specified title and index location.
|
||||
@param aTitle the new itme's title
|
||||
@param anIndex the item's index in the menu
|
||||
@@ -172,10 +164,10 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
if ([items[count] title] == aTitle)
|
||||
[self removeItemAtIndex:count];
|
||||
|
||||
[_menu insertItemWithTitle:aTitle action:NULL keyEquivalent:NULL atIndex:anIndex];
|
||||
[_menu insertItemWithTitle:aTitle action:NULL keyEquivalent:nil atIndex:anIndex];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Removes all menu items from the pop-up button's menu
|
||||
*/
|
||||
- (void)removeAllItems
|
||||
@@ -186,7 +178,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
[_menu removeItemAtIndex:0];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Removes a menu item with the specified title from the button.
|
||||
@param aTitle the title of the item to remove
|
||||
*/
|
||||
@@ -196,7 +188,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Removes the menu item at the specified index
|
||||
@param anIndex the index of the item to remove
|
||||
*/
|
||||
@@ -207,27 +199,27 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
}
|
||||
|
||||
// Getting the User's Selection
|
||||
/*
|
||||
Returns the selected item or <code>nil</code> if no item is selected.
|
||||
/*!
|
||||
Returns the selected item or \c nil if no item is selected.
|
||||
*/
|
||||
- (CPMenuItem)selectedItem
|
||||
{
|
||||
if (_selectedIndex < 0)
|
||||
if (_selectedIndex < 0 || _selectedIndex > [self numberOfItems] - 1)
|
||||
return nil;
|
||||
|
||||
return [_menu itemAtIndex:_selectedIndex];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the title of the selected item or <code>nil</code> if no item is selected.
|
||||
/*!
|
||||
Returns the title of the selected item or \c nil if no item is selected.
|
||||
*/
|
||||
- (CPString)titleOfSelectedItem
|
||||
{
|
||||
return [[self selectedItem] title];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the index of the selected item. If no item is selected, it returns <objj>CPNotFound</objj>.
|
||||
/*!
|
||||
Returns the index of the selected item. If no item is selected, it returns CPNotFound.
|
||||
*/
|
||||
- (int)indexOfSelectedItem
|
||||
{
|
||||
@@ -235,8 +227,8 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
}
|
||||
|
||||
// For us, CPNumber is toll-free bridged to Number, so just return the selected index.
|
||||
/*
|
||||
Returns the selected item's index. If no item is selected, it returns <objj>CPNotFound</objj>.
|
||||
/*!
|
||||
Returns the selected item's index. If no item is selected, it returns CPNotFound.
|
||||
*/
|
||||
- (id)objectValue
|
||||
{
|
||||
@@ -244,7 +236,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
}
|
||||
|
||||
// Setting the Current Selection
|
||||
/*
|
||||
/*!
|
||||
Selects the specified menu item.
|
||||
@param aMenuItem the item to select
|
||||
*/
|
||||
@@ -253,7 +245,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
[self selectItemAtIndex:[self indexOfItem:aMenuItem]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Selects the item at the specified index
|
||||
@param anIndex the index of the item to select
|
||||
*/
|
||||
@@ -262,18 +254,18 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
if (_selectedIndex == anIndex)
|
||||
return;
|
||||
|
||||
if (_selectedIndex >= 0 && !_pullsDown)
|
||||
if (_selectedIndex >= 0 && ![self pullsDown])
|
||||
[[self selectedItem] setState:CPOffState];
|
||||
|
||||
_selectedIndex = anIndex;
|
||||
|
||||
if (_selectedIndex >= 0 && !_pullsDown)
|
||||
if (_selectedIndex >= 0 && ![self pullsDown])
|
||||
[[self selectedItem] setState:CPOnState];
|
||||
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Selects the menu item with the specified tag
|
||||
@param the tag of the item to select
|
||||
*/
|
||||
@@ -282,7 +274,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
[self selectItemAtIndex:[self indexOfItemWithTag:aTag]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Selects the item with the specified title
|
||||
@param the title of the item to select
|
||||
*/
|
||||
@@ -291,7 +283,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
[self selectItemAtIndex:[self indexOfItemWithTitle:aTitle]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the object for the selected item. If no item is selected, then this method has no effect.
|
||||
@param the object set for the selected item
|
||||
*/
|
||||
@@ -301,7 +293,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
}
|
||||
|
||||
// Getting Menu Items
|
||||
/*
|
||||
/*!
|
||||
Returns the button's menu of items.
|
||||
*/
|
||||
- (CPMenu)menu
|
||||
@@ -309,12 +301,12 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
return _menu;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the menu for the button
|
||||
*/
|
||||
- (void)setMenu:(CPMenu)aMenu
|
||||
{
|
||||
if (_menu == aMenu)
|
||||
if (_menu === aMenu)
|
||||
return;
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
@@ -323,19 +315,16 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
{
|
||||
[defaultCenter
|
||||
removeObserver:self
|
||||
selector:@selector(menuDidAddItem:)
|
||||
name:CPMenuDidAddItemNotification
|
||||
object:_menu];
|
||||
|
||||
[defaultCenter
|
||||
removeObserver:self
|
||||
selector:@selector(menuDidChangeItem:)
|
||||
name:CPMenuDidChangeItemNotification
|
||||
object:_menu];
|
||||
|
||||
[defaultCenter
|
||||
removeObserver:self
|
||||
selector:@selector(menuDidRemoveItem:)
|
||||
name:CPMenuDidRemoveItemNotification
|
||||
object:_menu];
|
||||
}
|
||||
@@ -366,7 +355,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a count of the number of items in the button's menu.
|
||||
*/
|
||||
- (int)numberOfItems
|
||||
@@ -374,7 +363,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
return [_menu numberOfItems];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns an array of the items in the menu
|
||||
*/
|
||||
- (CPArray)itemArray
|
||||
@@ -382,8 +371,8 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
return [_menu itemArray];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the item at the specified index or <code>nil</code> if the item does not exist.
|
||||
/*!
|
||||
Returns the item at the specified index or \c nil if the item does not exist.
|
||||
@param anIndex the index of the item to obtain
|
||||
*/
|
||||
- (CPMenuItem)itemAtIndex:(unsigned)anIndex
|
||||
@@ -391,8 +380,8 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
return [_menu itemAtIndex:anIndex];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the title of the item at the specified index or <code>nil</code> if no item exists.
|
||||
/*!
|
||||
Returns the title of the item at the specified index or \c nil if no item exists.
|
||||
@param anIndex the index of the item
|
||||
*/
|
||||
- (CPString)itemTitleAtIndex:(unsigned)anIndex
|
||||
@@ -400,7 +389,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
return [[_menu itemAtIndex:anIndex] title];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns an array of all the menu item titles.
|
||||
*/
|
||||
- (CPArray)itemTitles
|
||||
@@ -415,7 +404,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
items.push([items[index] title]);
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the menu item with the specified title.
|
||||
@param aTitle the title of the desired menu item
|
||||
*/
|
||||
@@ -424,7 +413,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
return [_menu itemAtIndex:[_menu indexOfItemWithTitle:aTitle]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the last menu item
|
||||
*/
|
||||
- (CPMenuItem)lastItem
|
||||
@@ -433,8 +422,8 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
}
|
||||
|
||||
// Getting the Indices of Menu Items
|
||||
/*
|
||||
Returns the index of the specified item or <objj>CPNotFound</objj> if the item is not in the list.
|
||||
/*!
|
||||
Returns the index of the specified item or CPNotFound if the item is not in the list.
|
||||
@param aMenuItem the item to obtain the index for
|
||||
*/
|
||||
- (int)indexOfItem:(CPMenuItem)aMenuItem
|
||||
@@ -442,17 +431,17 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
return [_menu indexOfItem:aMenuItem];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the index of the item with the specified tag or <objj>CPNotFound</objj> if the item is not in the list.
|
||||
/*!
|
||||
Returns the index of the item with the specified tag or CPNotFound if the item is not in the list.
|
||||
@param aTag the item's tag
|
||||
*/
|
||||
- (int)indexOfItemWithTag:(int)aTag
|
||||
{
|
||||
return [_menu indexOfItemWithTag:aMenuItem];
|
||||
return [_menu indexOfItemWithTag:aTag];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the index of the item with the specified title or <objj>CPNotFound</objj>.
|
||||
/*!
|
||||
Returns the index of the item with the specified title or CPNotFound.
|
||||
@param aTitle the item's titel
|
||||
*/
|
||||
- (int)indexOfItemWithTitle:(CPString)aTitle
|
||||
@@ -460,20 +449,20 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
return [_menu indexOfItemWithTitle:aTitle];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the index of the item with the specified
|
||||
represented object or <objj>CPNotFound</objj>
|
||||
represented object or CPNotFound
|
||||
if a match does not exist.
|
||||
@param anObject the item's represented object
|
||||
*/
|
||||
- (int)indexOfItemWithRepresentedObject:(id)anObject
|
||||
{
|
||||
return [_menu indexOfItemWithRepresentedObejct:anObject];
|
||||
return [_menu indexOfItemWithRepresentedObject:anObject];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the index of the item with the specified target
|
||||
and action. Returns <objj>CPNotFound</objj> if the no
|
||||
and action. Returns CPNotFound if the no
|
||||
such item is in the list.
|
||||
@param aTarget the item's target
|
||||
@param anAction the item's action
|
||||
@@ -484,7 +473,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
}
|
||||
|
||||
// Setting the Cell Edge to Pop out in Restricted Situations
|
||||
/*
|
||||
/*!
|
||||
Returns the button's edge where the pop-up menu will be
|
||||
displayed when there is not enough room to display directly
|
||||
above the button.
|
||||
@@ -494,7 +483,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
return _preferredEdge;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the preffered edge of the button to display the
|
||||
pop-up when there is a limited amount of screen space.
|
||||
By default, the pop-up should draw on top of the button.
|
||||
@@ -505,19 +494,27 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
}
|
||||
|
||||
// Setting the Title
|
||||
/*
|
||||
/*!
|
||||
Sets the pop-up button's title.
|
||||
@param aTitle the new title
|
||||
*/
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
{
|
||||
if ([self title] == aTitle)
|
||||
if ([self title] === aTitle)
|
||||
return;
|
||||
|
||||
if (_pullsDown)
|
||||
|
||||
if ([self pullsDown])
|
||||
{
|
||||
[_items[0] setTitle:aTitle];
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
var items = [_menu itemArray];
|
||||
|
||||
if ([items count] <= 0)
|
||||
[self addItemWithTitle:aTitle];
|
||||
|
||||
else
|
||||
{
|
||||
[items[0] setTitle:aTitle];
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -535,7 +532,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
}
|
||||
|
||||
// Setting the Image
|
||||
/*
|
||||
/*!
|
||||
This method has no effect. Because the image is taken
|
||||
from the currently selected item, this method serves
|
||||
no purpose.
|
||||
@@ -546,7 +543,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
}
|
||||
|
||||
// Setting the State
|
||||
/*
|
||||
/*!
|
||||
Makes sure the selected item and the item
|
||||
being displayed are one and the same.
|
||||
*/
|
||||
@@ -554,11 +551,11 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
{
|
||||
var item = nil;
|
||||
|
||||
if (_pullsDown)
|
||||
if ([self pullsDown])
|
||||
{
|
||||
var items = [_menu itemArray];
|
||||
|
||||
if (items.length > 0)
|
||||
if ([items count] > 0)
|
||||
item = items[0];
|
||||
}
|
||||
else
|
||||
@@ -569,7 +566,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
}
|
||||
|
||||
//
|
||||
/*
|
||||
/*!
|
||||
Called when the menu has a new item added to it.
|
||||
@param aNotification information about the event
|
||||
*/
|
||||
@@ -586,7 +583,7 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
else if (index < _selectedIndex)
|
||||
++_selectedIndex;
|
||||
|
||||
if (index == 0 && _pullsDown)
|
||||
if (index == 0 && [self pullsDown])
|
||||
{
|
||||
var items = [_menu itemArray];
|
||||
|
||||
@@ -595,9 +592,18 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
if (items.length > 0)
|
||||
[items[1] setHidden:NO];
|
||||
}
|
||||
|
||||
var item = [_menu itemArray][index],
|
||||
action = [item action];
|
||||
|
||||
if (!action || (action === @selector(_popUpItemAction:)))
|
||||
{
|
||||
[item setTarget:self];
|
||||
[item setAction:@selector(_popUpItemAction:)];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Called when a menu item has changed.
|
||||
@param aNotification information about the event
|
||||
*/
|
||||
@@ -605,16 +611,16 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
{
|
||||
var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"];
|
||||
|
||||
if (_pullsDown && index != 0)
|
||||
if ([self pullsDown] && index != 0)
|
||||
return;
|
||||
|
||||
if (!_pullsDown && index != _selectedIndex)
|
||||
if (![self pullsDown] && index != _selectedIndex)
|
||||
return;
|
||||
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Called when an item was removed from the menu.
|
||||
@param aNotification information about the event
|
||||
*/
|
||||
@@ -622,46 +628,54 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
{
|
||||
var numberOfItems = [self numberOfItems];
|
||||
|
||||
if (numberOfItems <= _selectedIndex)
|
||||
if (numberOfItems <= _selectedIndex && numberOfItems > 0)
|
||||
[self selectItemAtIndex:numberOfItems - 1];
|
||||
else
|
||||
[self synchronizeTitleAndSelectedItem];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled])
|
||||
if (![self isEnabled] || ![self numberOfItems])
|
||||
return;
|
||||
|
||||
[self highlight:YES];
|
||||
|
||||
var theWindow = [self window],
|
||||
menuWindow = [_CPMenuWindow menuWindowWithMenu:[self menu] font:[self font]];
|
||||
var menu = [self menu],
|
||||
theWindow = [self window],
|
||||
menuWindow = [_CPMenuWindow menuWindowWithMenu:menu font:[self font]];
|
||||
|
||||
[menuWindow setDelegate:self];
|
||||
[menuWindow setBackgroundStyle:_CPMenuWindowPopUpBackgroundStyle];
|
||||
|
||||
var menuOrigin = [theWindow convertBaseToBridge:[self convertPoint:CGPointMakeZero() toView:nil]];
|
||||
|
||||
// Pull Down Menus show up directly below their buttons.
|
||||
if (_pullsDown)
|
||||
menuOrigin.y += CGRectGetHeight([self frame]);
|
||||
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.
|
||||
else
|
||||
{
|
||||
var contentRect = [menuWindow rectForItemAtIndex:_selectedIndex];
|
||||
// This is confusing, I KNOW, so let me explain it to you.
|
||||
// We want the *content* of the selected menu item to overlap the *content* of our pop up.
|
||||
// 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],
|
||||
menuOrigin = [theWindow convertBaseToGlobal:contentRect.origin],
|
||||
menuItemRect = [menuWindow rectForItemAtIndex:_selectedIndex];
|
||||
|
||||
menuOrigin.x -= CGRectGetMinX(contentRect) + [_CPMenuItemView leftMargin];
|
||||
menuOrigin.y -= CGRectGetMinY(contentRect);
|
||||
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 = CGRectGetMaxX([self convertRect:[self bounds] toView:nil]);
|
||||
|
||||
buttonMaxX = [theWindow convertBaseToGlobal:CGPointMake(CGRectGetMaxX([self convertRect:[self bounds] toView:nil]), 0.0)].x;
|
||||
|
||||
if (menuMaxX < buttonMaxX)
|
||||
[menuWindow setMinWidth:CGRectGetWidth([menuWindow frame]) + buttonMaxX - menuMaxX - VISIBLE_MARGIN];
|
||||
|
||||
[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:)];
|
||||
}
|
||||
@@ -682,21 +696,12 @@ var CPPopUpButtonArrowsImage = nil;
|
||||
|
||||
[self selectItemAtIndex:index];
|
||||
|
||||
var selectedItem = [self selectedItem],
|
||||
target = nil,
|
||||
action = [selectedItem action];
|
||||
|
||||
if (!action)
|
||||
{
|
||||
target = [self target];
|
||||
action = [self action];
|
||||
}
|
||||
|
||||
// FIXME: If [selectedItem target] == nil do we use our own target?
|
||||
else
|
||||
target = [selectedItem target];
|
||||
[CPApp sendAction:[aMenuItem action] to:[aMenuItem target] from:aMenuItem];
|
||||
}
|
||||
|
||||
[self sendAction:action to:target];
|
||||
- (void)_popUpItemAction:(id)aSender
|
||||
{
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -706,7 +711,7 @@ var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
||||
CPPopUpButtonPullsDownKey = @"CPPopUpButtonPullsDownKey";
|
||||
|
||||
@implementation CPPopUpButton (CPCoding)
|
||||
/*
|
||||
/*!
|
||||
Initializes the pop-up button with data from the
|
||||
specified coder.
|
||||
@param aCoder the coder from which to read
|
||||
@@ -719,15 +724,17 @@ var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
||||
|
||||
if (self)
|
||||
{
|
||||
// Nothing is currently selected
|
||||
_selectedIndex = -1;
|
||||
|
||||
[self setMenu:[aCoder decodeObjectForKey:CPPopUpButtonMenuKey]];
|
||||
[self selectItemAtIndex:[aCoder decodeObjectForKey:CPPopUpButtonSelectedIndexKey]];
|
||||
[self setPullsDown:[aCoder decodeBoolForKey:CPPopUpButtonPullsDownKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Encodes the data of the pop-up button into a coder
|
||||
@param aCoder the coder to which the data
|
||||
will be written
|
||||
@@ -738,7 +745,6 @@ var CPPopUpButtonMenuKey = @"CPPopUpButtonMenuKey",
|
||||
|
||||
[aCoder encodeObject:_menu forKey:CPPopUpButtonMenuKey];
|
||||
[aCoder encodeInt:_selectedIndex forKey:CPPopUpButtonSelectedIndexKey];
|
||||
[aCoder encodeBool:_pullsDown forKey:CPPopUpButtonPullsDownKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -20,12 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <AppKit/CGGeometry.j>
|
||||
import <AppKit/CPImageView.j>
|
||||
import <AppKit/CPView.j>
|
||||
@import <AppKit/CGGeometry.j>
|
||||
@import <AppKit/CPImageView.j>
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPProgressIndicatorStyle
|
||||
@@ -48,7 +49,10 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
CPProgressIndicatorStyleIdentifiers = nil,
|
||||
CPProgressIndicatorStyleSizes = nil;
|
||||
|
||||
/*
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPProgressIndicator
|
||||
|
||||
This class is used in a Cappuccino GUI to display the progress of a
|
||||
function or task. If the duration of the task is unknown, there is
|
||||
also an indeterminate mode for the indicator.
|
||||
@@ -178,7 +182,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Starts the animation of the progress indicator in indeterminate mode.
|
||||
@param the requesting object
|
||||
*/
|
||||
@@ -189,7 +193,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
[self _hideOrDisplay];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Stops the animation of the progress indicator in indeterminate mode.
|
||||
@param the requesting object
|
||||
*/
|
||||
@@ -200,8 +204,8 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
[self _hideOrDisplay];
|
||||
}
|
||||
|
||||
/*
|
||||
Always returns <code>NO</code>. Cappuccino does not have multiple threads.
|
||||
/*!
|
||||
Always returns \c NO. Cappuccino does not have multiple threads.
|
||||
*/
|
||||
- (BOOL)usesThreadedAnimation
|
||||
{
|
||||
@@ -209,7 +213,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
}
|
||||
|
||||
// Advancing the Progress Bar
|
||||
/*
|
||||
/*!
|
||||
Increases the progress of the bar by the specified value.
|
||||
@param aValue the amount to increase the progress value
|
||||
*/
|
||||
@@ -218,7 +222,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
[self setDoubleValue:_doubleValue + aValue];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the progress value of the indicator.
|
||||
*/
|
||||
- (void)setDoubleValue:(double)aValue
|
||||
@@ -228,7 +232,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
[self drawBar];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the value of the progress indicator.
|
||||
*/
|
||||
- (double)doubleValue
|
||||
@@ -236,7 +240,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
return _doubleValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the minimum value of the progress indicator. The default is 0.0.
|
||||
@param aValue the new minimum value
|
||||
*/
|
||||
@@ -245,7 +249,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
_minValue = aValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the minimum value of the progress indicator.
|
||||
*/
|
||||
- (double)minValue
|
||||
@@ -253,7 +257,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
return _minValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the maximum value of the progress indicator. The default is 100.0.
|
||||
@param aValue the new maximum value.
|
||||
*/
|
||||
@@ -262,7 +266,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
_maxValue = aValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the maximum value of the progress indicator.
|
||||
*/
|
||||
- (double)maxValue
|
||||
@@ -271,7 +275,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
}
|
||||
|
||||
// Setting the Appearance
|
||||
/*
|
||||
/*!
|
||||
Sets the progress indicator's size.
|
||||
@param aControlSize the new size
|
||||
*/
|
||||
@@ -285,7 +289,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
[self updateBackgroundColor];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the progress indicator's size
|
||||
*/
|
||||
- (CPControlSize)controlSize
|
||||
@@ -323,13 +327,13 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Specifies whether this progress indicator should be indeterminate or display progress based on it's max and min.
|
||||
@param isDeterminate <code>YES</code> makes the indicator indeterminate
|
||||
@param isDeterminate \c YES makes the indicator indeterminate
|
||||
*/
|
||||
- (void)setIndeterminate:(BOOL)isIndeterminate
|
||||
{
|
||||
if (_indeterminate == isIndeterminate)
|
||||
if (_isIndeterminate == isIndeterminate)
|
||||
return;
|
||||
|
||||
_isIndeterminate = isIndeterminate;
|
||||
@@ -337,15 +341,15 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
[self updateBackgroundColor];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the progress bar is indeterminate.
|
||||
/*!
|
||||
Returns \c YES if the progress bar is indeterminate.
|
||||
*/
|
||||
- (BOOL)isIndeterminate
|
||||
{
|
||||
return _isIndeterminate;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the progress indicator's style
|
||||
@param aStyle the style to set it to
|
||||
*/
|
||||
@@ -359,7 +363,7 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
[self updateBackgroundColor];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Resizes the indicator based on it's style.
|
||||
*/
|
||||
- (void)sizeToFit
|
||||
@@ -372,10 +376,10 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
_CPControlIdentifierForControlSize(_controlSize)][0].height)];
|
||||
}
|
||||
|
||||
/*
|
||||
Sets whether the indicator should be displayed when it isn't animating. By default this is <code>YES</code> if the style
|
||||
is <objj>CPProgressIndicatorBarStyle</objj>, and <code>NO</code> if it's <objj>CPProgressIndicatorSpinningStyle</objj>.
|
||||
@param isDisplayedWhenStopped <code>YES</code> means the indicator will be displayed when it's not animating.
|
||||
/*!
|
||||
Sets whether the indicator should be displayed when it isn't animating. By default this is \c YES if the style
|
||||
is CPProgressIndicatorBarStyle, and \c NO if it's CPProgressIndicatorSpinningStyle.
|
||||
@param isDisplayedWhenStopped \c YES means the indicator will be displayed when it's not animating.
|
||||
*/
|
||||
- (void)setDisplayedWhenStopped:(BOOL)isDisplayedWhenStopped
|
||||
{
|
||||
@@ -389,8 +393,8 @@ var CPProgressIndicatorSpinningStyleColors = nil,
|
||||
[self _hideOrDisplay];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the progress bar is displayed when not animating.
|
||||
/*!
|
||||
Returns \c YES if the progress bar is displayed when not animating.
|
||||
*/
|
||||
- (BOOL)isDisplayedWhenStopped
|
||||
{
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* CPRadio.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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/CPObject.j>
|
||||
@import <Foundation/CPSet.j>
|
||||
|
||||
@import "CPButton.j"
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
|
||||
from this mailing list thread:
|
||||
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
|
||||
radioGroup]];
|
||||
...
|
||||
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.
|
||||
|
||||
*/
|
||||
@implementation CPRadio : CPButton
|
||||
{
|
||||
CPRadioGroup _radioGroup;
|
||||
}
|
||||
|
||||
+ (id)radioWithTitle:(CPString)aTitle theme:(CPTheme)aTheme
|
||||
{
|
||||
return [self buttonWithTitle:aTitle theme:aTheme];
|
||||
}
|
||||
|
||||
+ (id)radioWithTitle:(CPString)aTitle
|
||||
{
|
||||
return [self buttonWithTitle:aTitle];
|
||||
}
|
||||
|
||||
+ (CPButton)standardButtonWithTitle:(CPString)aTitle
|
||||
{
|
||||
var button = [[CPRadio alloc] init];
|
||||
|
||||
[button setTitle:aTitle];
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
{
|
||||
return @"radio";
|
||||
}
|
||||
|
||||
// Designated Initializer
|
||||
- (id)initWithFrame:(CGRect)aFrame radioGroup:(CPRadioGroup)aRadioGroup
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setRadioGroup:aRadioGroup];
|
||||
|
||||
[self setHighlightsBy:CPContentsCellMask];
|
||||
[self setShowsStateBy:CPContentsCellMask];
|
||||
|
||||
// Defaults?
|
||||
[self setImagePosition:CPImageLeft];
|
||||
[self setAlignment:CPLeftTextAlignment];
|
||||
|
||||
[self setBordered:YES];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
return [self initWithFrame:aFrame radioGroup:[CPRadioGroup new]];
|
||||
}
|
||||
|
||||
- (CPInteger)nextState
|
||||
{
|
||||
return CPOnState;
|
||||
}
|
||||
|
||||
- (void)setRadioGroup:(CPRadioGroup)aRadioGroup
|
||||
{
|
||||
if (_radioGroup === aRadioGroup)
|
||||
return;
|
||||
|
||||
[_radioGroup _removeRadio:self];
|
||||
_radioGroup = aRadioGroup;
|
||||
[_radioGroup _addRadio:self];
|
||||
}
|
||||
|
||||
- (CPRadioGroup)radioGroup
|
||||
{
|
||||
return _radioGroup;
|
||||
}
|
||||
|
||||
- (void)setObjectValue:(id)aValue
|
||||
{
|
||||
[super setObjectValue:aValue];
|
||||
|
||||
if ([self state] === CPOnState)
|
||||
[_radioGroup _setSelectedRadio:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
||||
|
||||
@implementation CPRadio (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
_radioGroup = [aCoder decodeObjectForKey:CPRadioRadioGroupKey];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeObject:_radioGroup forKey:CPRadioRadioGroupKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPRadioGroup : CPObject
|
||||
{
|
||||
CPSet _radios;
|
||||
CPRadio _selectedRadio;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_radios = [CPSet set];
|
||||
_selectedRadio = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_addRadio:(CPRadio)aRadio
|
||||
{
|
||||
[_radios addObject:aRadio];
|
||||
|
||||
if ([aRadio state] === CPOnState)
|
||||
[self _setSelectedRadio:aRadio];
|
||||
}
|
||||
|
||||
- (void)_removeRadio:(CPRadio)aRadio
|
||||
{
|
||||
if (_selectedRadio === aRadio)
|
||||
_selectedRadio = nil;
|
||||
|
||||
[_radios removeObject:aRadio];
|
||||
}
|
||||
|
||||
- (void)_setSelectedRadio:(CPRadio)aRadio
|
||||
{
|
||||
if (_selectedRadio === aRadio)
|
||||
return;
|
||||
|
||||
[_selectedRadio setState:CPOffState];
|
||||
_selectedRadio = aRadio;
|
||||
}
|
||||
|
||||
- (CPRadio)selectedRadio
|
||||
{
|
||||
return _selectedRadio;
|
||||
}
|
||||
|
||||
- (CPArray)radios
|
||||
{
|
||||
return [_radios allObjects];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPRadioGroupRadiosKey = @"CPRadioGroupRadiosKey",
|
||||
CPRadioGroupSelectedRadioKey = @"CPRadioGroupSelectedRadioKey";
|
||||
|
||||
@implementation CPRadioGroup (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_radios = [aCoder decodeObjectForKey:CPRadioGroupRadiosKey];
|
||||
_selectedRadio = [aCoder decodeObjectForKey:CPRadioGroupSelectedRadioKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_radios forKey:CPRadioGroupRadiosKey];
|
||||
[aCoder encodeObject:_selectedRadio forKey:CPRadioGroupSelectedRadioKey];
|
||||
}
|
||||
|
||||
@end
|
||||
+98
-57
@@ -20,14 +20,24 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
CPDeleteKeyCode = 8;
|
||||
CPUpArrowKeyCode = 63232;
|
||||
CPDownArrowKeyCode = 63233;
|
||||
CPLeftArrowKeyCode = 63234;
|
||||
CPRightArrowKeyCode = 63235;
|
||||
CPTabKeyCode = 9;
|
||||
CPReturnKeyCode = 13;
|
||||
CPEscapeKeyCode = 27;
|
||||
CPLeftArrowKeyCode = 37;
|
||||
CPUpArrowKeyCode = 38;
|
||||
CPRightArrowKeyCode = 39;
|
||||
CPDownArrowKeyCode = 40;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPResponder
|
||||
|
||||
Subclasses of CPResonder can be part of the responder chain.
|
||||
*/
|
||||
@implementation CPResponder : CPObject
|
||||
{
|
||||
CPMenu _menu;
|
||||
@@ -35,27 +45,27 @@ CPRightArrowKeyCode = 63235;
|
||||
}
|
||||
|
||||
// Changing the first responder
|
||||
/*
|
||||
Returns <code>YES</code> if the receiver is able to become the first responder. <code>NO</code> otherwise.
|
||||
/*!
|
||||
Returns \c YES if the receiver is able to become the first responder. \c NO otherwise.
|
||||
*/
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Notifies the receiver that it will become the first responder. The receiver can reject first
|
||||
responder if it returns <code>NO</code>. The default implementation always returns <code>YES</code>.
|
||||
@return <code>YES</code> if the receiver accepts first responder status.
|
||||
responder if it returns \c NO. The default implementation always returns \c YES.
|
||||
@return \c YES if the receiver accepts first responder status.
|
||||
*/
|
||||
- (BOOL)becomeFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Notifies the receiver that it has been asked to give up first responder status.
|
||||
@return <code>YES</code> if the receiver is willing to give up first responder status.
|
||||
@return \c YES if the receiver is willing to give up first responder status.
|
||||
*/
|
||||
- (BOOL)resignFirstResponder
|
||||
{
|
||||
@@ -63,7 +73,7 @@ CPRightArrowKeyCode = 63235;
|
||||
}
|
||||
|
||||
// Setting the next responder
|
||||
/*
|
||||
/*!
|
||||
Sets the receiver's next responder.
|
||||
@param aResponder the responder after the receiver
|
||||
*/
|
||||
@@ -72,7 +82,7 @@ CPRightArrowKeyCode = 63235;
|
||||
_nextResponder = aResponder;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the responder after the receiver.
|
||||
*/
|
||||
- (CPResponder)nextResponder
|
||||
@@ -80,38 +90,53 @@ CPRightArrowKeyCode = 63235;
|
||||
return _nextResponder;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Called to interpret a series of key events.
|
||||
@param events an array of key <objj>CPEvent</objj>s
|
||||
@param events an array of key CPEvents
|
||||
*/
|
||||
- (void)interpretKeyEvents:(CPArray)events
|
||||
{
|
||||
var event,
|
||||
index = 0;
|
||||
|
||||
while(event = events[index++])
|
||||
var index = 0,
|
||||
count = [events count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var event = events[index];
|
||||
|
||||
switch([event keyCode])
|
||||
{
|
||||
case CPLeftArrowKeyCode: [self moveBackward:self];
|
||||
case CPLeftArrowKeyCode: [self doCommandBySelector:@selector(moveLeft:)];
|
||||
break;
|
||||
case CPRightArrowKeyCode: [self moveForward:self];
|
||||
case CPRightArrowKeyCode: [self doCommandBySelector:@selector(moveRight:)];
|
||||
break;
|
||||
case CPUpArrowKeyCode: [self moveUp:self];
|
||||
case CPUpArrowKeyCode: [self doCommandBySelector:@selector(moveUp:)];
|
||||
break;
|
||||
case CPDownArrowKeyCode: [self moveDown:self];
|
||||
case CPDownArrowKeyCode: [self doCommandBySelector:@selector(moveDown:)];
|
||||
break;
|
||||
case CPDeleteKeyCode: [self deleteBackward:self];
|
||||
case CPDeleteKeyCode: [self doCommandBySelector:@selector(deleteBackward:)];
|
||||
break;
|
||||
case 3:
|
||||
case 13: [self insertLineBreak:self];
|
||||
case CPReturnKeyCode:
|
||||
case 3: [self doCommandBySelector:@selector(insertLineBreak:)];
|
||||
break;
|
||||
|
||||
case CPEscapeKeyCode: [self doCommandBySelector:@selector(cancel:)];
|
||||
break;
|
||||
|
||||
case CPTabKeyCode: var shift = [event modifierFlags] & CPShiftKeyMask;
|
||||
|
||||
if (!shift)
|
||||
[self doCommandBySelector:@selector(insertTab:)];
|
||||
else
|
||||
[self doCommandBySelector:@selector(insertBackTab:)];
|
||||
|
||||
break;
|
||||
|
||||
default: [self insertText:[event characters]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Notifies the receiver that the user has clicked the mouse down in its area.
|
||||
@param anEvent contains information about the click
|
||||
*/
|
||||
@@ -120,7 +145,7 @@ CPRightArrowKeyCode = 63235;
|
||||
[_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.
|
||||
@param anEvent contains information about the drag
|
||||
@@ -130,7 +155,7 @@ CPRightArrowKeyCode = 63235;
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Notifies the receiver that the user has released the left mouse button.
|
||||
@param anEvent contains information about the release
|
||||
*/
|
||||
@@ -139,7 +164,7 @@ CPRightArrowKeyCode = 63235;
|
||||
[_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
|
||||
*/
|
||||
@@ -153,7 +178,7 @@ CPRightArrowKeyCode = 63235;
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Notifies the receiver that the mouse exited the receiver's area.
|
||||
@param anEvent contains information about the exit
|
||||
*/
|
||||
@@ -162,7 +187,7 @@ CPRightArrowKeyCode = 63235;
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Notifies the receiver that the mouse scroll wheel has moved.
|
||||
@param anEvent information about the scroll
|
||||
*/
|
||||
@@ -171,7 +196,7 @@ CPRightArrowKeyCode = 63235;
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Notifies the receiver that the user has pressed a key.
|
||||
@param anEvent information about the key press
|
||||
*/
|
||||
@@ -180,7 +205,7 @@ CPRightArrowKeyCode = 63235;
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Notifies the receiver that the user has released a key.
|
||||
@param anEvent information about the key press
|
||||
*/
|
||||
@@ -191,9 +216,9 @@ CPRightArrowKeyCode = 63235;
|
||||
|
||||
/*
|
||||
FIXME This description is bad.
|
||||
Based on <code>anEvent</code>, the receiver should simulate the event.
|
||||
Based on \c anEvent, the receiver should simulate the event.
|
||||
@param anEvent the event to simulate
|
||||
@return <code>YES</code> if the event receiver simulated the event
|
||||
@return \c YES if the event receiver simulated the event
|
||||
*/
|
||||
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
||||
{
|
||||
@@ -201,23 +226,37 @@ CPRightArrowKeyCode = 63235;
|
||||
}
|
||||
|
||||
// Action Methods
|
||||
/*
|
||||
Deletes one character backward, or the selection if anything is selected.
|
||||
@param aSender the object requesting this
|
||||
*/
|
||||
- (void)deleteBackward:(id)aSender
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Insert a line break at the caret position or selection.
|
||||
@param aSender the object requesting this
|
||||
*/
|
||||
- (void)insertLineBreak:(id)aSender
|
||||
{
|
||||
[self insertNewline:aSender];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Insert a line break at the caret position or selection.
|
||||
@param aSender the object requesting this
|
||||
*/
|
||||
- (void)insertNewline:(id)aSender
|
||||
{
|
||||
[[self nextResponder] insertNewline:aSender];
|
||||
}
|
||||
|
||||
- (void)cancel:(id)sender
|
||||
{
|
||||
}
|
||||
|
||||
- (void)insertTab:(id)sender
|
||||
{
|
||||
}
|
||||
|
||||
- (void)insertBackTab:(id)sender
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Inserts some text at the caret position or selection.
|
||||
@param aString the string to insert
|
||||
*/
|
||||
@@ -226,24 +265,24 @@ CPRightArrowKeyCode = 63235;
|
||||
}
|
||||
|
||||
// Dispatch methods
|
||||
/*
|
||||
/*!
|
||||
The receiver will attempt to perform the command,
|
||||
if it responds to it. If not, the <code>nextResponder</code> will be called to do it.
|
||||
if it responds to it. If not, the \c -nextResponder will be called to do it.
|
||||
@param aSelector the command to attempt
|
||||
*/
|
||||
- (void)doCommandBySelector:(SEL)aSelector
|
||||
{
|
||||
if([self respondsToSelector:aSelector])
|
||||
if ([self respondsToSelector:aSelector])
|
||||
[self performSelector:aSelector];
|
||||
else
|
||||
[_nextResponder doCommandBySelector:aSelector];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
The receiver will attempt to perform the command, or pass it on to the next responder if it doesn't respond to it.
|
||||
@param aSelector the command to perform
|
||||
@param anObject the argument to the method
|
||||
@return <code>YES</code> if the receiver was able to perform the command, or a responder down the chain was
|
||||
@return \c YES if the receiver was able to perform the command, or a responder down the chain was
|
||||
able to perform the command.
|
||||
*/
|
||||
- (BOOL)tryToPerform:(SEL)aSelector with:(id)anObject
|
||||
@@ -271,7 +310,7 @@ CPRightArrowKeyCode = 63235;
|
||||
}
|
||||
|
||||
// Getting the Undo Manager
|
||||
/*
|
||||
/*!
|
||||
Returns the undo manager for the receiver.
|
||||
*/
|
||||
- (CPUndoManager)undoManager
|
||||
@@ -280,7 +319,7 @@ CPRightArrowKeyCode = 63235;
|
||||
}
|
||||
|
||||
// Terminating the responder chain
|
||||
/*
|
||||
/*!
|
||||
Called when an event finds no suitable responder.
|
||||
@param anEventSelector the command that failed
|
||||
*/
|
||||
@@ -294,7 +333,7 @@ var CPResponderNextResponderKey = @"CPResponderNextResponderKey";
|
||||
|
||||
@implementation CPResponder (CPCoding)
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the responder with data from a coder.
|
||||
@param aCoder the coder from which data will be read
|
||||
@return the initialized responder
|
||||
@@ -309,13 +348,15 @@ var CPResponderNextResponderKey = @"CPResponderNextResponderKey";
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Archives the responder to a coder.
|
||||
@param aCoder the coder to which the responder will be archived
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeConditionalObject:_nextResponder forKey:CPResponderNextResponderKey];
|
||||
// This will come out nil on the other side with decodeObjectForKey:
|
||||
if (_nextResponder !== nil)
|
||||
[aCoder encodeConditionalObject:_nextResponder forKey:CPResponderNextResponderKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
|
||||
@import <AppKit/CPPanel.j>
|
||||
|
||||
|
||||
@implementation CPSavePanel : CPPanel
|
||||
{
|
||||
CPURL _URL;
|
||||
|
||||
BOOL _isExtensionHidden @accessors(getter=isExtensionHidden, setter=setExtensionHidden:);
|
||||
BOOL _canSelectHiddenExtension @accessors(property=canSelectHiddenExtension);
|
||||
BOOL _allowsOtherFileTypes @accessors(property=allowsOtherFileTypes);
|
||||
BOOL _canCreateDirectories @accessors(property=canCreateDirectories);
|
||||
|
||||
CPArray _allowedFileTypes @accessors(property=allowedFileTypes);
|
||||
}
|
||||
|
||||
+ (id)savePanel
|
||||
{
|
||||
return [[CPSavePanel alloc] init];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_canCreateDirectories = YES;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPInteger)runModal
|
||||
{
|
||||
// FIXME: Is this correct???
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
if (typeof window["cpSavePanel"] === "function")
|
||||
{
|
||||
var resultObject = window.cpSavePanel({
|
||||
isExtensionHidden: _isExtensionHidden,
|
||||
canSelectHiddenExtension: _canSelectHiddenExtension,
|
||||
allowsOtherFileTypes: _allowsOtherFileTypes,
|
||||
canCreateDirectories: _canCreateDirectories,
|
||||
allowedFileTypes: _allowedFileTypes
|
||||
}),
|
||||
result = resultObject.button;
|
||||
|
||||
_URL = result ? [CPURL URLWithString:resultObject.URL] : nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: This is not the best way to do this.
|
||||
var documentName = window.prompt("Document Name:"),
|
||||
result = documentName !== null;
|
||||
|
||||
_URL = result ? [[self class] proposedFileURLWithDocumentName:documentName] : nil;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (CPURL)URL
|
||||
{
|
||||
return _URL;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
#import "CoreGraphics/CGGeometry.h"
|
||||
|
||||
@implementation CPScreen : CPObject
|
||||
{
|
||||
}
|
||||
|
||||
- (CGRect)visibleFrame
|
||||
{
|
||||
return _CGRectMake(window.screen.availLeft, window.screen.availTop, window.screen.availWidth, window.screen.availHeight);
|
||||
}
|
||||
|
||||
@end
|
||||
+310
-169
@@ -20,21 +20,27 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPView.j"
|
||||
import "CPClipView.j"
|
||||
import "CPScroller.j"
|
||||
@import "CPView.j"
|
||||
@import "CPClipView.j"
|
||||
@import "CPScroller.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
/*
|
||||
Used to display views that are too large for the viewing area. the <objj>CPScrollView</objj>
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPScrollView
|
||||
|
||||
Used to display views that are too large for the viewing area. the CPScrollView
|
||||
places scroll bars on the side of the view to allow the user to scroll and see the entire
|
||||
contents of the view.
|
||||
*/
|
||||
@implementation CPScrollView : CPView
|
||||
{
|
||||
CPClipView _contentView;
|
||||
|
||||
CPClipView _headerClipView;
|
||||
CPView _cornerView;
|
||||
|
||||
BOOL _hasVerticalScroller;
|
||||
BOOL _hasHorizontalScroller;
|
||||
BOOL _autohidesScrollers;
|
||||
@@ -53,28 +59,32 @@ import "CPScroller.j"
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_verticalLineScroll = 10.0;
|
||||
_verticalPageScroll = 10.0;
|
||||
|
||||
|
||||
_horizontalLineScroll = 10.0;
|
||||
_horizontalPageScroll = 10.0;
|
||||
|
||||
_contentView = [[CPClipView alloc] initWithFrame:[self bounds]];
|
||||
|
||||
|
||||
[self addSubview:_contentView];
|
||||
|
||||
|
||||
_headerClipView = [[CPClipView alloc] init];
|
||||
|
||||
[self addSubview:_headerClipView];
|
||||
|
||||
[self setHasVerticalScroller:YES];
|
||||
[self setHasHorizontalScroller:YES];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// Determining component sizes
|
||||
/*
|
||||
/*!
|
||||
Returns the size of the scroll view's content view.
|
||||
*/
|
||||
- (CGRect)contentSize
|
||||
@@ -82,7 +92,7 @@ import "CPScroller.j"
|
||||
return [_contentView frame].size;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the view that is scrolled for the user.
|
||||
*/
|
||||
- (id)documentView
|
||||
@@ -90,33 +100,33 @@ import "CPScroller.j"
|
||||
return [_contentView documentView];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the content view that clips the document
|
||||
@param aContentView the content view
|
||||
*/
|
||||
- (void)setContentView:(CPClipView)aContentView
|
||||
{
|
||||
if (!aContentView)
|
||||
if (_contentView !== aContentView || !aContentView)
|
||||
return;
|
||||
|
||||
|
||||
var documentView = [aContentView documentView];
|
||||
|
||||
|
||||
if (documentView)
|
||||
[documentView removeFromSuperview];
|
||||
|
||||
|
||||
[_contentView removeFromSuperview];
|
||||
|
||||
var size = [self contentSize];
|
||||
|
||||
|
||||
_contentView = aContentView;
|
||||
|
||||
[_contentView setFrame:CGRectMake(0.0, 0.0, size.width, size.height)];
|
||||
|
||||
[_contentView setDocumentView:documentView];
|
||||
|
||||
[self addSubview:_contentView];
|
||||
|
||||
// This will size the content view appropriately, so no need to size it in this method.
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the content view that clips the document.
|
||||
*/
|
||||
- (CPClipView)contentView
|
||||
@@ -124,24 +134,26 @@ import "CPScroller.j"
|
||||
return _contentView;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the view that is scrolled for the user.
|
||||
@param aView the view that will be scrolled
|
||||
*/
|
||||
- (void)setDocumentView:(CPView)aView
|
||||
{
|
||||
[_contentView setDocumentView:aView];
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
[_contentView setDocumentView:aView];
|
||||
|
||||
// FIXME: This should be observed.
|
||||
[self _updateCornerAndHeaderView];
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
/*
|
||||
Resizes the scroll view to contain the specified
|
||||
clip view.
|
||||
/*!
|
||||
Resizes the scroll view to contain the specified clip view.
|
||||
@param aClipView the clip view to resize to
|
||||
*/
|
||||
- (void)reflectScrolledClipView:(CPClipView)aClipView
|
||||
{
|
||||
if(_contentView != aClipView)
|
||||
if(_contentView !== aClipView)
|
||||
return;
|
||||
|
||||
if (_recursionCount > 5)
|
||||
@@ -160,84 +172,112 @@ import "CPScroller.j"
|
||||
}
|
||||
else
|
||||
{
|
||||
[_verticalScroller setEnabled:NO];
|
||||
[_horizontalScroller setEnabled:NO];
|
||||
// [_verticalScroller setEnabled:NO];
|
||||
// [_horizontalScroller setEnabled:NO];
|
||||
}
|
||||
|
||||
|
||||
[_contentView setFrame:[self bounds]];
|
||||
|
||||
[_headerClipView setFrame:_CGRectMakeZero()];
|
||||
|
||||
--_recursionCount;
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var documentFrame = [documentView frame],
|
||||
contentViewFrame = [self bounds],
|
||||
scrollPoint = [_contentView bounds].origin,
|
||||
difference = _CGSizeMake(CPRectGetWidth(documentFrame) - CPRectGetWidth(contentViewFrame), CPRectGetHeight(documentFrame) - CPRectGetHeight(contentViewFrame)),
|
||||
shouldShowVerticalScroller = (!_autohidesScrollers || difference.height > 0.0) && _hasVerticalScroller,
|
||||
shouldShowHorizontalScroller = (!_autohidesScrollers || difference.width > 0.0) && _hasHorizontalScroller,
|
||||
wasShowingVerticalScroller = ![_verticalScroller isHidden],
|
||||
wasShowingHorizontalScroller = ![_horizontalScroller isHidden],
|
||||
verticalScrollerWidth = [CPScroller scrollerWidthForControlSize:[_verticalScroller controlSize]],
|
||||
horizontalScrollerHeight = [CPScroller scrollerWidthForControlSize:[_horizontalScroller controlSize]];
|
||||
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)
|
||||
headerClipViewFrame = [self _headerClipViewFrame],
|
||||
headerClipViewHeight = _CGRectGetHeight(headerClipViewFrame);
|
||||
|
||||
if (_autohidesScrollers)
|
||||
{
|
||||
if (shouldShowVerticalScroller)
|
||||
shouldShowHorizontalScroller = (!_autohidesScrollers || difference.width > -verticalScrollerWidth) && _hasHorizontalScroller;
|
||||
if (shouldShowHorizontalScroller)
|
||||
shouldShowVerticalScroller = (!_autohidesScrollers || difference.height > -horizontalScrollerHeight) && _hasVerticalScroller;
|
||||
}
|
||||
|
||||
[_verticalScroller setHidden:!shouldShowVerticalScroller];
|
||||
[_verticalScroller setEnabled:!_autohidesScrollers && difference.height < 0];
|
||||
contentFrame.origin.y += headerClipViewHeight;
|
||||
contentFrame.size.height -= headerClipViewHeight;
|
||||
|
||||
[_horizontalScroller setHidden:!shouldShowHorizontalScroller];
|
||||
[_horizontalScroller setEnabled:!_autohidesScrollers && difference.width < 0];
|
||||
var difference = _CGSizeMake(_CGRectGetWidth(documentFrame) - _CGRectGetWidth(contentFrame), _CGRectGetHeight(documentFrame) - _CGRectGetHeight(contentFrame)),
|
||||
verticalScrollerWidth = _CGRectGetWidth([_verticalScroller frame]),
|
||||
horizontalScrollerHeight = _CGRectGetHeight([_horizontalScroller frame]),
|
||||
hasVerticalScroll = difference.height > 0.0,
|
||||
hasHorizontalScroll = difference.width > 0.0,
|
||||
shouldShowVerticalScroller = _hasVerticalScroller && (!_autohidesScrollers || hasVerticalScroll),
|
||||
shouldShowHorizontalScroller = _hasHorizontalScroller && (!_autohidesScrollers || hasHorizontalScroll);
|
||||
|
||||
// Now we have to account for the shown scrollers affecting the deltas.
|
||||
if (shouldShowVerticalScroller)
|
||||
{
|
||||
var verticalScrollerHeight = CPRectGetHeight(contentViewFrame);
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
verticalScrollerHeight -= horizontalScrollerHeight;
|
||||
|
||||
difference.width += verticalScrollerWidth;
|
||||
contentViewFrame.size.width -= verticalScrollerWidth;
|
||||
|
||||
[_verticalScroller setFloatValue:(difference.height <= 0.0) ? 0.0 : scrollPoint.y / difference.height
|
||||
knobProportion:CPRectGetHeight(contentViewFrame) / CPRectGetHeight(documentFrame)];
|
||||
[_verticalScroller setFrame:CPRectMake(CPRectGetMaxX(contentViewFrame), 0.0, verticalScrollerWidth, verticalScrollerHeight)];
|
||||
hasHorizontalScroll = difference.width > 0.0;
|
||||
shouldShowHorizontalScroller = _hasHorizontalScroller && (!_autohidesScrollers || hasHorizontalScroll);
|
||||
}
|
||||
else if (wasShowingVerticalScroller)
|
||||
[_verticalScroller setFloatValue:0.0 knobProportion:1.0];
|
||||
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
{
|
||||
difference.height += horizontalScrollerHeight;
|
||||
contentViewFrame.size.height -= horizontalScrollerHeight;
|
||||
|
||||
[_horizontalScroller setFloatValue:(difference.width <= 0.0) ? 0.0 : scrollPoint.x / difference.width
|
||||
knobProportion:CPRectGetWidth(contentViewFrame) / CPRectGetWidth(documentFrame)];
|
||||
[_horizontalScroller setFrame:CPRectMake(0.0, CPRectGetMaxY(contentViewFrame), CPRectGetWidth(contentViewFrame), horizontalScrollerHeight)];
|
||||
hasVerticalScroll = difference.height > 0.0;
|
||||
shouldShowVerticalScroller = _hasVerticalScroller && (!_autohidesScrollers || hasVerticalScroll);
|
||||
}
|
||||
|
||||
// We now definitively know which scrollers are shown or not, as well as whether they are showing scroll values.
|
||||
[_verticalScroller setHidden:!shouldShowVerticalScroller];
|
||||
[_verticalScroller setEnabled:hasVerticalScroll];
|
||||
|
||||
[_horizontalScroller setHidden:!shouldShowHorizontalScroller];
|
||||
[_horizontalScroller setEnabled:hasHorizontalScroll];
|
||||
|
||||
// We can thus appropriately account for them changing the content size.
|
||||
if (shouldShowVerticalScroller)
|
||||
contentFrame.size.width -= verticalScrollerWidth;
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
contentFrame.size.height -= horizontalScrollerHeight;
|
||||
|
||||
var scrollPoint = [_contentView bounds].origin,
|
||||
wasShowingVerticalScroller = ![_verticalScroller isHidden],
|
||||
wasShowingHorizontalScroller = ![_horizontalScroller isHidden];
|
||||
|
||||
if (shouldShowVerticalScroller)
|
||||
{
|
||||
var verticalScrollerY = MAX(_CGRectGetHeight([self _cornerViewFrame]), headerClipViewHeight),
|
||||
verticalScrollerHeight = _CGRectGetHeight([self bounds]) - verticalScrollerY;
|
||||
|
||||
if (shouldShowHorizontalScroller)
|
||||
verticalScrollerHeight -= horizontalScrollerHeight;
|
||||
|
||||
[_verticalScroller setFloatValue:(difference.height <= 0.0) ? 0.0 : scrollPoint.y / difference.height];
|
||||
[_verticalScroller setKnobProportion:_CGRectGetHeight(contentFrame) / _CGRectGetHeight(documentFrame)];
|
||||
[_verticalScroller setFrame:_CGRectMake(_CGRectGetMaxX(contentFrame), verticalScrollerY, verticalScrollerWidth, verticalScrollerHeight)];
|
||||
}
|
||||
else if (wasShowingVerticalScroller)
|
||||
{
|
||||
[_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)];
|
||||
}
|
||||
else if (wasShowingHorizontalScroller)
|
||||
[_horizontalScroller setFloatValue:0.0 knobProportion:1.0];
|
||||
|
||||
[_contentView setFrame:contentViewFrame];
|
||||
|
||||
{
|
||||
[_horizontalScroller setFloatValue:0.0];
|
||||
[_horizontalScroller setKnobProportion:1.0];
|
||||
}
|
||||
|
||||
[_contentView setFrame:contentFrame];
|
||||
[_headerClipView setFrame:headerClipViewFrame];
|
||||
[_cornerView setFrame:[self _cornerViewFrame]];
|
||||
|
||||
--_recursionCount;
|
||||
}
|
||||
|
||||
// Managing Scrollers
|
||||
/*
|
||||
/*!
|
||||
Sets the scroll view's horizontal scroller.
|
||||
@param aScroller the horizontal scroller for the scroll view
|
||||
*/
|
||||
- (void)setHorizontalScroller:(CPScroller)aScroller
|
||||
{
|
||||
if (_horizontalScroller == aScroller)
|
||||
if (_horizontalScroller === aScroller)
|
||||
return;
|
||||
|
||||
[_horizontalScroller removeFromSuperview];
|
||||
@@ -254,7 +294,7 @@ import "CPScroller.j"
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the scroll view's horizontal scroller
|
||||
*/
|
||||
- (CPScroller)horizontalScroller
|
||||
@@ -262,45 +302,39 @@ import "CPScroller.j"
|
||||
return _horizontalScroller;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Specifies whether the scroll view can have a horizontal scroller.
|
||||
@param hasHorizontalScroller <code>YES</code> lets the scroll view
|
||||
@param hasHorizontalScroller \c YES lets the scroll view
|
||||
allocate a horizontal scroller if necessary.
|
||||
*/
|
||||
- (void)setHasHorizontalScroller:(BOOL)shouldHaveHorizontalScroller
|
||||
{
|
||||
if (_hasHorizontalScroller == shouldHaveHorizontalScroller)
|
||||
if (_hasHorizontalScroller === shouldHaveHorizontalScroller)
|
||||
return;
|
||||
|
||||
_hasHorizontalScroller = shouldHaveHorizontalScroller;
|
||||
|
||||
|
||||
if (_hasHorizontalScroller && !_horizontalScroller)
|
||||
[self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CPRectMake(0.0, 0.0, CPRectGetWidth([self bounds]), [CPScroller scrollerWidth])]];
|
||||
[self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, _CGRectGetWidth([self bounds]), [CPScroller scrollerWidth])]];
|
||||
|
||||
else if (!_hasHorizontalScroller && _horizontalScroller)
|
||||
{
|
||||
[_horizontalScroller setHidden:YES];
|
||||
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the scroll view can have a horizontal
|
||||
scroller.
|
||||
/*!
|
||||
Returns \c YES if the scroll view can have a horizontal scroller.
|
||||
*/
|
||||
- (BOOL)hasHorizontalScroller
|
||||
{
|
||||
return _hasHorizontalScroller;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the scroll view's vertical scroller.
|
||||
@param aScroller the vertical scroller
|
||||
*/
|
||||
- (void)setVerticalScroller:(CPScroller)aScroller
|
||||
{
|
||||
if (_verticalScroller == aScroller)
|
||||
if (_verticalScroller === aScroller)
|
||||
return;
|
||||
|
||||
[_verticalScroller removeFromSuperview];
|
||||
@@ -317,7 +351,7 @@ import "CPScroller.j"
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Return's the scroll view's vertical scroller
|
||||
*/
|
||||
- (CPScroller)verticalScroller
|
||||
@@ -325,64 +359,119 @@ import "CPScroller.j"
|
||||
return _verticalScroller;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Specifies whether the scroll view has can have
|
||||
a vertical scroller. It allocates it if necessary.
|
||||
@param hasVerticalScroller <code>YES</code> allows
|
||||
@param hasVerticalScroller \c YES allows
|
||||
the scroll view to display a vertical scroller
|
||||
*/
|
||||
- (void)setHasVerticalScroller:(BOOL)shouldHaveVerticalScroller
|
||||
{
|
||||
if (_hasVerticalScroller == shouldHaveVerticalScroller)
|
||||
if (_hasVerticalScroller === shouldHaveVerticalScroller)
|
||||
return;
|
||||
|
||||
_hasVerticalScroller = shouldHaveVerticalScroller;
|
||||
|
||||
if (_hasVerticalScroller && !_verticalScroller)
|
||||
[self setVerticalScroller:[[CPScroller alloc] initWithFrame:CPRectMake(0.0, 0.0, [CPScroller scrollerWidth], CPRectGetHeight([self bounds]))]];
|
||||
[self setVerticalScroller:[[CPScroller alloc] initWithFrame:_CGRectMake(0.0, 0.0, [CPScroller scrollerWidth], _CGRectGetHeight([self bounds]))]];
|
||||
|
||||
else if (!_hasVerticalScroller && _verticalScroller)
|
||||
{
|
||||
[_verticalScroller setHidden:YES];
|
||||
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the scroll view can have
|
||||
a vertical scroller.
|
||||
/*!
|
||||
Returns \c YES if the scroll view can have a vertical scroller.
|
||||
*/
|
||||
- (BOOL)hasHorizontalScroller
|
||||
- (BOOL)hasVerticalScroller
|
||||
{
|
||||
return _hasHorizontalScroller;
|
||||
return _hasVerticalScroller;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets whether the scroll view hides its scoll bars when not needed.
|
||||
@param autohidesScrollers <code>YES</code> causes the scroll bars
|
||||
@param autohidesScrollers \c YES causes the scroll bars
|
||||
to be hidden when not needed.
|
||||
*/
|
||||
- (void)setAutohidesScrollers:(BOOL)autohidesScrollers
|
||||
{
|
||||
if (_autohidesScrollers == autohidesScrollers)
|
||||
return;
|
||||
|
||||
_autohidesScrollers = autohidesScrollers;
|
||||
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the scroll view hides its scroll
|
||||
/*!
|
||||
Returns \c YES if the scroll view hides its scroll
|
||||
bars when not necessary.
|
||||
*/
|
||||
- (BOOL)autohidesScrollers
|
||||
{
|
||||
return _autohidesScrollers;
|
||||
}
|
||||
/*
|
||||
- (void)setFrameSize:(CPRect)aSize
|
||||
|
||||
- (void)_updateCornerAndHeaderView
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
var documentView = [self documentView],
|
||||
currentHeaderView = [self _headerView],
|
||||
documentHeaderView = [documentView respondsToSelector:@selector(headerView)] ? [documentView headerView] : nil;
|
||||
|
||||
if (currentHeaderView !== documentHeaderView)
|
||||
{
|
||||
[currentHeaderView removeFromSuperview];
|
||||
[_headerClipView setDocumentView:documentHeaderView];
|
||||
}
|
||||
|
||||
var documentCornerView = [documentView respondsToSelector:@selector(cornerView)] ? [documentView cornerView] : nil;
|
||||
|
||||
if (_cornerView !== documentCornerView)
|
||||
{
|
||||
[_cornerView removeFromSuperview];
|
||||
|
||||
_cornerView = documentCornerView;
|
||||
|
||||
if (_cornerView)
|
||||
[self addSubview:_cornerView];
|
||||
}
|
||||
|
||||
[self reflectScrolledClipView:_contentView];
|
||||
}*/
|
||||
}
|
||||
|
||||
- (CPView)_headerView
|
||||
{
|
||||
var headerClipViewSubviews = [_headerClipView subviews];
|
||||
|
||||
return [headerClipViewSubviews count] ? headerClipViewSubviews[0] : nil;
|
||||
}
|
||||
|
||||
- (CGRect)_cornerViewFrame
|
||||
{
|
||||
if (!_cornerView)
|
||||
return _CGRectMakeZero();
|
||||
|
||||
var bounds = [self bounds],
|
||||
frame = [_cornerView frame];
|
||||
|
||||
frame.origin.x = _CGRectGetMaxX(bounds) - _CGRectGetWidth(frame);
|
||||
frame.origin.y = 0;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (CGRect)_headerClipViewFrame
|
||||
{
|
||||
var headerView = [self _headerView];
|
||||
|
||||
if (!headerView)
|
||||
return _CGRectMakeZero();
|
||||
|
||||
var frame = [self bounds];
|
||||
|
||||
frame.size.height = _CGRectGetHeight([headerView frame]);
|
||||
frame.size.width -= _CGRectGetWidth([self _cornerViewFrame]);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_verticalScrollerDidScroll:(CPScroller)aScroller
|
||||
@@ -407,7 +496,8 @@ import "CPScroller.j"
|
||||
|
||||
case CPScrollerKnobSlot:
|
||||
case CPScrollerKnob:
|
||||
default: contentBounds.origin.y = value * (_CGRectGetHeight(documentFrame) - _CGRectGetHeight(contentBounds));
|
||||
// We want integral bounds!
|
||||
default: contentBounds.origin.y = ROUND(value * (_CGRectGetHeight(documentFrame) - _CGRectGetHeight(contentBounds)));
|
||||
}
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
@@ -436,13 +526,15 @@ import "CPScroller.j"
|
||||
|
||||
case CPScrollerKnobSlot:
|
||||
case CPScrollerKnob:
|
||||
default: contentBounds.origin.x = value * (_CGRectGetWidth(documentFrame) - _CGRectGetWidth(contentBounds));
|
||||
// We want integral bounds!
|
||||
default: contentBounds.origin.x = ROUND(value * (_CGRectGetWidth(documentFrame) - _CGRectGetWidth(contentBounds)));
|
||||
}
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0.0)];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Lays out the scroll view's components.
|
||||
*/
|
||||
- (void)tile
|
||||
@@ -462,11 +554,9 @@ import "CPScroller.j"
|
||||
}
|
||||
|
||||
// Setting Scrolling Behavior
|
||||
/*
|
||||
Sets how much the document moves when scrolled. Sets
|
||||
the vertical and horizontal scroll.
|
||||
@param aLineScroll the amount to move the document
|
||||
when scrolled
|
||||
/*!
|
||||
Sets how much the document moves when scrolled. Sets the vertical and horizontal scroll.
|
||||
@param aLineScroll the amount to move the document when scrolled
|
||||
*/
|
||||
- (void)setLineScroll:(float)aLineScroll
|
||||
{
|
||||
@@ -474,59 +564,51 @@ import "CPScroller.j"
|
||||
[self setVerticalLineScroll:aLineScroll];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns how much the document moves
|
||||
when scrolled
|
||||
/*!
|
||||
Returns how much the document moves when scrolled
|
||||
*/
|
||||
- (float)lineScroll
|
||||
{
|
||||
return [self horizontalLineScroll];
|
||||
}
|
||||
|
||||
/*
|
||||
Sets how much the document moves when scrolled
|
||||
horizontally.
|
||||
@param aLineScroll the amount to move horizontally
|
||||
when scrolled.
|
||||
/*!
|
||||
Sets how much the document moves when scrolled horizontally.
|
||||
@param aLineScroll the amount to move horizontally when scrolled.
|
||||
*/
|
||||
- (void)setHorizontalLineScroll:(float)aLineScroll
|
||||
{
|
||||
_horizontalLineScroll = aLineScroll;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns how much the document moves horizontally
|
||||
when scrolled.
|
||||
/*!
|
||||
Returns how much the document moves horizontally when scrolled.
|
||||
*/
|
||||
- (float)horizontalLineScroll
|
||||
{
|
||||
return _horizontalLineScroll;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets how much the document moves when scrolled
|
||||
vertically.
|
||||
@param aLineScroll the new amount to move vertically
|
||||
when scrolled.
|
||||
/*!
|
||||
Sets how much the document moves when scrolled vertically.
|
||||
@param aLineScroll the new amount to move vertically when scrolled.
|
||||
*/
|
||||
- (void)setVerticalLineScroll:(float)aLineScroll
|
||||
{
|
||||
_verticalLineScroll = aLineScroll;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns how much the document moves vertically
|
||||
when scrolled.
|
||||
/*!
|
||||
Returns how much the document moves vertically when scrolled.
|
||||
*/
|
||||
- (float)verticalLineScroll
|
||||
{
|
||||
return _verticalLineScroll;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the horizontal and vertical page scroll amount.
|
||||
@param aPageScroll the new horizontal and vertical page
|
||||
scroll amount
|
||||
@param aPageScroll the new horizontal and vertical page scroll amount
|
||||
*/
|
||||
- (void)setPageScroll:(float)aPageScroll
|
||||
{
|
||||
@@ -534,16 +616,15 @@ import "CPScroller.j"
|
||||
[self setVerticalPageScroll:aPageScroll];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the vertical and horizontal page scroll
|
||||
amount.
|
||||
/*!
|
||||
Returns the vertical and horizontal page scroll amount.
|
||||
*/
|
||||
- (float)pageScroll
|
||||
{
|
||||
return [self horizontalPageScroll];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the horizontal page scroll amount.
|
||||
@param aPageScroll the new horizontal page scroll amount
|
||||
*/
|
||||
@@ -552,7 +633,7 @@ import "CPScroller.j"
|
||||
_horizontalPageScroll = aPageScroll;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the horizontal page scroll amount.
|
||||
*/
|
||||
- (float)horizontalPageScroll
|
||||
@@ -560,17 +641,16 @@ import "CPScroller.j"
|
||||
return _horizontalPageScroll;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the vertical page scroll amount.
|
||||
@param aPageScroll the new vertcal page scroll
|
||||
amount
|
||||
@param aPageScroll the new vertcal page scroll amount
|
||||
*/
|
||||
- (void)setVerticalPageScroll:(float)aPageScroll
|
||||
{
|
||||
_verticalPageScroll = aPageScroll;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the vertical page scroll amount.
|
||||
*/
|
||||
- (float)verticalPageScroll
|
||||
@@ -578,26 +658,26 @@ import "CPScroller.j"
|
||||
return _verticalPageScroll;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Handles a scroll wheel event from the user.
|
||||
@param anEvent the scroll wheel event
|
||||
*/
|
||||
- (void)scrollWheel:(CPEvent)anEvent
|
||||
{
|
||||
var value = [_verticalScroller floatValue],
|
||||
documentFrame = [[self documentView] frame],
|
||||
var documentFrame = [[self documentView] frame],
|
||||
contentBounds = [_contentView bounds];
|
||||
|
||||
contentBounds.origin.x += [anEvent deltaX] * _horizontalLineScroll;
|
||||
contentBounds.origin.y += [anEvent deltaY] * _verticalLineScroll;
|
||||
// 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)];
|
||||
}
|
||||
|
||||
- (void)keyDown:(CPEvent)anEvent
|
||||
{
|
||||
var keyCode = [anEvent keyCode],
|
||||
value = [_verticalScroller floatValue],
|
||||
documentFrame = [[self documentView] frame],
|
||||
contentBounds = [_contentView bounds];
|
||||
|
||||
@@ -631,6 +711,67 @@ import "CPScroller.j"
|
||||
}
|
||||
|
||||
[_contentView scrollToPoint:contentBounds.origin];
|
||||
[_headerClipView scrollToPoint:CGPointMake(contentBounds.origin, 0)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPScrollViewContentViewKey = "CPScrollViewContentView",
|
||||
CPScrollViewVLineScrollKey = "CPScrollViewVLineScroll",
|
||||
CPScrollViewHLineScrollKey = "CPScrollViewHLineScroll",
|
||||
CPScrollViewVPageScrollKey = "CPScrollViewVPageScroll",
|
||||
CPScrollViewHPageScrollKey = "CPScrollViewHPageScroll",
|
||||
CPScrollViewHasVScrollerKey = "CPScrollViewHasVScroller",
|
||||
CPScrollViewHasHScrollerKey = "CPScrollViewHasHScroller",
|
||||
CPScrollViewVScrollerKey = "CPScrollViewVScroller",
|
||||
CPScrollViewHScrollerKey = "CPScrollViewHScroller",
|
||||
CPScrollViewAutohidesScrollerKey = "CPScrollViewAutohidesScroller";
|
||||
|
||||
@implementation CPScrollView (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
_verticalLineScroll = [aCoder decodeFloatForKey:CPScrollViewVLineScrollKey];
|
||||
_verticalPageScroll = [aCoder decodeFloatForKey:CPScrollViewVPageScrollKey];
|
||||
|
||||
_horizontalLineScroll = [aCoder decodeFloatForKey:CPScrollViewHLineScrollKey];
|
||||
_horizontalPageScroll = [aCoder decodeFloatForKey:CPScrollViewHPageScrollKey];
|
||||
|
||||
_contentView = [aCoder decodeObjectForKey:CPScrollViewContentViewKey];
|
||||
|
||||
_verticalScroller = [aCoder decodeObjectForKey:CPScrollViewVScrollerKey];
|
||||
_horizontalScroller = [aCoder decodeObjectForKey:CPScrollViewHScrollerKey];
|
||||
|
||||
_hasVerticalScroller = [aCoder decodeBoolForKey:CPScrollViewHasVScrollerKey];
|
||||
_hasHorizontalScroller = [aCoder decodeBoolForKey:CPScrollViewHasHScrollerKey];
|
||||
_autohidesScrollers = [aCoder decodeBoolForKey:CPScrollViewAutohidesScrollerKey];
|
||||
|
||||
// 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:_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];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+243
-316
@@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPControl.j"
|
||||
@import "CPControl.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
@@ -41,38 +41,20 @@ CPNoScrollerParts = 0;
|
||||
CPOnlyScrollerArrows = 1;
|
||||
CPAllScrollerParts = 2;
|
||||
|
||||
var _CPScrollerWidths = [],
|
||||
_CPScrollerKnobMinimumHeights = [],
|
||||
_CPScrollerArrowHeights = [],
|
||||
_CPScrollerArrowUsableHeights = [];
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPScroller
|
||||
*/
|
||||
|
||||
_CPScrollerWidths[CPRegularControlSize] = 14.0;
|
||||
_CPScrollerWidths[CPSmallControlSize] = 11.0;
|
||||
_CPScrollerWidths[CPMiniControlSize] = 11.0; // FIXME
|
||||
|
||||
_CPScrollerKnobMinimumHeights[CPRegularControlSize] = 18.0;
|
||||
_CPScrollerKnobMinimumHeights[CPSmallControlSize] = 12.0;
|
||||
_CPScrollerKnobMinimumHeights[CPMiniControlSize] = 12.0; // FIXME
|
||||
var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
|
||||
NAMES_FOR_PARTS = {},
|
||||
PARTS_FOR_NAMES = {};
|
||||
|
||||
_CPScrollerArrowHeights[CPRegularControlSize] = 21.0;
|
||||
_CPScrollerArrowHeights[CPSmallControlSize] = 16.0;
|
||||
_CPScrollerArrowHeights[CPMiniControlSize] = 16.0; // FIXME
|
||||
NAMES_FOR_PARTS[CPScrollerDecrementLine] = @"decrement-line";
|
||||
NAMES_FOR_PARTS[CPScrollerIncrementLine] = @"increment-line";
|
||||
NAMES_FOR_PARTS[CPScrollerKnobSlot] = @"knob-slot";
|
||||
NAMES_FOR_PARTS[CPScrollerKnob] = @"knob";
|
||||
|
||||
_CPScrollerArrowUsableHeights[CPRegularControlSize] = 16.0
|
||||
_CPScrollerArrowUsableHeights[CPSmallControlSize] = 10.0;
|
||||
_CPScrollerArrowUsableHeights[CPMiniControlSize] = 10.0; // FIXME
|
||||
|
||||
var _CPScrollerKnobIdentifier = @"Knob",
|
||||
_CPScrollerKnobSlotIdentifier = @"KnobSlot",
|
||||
_CPScrollerDecrementArrowIdentifier = @"DecrementArrow",
|
||||
_CPScrollerIncrementArrowIdentifier = @"IncrementArrow",
|
||||
_CPScrollerHorizontalIdentifier = @"Horizontal",
|
||||
_CPScrollerVerticalIdentifier = @"Vertical",
|
||||
_CPScrollerHighlightedIdentifier = @"Highlighted",
|
||||
_CPScrollerDisabledIdentifier = @"Disabled";
|
||||
|
||||
var _CPScrollerClassName = nil,
|
||||
_CPScrollerPartSizes = {};
|
||||
|
||||
@implementation CPScroller : CPControl
|
||||
{
|
||||
@@ -80,105 +62,37 @@ var _CPScrollerClassName = nil,
|
||||
CPUsableScrollerParts _usableParts;
|
||||
CPArray _partRects;
|
||||
|
||||
BOOL _isHorizontal;
|
||||
BOOL _isVertical @accessors(readonly, getter=isVertical);
|
||||
float _knobProportion;
|
||||
|
||||
|
||||
CPScrollerPart _hitPart;
|
||||
|
||||
|
||||
CPScrollerPart _trackingPart;
|
||||
float _trackingFloatValue;
|
||||
CGPoint _trackingStartPoint;
|
||||
|
||||
CPView _knobView;
|
||||
CPView _knobSlotView;
|
||||
|
||||
CPView _decrementArrowView;
|
||||
CPView _incrementArrowView;
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
+ (void)initialize
|
||||
+ (CPString)themeClass
|
||||
{
|
||||
if (self != [CPScroller class])
|
||||
return;
|
||||
|
||||
_CPScrollerClassName = [self className];
|
||||
|
||||
var regularIdentifier = _CPControlIdentifierForControlSize(CPRegularControlSize),
|
||||
smallIdentifier = _CPControlIdentifierForControlSize(CPSmallControlSize),
|
||||
miniIdentifier = _CPControlIdentifierForControlSize(CPMiniControlSize);
|
||||
|
||||
// Horizontal Knob Sizes
|
||||
var prefix = _CPScrollerClassName + _CPScrollerKnobIdentifier + _CPScrollerHorizontalIdentifier;
|
||||
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier] = [_CGSizeMake(9.0, _CPScrollerWidths[CPRegularControlSize]), _CGSizeMake(1.0, _CPScrollerWidths[CPRegularControlSize]), _CGSizeMake(9.0, _CPScrollerWidths[CPRegularControlSize])];
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier] = [_CGSizeMake(6.0, _CPScrollerWidths[CPSmallControlSize]), _CGSizeMake(1.0, _CPScrollerWidths[CPSmallControlSize]), _CGSizeMake(6.0, _CPScrollerWidths[CPSmallControlSize])];
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier] = [_CGSizeMake(6.0, _CPScrollerWidths[CPMiniControlSize]), _CGSizeMake(1.0, _CPScrollerWidths[CPMiniControlSize]), _CGSizeMake(6.0, _CPScrollerWidths[CPMiniControlSize])];
|
||||
|
||||
// Vertical Knob Sizes
|
||||
var prefix = _CPScrollerClassName + _CPScrollerKnobIdentifier + _CPScrollerVerticalIdentifier;
|
||||
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier] = [_CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 9.0), _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 1.0), _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 9.0)];
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier] = [_CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 6.0), _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 1.0), _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 6.0)];
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier] = [_CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 6.0), _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 1.0), _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 6.0)];
|
||||
|
||||
// Horizontal Knob Slot Sizes
|
||||
var prefix = _CPScrollerClassName + _CPScrollerKnobSlotIdentifier + _CPScrollerHorizontalIdentifier;
|
||||
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(1.0, _CPScrollerWidths[CPRegularControlSize]);
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(1.0, _CPScrollerWidths[CPSmallControlSize]);
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(1.0, _CPScrollerWidths[CPMiniControlSize]);
|
||||
|
||||
// Vertical Knob Slot Sizes
|
||||
var prefix = _CPScrollerClassName + _CPScrollerKnobSlotIdentifier + _CPScrollerVerticalIdentifier;
|
||||
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], 1.0);
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], 1.0);
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], 1.0);
|
||||
|
||||
// Horizontal Decrement Arrows Sizes
|
||||
var prefix = _CPScrollerClassName + _CPScrollerDecrementArrowIdentifier + _CPScrollerHorizontalIdentifier;
|
||||
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]);
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]);
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize] , _CPScrollerWidths[CPSmallControlSize]);
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize] , _CPScrollerWidths[CPSmallControlSize]);
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]);
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]);
|
||||
|
||||
// Vertical Decrement Arrows Sizes
|
||||
var prefix = _CPScrollerClassName + _CPScrollerDecrementArrowIdentifier + _CPScrollerVerticalIdentifier;
|
||||
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]);
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]);
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]);
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]);
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]);
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]);
|
||||
|
||||
// Horizontal Increment Arrows Sizes
|
||||
var prefix = _CPScrollerClassName + _CPScrollerIncrementArrowIdentifier + _CPScrollerHorizontalIdentifier;
|
||||
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]);
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPRegularControlSize], _CPScrollerWidths[CPRegularControlSize]);
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize], _CPScrollerWidths[CPSmallControlSize]);
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPSmallControlSize], _CPScrollerWidths[CPSmallControlSize]);
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]);
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerArrowHeights[CPMiniControlSize], _CPScrollerWidths[CPMiniControlSize]);
|
||||
|
||||
// Vertical Increment Arrows Sizes
|
||||
var prefix = _CPScrollerClassName + _CPScrollerIncrementArrowIdentifier + _CPScrollerVerticalIdentifier;
|
||||
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]);
|
||||
_CPScrollerPartSizes[prefix + regularIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPRegularControlSize], _CPScrollerArrowHeights[CPRegularControlSize]);
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]);
|
||||
_CPScrollerPartSizes[prefix + smallIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPSmallControlSize], _CPScrollerArrowHeights[CPSmallControlSize]);
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]);
|
||||
_CPScrollerPartSizes[prefix + miniIdentifier + _CPScrollerHighlightedIdentifier] = _CGSizeMake(_CPScrollerWidths[CPMiniControlSize], _CPScrollerArrowHeights[CPMiniControlSize]);
|
||||
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"]];
|
||||
}
|
||||
|
||||
|
||||
// Calculating Layout
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
@@ -190,40 +104,36 @@ var _CPScrollerClassName = nil,
|
||||
_controlSize = CPRegularControlSize;
|
||||
_partRects = [];
|
||||
|
||||
[self setFloatValue:0.0 knobProportion:1.0];
|
||||
|
||||
_isHorizontal = CPRectGetWidth(aFrame) > CPRectGetHeight(aFrame);
|
||||
[self setFloatValue:0.0];
|
||||
[self setKnobProportion:1.0];
|
||||
|
||||
_hitPart = CPScrollerNoPart;
|
||||
|
||||
[self checkSpaceForParts];
|
||||
[self drawParts];
|
||||
|
||||
[self layoutSubviews];
|
||||
|
||||
[self _calculateIsVertical];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// Determining CPScroller Size
|
||||
/*
|
||||
Returns the <objj>CPScroller</objj>'s width for a <objj>CPRegularControlSize</objj>.
|
||||
/*!
|
||||
Returns the CPScroller's width for a CPRegularControlSize.
|
||||
*/
|
||||
+ (float)scrollerWidth
|
||||
{
|
||||
return [self scrollerWidthForControlSize:CPRegularControlSize];
|
||||
return 15.0;//[self scrollerWidthForControlSize:CPRegularControlSize];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the width of a <objj>CPScroller</objj> for the specified <objj>CPControlSize</objj>.
|
||||
/*!
|
||||
Returns the width of a CPScroller for the specified CPControlSize.
|
||||
@param aControlSize the size of a controller to return the width for
|
||||
*/
|
||||
+ (float)scrollerWidthForControlSize:(CPControlSize)aControlSize
|
||||
{
|
||||
return _CPScrollerWidths[aControlSize];
|
||||
return 15.0;//_CPScrollerWidths[aControlSize];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the scroller's size.
|
||||
@param aControlSize the scroller's size
|
||||
*/
|
||||
@@ -234,15 +144,11 @@ var _CPScrollerClassName = nil,
|
||||
|
||||
_controlSize = aControlSize;
|
||||
|
||||
[self drawKnobSlot];
|
||||
[self drawKnob];
|
||||
[self drawArrow:CPScrollerDecrementArrow highlight:NO];
|
||||
[self drawArrow:CPScrollerIncrementArrow highlight:NO];
|
||||
|
||||
[self layoutSubviews];
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the scroller's control size
|
||||
*/
|
||||
- (CPControlSize)controlSize
|
||||
@@ -250,32 +156,20 @@ var _CPScrollerClassName = nil,
|
||||
return _controlSize;
|
||||
}
|
||||
|
||||
// Setting the Knob Position
|
||||
/*
|
||||
Sets the scroller's knob position (ranges from 0.0 to 1.0).
|
||||
@param aValue the knob position (ranges from 0.0 to 1.0)
|
||||
*/
|
||||
- (void)setFloatValue:(float)aValue
|
||||
- (void)setObjectValue:(id)aValue
|
||||
{
|
||||
[super setFloatValue:MIN(1.0, MAX(0.0, aValue))];
|
||||
|
||||
[self checkSpaceForParts];
|
||||
[self layoutSubviews];
|
||||
[super setObjectValue:MIN(1.0, MAX(0.0, +aValue))];
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the position and proportion of the knob.
|
||||
@param aValue the knob position (ranges from 0.0 to 1.0)
|
||||
@param aProportion the knob's proportion (ranges from 0.0 to 1.0)
|
||||
*/
|
||||
- (void)setFloatValue:(float)aValue knobProportion:(float)aProportion
|
||||
- (void)setKnobProportion:(float)aProportion
|
||||
{
|
||||
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
||||
|
||||
[self setFloatValue:aValue];
|
||||
[self setNeedsDisplay:YES];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Return's the knob's proportion
|
||||
*/
|
||||
- (float)knobProportion
|
||||
@@ -283,6 +177,16 @@ var _CPScrollerClassName = nil,
|
||||
return _knobProportion;
|
||||
}
|
||||
|
||||
- (id)currentValueForThemeAttribute:(CPString)anAttributeName
|
||||
{
|
||||
var themeState = _themeState;
|
||||
|
||||
if (NAMES_FOR_PARTS[_hitPart] + "-color" !== anAttributeName)
|
||||
themeState &= ~CPThemeStateHighlighted;
|
||||
|
||||
return [self valueForThemeAttribute:anAttributeName inState:themeState];
|
||||
}
|
||||
|
||||
// Calculating Layout
|
||||
|
||||
- (CGRect)rectForPart:(CPScrollerPart)aPart
|
||||
@@ -293,8 +197,8 @@ var _CPScrollerClassName = nil,
|
||||
return _partRects[aPart];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the part of the scroller that would be hit by <code>aPoint</code>.
|
||||
/*!
|
||||
Returns the part of the scroller that would be hit by \c aPoint.
|
||||
@param aPoint the simulated point hit
|
||||
@return the part of the scroller that intersects the point
|
||||
*/
|
||||
@@ -326,7 +230,7 @@ var _CPScrollerClassName = nil,
|
||||
return CPScrollerNoPart;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Check if there's enough space in the scroller to display the knob
|
||||
*/
|
||||
- (void)checkSpaceForParts
|
||||
@@ -334,7 +238,7 @@ var _CPScrollerClassName = nil,
|
||||
var bounds = [self bounds];
|
||||
|
||||
// Assume we won't be needing the arrows.
|
||||
if (_knobProportion == 1.0)
|
||||
if (_knobProportion === 1.0)
|
||||
{
|
||||
_usableParts = CPNoScrollerParts;
|
||||
|
||||
@@ -350,43 +254,56 @@ var _CPScrollerClassName = nil,
|
||||
return;
|
||||
}
|
||||
|
||||
var width = _CGRectGetWidth(bounds),
|
||||
height = _CGRectGetHeight(bounds),
|
||||
usableArrowHeight = _CPScrollerArrowUsableHeights[_controlSize],
|
||||
slotWidth = (_isHorizontal ? width : height) - 2.0 * usableArrowHeight,
|
||||
knobWidth = MAX(_CPScrollerKnobMinimumHeights[_controlSize], (slotWidth * _knobProportion));
|
||||
|
||||
_usableParts = CPAllScrollerParts;
|
||||
|
||||
var arrowHeight = _CPScrollerArrowHeights[_controlSize],
|
||||
knobLocation = usableArrowHeight + (slotWidth - knobWidth) * [self floatValue];
|
||||
|
||||
// At this point we know we're going to need arrows.
|
||||
if (_isHorizontal)
|
||||
_usableParts = CPAllScrollerParts;
|
||||
|
||||
var knobInset = [self currentValueForThemeAttribute:@"knob-inset"],
|
||||
trackInset = [self currentValueForThemeAttribute:@"track-inset"],
|
||||
width = _CGRectGetWidth(bounds),
|
||||
height = _CGRectGetHeight(bounds);
|
||||
|
||||
if ([self isVertical])
|
||||
{
|
||||
// ASSERT(_CPScrollerWidths[_controlSize] == height)
|
||||
|
||||
_partRects[CPScrollerDecrementPage] = _CGRectMake(usableArrowHeight, 0.0, knobLocation - usableArrowHeight, height);
|
||||
_partRects[CPScrollerKnob] = _CGRectMake(knobLocation, 0.0, knobWidth, _CPScrollerWidths[_controlSize]);
|
||||
_partRects[CPScrollerIncrementPage] = _CGRectMake(knobLocation + knobWidth, 0.0, width - (knobLocation + knobWidth) - usableArrowHeight, height);
|
||||
_partRects[CPScrollerKnobSlot] = _CGRectMake(usableArrowHeight, 0.0, slotWidth, height);
|
||||
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, arrowHeight, height);
|
||||
_partRects[CPScrollerIncrementLine] = _CGRectMake(width - _CPScrollerArrowHeights[_controlSize], 0.0, arrowHeight, height);
|
||||
var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
|
||||
incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
|
||||
effectiveDecrementLineHeight = decrementLineSize.height + trackInset.top,
|
||||
effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
|
||||
slotHeight = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
|
||||
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
||||
knobWidth = width - knobInset.left - knobInset.right,
|
||||
knobHeight = MAX(minimumKnobLength, (slotHeight * _knobProportion)),
|
||||
knobLocation = effectiveDecrementLineHeight + (slotHeight - knobHeight) * [self floatValue];
|
||||
|
||||
_partRects[CPScrollerDecrementPage] = _CGRectMake(0.0, effectiveDecrementLineHeight, width, knobLocation - effectiveDecrementLineHeight);
|
||||
_partRects[CPScrollerKnob] = _CGRectMake(knobInset.left, knobLocation, knobWidth, knobHeight);
|
||||
_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);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// ASSERT(_CPScrollerWidths[_controlSize] == width)
|
||||
|
||||
_partRects[CPScrollerDecrementPage] = _CGRectMake(0.0, usableArrowHeight, width, knobLocation - usableArrowHeight);
|
||||
_partRects[CPScrollerKnob] = _CGRectMake(0.0, knobLocation, _CPScrollerWidths[_controlSize], knobWidth);
|
||||
_partRects[CPScrollerIncrementPage] = _CGRectMake(0.0, knobLocation + knobWidth, width, height - (knobLocation + knobWidth) - usableArrowHeight);
|
||||
_partRects[CPScrollerKnobSlot] = _CGRectMake(0.0, usableArrowHeight, width, slotWidth);
|
||||
_partRects[CPScrollerDecrementLine] = _CGRectMake(0.0, 0.0, width, arrowHeight);
|
||||
_partRects[CPScrollerIncrementLine] = _CGRectMake(0.0, height - _CPScrollerArrowHeights[_controlSize], width, arrowHeight);
|
||||
var decrementLineSize = [self currentValueForThemeAttribute:"decrement-line-size"],
|
||||
incrementLineSize = [self currentValueForThemeAttribute:"increment-line-size"],
|
||||
effectiveDecrementLineWidth = decrementLineSize.width + trackInset.left,
|
||||
effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right;
|
||||
slotWidth = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
|
||||
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
||||
knobWidth = MAX(minimumKnobLength, (slotWidth * _knobProportion)),
|
||||
knobHeight = height - knobInset.top - knobInset.bottom,
|
||||
knobLocation = effectiveDecrementLineWidth + (slotWidth - knobWidth) * [self floatValue];
|
||||
|
||||
_partRects[CPScrollerDecrementPage] = _CGRectMake(effectiveDecrementLineWidth, 0.0, knobLocation - effectiveDecrementLineWidth, height);
|
||||
_partRects[CPScrollerKnob] = _CGRectMake(knobLocation, knobInset.top, knobWidth, knobHeight);
|
||||
_partRects[CPScrollerIncrementPage] = _CGRectMake(knobLocation + knobWidth, 0.0, width - (knobLocation + knobWidth) - effectiveIncrementLineWidth, height);
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns all the parts of the scroller that
|
||||
are usable for displaying.
|
||||
*/
|
||||
@@ -396,107 +313,86 @@ var _CPScrollerClassName = nil,
|
||||
}
|
||||
|
||||
// Drawing the Parts
|
||||
/*
|
||||
/*!
|
||||
Draws the specified arrow and sets the highlight.
|
||||
@param anArrow the arrow to draw
|
||||
@param shouldHighlight sets whether the arrow should be highlighted
|
||||
*/
|
||||
- (void)drawArrow:(CPScrollerArrow)anArrow highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
var identifier = (anArrow == CPScrollerDecrementArrow ? _CPScrollerDecrementArrowIdentifier : _CPScrollerIncrementArrowIdentifier),
|
||||
arrowView = (anArrow == CPScrollerDecrementArrow ? _decrementArrowView : _incrementArrowView);
|
||||
|
||||
[arrowView setBackgroundColor:_CPControlColorWithPatternImage(
|
||||
_CPScrollerPartSizes,
|
||||
_CPScrollerClassName,
|
||||
identifier,
|
||||
_isHorizontal ? _CPScrollerHorizontalIdentifier : _CPScrollerVerticalIdentifier,
|
||||
_CPControlIdentifierForControlSize(_controlSize),
|
||||
shouldHighlight ? _CPScrollerHighlightedIdentifier : @"")];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Draws the knob
|
||||
*/
|
||||
- (void)drawKnob
|
||||
{
|
||||
[_knobView setBackgroundColor:_CPControlThreePartImagePattern(
|
||||
!_isHorizontal,
|
||||
_CPScrollerPartSizes,
|
||||
_CPScrollerClassName,
|
||||
_CPScrollerKnobIdentifier,
|
||||
_isHorizontal ? _CPScrollerHorizontalIdentifier : _CPScrollerVerticalIdentifier,
|
||||
_CPControlIdentifierForControlSize(_controlSize))];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Draws the knob's slot
|
||||
*/
|
||||
- (void)drawKnobSlot
|
||||
{
|
||||
[_knobSlotView setBackgroundColor:_CPControlColorWithPatternImage(
|
||||
_CPScrollerPartSizes,
|
||||
_CPScrollerClassName,
|
||||
_CPScrollerKnobSlotIdentifier,
|
||||
_isHorizontal ? _CPScrollerHorizontalIdentifier : _CPScrollerVerticalIdentifier,
|
||||
_CPControlIdentifierForControlSize(_controlSize))];
|
||||
}
|
||||
|
||||
/*
|
||||
- (CPView)createViewForPart:(CPScrollerPart)aPart
|
||||
{
|
||||
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
[view setHitTests:NO];
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
return _partRects[aName];
|
||||
}
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
[view setHitTests:NO];
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[self checkSpaceForParts];
|
||||
|
||||
var index = 0,
|
||||
count = PARTS_ARRANGEMENT.length;
|
||||
|
||||
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"]];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Caches images for the scroll arrow and knob.
|
||||
*/
|
||||
- (void)drawParts
|
||||
{
|
||||
_knobSlotView = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
[_knobSlotView setHitTests:NO];
|
||||
|
||||
[self addSubview:_knobSlotView];
|
||||
|
||||
{
|
||||
[self drawKnobSlot];
|
||||
|
||||
_knobView = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
[_knobView setHitTests:NO];
|
||||
|
||||
[self addSubview:_knobView];
|
||||
|
||||
[self drawKnob];
|
||||
|
||||
_decrementArrowView = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
[_decrementArrowView setHitTests:NO];
|
||||
|
||||
[self addSubview:_decrementArrowView];
|
||||
|
||||
[self drawArrow:CPScrollerDecrementArrow highlight:NO];
|
||||
|
||||
_incrementArrowView = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
[_incrementArrowView setHitTests:NO];
|
||||
|
||||
[self addSubview:_incrementArrowView];
|
||||
|
||||
[self drawArrow:CPScrollerIncrementArrow highlight:NO];
|
||||
}
|
||||
|
||||
/*
|
||||
Draws the scroller's arrow with a possible highlight,
|
||||
if the user's mouse is over it.
|
||||
@param shouldHighlight <code>YES</code> will draw the
|
||||
arrow highlighted if the mouse is hovering over it.
|
||||
*/
|
||||
- (void)highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
if (_trackingPart == CPScrollerDecrementLine)
|
||||
[self drawArrow:CPScrollerDecrementArrow highlight:shouldHighlight];
|
||||
|
||||
else if (_trackingPart == CPScrollerIncrementLine)
|
||||
[self drawArrow:CPScrollerIncrementArrow highlight:shouldHighlight];
|
||||
}
|
||||
|
||||
// Event Handling
|
||||
/*
|
||||
/*!
|
||||
Returns the part of the scroller that was hit.
|
||||
*/
|
||||
- (CPScrollerPart)hitPart
|
||||
@@ -504,7 +400,7 @@ var _CPScrollerClassName = nil,
|
||||
return _hitPart;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Tracks the knob.
|
||||
@param anEvent the input event
|
||||
*/
|
||||
@@ -512,31 +408,31 @@ var _CPScrollerClassName = nil,
|
||||
{
|
||||
var type = [anEvent type];
|
||||
|
||||
if (type == CPLeftMouseUp)
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
_hitPart = CPScrollerNoPart;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == CPLeftMouseDown)
|
||||
if (type === CPLeftMouseDown)
|
||||
{
|
||||
_trackingFloatValue = [self floatValue];
|
||||
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
}
|
||||
|
||||
else if (type == CPLeftMouseDragged)
|
||||
else if (type === CPLeftMouseDragged)
|
||||
{
|
||||
var knobRect = [self rectForPart:CPScrollerKnob],
|
||||
knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
|
||||
remainder = _isHorizontal ? (_CGRectGetWidth(knobSlotRect) - _CGRectGetWidth(knobRect)) : (_CGRectGetHeight(knobSlotRect) - _CGRectGetHeight(knobRect));
|
||||
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];
|
||||
delta = _isHorizontal ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
|
||||
delta = ![self isVertical] ? location.x - _trackingStartPoint.x : location.y - _trackingStartPoint.y;
|
||||
|
||||
[self setFloatValue:_trackingFloatValue + delta / remainder];
|
||||
}
|
||||
@@ -547,7 +443,7 @@ var _CPScrollerClassName = nil,
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Tracks the scroll button.
|
||||
@param anEvent the input event
|
||||
*/
|
||||
@@ -555,7 +451,7 @@ var _CPScrollerClassName = nil,
|
||||
{
|
||||
var type = [anEvent type];
|
||||
|
||||
if (type == CPLeftMouseUp)
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
[self highlight:NO];
|
||||
[CPEvent stopPeriodicEvents];
|
||||
@@ -565,7 +461,7 @@ var _CPScrollerClassName = nil,
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == CPLeftMouseDown)
|
||||
if (type === CPLeftMouseDown)
|
||||
{
|
||||
_trackingPart = [self hitPart];
|
||||
|
||||
@@ -575,16 +471,18 @@ var _CPScrollerClassName = nil,
|
||||
{
|
||||
if (_trackingPart == CPScrollerDecrementLine)
|
||||
_hitPart = CPScrollerDecrementPage;
|
||||
|
||||
else if (_trackingPart == CPScrollerIncrementLine)
|
||||
_hitPart = CPScrollerIncrementPage;
|
||||
|
||||
else if (_trackingPart == CPScrollerDecrementPage || _trackingPart == CPScrollerIncrementPage)
|
||||
{
|
||||
var knobRect = [self rectForPart:CPScrollerKnob],
|
||||
knobWidth = _isHorizontal ? _CGRectGetWidth(knobRect) : _CGRectGetHeight(knobRect),
|
||||
knobWidth = ![self isVertical] ? _CGRectGetWidth(knobRect) : _CGRectGetHeight(knobRect),
|
||||
knobSlotRect = [self rectForPart:CPScrollerKnobSlot],
|
||||
remainder = (_isHorizontal ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth;
|
||||
remainder = (![self isVertical] ? _CGRectGetWidth(knobSlotRect) : _CGRectGetHeight(knobSlotRect)) - knobWidth;
|
||||
|
||||
[self setFloatValue:((_isHorizontal ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
|
||||
[self setFloatValue:((![self isVertical] ? _trackingStartPoint.x - _CGRectGetMinX(knobSlotRect) : _trackingStartPoint.y - _CGRectGetMinY(knobSlotRect)) - knobWidth / 2.0) / remainder];
|
||||
|
||||
_hitPart = CPScrollerKnob;
|
||||
|
||||
@@ -601,7 +499,7 @@ var _CPScrollerClassName = nil,
|
||||
[CPEvent startPeriodicEventsAfterDelay:0.5 withPeriod:0.04];
|
||||
}
|
||||
|
||||
else if (type == CPLeftMouseDragged)
|
||||
else if (type === CPLeftMouseDragged)
|
||||
{
|
||||
_trackingStartPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
@@ -625,52 +523,34 @@ var _CPScrollerClassName = nil,
|
||||
|
||||
}
|
||||
|
||||
- (void)_calculateIsVertical
|
||||
{
|
||||
// Recalculate isVertical.
|
||||
var bounds = [self bounds],
|
||||
width = _CGRectGetWidth(bounds),
|
||||
height = _CGRectGetHeight(bounds);
|
||||
|
||||
_isVertical = width < height ? 1 : (width > height ? 0 : -1);
|
||||
|
||||
if (_isVertical === 1)
|
||||
[self setThemeState:CPThemeStateVertical];
|
||||
else if (_isVertical === 0)
|
||||
[self unsetThemeState:CPThemeStateVertical];
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
|
||||
[self checkSpaceForParts];
|
||||
|
||||
var frame = [self frame],
|
||||
isHorizontal = CPRectGetWidth(frame) > CPRectGetHeight(frame);
|
||||
|
||||
if (_isHorizontal != isHorizontal)
|
||||
{
|
||||
_isHorizontal = isHorizontal;
|
||||
|
||||
[self drawParts];
|
||||
}
|
||||
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
/*
|
||||
Lays out the scrollers subviews
|
||||
*/
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[_knobSlotView setFrame:[self rectForPart:CPScrollerKnobSlot]];
|
||||
|
||||
var usableParts = [self usableParts],
|
||||
hidden = !(usableParts == CPAllScrollerParts);
|
||||
|
||||
if (hidden != [_knobView isHidden])
|
||||
{
|
||||
[_knobView setHidden:hidden];
|
||||
[_decrementArrowView setHidden:hidden];
|
||||
[_incrementArrowView setHidden:hidden];
|
||||
}
|
||||
|
||||
if (!hidden)
|
||||
{
|
||||
[_knobView setFrame:[self rectForPart:CPScrollerKnob]];
|
||||
[_decrementArrowView setFrame:[self rectForPart:CPScrollerDecrementLine]];
|
||||
[_incrementArrowView setFrame:[self rectForPart:CPScrollerIncrementLine]];
|
||||
}
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
_hitPart = [self testPart:[anEvent locationInWindow]];
|
||||
|
||||
switch (_hitPart)
|
||||
@@ -684,9 +564,56 @@ var _CPScrollerClassName = nil,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This is a result of the "dumb" code that just makes things transparent when disabled.
|
||||
- (void)setEnabled:(BOOL)shouldBeEnabled
|
||||
@end
|
||||
|
||||
var CPScrollerControlSizeKey = "CPScrollerControlSize",
|
||||
CPScrollerKnobProportionKey = "CPScrollerKnobProportion";
|
||||
|
||||
@implementation CPScroller (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
_controlSize = CPRegularControlSize;
|
||||
if ([aCoder containsValueForKey:CPScrollerControlSizeKey])
|
||||
_controlSize = [aCoder decodeIntForKey:CPScrollerControlSizeKey];
|
||||
|
||||
_knobProportion = 1.0;
|
||||
if ([aCoder containsValueForKey:CPScrollerKnobProportionKey])
|
||||
_knobProportion = [aCoder decodeFloatForKey:CPScrollerKnobProportionKey];
|
||||
|
||||
_partRects = [];
|
||||
|
||||
_hitPart = CPScrollerNoPart;
|
||||
|
||||
[self _calculateIsVertical];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeInt:_controlSize forKey:CPScrollerControlSizeKey];
|
||||
[aCoder encodeFloat:_knobProportion forKey:CPScrollerKnobProportionKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPScroller (Deprecated)
|
||||
|
||||
/*!
|
||||
Sets the position and proportion of the knob.
|
||||
@param aValue the knob position (ranges from 0.0 to 1.0)
|
||||
@param aProportion the knob's proportion (ranges from 0.0 to 1.0)
|
||||
*/
|
||||
- (void)setFloatValue:(float)aValue knobProportion:(float)aProportion
|
||||
{
|
||||
[self setFloatValue:aValue];
|
||||
[self setKnobProportion:aProportion];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,706 @@
|
||||
/*
|
||||
* CPSearchField.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by cacaodev.
|
||||
* Copyright 2009.
|
||||
*
|
||||
* 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 "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;
|
||||
|
||||
var CPSearchFieldSearchImage = nil,
|
||||
CPSearchFieldFindImage = nil,
|
||||
CPSearchFieldCancelImage = nil,
|
||||
CPSearchFieldCancelPressedImage = nil;
|
||||
|
||||
/*!
|
||||
@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.
|
||||
*/
|
||||
@implementation CPSearchField : CPTextField
|
||||
{
|
||||
CPButton _searchButton;
|
||||
CPButton _cancelButton;
|
||||
CPMenu _searchMenuTemplate;
|
||||
CPMenu _searchMenu;
|
||||
|
||||
CPString _recentsAutosaveName;
|
||||
CPArray _recentSearches;
|
||||
|
||||
int _maximumRecents;
|
||||
BOOL _sendsWholeSearchString;
|
||||
BOOL _sendsSearchStringImmediately;
|
||||
CPTimer _partialStringTimer;
|
||||
}
|
||||
|
||||
+ (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"]];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)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 PLATFORM(DOM)
|
||||
_cancelButton._DOMElement.style.cursor = "default";
|
||||
_searchButton._DOMElement.style.cursor = "default";
|
||||
#endif
|
||||
|
||||
[self setSearchMenuTemplate:[self _searchMenuTemplate]];
|
||||
[self addSubview:_searchButton];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Managing Buttons
|
||||
/*!
|
||||
Sets the button used to display the search-button image
|
||||
@param button The search button.
|
||||
*/
|
||||
- (void)setSearchButton:(CPButton)button
|
||||
{
|
||||
_searchButton = button;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the button used to display the search-button image.
|
||||
@return The search button.
|
||||
*/
|
||||
- (CPButton)searchButton
|
||||
{
|
||||
return _searchButton;
|
||||
}
|
||||
|
||||
/*!
|
||||
Resets the search button to its default attributes.
|
||||
This method resets the target, action, regular image, and pressed image. By default, when users click the search button or press the Return key, the action defined for the receiver is sent to its designated target. This method gives you a way to customize the search button for specific situations and then reset the button defaults without having to undo changes individually.
|
||||
*/
|
||||
- (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;
|
||||
}
|
||||
|
||||
[button setImage:searchButtonImage];
|
||||
[button setTarget:target];
|
||||
[button setAction:action];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the button object used to display the cancel-button image.
|
||||
@param button The cancel button.
|
||||
*/
|
||||
- (void)setCancelButton:(CPButton)button
|
||||
{
|
||||
_cancelButton = button;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the button object used to display the cancel-button image.
|
||||
@return The cancel button.
|
||||
*/
|
||||
- (CPButton)cancelButton
|
||||
{
|
||||
return _cancelButton;
|
||||
}
|
||||
|
||||
/*!
|
||||
Resets the cancel button to its default attributes.
|
||||
This method resets the target, action, regular image, and pressed image. By default, when users click the cancel button, the delete: action message is sent up the responder chain. This method gives you a way to customize the cancel button for specific situations and then reset the button defaults without having to undo changes individually.
|
||||
*/
|
||||
- (void)resetCancelButton
|
||||
{
|
||||
var button = [self cancelButton];
|
||||
[button setBezelStyle:CPRegularSquareBezelStyle];
|
||||
[button setBordered:NO];
|
||||
[button setImageScaling:CPScaleToFit];
|
||||
[button setImage:CPSearchFieldCancelImage];
|
||||
[button setAlternateImage:CPSearchFieldCancelPressedImage];
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(_searchFieldCancel:)];
|
||||
}
|
||||
|
||||
// Custom Layout
|
||||
/*!
|
||||
Modifies the bounding rectangle for the search-text field.
|
||||
@param rect The current bounding rectangle for the search text field.
|
||||
@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
|
||||
{
|
||||
var leftOffset = 0, width = rect.size.width;
|
||||
|
||||
if (_searchButton)
|
||||
{
|
||||
var searchRect = [_searchButton frame];
|
||||
leftOffset = searchRect.origin.x + searchRect.size.width;
|
||||
}
|
||||
|
||||
if (_cancelButton)
|
||||
{
|
||||
var cancelRect = [_cancelButton frame];
|
||||
width = cancelRect.origin.x - leftOffset;
|
||||
}
|
||||
|
||||
return CPMakeRect(leftOffset,rect.origin.y,width,rect.size.height);
|
||||
}
|
||||
|
||||
/*!
|
||||
Modifies the bounding rectangle for the search button.
|
||||
@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
|
||||
{
|
||||
return [_searchButton frame];
|
||||
}
|
||||
|
||||
/*!
|
||||
Modifies the bounding rectangle for the cancel button.
|
||||
@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
|
||||
{
|
||||
return [_cancelButton frame];
|
||||
}
|
||||
|
||||
// Managing Menu Templates
|
||||
/*!
|
||||
Returns the menu template object used to dynamically construct the search pop-up icon menu.
|
||||
@return The current menu template.
|
||||
*/
|
||||
- (CPMenu)searchMenuTemplate
|
||||
{
|
||||
return _searchMenuTemplate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the menu template object used to dynamically construct the receiver's pop-up icon menu.
|
||||
@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
|
||||
{
|
||||
_searchMenuTemplate = menu;
|
||||
|
||||
[self resetSearchButton];
|
||||
[self _updateSearchMenu];
|
||||
}
|
||||
|
||||
// Managing Search Modes
|
||||
/*!
|
||||
Returns a Boolean value indicating whether the receiver sends the search action message when the user clicks the search button (or presses return) or after each keystroke.
|
||||
@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;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the receiver sends the search action message when the user clicks the search button (or presses return) or after each keystroke.
|
||||
@param flag \c YES to send the action message all at once when the user clicks the search button or presses return; otherwise, NO to send the search string after each keystroke.
|
||||
*/
|
||||
- (void)setSendsWholeSearchString:(BOOL)flag
|
||||
{
|
||||
_sendsWholeSearchString = flag;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a Boolean value indicating whether the receiver sends its action immediately upon being notified of changes to the search field text or after a brief pause.
|
||||
@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;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether the text field sends its action message to the target immediately upon notification of any changes to the search field text or after a brief pause.
|
||||
@param flag \c YES to send the text field's action immediately upon notification of any changes to the search field; otherwise, NO if you want the text field to pause briefly before sending its action message. Pausing gives the user the opportunity to type more text into the search field before initiating the search.
|
||||
*/
|
||||
- (void)setSendsSearchStringImmediately:(BOOL)flag
|
||||
{
|
||||
_sendsSearchStringImmediately = flag;
|
||||
}
|
||||
|
||||
// Managing Recent Search Strings
|
||||
/*!
|
||||
Returns the maximum number of recent search strings to display in the custom search menu.
|
||||
@return The maximum number of search strings that can appear in the menu. This value is between 0 and 254.
|
||||
*/
|
||||
- (int)maximumRecents
|
||||
{
|
||||
return _maximumRecents;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the maximum number of search strings that can appear in the search menu.
|
||||
@param maxRecents The maximum number of search strings that can appear in the menu. This value can be between 0 and 254. Specifying a value less than 0 sets the value to the default, which is 10. Specifying a value greater than 254 sets the maximum to 254.
|
||||
*/
|
||||
- (void)setMaximumRecents:(int)max
|
||||
{
|
||||
if (max > 254)
|
||||
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.
|
||||
*/
|
||||
- (CPArray)recentSearches
|
||||
{
|
||||
return _recentSearches;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the list of recent search strings to list in the pop-up icon menu of the receiver.
|
||||
@param searches An array of CPString objects containing the search strings.
|
||||
You might use this method to set the recent list of searches from an archived copy.
|
||||
*/
|
||||
- (void)setRecentSearches:(CPArray)searches
|
||||
{
|
||||
var max = MIN([self maximumRecents],[searches count]);
|
||||
var searches = [searches subarrayWithRange:CPMakeRange(0,max)];
|
||||
_recentSearches = searches;
|
||||
|
||||
[self _autosaveRecentSearchList];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the key under which the prior list of recent search strings has been archived.
|
||||
@return The autosave name, which is used as a key in the standard user defaults to save the recent searches. The default value is nil, which causes searches not to be autosaved.
|
||||
*/
|
||||
- (CPString)recentsAutosaveName
|
||||
{
|
||||
return _recentsAutosaveName;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the autosave name under which the receiver automatically archives the list of recent search strings.
|
||||
@param name The autosave name, which is used as a key in the standard user defaults to save the recent searches. If you specify nil or an empty string for this parameter, no autosave name is set and searches are not autosaved.
|
||||
*/
|
||||
- (void)setRecentsAutosaveName:(CPString)name
|
||||
{
|
||||
_recentsAutosaveName = name;
|
||||
|
||||
if(name != nil)
|
||||
[self _registerForAutosaveNotification];
|
||||
else
|
||||
[self _deregisterForAutosaveNotification];
|
||||
}
|
||||
|
||||
// Private methods and subclassing
|
||||
|
||||
- (CPRect)contentRectForBounds:(CPRect)bounds
|
||||
{
|
||||
var superbounds = [super contentRectForBounds:bounds];
|
||||
return [self searchTextRectForBounds:superbounds];
|
||||
}
|
||||
|
||||
+ (double)_keyboardDelayForPartialSearchString:(CPString)string
|
||||
{
|
||||
return (6 - MIN([string length],4))/10;
|
||||
}
|
||||
|
||||
- (CPMenu)menu
|
||||
{
|
||||
return _searchMenu;
|
||||
}
|
||||
|
||||
- (BOOL)isOpaque
|
||||
{
|
||||
return [super isOpaque] && [_cancelButton isOpaque] && [_searchButton isOpaque];
|
||||
}
|
||||
|
||||
- (void)_updateCancelButtonVisibility
|
||||
{
|
||||
[_cancelButton setHidden:([[self stringValue] length] == 0)];
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(CPNotification)aNotification
|
||||
{
|
||||
if(!_sendsWholeSearchString)
|
||||
{
|
||||
if(_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];
|
||||
}
|
||||
}
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (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 _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)_addStringToRecentSearches:(CPString)string
|
||||
{
|
||||
var newSearches = [CPMutableArray arrayWithArray:_recentSearches];
|
||||
[newSearches addObject:string];
|
||||
[self setRecentSearches:newSearches];
|
||||
}
|
||||
|
||||
- (BOOL)trackMouse:(CPEvent)event
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
- (CPMenu)_searchMenuTemplate
|
||||
{
|
||||
var template, item;
|
||||
|
||||
template = [[CPMenu alloc] init];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent searches" action:NULL keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldRecentsTitleMenuItemTag];
|
||||
[item setEnabled:NO];
|
||||
[template addItem:item];
|
||||
|
||||
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 setTag:CPSearchFieldClearRecentsMenuItemTag];
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"No recent searches" action:NULL keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldNoRecentsMenuItemTag];
|
||||
[item setEnabled:NO];
|
||||
[template addItem:item];
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
- (void)_updateSearchMenu
|
||||
{
|
||||
if(_searchMenuTemplate == nil)
|
||||
return;
|
||||
|
||||
var i, menu = [[CPMenu alloc] init];
|
||||
var countOfRecents = [_recentSearches count];
|
||||
|
||||
for (i = 0; i < [_searchMenuTemplate numberOfItems]; i++)
|
||||
{
|
||||
var item = [_searchMenuTemplate itemAtIndex:i];
|
||||
var tag = [item tag];
|
||||
|
||||
if(tag == CPSearchFieldClearRecentsMenuItemTag && countOfRecents != 0)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
var rencentItem = [[CPMenuItem alloc] initWithTitle:[_recentSearches objectAtIndex:j] action:[item action] keyEquivalent:[item keyEquivalent]];
|
||||
[rencentItem setTarget:[item target]];
|
||||
[menu addItem:rencentItem];
|
||||
}
|
||||
}
|
||||
}
|
||||
_searchMenu = menu;
|
||||
}
|
||||
|
||||
|
||||
- (void)_showMenu:(id)sender
|
||||
{
|
||||
if(_searchMenu == nil || ![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];
|
||||
}
|
||||
|
||||
- (void)_sendPartialString
|
||||
{
|
||||
[[self target] performSelector:[self action] withObject:self];
|
||||
}
|
||||
|
||||
- (void)_searchFieldCancel:(id)sender
|
||||
{
|
||||
[self setObjectValue:nil];
|
||||
[self _sendPartialString];
|
||||
[self _updateCancelButtonVisibility];
|
||||
[sender setHidden:YES];
|
||||
}
|
||||
|
||||
- (void)_searchFieldSearch:(id)sender
|
||||
{
|
||||
[self setObjectValue:[sender title]];
|
||||
[self _sendPartialString];
|
||||
[self _updateCancelButtonVisibility];
|
||||
}
|
||||
|
||||
- (void)_searchFieldClearRecents:(id)sender
|
||||
{
|
||||
[self setRecentSearches:[CPArray array]];
|
||||
[self _updateSearchMenu];
|
||||
}
|
||||
|
||||
- (void)_registerForAutosaveNotification
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_updateAutosavedRecents:) name:@"CPAutosavedRecentsChangedNotification" object:nil];
|
||||
}
|
||||
|
||||
- (void)_deregisterForAutosaveNotification
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:self name:@"CPAutosavedRecentsChangedNotification" object:nil];
|
||||
}
|
||||
|
||||
- (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];
|
||||
}
|
||||
|
||||
- (void)_loadRecentSearchList
|
||||
{
|
||||
var list,
|
||||
name = [self recentsAutosaveName];
|
||||
|
||||
list = [[CPUserDefaults standardUserDefaults] objectForKey:name];
|
||||
_recentSearches = list;
|
||||
}
|
||||
|
||||
/*
|
||||
- (BOOL)trackMouse:(CPEvent)theEvent inRect:(CPRect)cellFrame ofView:(CPView)aTextView untilMouseUp:(BOOL)flag
|
||||
{
|
||||
}
|
||||
|
||||
- (BOOL)_trimRecentSearchList
|
||||
{
|
||||
}
|
||||
|
||||
- (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",
|
||||
CPSendsWholeSearchStringKey = @"CPSendsWholeSearchStringKey",
|
||||
CPSendsSearchStringImmediatelyKey = @"CPSendsSearchStringImmediatelyKey",
|
||||
CPMaximumRecentsKey = @"CPMaximumRecentsKey",
|
||||
CPSearchMenuTemplateKey = @"CPSearchMenuTemplateKey";
|
||||
|
||||
@implementation CPSearchField (CPCoding)
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)coder
|
||||
{
|
||||
[super encodeWithCoder:coder];
|
||||
|
||||
[coder encodeObject:_searchButton forKey:CPSearchButtonKey];
|
||||
[coder encodeObject:_cancelButton forKey:CPCancelButtonKey];
|
||||
[coder encodeObject:_recentsAutosaveName forKey:CPRecentsAutosaveNameKey];
|
||||
[coder encodeBool:_sendsWholeSearchString forKey:CPSendsWholeSearchStringKey];
|
||||
[coder encodeBool:_sendsSearchStringImmediately forKey:CPSendsSearchStringImmediatelyKey];
|
||||
[coder encodeInt:_maximumRecents forKey:CPMaximumRecentsKey];
|
||||
[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];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,103 +1,16 @@
|
||||
import "CPTextField.j"
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
|
||||
|
||||
var TOP_PADDING = 4.0,
|
||||
BOTTOM_PADDING = 3.0;
|
||||
HORIZONTAL_PADDING = 3.0;
|
||||
|
||||
var CPSecureTextFieldDOMInputElement = nil;
|
||||
@import "CPTextField.j"
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
@implementation CPSecureTextField : CPTextField
|
||||
{
|
||||
}
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
+ (DOMElement)_inputElement
|
||||
- (BOOL)isSecure
|
||||
{
|
||||
if (!CPSecureTextFieldDOMInputElement)
|
||||
{
|
||||
CPSecureTextFieldDOMInputElement = document.createElement("input");
|
||||
CPSecureTextFieldDOMInputElement.type = "password";
|
||||
CPSecureTextFieldDOMInputElement.style.position = "absolute";
|
||||
CPSecureTextFieldDOMInputElement.style.top = "0px";
|
||||
CPSecureTextFieldDOMInputElement.style.left = "0px";
|
||||
CPSecureTextFieldDOMInputElement.style.width = "100%"
|
||||
CPSecureTextFieldDOMInputElement.style.height = "100%";
|
||||
CPSecureTextFieldDOMInputElement.style.border = "0px";
|
||||
CPSecureTextFieldDOMInputElement.style.padding = "0px";
|
||||
CPSecureTextFieldDOMInputElement.style.whiteSpace = "pre";
|
||||
CPSecureTextFieldDOMInputElement.style.background = "transparent";
|
||||
CPSecureTextFieldDOMInputElement.style.outline = "none";
|
||||
CPSecureTextFieldDOMInputElement.style.paddingLeft = HORIZONTAL_PADDING + "px";
|
||||
CPSecureTextFieldDOMInputElement.style.paddingTop = TOP_PADDING - 2.0 + "px";
|
||||
CPSecureTextFieldDOMInputElement.style.margin = "0px";
|
||||
}
|
||||
|
||||
return CPSecureTextFieldDOMInputElement;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.removeChild(_DOMTextElement);
|
||||
|
||||
_DOMTextElement = document.createElement("input");
|
||||
_DOMTextElement.type = "password";
|
||||
_DOMTextElement.style.position = "absolute";
|
||||
_DOMTextElement.style.top = TOP_PADDING + "px";
|
||||
_DOMTextElement.style.left = HORIZONTAL_PADDING + "px";
|
||||
_DOMTextElement.style.width = MAX(0.0, CGRectGetWidth(aFrame) - 2.0 * HORIZONTAL_PADDING) + "px";
|
||||
_DOMTextElement.style.height = MAX(0.0, CGRectGetHeight(aFrame) - TOP_PADDING - BOTTOM_PADDING) + "px";
|
||||
_DOMTextElement.style.whiteSpace = "pre";
|
||||
_DOMTextElement.style.cursor = "default";
|
||||
_DOMTextElement.style.zIndex = 100;
|
||||
_DOMTextElement.style.border = "0";
|
||||
_DOMTextElement.style.font = _DOMElement.style.font;
|
||||
_DOMTextElement.style.padding = "0px";
|
||||
_DOMTextElement.style.margin = "0px";
|
||||
|
||||
_DOMElement.appendChild(_DOMTextElement);
|
||||
#endif
|
||||
}
|
||||
|
||||
return self;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
[super setFont:aFont];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMTextElement)
|
||||
_DOMTextElement.style.font = _DOMElement.style.font;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (CPString)stringValue
|
||||
{
|
||||
// All of this needs to be better.
|
||||
#if PLATFORM(DOM)
|
||||
if ([[self window] firstResponder] == self)
|
||||
return [[self class] _inputElement].value;
|
||||
#endif
|
||||
|
||||
return _DOMTextElement.value;
|
||||
}
|
||||
|
||||
- (void)setStringValue:(CPString)aStringValue
|
||||
{
|
||||
_value = aStringValue;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMTextElement.value = _value;
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
+471
-204
@@ -20,9 +20,11 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPArray.j>
|
||||
@import <Foundation/CPArray.j>
|
||||
|
||||
import "CPControl.j"
|
||||
@import "CPControl.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
/*
|
||||
@global
|
||||
@@ -40,40 +42,54 @@ CPSegmentSwitchTrackingSelectAny = 1;
|
||||
*/
|
||||
CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
/*
|
||||
This class is a horizontal button with multiple
|
||||
segments.
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPSegmentedControl
|
||||
|
||||
This class is a horizontal button with multiple segments.
|
||||
*/
|
||||
@implementation CPSegmentedControl : CPControl
|
||||
{
|
||||
unsigned _segmentCount;
|
||||
|
||||
CPArray _segments;
|
||||
CPArray _selectedSegment;
|
||||
|
||||
CPArray _themeStates;
|
||||
|
||||
int _selectedSegment;
|
||||
int _segmentStyle;
|
||||
CPSegmentSwitchTracking _trackingMode;
|
||||
|
||||
unsigned _trackingSegment;
|
||||
BOOL _trackingHighlighted;
|
||||
}
|
||||
|
||||
+ (CPString)themeClass
|
||||
{
|
||||
return "segmented-control";
|
||||
}
|
||||
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[CPCenterTextAlignment, CPCenterVerticalTextAlignment, CPImageLeft, CPScaleNone, _CGInsetMakeZero(), _CGInsetMakeZero(), nil, nil, nil, nil, 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"]];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aRect
|
||||
{
|
||||
_segments = [];
|
||||
_themeStates = [];
|
||||
|
||||
self = [super initWithFrame:aRect];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_segments = [];
|
||||
_selectedSegment = -1;
|
||||
|
||||
_segmentCount = 0;
|
||||
|
||||
_trackingMode = CPSegmentSwitchTrackingSelectOne;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the tag of the selected segment.
|
||||
*/
|
||||
- (int)selectedTag
|
||||
@@ -82,62 +98,50 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
}
|
||||
|
||||
// Specifying the number of segments
|
||||
/*
|
||||
/*!
|
||||
Sets the number of segments in the button.
|
||||
@param aCount the number of segments on
|
||||
the button
|
||||
@param aCount the number of segments on the button
|
||||
*/
|
||||
- (void)setSegmentCount:(unsigned)aCount
|
||||
{
|
||||
if (_segmentCount == aCount)
|
||||
if (_segments.length == aCount)
|
||||
return;
|
||||
|
||||
var height = CGRectGetHeight([self bounds]);
|
||||
|
||||
if (_segmentCount < aCount)
|
||||
{
|
||||
var index = _segmentCount;
|
||||
|
||||
for (; index < aCount; ++index)
|
||||
if (_segments.length < aCount)
|
||||
{
|
||||
for (var index = _segments.length; index < aCount; ++index)
|
||||
{
|
||||
_segments[index] = _CPSegmentMake();
|
||||
_segments[index].frame.size.height = height;
|
||||
_segments[index] = [[_CPSegmentItem alloc] init];
|
||||
_themeStates[index] = CPThemeStateNormal;
|
||||
}
|
||||
}
|
||||
else if (aCount < _segmentCount)
|
||||
else if (aCount < _segments.length)
|
||||
{
|
||||
var index = aCount;
|
||||
|
||||
for (; index < _segmentCount; ++index)
|
||||
{
|
||||
[_segments[index].imageView removeFromSuperview];
|
||||
[_segments[index].labelView removeFromSuperview];
|
||||
|
||||
_segments[index] = nil;
|
||||
}
|
||||
_segments.length = aCount;
|
||||
_themeStates.length = aCount;
|
||||
}
|
||||
|
||||
_segmentCount = aCount;
|
||||
|
||||
if (_selectedSegment < _segmentCount)
|
||||
|
||||
if (_selectedSegment >= _segments.length)
|
||||
_selectedSegment = -1;
|
||||
|
||||
|
||||
[self tileWithChangedSegment:0];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the number of segments in the button.
|
||||
*/
|
||||
- (unsigned)segmentCount
|
||||
{
|
||||
return _segmentCount;
|
||||
return _segments.length;
|
||||
}
|
||||
|
||||
// Specifying Selected Segment
|
||||
/*
|
||||
/*!
|
||||
Selects a segment.
|
||||
@param aSegment the segment to select
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setSelectedSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -145,7 +149,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
[self setSelected:YES forSegment:aSegment];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the selected segment.
|
||||
*/
|
||||
- (unsigned)selectedSegment
|
||||
@@ -153,15 +157,14 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
return _selectedSegment;
|
||||
}
|
||||
|
||||
/*
|
||||
Selects the button segment with the
|
||||
specified tag.
|
||||
/*!
|
||||
Selects the button segment with the specified tag.
|
||||
*/
|
||||
- (BOOL)selectSegmentWithTag:(int)aTag
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
for (; index < _segmentCount; ++index)
|
||||
for (; index < _segments.length; ++index)
|
||||
if (_segments[index].tag == aTag)
|
||||
{
|
||||
[self setSelectedSegment:index];
|
||||
@@ -191,7 +194,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
var index = 0,
|
||||
selected = NO;
|
||||
|
||||
for (; index < _segmentCount; ++index)
|
||||
for (; index < _segments.length; ++index)
|
||||
if (_segments[index].selected)
|
||||
if (selected)
|
||||
[self setSelected:NO forSegment:index];
|
||||
@@ -203,13 +206,13 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
for (; index < _segmentCount; ++index)
|
||||
for (; index < _segments.length; ++index)
|
||||
if (_segments[index].selected)
|
||||
[self setSelected:NO forSegment:index];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the control's tracking mode.
|
||||
*/
|
||||
- (CPSegmentSwitchTracking)trackingMode
|
||||
@@ -218,11 +221,11 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
}
|
||||
|
||||
// Working with Individual Segments
|
||||
/*
|
||||
/*!
|
||||
Sets the width of the specified segment.
|
||||
@param aWidth the new width for the segment
|
||||
@param aSegment the segment to set the width for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -231,141 +234,93 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
[self tileWithChangedSegment:aSegment];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the width for the specified segment.
|
||||
@param aSegment the segment to get the width for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (float)widthForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].width;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the image for the specified segment.
|
||||
@param anImage the image for the segment
|
||||
@param aSegment the segment to set the image on
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
|
||||
{
|
||||
var segment = _segments[aSegment];
|
||||
|
||||
if (!anImage)
|
||||
{
|
||||
[segment.imageView removeFromSuperview];
|
||||
|
||||
segment.imageView = nil;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (!segment.imageView)
|
||||
{
|
||||
segment.imageView = [[CPImageView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[self addSubview:segment.imageView];
|
||||
}
|
||||
|
||||
[segment.imageView setImage:anImage];
|
||||
[segment.imageView setFrameSize:CGSizeMakeCopy([anImage size])];
|
||||
}
|
||||
|
||||
segment.image = anImage;
|
||||
|
||||
if (segment.width)
|
||||
[self drawSegment:aSegment highlight:NO];
|
||||
else
|
||||
[self tileWithChangedSegment:aSegment];
|
||||
[self tileWithChangedSegment:aSegment];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the image for the specified segment
|
||||
@param aSegment the segment to obtain the image for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (CPImage)imageForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].image;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the label for the specified segment
|
||||
@param aLabel the label for the segment
|
||||
@param aSegment the segment to label
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
|
||||
{
|
||||
var segment = _segments[aSegment];
|
||||
|
||||
if (!aLabel || !aLabel.length)
|
||||
{
|
||||
[segment.labelView removeFromSuperview];
|
||||
|
||||
segment.labelView = nil;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (!segment.labelView)
|
||||
{
|
||||
segment.labelView = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[segment.labelView setFont:[self font]];
|
||||
|
||||
[self addSubview:segment.labelView];
|
||||
}
|
||||
|
||||
[segment.labelView setStringValue:aLabel];
|
||||
[segment.labelView sizeToFit];
|
||||
}
|
||||
|
||||
_segments[aSegment].label = aLabel;
|
||||
|
||||
if (segment.width)
|
||||
[self drawSegment:aSegment highlight:NO];
|
||||
else
|
||||
[self tileWithChangedSegment:aSegment];
|
||||
[self tileWithChangedSegment:aSegment];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the label for the specified segment
|
||||
@param the segment to obtain the label for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (CPString)labelForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].label;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the menu for the specified segment
|
||||
@param aMenu the menu to set
|
||||
@param aSegment the segment to set the menu on
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
|
||||
{
|
||||
_segments[aSegment].menu = aMenu;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the menu for the specified segment.
|
||||
@param aSegment the segment to obtain the menu for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (CPMenu)menuForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].menu;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the selection for the specified segment. If only one segment
|
||||
can be selected at a time, any other segment will be deselected.
|
||||
@param isSelected <code>YES</code> selects the segment. <code>NO</code> deselects it.
|
||||
@param isSelected \c YES selects the segment. \c NO deselects it.
|
||||
@param aSegment the segment to set the selection for
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment
|
||||
{
|
||||
@@ -377,6 +332,8 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
segment.selected = isSelected;
|
||||
|
||||
_themeStates[aSegment] = isSelected ? CPThemeStateSelected : CPThemeStateNormal;
|
||||
|
||||
// We need to do some cleanup if we only allow one selection.
|
||||
if (isSelected)
|
||||
{
|
||||
@@ -387,6 +344,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
|
||||
{
|
||||
_segments[oldSelectedSegment].selected = NO;
|
||||
_themeStates[oldSelectedSegment] = CPThemeStateNormal;
|
||||
|
||||
[self drawSegmentBezel:oldSelectedSegment highlight:NO];
|
||||
}
|
||||
@@ -394,40 +352,46 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
if (_trackingMode != CPSegmentSwitchTrackingMomentary)
|
||||
[self drawSegmentBezel:aSegment highlight:NO];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the specified segment is selected.
|
||||
/*!
|
||||
Returns \c YES if the specified segment is selected.
|
||||
@param aSegment the segment to check for selection
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (BOOL)isSelectedForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].selected;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Enables/diables the specified segment.
|
||||
@param isEnabled <code>YES</code> enables the segment
|
||||
@param isEnabled \c YES enables the segment
|
||||
@param aSegment the segment to enable/disble
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (void)setEnabled:(BOOL)isEnabled forSegment:(unsigned)aSegment
|
||||
{
|
||||
_segments[aSegment].enabled = isEnabled;
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the specified segment is enabled.
|
||||
/*!
|
||||
Returns \c YES if the specified segment is enabled.
|
||||
@param aSegment the segment to check
|
||||
@throws CPRangeException if <code>aSegment</code> is out of bounds
|
||||
@throws CPRangeException if \c aSegment is out of bounds
|
||||
*/
|
||||
- (BOOL)isEnabledForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].enabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets a tag for the specified segment.
|
||||
@param aTag the tag to set
|
||||
@param aSegment the segment to set the tag on
|
||||
@@ -437,7 +401,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
_segments[aSegment].tag = aTag;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the tag for the specified segment.
|
||||
@param aSegment the segment to obtain the tag for
|
||||
*/
|
||||
@@ -447,83 +411,226 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
}
|
||||
|
||||
// Drawings
|
||||
/*
|
||||
/*!
|
||||
Draws the specified segment bezel
|
||||
@param aSegment the segment to draw the bezel for
|
||||
@param shouldHighlight <code>YES</code> highlights the bezel
|
||||
@param shouldHighlight \c YES highlights the bezel
|
||||
*/
|
||||
- (void)drawSegmentBezel:(int)aSegment highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
if (shouldHighlight)
|
||||
_themeStates[aSegment] |= CPThemeStateHighlighted;
|
||||
else
|
||||
_themeStates[aSegment] &= ~CPThemeStateHighlighted;
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
- (float)_leftOffsetForSegment:(unsigned)segment
|
||||
{
|
||||
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
|
||||
|
||||
if (segment == 0)
|
||||
return bezelInset.left;
|
||||
|
||||
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
|
||||
|
||||
return [self _leftOffsetForSegment:segment - 1] + [self widthForSegment:segment - 1] + thickness;
|
||||
}
|
||||
|
||||
- (unsigned)_indexOfLastSegment
|
||||
{
|
||||
var lastSegmentIndex = [_segments count] - 1;
|
||||
|
||||
if (lastSegmentIndex < 0)
|
||||
lastSegmentIndex = 0;
|
||||
|
||||
return lastSegmentIndex;
|
||||
}
|
||||
|
||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
var height = [self currentValueForThemeAttribute:@"default-height"],
|
||||
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
|
||||
bounds = [self bounds];
|
||||
|
||||
if (aName === "left-segment-bezel")
|
||||
{
|
||||
return CGRectMake(bezelInset.left, bezelInset.top, contentInset.left, height);
|
||||
}
|
||||
else if (aName === "right-segment-bezel")
|
||||
{
|
||||
var lastSegmentLeftOffset = [self _leftOffsetForSegment:[self _indexOfLastSegment]];
|
||||
|
||||
return CPRectMake(lastSegmentLeftOffset + [self widthForSegment:[self _indexOfLastSegment]] - contentInset.right,
|
||||
bezelInset.top,
|
||||
contentInset.right,
|
||||
height);
|
||||
}
|
||||
else if (aName.substring(0, "segment-bezel".length) == "segment-bezel")
|
||||
{
|
||||
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
|
||||
frame = CGRectCreateCopy(_segments[segment].frame);
|
||||
|
||||
if (segment == 0)
|
||||
{
|
||||
frame.origin.x += contentInset.left;
|
||||
frame.size.width -= contentInset.left;
|
||||
}
|
||||
|
||||
if (segment == _segments.length - 1)
|
||||
frame.size.width -= contentInset.right;
|
||||
|
||||
return frame;
|
||||
}
|
||||
else if (aName.substring(0, "divider-bezel".length) == "divider-bezel")
|
||||
{
|
||||
var segment = parseInt(aName.substring("divider-bezel-".length), 10),
|
||||
width = [self widthForSegment:segment],
|
||||
left = [self _leftOffsetForSegment:segment],
|
||||
thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
|
||||
|
||||
return CGRectMake(left + width, bezelInset.top, thickness, height);
|
||||
}
|
||||
else if (aName.substring(0, "segment-content".length) == "segment-content")
|
||||
{
|
||||
var segment = parseInt(aName.substring("segment-content-".length), 10);
|
||||
|
||||
return [self contentFrameForSegment:segment];
|
||||
}
|
||||
|
||||
return [super rectForEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if (aName.substring(0, "segment-content".length) == "segment-content")
|
||||
return [[_CPImageAndTextView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
|
||||
return [[CPView alloc] initWithFrame:_CGRectMakeZero()];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
if (_segments.length <= 0)
|
||||
return;
|
||||
|
||||
var leftCapColor = [self valueForThemeAttribute:@"left-segment-bezel-color"
|
||||
inState:_themeStates[0]];
|
||||
|
||||
var 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 rightBezelView = [self layoutEphemeralSubviewNamed:@"right-segment-bezel"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[rightBezelView setBackgroundColor:rightCapColor];
|
||||
|
||||
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 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
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:@"segment-bezel-"+i];
|
||||
|
||||
[contentView setText:segment.label];
|
||||
[contentView setImage:segment.image];
|
||||
|
||||
[contentView setFont:[self valueForThemeAttribute:@"font" inState:themeState]];
|
||||
[contentView setTextColor:[self valueForThemeAttribute:@"text-color" inState:themeState]];
|
||||
[contentView setAlignment:[self valueForThemeAttribute:@"alignment" inState:themeState]];
|
||||
[contentView setVerticalAlignment:[self valueForThemeAttribute:@"vertical-alignment" inState:themeState]];
|
||||
[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 setImagePosition:[self valueForThemeAttribute:@"image-position" inState:themeState]];
|
||||
else if (segment.image)
|
||||
[contentView setImagePosition:CPImageOnly];
|
||||
|
||||
if (i == count - 1)
|
||||
continue;
|
||||
|
||||
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];
|
||||
|
||||
var borderView = [self layoutEphemeralSubviewNamed:"divider-bezel-"+i
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:nil];
|
||||
|
||||
[borderView setBackgroundColor:borderColor];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Draws the specified segment
|
||||
@param aSegment the segment to draw
|
||||
@param shouldHighlight <code>YES</code> highlights the bezel
|
||||
@param shouldHighlight \c YES highlights the bezel
|
||||
*/
|
||||
- (void)drawSegment:(int)aSegment highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
var segment = _segments[aSegment],
|
||||
|
||||
imageView = segment.imageView,
|
||||
labelView = segment.labelView,
|
||||
|
||||
frame = segment.frame,
|
||||
|
||||
segmentX = CGRectGetMinX(frame),
|
||||
segmentWidth = CGRectGetWidth(frame),
|
||||
segmentHeight = CGRectGetHeight(frame) - 1.0;
|
||||
|
||||
if (imageView && labelView)
|
||||
{
|
||||
var imageViewSize = [imageView frame].size,
|
||||
labelViewSize = [labelView frame].size,
|
||||
totalHeight = imageViewSize.height + labelViewSize.height,
|
||||
labelWidth = MIN(labelViewSize.width, width),
|
||||
y = (segmentHeight - totalHeight) / 2.0;
|
||||
|
||||
[imageView setFrameOrigin:CGPointMake(segmentX + (segmentWidth - imageViewSize.width) / 2.0, y)];
|
||||
|
||||
if (labelWidth < labelViewSize.width)
|
||||
[labelView setFrameSize:CGSizeMake(labelWidth, labelViewSize.height)];
|
||||
|
||||
[labelView setFrameOrigin:CGPointMake(segmentX + (segmentWidth - labelWidth) / 2.0, y + imageViewSize.height)];
|
||||
}
|
||||
else if (imageView)
|
||||
{
|
||||
var imageViewSize = [imageView frame].size;
|
||||
|
||||
[imageView setFrameOrigin:CGPointMake(segmentX + (segmentWidth - imageViewSize.width) / 2.0, (segmentHeight - imageViewSize.height) / 2.0)];
|
||||
}
|
||||
else if (labelView)
|
||||
{
|
||||
var labelViewSize = [labelView frame].size,
|
||||
labelWidth = MIN(labelViewSize.width, segmentWidth);
|
||||
|
||||
if (labelWidth < labelViewSize.width)
|
||||
[labelView setFrameSize:CGSizeMake(labelWidth, labelViewSize.height)];
|
||||
|
||||
[labelView setFrameOrigin:CGPointMake(segmentX + (segmentWidth - labelWidth) / 2.0, (segmentHeight - labelViewSize.height) / 2.0)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tileWithChangedSegment:(unsigned)aSegment
|
||||
{
|
||||
if (aSegment >= _segments.length)
|
||||
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];
|
||||
|
||||
if (!segmentWidth)
|
||||
{
|
||||
if (segment.labelView && segment.imageView)
|
||||
segmentWidth = MAX(CGRectGetWidth([segment.labelView frame]) , CGRectGetWidth([segment.imageView frame]));
|
||||
else if (segment.labelView)
|
||||
segmentWidth = CGRectGetWidth([segment.labelView frame]);
|
||||
else if (segment.imageView)
|
||||
segmentWidth = CGRectGetWidth([segment.imageView frame]);
|
||||
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;
|
||||
else if (segment.label)
|
||||
segmentWidth = [segment.label sizeWithFont:font].width + contentInset.left + contentInset.right;
|
||||
else
|
||||
segmentWidth = 0.0;
|
||||
}
|
||||
|
||||
|
||||
var delta = segmentWidth - CGRectGetWidth(segment.frame);
|
||||
|
||||
|
||||
if (!delta)
|
||||
return;
|
||||
|
||||
@@ -533,12 +640,13 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
[self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
|
||||
|
||||
// Update Segment Width
|
||||
segment.frame.size.width = segmentWidth;
|
||||
segment.width = segmentWidth;
|
||||
segment.frame = [self frameForSegment:aSegment];;
|
||||
|
||||
// Update Following Segments Widths
|
||||
var index = aSegment + 1;
|
||||
|
||||
for (; index < _segmentCount; ++index)
|
||||
for (; index < _segments.length; ++index)
|
||||
{
|
||||
_segments[index].frame.origin.x += delta;
|
||||
|
||||
@@ -548,18 +656,41 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
[self drawSegmentBezel:aSegment highlight:NO];
|
||||
[self drawSegment:aSegment highlight:NO];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the bounding rectangle for the specified segment.
|
||||
@param aSegment the segment to get the rectangle for
|
||||
*/
|
||||
- (CGRect)frameForSegment:(unsigned)aSegment
|
||||
{
|
||||
return _segments[aSegment].frame;
|
||||
return [self bezelFrameForSegment:aSegment];
|
||||
}
|
||||
|
||||
/*
|
||||
- (CGRect)bezelFrameForSegment:(unsigned)aSegment
|
||||
{
|
||||
var height = [self currentValueForThemeAttribute:@"default-height"],
|
||||
bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
|
||||
width = [self widthForSegment:aSegment],
|
||||
left = [self _leftOffsetForSegment:aSegment];
|
||||
|
||||
return CGRectMake(left, bezelInset.top, width, height);
|
||||
}
|
||||
|
||||
- (CGRect)contentFrameForSegment:(unsigned)aSegment
|
||||
{
|
||||
var height = [self currentValueForThemeAttribute:@"default-height"],
|
||||
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
width = [self widthForSegment:aSegment],
|
||||
left = [self _leftOffsetForSegment:aSegment];
|
||||
|
||||
return CGRectMake(left + contentInset.left, contentInset.top, width - contentInset.left - contentInset.right, height - contentInset.top - contentInset.bottom);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the segment that is hit by the specified point.
|
||||
@param aPoint the point to test for a segment hit
|
||||
@return the intersecting segment
|
||||
@@ -589,7 +720,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Handles events for the segment
|
||||
@param anEvent the event to handle
|
||||
*/
|
||||
@@ -600,6 +731,9 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
if (type == CPLeftMouseUp)
|
||||
{
|
||||
if (_trackingSegment == -1)
|
||||
return;
|
||||
|
||||
if (CGRectContainsPoint(_segments[_trackingSegment].frame, location))
|
||||
{
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
|
||||
@@ -633,12 +767,15 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
_trackingHighlighted = YES;
|
||||
_trackingSegment = [self testSegment:location];
|
||||
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
}
|
||||
|
||||
else if (type == CPLeftMouseDragged)
|
||||
{
|
||||
if (_trackingSegment == -1)
|
||||
return;
|
||||
|
||||
var highlighted = CGRectContainsPoint(_segments[_trackingSegment].frame, location);
|
||||
|
||||
if (highlighted != _trackingHighlighted)
|
||||
@@ -656,20 +793,150 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
[super setFont:aFont];
|
||||
|
||||
var count = _segmentCount;
|
||||
|
||||
if (!count)
|
||||
return;
|
||||
|
||||
while (count--)
|
||||
[_segments[count].labelView setFont:aFont];
|
||||
|
||||
[self tileWithChangedSegment:0];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPSegmentMake = function()
|
||||
var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
||||
CPSegmentedControlSelectedKey = "CPSegmentedControlSelectedKey",
|
||||
CPSegmentedControlSegmentStyleKey = "CPSegmentedControlSegmentStyleKey",
|
||||
CPSegmentedControlTrackingModeKey = "CPSegmentedControlTrackingModeKey";
|
||||
|
||||
@implementation CPSegmentedControl (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
return { width:0, image:nil, label:@"", menu:nil, selected:NO, enabled:NO, tag:0, labelView:nil, imageView:nil, frame:CGRectMakeZero() }
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
var frame = [self frame],
|
||||
originalWidth = frame.size.width;
|
||||
|
||||
frame.size.width = 0;
|
||||
|
||||
[self setFrame:frame];
|
||||
|
||||
_segments = [aCoder decodeObjectForKey:CPSegmentedControlSegmentsKey];
|
||||
_segmentStyle = [aCoder decodeIntForKey:CPSegmentedControlSegmentStyleKey];
|
||||
_themeStates = [];
|
||||
|
||||
if ([aCoder containsValueForKey:CPSegmentedControlSelectedKey])
|
||||
_selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
|
||||
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;
|
||||
[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];
|
||||
|
||||
[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];
|
||||
[aCoder encodeInt:_trackingMode forKey:CPSegmentedControlTrackingModeKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation _CPSegmentItem : CPObject
|
||||
{
|
||||
CPImage image;
|
||||
CPString label;
|
||||
CPMenu menu;
|
||||
BOOL selected;
|
||||
BOOL enabled;
|
||||
int tag;
|
||||
int width;
|
||||
|
||||
CGRect frame;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
image = nil;
|
||||
label = @"";
|
||||
menu = nil;
|
||||
selected = NO;
|
||||
enabled = NO;
|
||||
tag = 0;
|
||||
width = 0;
|
||||
|
||||
frame = CGRectMakeZero();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPSegmentItemImageKey = "CPSegmentItemImageKey",
|
||||
CPSegmentItemLabelKey = "CPSegmentItemLabelKey",
|
||||
CPSegmentItemMenuKey = "CPSegmentItemMenuKey",
|
||||
CPSegmentItemSelectedKey = "CPSegmentItemSelectedKey",
|
||||
CPSegmentItemEnabledKey = "CPSegmentItemEnabledKey",
|
||||
CPSegmentItemTagKey = "CPSegmentItemTagKey",
|
||||
CPSegmentItemWidthKey = "CPSegmentItemWidthKey";
|
||||
|
||||
@implementation _CPSegmentItem (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
image = [aCoder decodeObjectForKey:CPSegmentItemImageKey];
|
||||
label = [aCoder decodeObjectForKey:CPSegmentItemLabelKey];
|
||||
menu = [aCoder decodeObjectForKey:CPSegmentItemMenuKey];
|
||||
selected = [aCoder decodeBoolForKey:CPSegmentItemSelectedKey];
|
||||
enabled = [aCoder decodeBoolForKey:CPSegmentItemEnabledKey];
|
||||
tag = [aCoder decodeIntForKey:CPSegmentItemTagKey];
|
||||
width = [aCoder decodeFloatForKey:CPSegmentItemWidthKey];
|
||||
|
||||
frame = CGRectMakeZero();
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:image forKey:CPSegmentItemImageKey];
|
||||
[aCoder encodeObject:label forKey:CPSegmentItemLabelKey];
|
||||
[aCoder encodeObject:menu forKey:CPSegmentItemMenuKey];
|
||||
[aCoder encodeBool:selected forKey:CPSegmentItemSelectedKey];
|
||||
[aCoder encodeBool:enabled forKey:CPSegmentItemEnabledKey];
|
||||
[aCoder encodeInt:tag forKey:CPSegmentItemTagKey];
|
||||
[aCoder encodeFloat:width forKey:CPSegmentItemWidthKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
+11
-7
@@ -20,9 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
/*!
|
||||
@deprecated
|
||||
@class CPShadow
|
||||
|
||||
/*
|
||||
Instances of this class contain the attributes of a drop shadow used in Cappuccino.
|
||||
*/
|
||||
@implementation CPShadow : CPObject
|
||||
@@ -34,7 +38,7 @@ import <Foundation/CPObject.j>
|
||||
CPString _cssString;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Creates a shadow with the specified attributes.
|
||||
@param anOffset the shadow's offset
|
||||
@param aBlurRadius the shadow's blur radius
|
||||
@@ -63,7 +67,7 @@ import <Foundation/CPObject.j>
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the shadow's offset.
|
||||
*/
|
||||
- (CGSize)shadowOffset
|
||||
@@ -71,7 +75,7 @@ import <Foundation/CPObject.j>
|
||||
return _offset;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the shadow's blur radius
|
||||
*/
|
||||
- (float)shadowBlurRadius
|
||||
@@ -79,7 +83,7 @@ import <Foundation/CPObject.j>
|
||||
return _blurRadius;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the shadow's color.
|
||||
*/
|
||||
- (CPColor)shadowColor
|
||||
@@ -87,7 +91,7 @@ import <Foundation/CPObject.j>
|
||||
return _color;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns a CSS string representation of the shadow.
|
||||
*/
|
||||
- (CPString)cssString
|
||||
|
||||
+10
-5
@@ -20,12 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPBundle.j>
|
||||
@import <Foundation/CPBundle.j>
|
||||
|
||||
@import "CGGeometry.j"
|
||||
@import "CPImage.j"
|
||||
@import "CPImageView.j"
|
||||
@import "CPView.j"
|
||||
|
||||
import "CGGeometry.j"
|
||||
import "CPImage.j"
|
||||
import "CPImageView.j"
|
||||
import "CPView.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
@@ -45,6 +46,10 @@ var LIGHT_LEFT_INSET = 3.0,
|
||||
HEAVY_TOP_INSET = 5.0,
|
||||
HEAVY_BOTTOM_INSET = 5.0;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
|
||||
@implementation CPShadowView : CPView
|
||||
{
|
||||
CPShadowWeight _weight;
|
||||
|
||||
+350
-306
@@ -3,7 +3,7 @@
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2008, 280 North, Inc.
|
||||
* 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
|
||||
@@ -20,393 +20,437 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPControl.j"
|
||||
@import "CPControl.j"
|
||||
|
||||
var CPSliderHorizontalKnobImage = nil,
|
||||
CPSliderHorizontalBarLeftImage = nil,
|
||||
CPSliderHorizontalBarRightImage = nil,
|
||||
CPSliderHorizontalBarCenterImage = nil;
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
/*
|
||||
An <objj>CPSlider</objj> displays, and allows control of, some value in the application. It represents a continuous stream of values of type <code>float</code>, which can be retrieved by the method <code>floatValue</code> and set by the method <code>setFloatValue:</code>.
|
||||
/*! SLIDER STATES */
|
||||
|
||||
CPLinearSlider = 0;
|
||||
CPCircularSlider = 1;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
|
||||
@implementation CPSlider : CPControl
|
||||
{
|
||||
double _value;
|
||||
double _minValue;
|
||||
double _maxValue;
|
||||
double _altIncrementValue;
|
||||
|
||||
CPView _bar;
|
||||
CPView _knob;
|
||||
double _minValue;
|
||||
double _maxValue;
|
||||
double _altIncrementValue;
|
||||
|
||||
BOOL _isVertical;
|
||||
CPImageView _standardKnob;
|
||||
CPView _standardVerticalBar;
|
||||
CPView _standardHorizontalBar;
|
||||
BOOL _isVertical;
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
+ (void)initialize
|
||||
+ (CPString)themeClass
|
||||
{
|
||||
if (self != [CPSlider class])
|
||||
return;
|
||||
return "slider";
|
||||
}
|
||||
|
||||
var bundle = [CPBundle bundleForClass:self];
|
||||
|
||||
CPSliderKnobImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSlider/CPSliderKnobRegular.png"] size:CPSizeMake(11.0, 11.0)],
|
||||
CPSliderKnobPushedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSlider/CPSliderKnobRegularPushed.png"] size:CPSizeMake(11.0, 11.0)],
|
||||
CPSliderHorizontalBarLeftImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSlider/CPSliderTrackHorizontalLeft.png"] size:CPSizeMake(2.0, 4.0)],
|
||||
CPSliderHorizontalBarRightImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSlider/CPSliderTrackHorizontalRight.png"] size:CPSizeMake(2.0, 4.0)],
|
||||
CPSliderHorizontalBarCenterImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPSlider/CPSliderTrackHorizontalCenter.png"] size:CPSizeMake(1.0, 4.0)];
|
||||
+ (id)themeAttributes
|
||||
{
|
||||
return [CPDictionary dictionaryWithObjects:[nil, _CGSizeMakeZero(), 0.0, nil]
|
||||
forKeys:[@"knob-color", @"knob-size", @"track-width", @"track-color"]];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_value = 50.0;
|
||||
_minValue = 0.0;
|
||||
_maxValue = 100.0;
|
||||
|
||||
_bar = [self bar];
|
||||
_knob = [self knob];
|
||||
_knobSize = [[self knobImage] size];
|
||||
_isVertical = [self isVertical];
|
||||
|
||||
[_knob setFrameOrigin:[self knobPosition]];
|
||||
[self setObjectValue:50.0];
|
||||
|
||||
[self addSubview:_bar];
|
||||
[self addSubview:_knob];
|
||||
[self setContinuous:YES];
|
||||
|
||||
[self _recalculateIsVertical];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setMinValue:(float)aMinimumValue
|
||||
{
|
||||
if (_minValue === aMinimumValue)
|
||||
return;
|
||||
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
if(aSize.height > 21.0)
|
||||
aSize.height = 21.0;
|
||||
_minValue = aMinimumValue;
|
||||
|
||||
var doubleValue = [self doubleValue];
|
||||
|
||||
if (_isVertical != [self isVertical])
|
||||
{
|
||||
_isVertical = [self isVertical];
|
||||
|
||||
var bar = [self bar],
|
||||
knob = [self knob];
|
||||
|
||||
if (_bar != bar)
|
||||
[self replaceSubview:_bar = bar withView:_bar];
|
||||
|
||||
if (_knob != knob)
|
||||
{
|
||||
[self replaceSubview:knob withView:_knob];
|
||||
|
||||
_knob = knob;
|
||||
[_knob setFrameOrigin:[self knobPosition]];
|
||||
}
|
||||
}
|
||||
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
[_knob setFrameOrigin:[self knobPosition]];
|
||||
if (doubleValue < _minValue)
|
||||
[self setDoubleValue:_minValue];
|
||||
|
||||
// The relative position may have (did) change.
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the value by which the slider will be
|
||||
incremented if the user holds down the <code>ALT</code>s key.
|
||||
*/
|
||||
- (double)altIncrementValue
|
||||
{
|
||||
return _altIncrementValue;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the thickness of the slider's knob. This value is in pixels,
|
||||
and is the size of the knob along the slider's track.
|
||||
*/
|
||||
- (float)knobThickness
|
||||
{
|
||||
return CPRectGetWidth([_knob frame]);
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (CPImage)leftTrackImage
|
||||
{
|
||||
return CPSliderHorizontalBarLeftImage;
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (CPImage)rightTrackImage
|
||||
{
|
||||
return CPSliderHorizontalBarRightImage;
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (CPImage)centerTrackImage
|
||||
{
|
||||
return CPSliderHorizontalBarCenterImage
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (CPImage)knobImage
|
||||
{
|
||||
return CPSliderKnobImage;
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (CPImage)pushedKnobImage
|
||||
{
|
||||
return CPSliderKnobPushedImage;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the slider's knob.
|
||||
*/
|
||||
- (CPView)knob
|
||||
{
|
||||
if (!_standardKnob)
|
||||
{
|
||||
var knobImage = [self knobImage],
|
||||
knobSize = [knobImage size];
|
||||
|
||||
_standardKnob = [[CPImageView alloc] initWithFrame:CPRectMake(0.0, 0.0, knobSize.width, knobSize.height)];
|
||||
|
||||
[_standardKnob setImage:knobImage];
|
||||
}
|
||||
|
||||
return _standardKnob;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the slider's bar.
|
||||
*/
|
||||
- (CPView)bar
|
||||
{
|
||||
// FIXME: veritcal.
|
||||
if ([self isVertical])
|
||||
return nil;
|
||||
else
|
||||
{
|
||||
if (!_standardHorizontalBar)
|
||||
{
|
||||
var frame = [self frame],
|
||||
barFrame = CPRectMake(0.0, 0.0, CPRectGetWidth(frame), 4.0);
|
||||
|
||||
_standardHorizontalBar = [[CPView alloc] initWithFrame:barFrame];
|
||||
|
||||
[_standardHorizontalBar setBackgroundColor:[CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
|
||||
[[self leftTrackImage], [self centerTrackImage], [self rightTrackImage]] isVertical:NO]]];
|
||||
|
||||
[_standardHorizontalBar setFrame:CPRectMake(0.0, (CPRectGetHeight(frame) - CPRectGetHeight(barFrame)) / 2.0, CPRectGetWidth(_isVertical ? barFrame : frame), CPRectGetHeight(_isVertical ? frame : barFrame))];
|
||||
[_standardHorizontalBar setAutoresizingMask:_isVertical ? CPViewHeightSizable : CPViewWidthSizable];
|
||||
}
|
||||
|
||||
return _standardHorizontalBar;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the value the slider will be incremented if the user holds the <code>ALT</code> key.
|
||||
*/
|
||||
- (void)setAltIncrementValue:(double)anIncrementValue
|
||||
{
|
||||
_altIncrementValue = anIncrementValue;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the slider is vertical.
|
||||
*/
|
||||
- (BOOL)isVertical
|
||||
{
|
||||
var frame = [self frame];
|
||||
|
||||
if (CPRectGetWidth(frame) == CPRectGetHeight(frame))
|
||||
return -1;
|
||||
|
||||
return CPRectGetWidth(frame) < CPRectGetHeight(frame);
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the slider's maximum value
|
||||
*/
|
||||
- (double)maxValue
|
||||
{
|
||||
return _maxValue;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the slider's minimum value
|
||||
*/
|
||||
- (double)minValue
|
||||
- (float)minValue
|
||||
{
|
||||
return _minValue;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the slider's maximum value
|
||||
@param aMaxValue the new maximum value
|
||||
*/
|
||||
- (void)setMaxValue:(double)aMaxValue
|
||||
- (void)setMaxValue:(float)aMaximumValue
|
||||
{
|
||||
_maxValue = aMaxValue;
|
||||
if (_maxValue === aMaximumValue)
|
||||
return;
|
||||
|
||||
_maxValue = aMaximumValue;
|
||||
|
||||
var doubleValue = [self doubleValue];
|
||||
|
||||
if (doubleValue > _maxValue)
|
||||
[self setDoubleValue:_maxValue];
|
||||
|
||||
// The relative position may have (did) change.
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the slider's minimum value
|
||||
@param aMinValue the new minimum value
|
||||
*/
|
||||
- (void)setMinValue:(double)aMinValue
|
||||
- (float)maxValue
|
||||
{
|
||||
_minValue = aMinValue;
|
||||
return _maxValue;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the slider's value
|
||||
@param aValue the new slider value
|
||||
*/
|
||||
- (void)setValue:(double)aValue
|
||||
- (void)setObjectValue:(id)aValue
|
||||
{
|
||||
_value = aValue;
|
||||
[super setObjectValue:MIN(MAX(aValue, _minValue), _maxValue)];
|
||||
|
||||
[_knob setFrameOrigin:[self knobPosition]];
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the slider's value
|
||||
*/
|
||||
- (double)value
|
||||
- (void)setSliderType:(CPSliderType)aSliderType
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the knob's position
|
||||
@ignore
|
||||
*/
|
||||
- (CGPoint)knobPosition
|
||||
{
|
||||
if ([self isVertical])
|
||||
return CPPointMake(0.0, 0.0);
|
||||
if (aSliderType === CPCircularSlider)
|
||||
[self setThemeState:CPThemeStateCircular];
|
||||
else
|
||||
return CPPointMake(
|
||||
((_value - _minValue) / (_maxValue - _minValue)) * (CPRectGetWidth([self frame]) - CPRectGetWidth([_knob frame])),
|
||||
(CPRectGetHeight([self frame]) - CPRectGetHeight([_knob frame])) / 2.0);
|
||||
[self unsetThemeState:CPThemeStateCircular];
|
||||
}
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
- (float)valueForKnobPosition:(CGPoint)aPoint
|
||||
- (CPSliderType)sliderType
|
||||
{
|
||||
if ([self isVertical])
|
||||
return 0.0;
|
||||
return [self hasThemeState:CPThemeStateCircular] ? CPCircularSlider : CPLinearSlider;
|
||||
}
|
||||
|
||||
- (CGRect)trackRectForBounds:(CGRect)bounds
|
||||
{
|
||||
if ([self hasThemeState:CPThemeStateCircular])
|
||||
{
|
||||
var originalBounds = CGRectCreateCopy(bounds);
|
||||
|
||||
bounds.size.width = MIN(bounds.size.width, bounds.size.height);
|
||||
bounds.size.height = bounds.size.width;
|
||||
|
||||
if (bounds.size.width < originalBounds.size.width)
|
||||
bounds.origin.x += (originalBounds.size.width - bounds.size.width) / 2.0;
|
||||
else
|
||||
bounds.origin.y += (originalBounds.size.height - bounds.size.height) / 2.0;
|
||||
}
|
||||
else
|
||||
return MAX(MIN((aPoint.x) * (_maxValue - _minValue) / ( CPRectGetWidth([self frame]) - CPRectGetWidth([_knob frame]) ) + _minValue, _maxValue), _minValue);
|
||||
{
|
||||
var trackWidth = [self currentValueForThemeAttribute:@"track-width"];
|
||||
|
||||
if (trackWidth <= 0)
|
||||
return _CGRectMakeZero();
|
||||
|
||||
if ([self isVertical])
|
||||
{
|
||||
bounds.origin.x = (_CGRectGetWidth(bounds) - trackWidth) / 2.0;
|
||||
bounds.size.width = trackWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
bounds.origin.y = (_CGRectGetHeight(bounds) - trackWidth) / 2.0;
|
||||
bounds.size.height = trackWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
- (CGPoint)constrainKnobPosition:(CGPoint)aPoint
|
||||
- (CGRect)knobRectForBounds:(CGRect)bounds
|
||||
{
|
||||
//FIXME
|
||||
aPoint.x -= _knobSize.width / 2.0;
|
||||
return CPPointMake(MAX(MIN(CPRectGetWidth([self bounds]) - _knobSize.width, aPoint.x), 0.0), (CPRectGetHeight([self bounds]) - CPRectGetHeight([_knob frame])) / 2.0);
|
||||
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,
|
||||
radius = CGRectGetWidth(trackRect) / 2.0 - 8.0;
|
||||
|
||||
knobRect.origin.x = radius * COS(angle) + CGRectGetMidX(trackRect) - 3.0;
|
||||
knobRect.origin.y = radius * SIN(angle) + CGRectGetMidY(trackRect) - 2.0;
|
||||
}
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
knobRect.origin.x = (([self doubleValue] - _minValue) / (_maxValue - _minValue)) * (_CGRectGetWidth(trackRect) - knobSize.width);
|
||||
knobRect.origin.y = _CGRectGetMidY(trackRect) - knobSize.height / 2.0;
|
||||
}
|
||||
|
||||
return knobRect;
|
||||
}
|
||||
|
||||
- (void)mouseUp:(CPEvent)anEvent
|
||||
- (CGRect)rectForEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
[[self knob] setImage: [self knobImage]];
|
||||
if (aName === "track-view")
|
||||
return [self trackRectForBounds:[self bounds]];
|
||||
|
||||
else if (aName === "knob-view")
|
||||
return [self knobRectForBounds:[self bounds]];
|
||||
|
||||
return [super rectForEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if (aName === "track-view" || aName === "knob-view")
|
||||
{
|
||||
var view = [[CPView alloc] init];
|
||||
|
||||
[view setHitTests:NO];
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
return [super createEphemeralSubviewNamed:aName];
|
||||
}
|
||||
|
||||
- (void)setAltIncrementValue:(float)anAltIncrementValue
|
||||
{
|
||||
_altIncrementValue = anAltIncrementValue;
|
||||
}
|
||||
|
||||
- (float)altIncrementValue
|
||||
{
|
||||
return _altIncrementValue;
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(CGSize)aSize
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
[self _recalculateIsVertical];
|
||||
}
|
||||
|
||||
- (void)_recalculateIsVertical
|
||||
{
|
||||
// Recalculate isVertical.
|
||||
var bounds = [self bounds],
|
||||
width = _CGRectGetWidth(bounds),
|
||||
height = _CGRectGetHeight(bounds);
|
||||
|
||||
_isVertical = width < height ? 1 : (width > height ? 0 : -1);
|
||||
|
||||
if (_isVertical === 1)
|
||||
[self setThemeState:CPThemeStateVertical];
|
||||
else if (_isVertical === 0)
|
||||
[self unsetThemeState:CPThemeStateVertical];
|
||||
}
|
||||
|
||||
- (int)isVertical
|
||||
{
|
||||
return _isVertical;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var trackView = [self layoutEphemeralSubviewNamed:@"track-view"
|
||||
positioned:CPWindowBelow
|
||||
relativeToEphemeralSubviewNamed:@"knob-view"];
|
||||
|
||||
if (trackView)
|
||||
[trackView setBackgroundColor:[self currentValueForThemeAttribute:@"track-color"]];
|
||||
|
||||
var knobView = [self layoutEphemeralSubviewNamed:@"knob-view"
|
||||
positioned:CPWindowAbove
|
||||
relativeToEphemeralSubviewNamed:@"track-view"];
|
||||
|
||||
if (knobView)
|
||||
[knobView setBackgroundColor:[self currentValueForThemeAttribute:"knob-color"]];
|
||||
}
|
||||
|
||||
- (BOOL)tracksMouseOutsideOfFrame
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (float)_valueAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
knobRect = [self knobRectForBounds:bounds],
|
||||
trackRect = [self trackRectForBounds:bounds];
|
||||
|
||||
if ([self hasThemeState:CPThemeStateCircular])
|
||||
{
|
||||
var knobWidth = _CGRectGetWidth(knobRect);
|
||||
|
||||
trackRect.origin.x += knobWidth / 2;
|
||||
trackRect.size.width -= knobWidth;
|
||||
|
||||
var minValue = [self minValue],
|
||||
dx = aPoint.x - _CGRectGetMidX(trackRect),
|
||||
dy = aPoint.y - _CGRectGetMidY(trackRect);
|
||||
|
||||
return MAX(0.0, MIN(1.0, 1.0 - (3 * PI_2 - ATAN2(dy, dx)) % PI2 / PI2)) * ([self maxValue] - minValue) + minValue;
|
||||
}
|
||||
else if ([self isVertical])
|
||||
{
|
||||
var knobHeight = _CGRectGetHeight(knobRect);
|
||||
|
||||
trackRect.origin.y += knobHeight / 2;
|
||||
trackRect.size.height -= knobHeight;
|
||||
|
||||
var minValue = [self minValue];
|
||||
|
||||
return MAX(0.0, MIN(1.0, (aPoint.y - _CGRectGetMinY(trackRect)) / _CGRectGetHeight(trackRect))) * ([self maxValue] - minValue) + minValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
var knobWidth = _CGRectGetWidth(knobRect);
|
||||
|
||||
trackRect.origin.x += knobWidth / 2;
|
||||
trackRect.size.width -= knobWidth;
|
||||
|
||||
var minValue = [self minValue];
|
||||
|
||||
return MAX(0.0, MIN(1.0, (aPoint.x - _CGRectGetMinX(trackRect)) / _CGRectGetWidth(trackRect))) * ([self maxValue] - minValue) + minValue;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)startTrackingAt:(CGPoint)aPoint
|
||||
{
|
||||
var bounds = [self bounds],
|
||||
knobRect = [self knobRectForBounds:_CGRectMakeCopy(bounds)];
|
||||
|
||||
if (_CGRectContainsPoint(knobRect, aPoint))
|
||||
_dragOffset = _CGSizeMake(_CGRectGetMidX(knobRect) - aPoint.x, _CGRectGetMidY(knobRect) - aPoint.y);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
- (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];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
/*!
|
||||
@ignore
|
||||
shoudl we have _continuous?
|
||||
*/
|
||||
- (void)setContinuous:(BOOL)flag
|
||||
{
|
||||
[_knob setFrameOrigin:[self constrainKnobPosition:[self convertPoint:[anEvent locationInWindow] fromView:nil]]];
|
||||
|
||||
_value = [self valueForKnobPosition:[_knob frame].origin];
|
||||
|
||||
[[self knob] setImage: [self pushedKnobImage]];
|
||||
[self sendAction:_action to:_target];
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
[_knob setFrameOrigin:[self constrainKnobPosition:[self convertPoint:[anEvent locationInWindow] fromView:nil]]];
|
||||
|
||||
_value = [self valueForKnobPosition:[_knob frame].origin];
|
||||
|
||||
[self sendAction:_action to:_target];
|
||||
if (flag)
|
||||
_sendActionOn |= CPLeftMouseDraggedMask;
|
||||
else
|
||||
_sendActionOn &= ~CPLeftMouseDraggedMask;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPSliderMinValueKey = @"CPSliderMinValueKey",
|
||||
CPSliderMaxValueKey = @"CPSliderMaxValueKey",
|
||||
CPSliderValueKey = @"CPSliderValueKey";
|
||||
var CPSliderMinValueKey = "CPSliderMinValueKey",
|
||||
CPSliderMaxValueKey = "CPSliderMaxValueKey",
|
||||
CPSliderAltIncrValueKey = "CPSliderAltIncrValueKey";
|
||||
|
||||
@implementation CPSlider (CPCoding)
|
||||
|
||||
/*
|
||||
Initializes the slider from the data in a coder.
|
||||
@param aCoder the coder from which to read the data
|
||||
@return the initialized slider
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
_minValue = [aCoder decodeDoubleForKey:CPSliderMinValueKey];
|
||||
_maxValue = [aCoder decodeDoubleForKey:CPSliderMaxValueKey];
|
||||
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_value = [aCoder decodeDoubleForKey:CPSliderValueKey];
|
||||
_minValue = [aCoder decodeDoubleForKey:CPSliderMinValueKey];
|
||||
_maxValue = [aCoder decodeDoubleForKey:CPSliderMaxValueKey];
|
||||
|
||||
_bar = [self bar];
|
||||
_knob = [self knob];
|
||||
_knobSize = [[self knobImage] size];
|
||||
_isVertical = [self isVertical];
|
||||
|
||||
[_knob setFrameOrigin:[self knobPosition]];
|
||||
|
||||
[self addSubview:_bar];
|
||||
[self addSubview:_knob];
|
||||
_altIncrementValue = [aCoder decodeDoubleForKey:CPSliderAltIncrValueKey];
|
||||
|
||||
[self setContinuous:YES];
|
||||
|
||||
[self _recalculateIsVertical];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
Writes out the slider's instance information to a coder.
|
||||
@param aCoder the coder to which to write the data
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
var subviews = _subviews;
|
||||
|
||||
_subviews = [];
|
||||
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
_subviews = subviews;
|
||||
|
||||
[aCoder encodeDouble:_value forKey:CPSliderValueKey];
|
||||
|
||||
[aCoder encodeDouble:_minValue forKey:CPSliderMinValueKey];
|
||||
[aCoder encodeDouble:_maxValue forKey:CPSliderMaxValueKey];
|
||||
[aCoder encodeDouble:_altIncrementValue forKey:CPSliderAltIncrValueKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPSlider (Deprecated)
|
||||
|
||||
- (id)value
|
||||
{
|
||||
CPLog.warn("[CPSlider value] is deprecated, use doubleValue or objectValue instead.");
|
||||
|
||||
return [self doubleValue];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue
|
||||
{
|
||||
CPLog.warn("[CPSlider setValue:] is deprecated, use setDoubleValue: or setObjectValue: instead.");
|
||||
|
||||
[self setObjectValue:aValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -20,12 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/Foundation.j>
|
||||
|
||||
import <AppKit/CPView.j>
|
||||
import <AppKit/CPSlider.j>
|
||||
import <AppKit/CPColorPicker.j>
|
||||
import <AppKit/CPColorPanel.j>
|
||||
@import "CPColorPicker.j"
|
||||
|
||||
/*
|
||||
@ignore
|
||||
@@ -68,15 +63,17 @@ import <AppKit/CPColorPanel.j>
|
||||
-(id)initView
|
||||
{
|
||||
aFrame = CPRectMake(0, 0, CPColorPickerViewWidth, CPColorPickerViewHeight);
|
||||
|
||||
_contentView = [[CPView alloc] initWithFrame:aFrame];
|
||||
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||
|
||||
_rgbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 10, 100, 20)];
|
||||
[_rgbLabel setStringValue: "RGB"];
|
||||
[_rgbLabel setTextColor:[CPColor whiteColor]];
|
||||
[_rgbLabel setStringValue: "Red, Green, Blue"];
|
||||
[_rgbLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_redLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 35, 15, 20)];
|
||||
[_redLabel setStringValue: "R"];
|
||||
[_redLabel setTextColor:[CPColor whiteColor]];
|
||||
[_redLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_redSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 35, aFrame.size.width - 70, 20)];
|
||||
[_redSlider setMaxValue: 1.0];
|
||||
@@ -92,28 +89,28 @@ import <AppKit/CPColorPanel.j>
|
||||
|
||||
switch(this)
|
||||
{
|
||||
case _redValue: [_redSlider setValue: MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||
[self sliderChanged: _redSlider];
|
||||
case _redValue: [_redSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||
//[self sliderChanged: _redSlider];
|
||||
break;
|
||||
|
||||
case _greenValue: [_greenSlider setValue: MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||
[self sliderChanged: _greenSlider];
|
||||
case _greenValue: [_greenSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||
//[self sliderChanged: _greenSlider];
|
||||
break;
|
||||
|
||||
case _blueValue: [_blueSlider setValue: MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||
[self sliderChanged: _blueSlider];
|
||||
case _blueValue: [_blueSlider setFloatValue:MAX(MIN(ROUND(this.value), 255) / 255.0, 0)];
|
||||
//[self sliderChanged: _blueSlider];
|
||||
break;
|
||||
|
||||
case _hueValue: [_hueSlider setValue: MAX(MIN(ROUND(this.value), 360), 0)];
|
||||
[self sliderChanged: _hueSlider];
|
||||
case _hueValue: [_hueSlider setFloatValue:MAX(MIN(ROUND(this.value), 360), 0)];
|
||||
//[self sliderChanged: _hueSlider];
|
||||
break;
|
||||
|
||||
case _saturationValue: [_saturationSlider setValue: MAX(MIN(ROUND(this.value), 100), 0)];
|
||||
[self sliderChanged: _saturationSlider];
|
||||
case _saturationValue: [_saturationSlider setFloatValue:MAX(MIN(ROUND(this.value), 100), 0)];
|
||||
//[self sliderChanged: _saturationSlider];
|
||||
break;
|
||||
|
||||
case _brightnessValue: [_brightnessSlider setValue: MAX(MIN(ROUND(this.value), 100), 0)];
|
||||
[self sliderChanged: _brightnessSlider];
|
||||
case _brightnessValue: [_brightnessSlider setFloatValue:MAX(MIN(ROUND(this.value), 100), 0)];
|
||||
//[self sliderChanged: _brightnessSlider];
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -141,8 +138,8 @@ import <AppKit/CPColorPanel.j>
|
||||
_redValue = document.createElement("input");
|
||||
_redValue.style.width = "40px";
|
||||
_redValue.style.backgroundColor = "transparent";
|
||||
_redValue.style.border = "1px solid white";
|
||||
_redValue.style.color = "white";
|
||||
_redValue.style.border = "1px solid black";
|
||||
_redValue.style.color = "black";
|
||||
_redValue.style.position = "absolute";
|
||||
_redValue.style.top = "0px";
|
||||
_redValue.style.left = "0px";
|
||||
@@ -153,7 +150,7 @@ import <AppKit/CPColorPanel.j>
|
||||
|
||||
_greenLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 58, 15, 20)];
|
||||
[_greenLabel setStringValue: "G"];
|
||||
[_greenLabel setTextColor:[CPColor whiteColor]];
|
||||
[_greenLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_greenSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 58, aFrame.size.width - 70, 20)];
|
||||
[_greenSlider setMaxValue: 1.0];
|
||||
@@ -174,7 +171,7 @@ import <AppKit/CPColorPanel.j>
|
||||
|
||||
_blueLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 81, 15, 20)];
|
||||
[_blueLabel setStringValue: "B"];
|
||||
[_blueLabel setTextColor:[CPColor whiteColor]];
|
||||
[_blueLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_blueSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 81, aFrame.size.width - 70, 20)];
|
||||
[_blueSlider setMaxValue: 1.0];
|
||||
@@ -193,13 +190,13 @@ import <AppKit/CPColorPanel.j>
|
||||
blueValue._DOMElement.appendChild(_blueValue);
|
||||
[_contentView addSubview: blueValue];
|
||||
|
||||
_hsbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 120, 100, 20)];
|
||||
[_hsbLabel setStringValue: "HSB"];
|
||||
[_hsbLabel setTextColor:[CPColor whiteColor]];
|
||||
_hsbLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 120, 190, 20)];
|
||||
[_hsbLabel setStringValue: "Hue, Saturation, Brightness"];
|
||||
[_hsbLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_hueLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 145, 15, 20)];
|
||||
[_hueLabel setStringValue: "H"];
|
||||
[_hueLabel setTextColor:[CPColor whiteColor]];
|
||||
[_hueLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_hueSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 145, aFrame.size.width - 70, 20)];
|
||||
[_hueSlider setMaxValue: 359.0];
|
||||
@@ -220,7 +217,7 @@ import <AppKit/CPColorPanel.j>
|
||||
|
||||
_saturationLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 168, 15, 20)];
|
||||
[_saturationLabel setStringValue: "S"];
|
||||
[_saturationLabel setTextColor:[CPColor whiteColor]];
|
||||
[_saturationLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_saturationSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 168, aFrame.size.width - 70, 20)];
|
||||
[_saturationSlider setMaxValue: 100.0];
|
||||
@@ -241,7 +238,7 @@ import <AppKit/CPColorPanel.j>
|
||||
|
||||
_brightnessLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 191, 15, 20)];
|
||||
[_brightnessLabel setStringValue: "B"];
|
||||
[_brightnessLabel setTextColor:[CPColor whiteColor]];
|
||||
[_brightnessLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
_brightnessSlider = [[CPSlider alloc] initWithFrame: CPRectMake(15, 191, aFrame.size.width - 70, 20)];
|
||||
[_brightnessSlider setMaxValue: 100.0];
|
||||
@@ -260,15 +257,15 @@ import <AppKit/CPColorPanel.j>
|
||||
brightnessValue._DOMElement.appendChild(_brightnessValue);
|
||||
[_contentView addSubview: brightnessValue];
|
||||
|
||||
_hexLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 230, 100, 20)];
|
||||
_hexLabel = [[CPTextField alloc] initWithFrame: CPRectMake(0, 230, 30, 20)];
|
||||
[_hexLabel setStringValue: "Hex"];
|
||||
[_hexLabel setTextColor:[CPColor whiteColor]];
|
||||
[_hexLabel setTextColor:[CPColor blackColor]];
|
||||
|
||||
//hex input box
|
||||
_hexValue = _redValue.cloneNode(false);
|
||||
_hexValue.style.top = "255px";
|
||||
_hexValue.style.top = "228px";
|
||||
_hexValue.style.width = "80px";
|
||||
_hexValue.style.left = "0px";
|
||||
_hexValue.style.left = "35px";
|
||||
_hexValue.onkeypress = function(aDOMEvent)
|
||||
{
|
||||
aDOMEvent = aDOMEvent || window.event;
|
||||
@@ -332,25 +329,28 @@ import <AppKit/CPColorPanel.j>
|
||||
|
||||
-(void)sliderChanged:(id)sender
|
||||
{
|
||||
var newColor;
|
||||
var newColor,
|
||||
colorPanel = [self colorPanel],
|
||||
alpha = [colorPanel opacity];
|
||||
|
||||
switch(sender)
|
||||
{
|
||||
case _hueSlider:
|
||||
case _saturationSlider:
|
||||
case _brightnessSlider: newColor = [CPColor colorWithHue: [_hueSlider value]
|
||||
saturation: [_saturationSlider value]
|
||||
brightness: [_brightnessSlider value]];
|
||||
case _brightnessSlider: newColor = [CPColor colorWithHue: [_hueSlider floatValue]
|
||||
saturation: [_saturationSlider floatValue]
|
||||
brightness: [_brightnessSlider floatValue]
|
||||
alpha: alpha];
|
||||
|
||||
[self updateRGBSliders: newColor];
|
||||
break;
|
||||
|
||||
case _redSlider:
|
||||
case _greenSlider:
|
||||
case _blueSlider: newColor = [CPColor colorWithCalibratedRed: [_redSlider value]
|
||||
green: [_greenSlider value]
|
||||
blue: [_blueSlider value]
|
||||
alpha: 1.0];
|
||||
case _blueSlider: newColor = [CPColor colorWithCalibratedRed: [_redSlider floatValue]
|
||||
green: [_greenSlider floatValue]
|
||||
blue: [_blueSlider floatValue]
|
||||
alpha: alpha];
|
||||
|
||||
[self updateHSBSliders: newColor];
|
||||
break;
|
||||
@@ -358,7 +358,7 @@ import <AppKit/CPColorPanel.j>
|
||||
|
||||
[self updateLabels];
|
||||
[self updateHex: newColor];
|
||||
[[self colorPanel] setColor: newColor];
|
||||
[colorPanel setColor: newColor];
|
||||
}
|
||||
|
||||
-(void)setColor:(CPColor)aColor
|
||||
@@ -373,9 +373,9 @@ import <AppKit/CPColorPanel.j>
|
||||
{
|
||||
var hsb = [aColor hsbComponents];
|
||||
|
||||
[_hueSlider setValue: hsb[0]];
|
||||
[_saturationSlider setValue: hsb[1]];
|
||||
[_brightnessSlider setValue: hsb[2]];
|
||||
[_hueSlider setFloatValue:hsb[0]];
|
||||
[_saturationSlider setFloatValue:hsb[1]];
|
||||
[_brightnessSlider setFloatValue:hsb[2]];
|
||||
}
|
||||
|
||||
- (void)updateHex:(CPColor)aColor
|
||||
@@ -387,21 +387,30 @@ import <AppKit/CPColorPanel.j>
|
||||
{
|
||||
var rgb = [aColor components];
|
||||
|
||||
[_redSlider setValue: rgb[0]];
|
||||
[_greenSlider setValue: rgb[1]];
|
||||
[_blueSlider setValue: rgb[2]];
|
||||
[_redSlider setFloatValue:rgb[0]];
|
||||
[_greenSlider setFloatValue:rgb[1]];
|
||||
[_blueSlider setFloatValue:rgb[2]];
|
||||
}
|
||||
|
||||
- (void)updateLabels
|
||||
{
|
||||
_hueValue.value = ROUND([_hueSlider value]);
|
||||
_saturationValue.value = ROUND([_saturationSlider value]);
|
||||
_brightnessValue.value = ROUND([_brightnessSlider value]);
|
||||
_hueValue.value = ROUND([_hueSlider floatValue]);
|
||||
_saturationValue.value = ROUND([_saturationSlider floatValue]);
|
||||
_brightnessValue.value = ROUND([_brightnessSlider floatValue]);
|
||||
|
||||
_redValue.value = ROUND([_redSlider value] * 255);
|
||||
_greenValue.value = ROUND([_greenSlider value] * 255);
|
||||
_blueValue.value = ROUND([_blueSlider value] * 255);
|
||||
_redValue.value = ROUND([_redSlider floatValue] * 255);
|
||||
_greenValue.value = ROUND([_greenSlider floatValue] * 255);
|
||||
_blueValue.value = ROUND([_blueSlider floatValue] * 255);
|
||||
}
|
||||
|
||||
- (CPImage)provideNewButtonImage
|
||||
{
|
||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:CPColorPicker] pathForResource:"slider_button.png"] size:CGSizeMake(32, 32)];
|
||||
}
|
||||
|
||||
- (CPImage)provideNewAlternateButtonImage
|
||||
{
|
||||
return [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:CPColorPicker] pathForResource:"slider_button_h.png"] size:CGSizeMake(32, 32)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+77
-29
@@ -20,8 +20,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPImage.j"
|
||||
import "CPView.j"
|
||||
@import "CPImage.j"
|
||||
@import "CPView.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
#include "Platform/Platform.h"
|
||||
@@ -34,6 +34,10 @@ CPSplitViewWillResizeSubviewsNotification = @"CPSplitViewWillResizeSubviewsNotif
|
||||
var CPSplitViewHorizontalImage = nil,
|
||||
CPSplitViewVerticalImage = nil;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
|
||||
@implementation CPSplitView : CPView
|
||||
{
|
||||
id _delegate;
|
||||
@@ -190,9 +194,11 @@ var CPSplitViewHorizontalImage = nil,
|
||||
{
|
||||
var count = [_subviews count] - 1;
|
||||
|
||||
if (count > 0)
|
||||
while (count--)
|
||||
[self drawDividerInRect:[self rectOfDividerAtIndex:count]];
|
||||
while ((count--) > 0)
|
||||
{
|
||||
_drawingDivider = count;
|
||||
[self drawDividerInRect:[self rectOfDividerAtIndex:count]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawDividerInRect:(CGRect)aRect
|
||||
@@ -204,7 +210,7 @@ var CPSplitViewHorizontalImage = nil,
|
||||
_DOMDividerElements[_drawingDivider].style.cursor = "move";
|
||||
_DOMDividerElements[_drawingDivider].style.position = "absolute";
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundRepeat = "repeat";
|
||||
|
||||
|
||||
CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]);
|
||||
|
||||
if (_isPaneSplitter)
|
||||
@@ -218,7 +224,7 @@ var CPSplitViewHorizontalImage = nil,
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundImage = "url('"+_dividerImagePath+"')";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, _CGRectGetMinX(aRect), _CGRectGetMinY(aRect));
|
||||
CPDOMDisplayServerSetStyleSize(_DOMDividerElements[_drawingDivider], _CGRectGetWidth(aRect), _CGRectGetHeight(aRect));
|
||||
#endif
|
||||
@@ -297,7 +303,7 @@ var CPSplitViewHorizontalImage = nil,
|
||||
- (void)trackDivider:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type];
|
||||
|
||||
|
||||
if (type == CPLeftMouseUp)
|
||||
{
|
||||
if (_currentDivider != CPNotFound)
|
||||
@@ -353,6 +359,9 @@ var CPSplitViewHorizontalImage = nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentDivider === CPNotFound)
|
||||
return;
|
||||
}
|
||||
|
||||
else if (type == CPLeftMouseDragged && _currentDivider != CPNotFound)
|
||||
@@ -367,6 +376,7 @@ var CPSplitViewHorizontalImage = nil,
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
// FIXME: This should not trap events if not on a divider!
|
||||
[self trackDivider:anEvent];
|
||||
}
|
||||
|
||||
@@ -382,10 +392,12 @@ var CPSplitViewHorizontalImage = nil,
|
||||
|
||||
- (float)minPossiblePositionOfDividerAtIndex:(int)dividerIndex
|
||||
{
|
||||
var frame = [_subviews[dividerIndex - 1] frame];
|
||||
|
||||
if (dividerIndex > 0)
|
||||
{
|
||||
var frame = [_subviews[dividerIndex - 1] frame];
|
||||
|
||||
return frame.origin[_originComponent] + frame.size[_sizeComponent] + [self dividerThickness];
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
@@ -412,9 +424,9 @@ var CPSplitViewHorizontalImage = nil,
|
||||
var frame = [self frame],
|
||||
viewA = _subviews[dividerIndex],
|
||||
frameA = [viewA frame],
|
||||
viewB = _subviews[dividerIndex+1],
|
||||
viewB = _subviews[dividerIndex + 1],
|
||||
frameB = [viewB frame];
|
||||
|
||||
|
||||
var realPosition = MAX(MIN(position, actualMax), actualMin);
|
||||
|
||||
if (position < proposedMin + (actualMin - proposedMin) / 2)
|
||||
@@ -451,25 +463,61 @@ var CPSplitViewHorizontalImage = nil,
|
||||
|
||||
[self _postNotificationWillResize];
|
||||
|
||||
var frame = [self frame];
|
||||
|
||||
var index = 0,
|
||||
count = [_subviews count],
|
||||
dividerThickness = [self dividerThickness];
|
||||
|
||||
for (; index < count; ++index)
|
||||
bounds = [self bounds],
|
||||
dividerThickness = [self dividerThickness],
|
||||
totalDividers = count - 1,
|
||||
totalSizableSpace = 0,
|
||||
nonSizableSpace = 0,
|
||||
lastSizableIndex = -1,
|
||||
totalSizablePanes = 0,
|
||||
isVertical = [self isVertical];
|
||||
|
||||
for (index = 0; index < count; ++index)
|
||||
{
|
||||
var view = _subviews[index],
|
||||
newFrame = CGRectCreateCopy(frame);
|
||||
|
||||
if (index + 1 == count)
|
||||
newFrame.size[_sizeComponent] = frame.size[_sizeComponent] - newFrame.origin[_originComponent];
|
||||
else
|
||||
newFrame.size[_sizeComponent] = frame.size[_sizeComponent] * ([view frame].size[_sizeComponent] / oldSize[_sizeComponent]);
|
||||
|
||||
frame.origin[_originComponent] += newFrame.size[_sizeComponent] + dividerThickness;
|
||||
|
||||
[view setFrame:newFrame];
|
||||
isSizable = isVertical ? [view autoresizingMask] & CPViewWidthSizable : [view autoresizingMask] & CPViewHeightSizable;
|
||||
|
||||
if (isSizable)
|
||||
{
|
||||
totalSizableSpace += [view frame].size[_sizeComponent];
|
||||
lastSizableIndex = index;
|
||||
totalSizablePanes++;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalSizablePanes === count)
|
||||
totalSizableSpace = 0;
|
||||
|
||||
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;
|
||||
|
||||
for (index = 0; index < count; ++index)
|
||||
{
|
||||
var view = _subviews[index],
|
||||
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");
|
||||
|
||||
bounds.origin[_originComponent] += viewFrame.size[_sizeComponent] + dividerThickness;
|
||||
|
||||
[view setFrame:viewFrame];
|
||||
}
|
||||
|
||||
[self _postNotificationDidResize];
|
||||
@@ -515,8 +563,8 @@ var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
||||
@implementation CPSplitView (CPCoding)
|
||||
|
||||
/*
|
||||
Initializes the split view by unarchiving data from <code>aCoder</code>.
|
||||
@param aCoder the coder containing the archived <objj>CPSplitView</objj>.
|
||||
Initializes the split view by unarchiving data from \c aCoder.
|
||||
@param aCoder the coder containing the archived CPSplitView.
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPString.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import "CPPlatformString.j"
|
||||
|
||||
var CPStringReferenceElement = nil;
|
||||
|
||||
@implementation CPString (CPStringDrawing)
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the string
|
||||
*/
|
||||
- (CPString)cssString
|
||||
@@ -37,34 +37,12 @@ var CPStringReferenceElement = nil;
|
||||
|
||||
- (CGSize)sizeWithFont:(CPFont)aFont
|
||||
{
|
||||
if (!CPStringReferenceElement)
|
||||
{
|
||||
CPStringReferenceElement = document.createElement("span");
|
||||
|
||||
var style = CPStringReferenceElement.style;
|
||||
|
||||
style.position = "absolute";
|
||||
style.whiteSpace = "pre";
|
||||
style.visibility = "visible";
|
||||
style.padding = "0px";
|
||||
style.margin = "0px";
|
||||
|
||||
style.left = "-100000px";
|
||||
style.top = "-100000px";
|
||||
style.zIndex = "10000";
|
||||
style.background = "red";
|
||||
|
||||
document.getElementsByTagName("body")[0].appendChild(CPStringReferenceElement);
|
||||
}
|
||||
|
||||
CPStringReferenceElement.style.font = [aFont ? aFont : [CPFont systemFontOfSize:12.0] cssString];
|
||||
return [self sizeWithFont:aFont inWidth:NULL];
|
||||
}
|
||||
|
||||
if (CPFeatureIsCompatible(CPJavascriptInnerTextFeature))
|
||||
CPStringReferenceElement.innerText = self;
|
||||
else if (CPFeatureIsCompatible(CPJavascriptTextContentFeature))
|
||||
CPStringReferenceElement.textContent = self;
|
||||
|
||||
return CGSizeMake(CPStringReferenceElement.clientWidth, CPStringReferenceElement.clientHeight);
|
||||
- (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
|
||||
{
|
||||
return [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+95
-38
@@ -20,12 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPImageView.j"
|
||||
import "CPTabViewItem.j"
|
||||
import "CPView.j"
|
||||
@import "CPImageView.j"
|
||||
@import "CPTabViewItem.j"
|
||||
@import "CPView.j"
|
||||
|
||||
#include "CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
/*
|
||||
Places tabs on top with a bezeled border.
|
||||
@global
|
||||
@@ -68,15 +69,18 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
CPTabViewWillSelectTabViewItemSelector = 4,
|
||||
CPTabViewDidChangeNumberOfTabViewItemsSelector = 8;
|
||||
|
||||
/*
|
||||
This class represents a view that has multiple subviews (<objj>CPTabViewItem</objj>) presented as individual tabs.
|
||||
Only one <objj>CPTabViewItem</objj> is shown at a time, and other <objj>CPTabViewItem</objj>s can be made visible
|
||||
(one at a time) by clicking on the <objj>CPTabViewItem</objj>'s tab at the top of the tab view.
|
||||
/*!
|
||||
@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 <objj>CPTabViewItem</objj> is the view that is displayed.
|
||||
THe currently selected CPTabViewItem is the view that is displayed.
|
||||
*/
|
||||
@implementation CPTabView : CPView
|
||||
{
|
||||
{
|
||||
CPView _labelsView;
|
||||
CPView _backgroundView;
|
||||
CPView _separatorView;
|
||||
@@ -219,8 +223,8 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
}
|
||||
|
||||
// Adding and Removing Tabs
|
||||
/*
|
||||
Adds a <objj>CPTabViewItem</objj> to the tab view.
|
||||
/*!
|
||||
Adds a CPTabViewItem to the tab view.
|
||||
@param aTabViewItem the item to add
|
||||
*/
|
||||
- (void)addTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
@@ -228,14 +232,17 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
[self insertTabViewItem:aTabViewItem atIndex:[_tabViewItems count]];
|
||||
}
|
||||
|
||||
/*
|
||||
Inserts a <objj>CPTabViewItem</objj> into the tab view
|
||||
/*!
|
||||
Inserts a CPTabViewItem into the tab view
|
||||
at the specified index.
|
||||
@param aTabViewItem the item to insert
|
||||
@param anIndex the index for the item
|
||||
*/
|
||||
- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(unsigned)anIndex
|
||||
{
|
||||
if (!_labelsView && _tabViewType == CPTopTabsBezelBorder)
|
||||
[self _createBezelBorder];
|
||||
|
||||
[_tabViewItems insertObject:aTabViewItem atIndex:anIndex];
|
||||
|
||||
[_labelsView tabView:self didAddTabViewItem:aTabViewItem];
|
||||
@@ -247,22 +254,24 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Removes the specified tab view item from the tab view.
|
||||
@param aTabViewItem the item to remove
|
||||
*/
|
||||
- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
var index = [self indexOfTabViewItem:aTabViewItem];
|
||||
|
||||
[_tabViewItems removeObjectIdenticalTo:aTabViewItem];
|
||||
|
||||
[_labelsView tabView:self didRemoveTabViewItem:aTabViewItem];
|
||||
[_labelsView tabView:self didRemoveTabViewItemAtIndex:index];
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||
}
|
||||
|
||||
// Accessing Tabs
|
||||
/*
|
||||
/*!
|
||||
Returns the index of the specified item
|
||||
@param aTabViewItem the item to find the index for
|
||||
*/
|
||||
@@ -271,8 +280,8 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
return [_tabViewItems indexOfObjectIdenticalTo:aTabViewItem];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the index of the <objj>CPTabViewItem</objj> with the specified identifier.
|
||||
/*!
|
||||
Returns the index of the CPTabViewItem with the specified identifier.
|
||||
@param anIdentifier the identifier of the item
|
||||
*/
|
||||
- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
|
||||
@@ -281,13 +290,13 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
count = [_tabViewItems count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
if ([[_tabViewItems[index] identifier] isEqualTo:anIdentifier])
|
||||
if ([[_tabViewItems[index] identifier] isEqual:anIdentifier])
|
||||
return index;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the number of items in the tab view.
|
||||
*/
|
||||
- (unsigned)numberOfTabViewItems
|
||||
@@ -295,15 +304,15 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
return [_tabViewItems count];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the <objj>CPTabViewItem</objj> at the specified index.
|
||||
/*!
|
||||
Returns the CPTabViewItem at the specified index.
|
||||
*/
|
||||
- (CPTabViewItem)tabViewItemAtIndex:(unsigned)anIndex
|
||||
{
|
||||
return _tabViewItems[anIndex];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the array of items that backs this tab view.
|
||||
*/
|
||||
- (CPArray)tabViewItems
|
||||
@@ -312,7 +321,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
}
|
||||
|
||||
// Selecting a Tab
|
||||
/*
|
||||
/*!
|
||||
Sets the first tab view item in the array to be displayed to the user.
|
||||
@param aSender the object making this request
|
||||
*/
|
||||
@@ -324,7 +333,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
[self selectTabViewItemAtIndex:0];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the last tab view item in the array to be displayed to the user.
|
||||
@param aSender the object making this request
|
||||
*/
|
||||
@@ -336,7 +345,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
[self selectTabViewItemAtIndex:count - 1];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the next tab item in the array to be displayed.
|
||||
@param aSender the object making this request
|
||||
*/
|
||||
@@ -351,7 +360,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
[self selectTabViewItemAtIndex:index + 1 % count];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Selects the previous item in the array for display.
|
||||
@param aSender the object making this request
|
||||
*/
|
||||
@@ -366,7 +375,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
[self selectTabViewItemAtIndex:index == 0 ? count : index - 1];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Displays the specified item in the tab view.
|
||||
@param aTabViewItem the item to display
|
||||
*/
|
||||
@@ -409,7 +418,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Selects the item at the specified index.
|
||||
@param anIndex the index of the item to display.
|
||||
*/
|
||||
@@ -418,7 +427,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
[self selectTabViewItem:_tabViewItems[anIndex]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the current item being displayed.
|
||||
*/
|
||||
- (CPTabViewItem)selectedTabViewItem
|
||||
@@ -427,7 +436,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
}
|
||||
|
||||
//
|
||||
/*
|
||||
/*!
|
||||
Sets the tab view type.
|
||||
@param aTabViewType the view type
|
||||
*/
|
||||
@@ -441,7 +450,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
if (_tabViewType == CPNoTabsBezelBorder || _tabViewType == CPNoTabsLineBorder || _tabViewType == CPNoTabsNoBorder)
|
||||
[_labelsView removeFromSuperview];
|
||||
else if (![_labelsView superview])
|
||||
[self addSubview:_lablesView];
|
||||
[self addSubview:_labelsView];
|
||||
|
||||
if (_tabViewType == CPNoTabsLineBorder || _tabViewType == CPNoTabsNoBorder)
|
||||
[_backgroundView removeFromSuperview];
|
||||
@@ -451,7 +460,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the tab view type.
|
||||
*/
|
||||
- (CPTabViewType)tabViewType
|
||||
@@ -460,7 +469,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
}
|
||||
|
||||
// Determining the Size
|
||||
/*
|
||||
/*!
|
||||
Returns the content rectangle.
|
||||
*/
|
||||
- (CGRect)contentRect
|
||||
@@ -483,7 +492,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
return contentRect;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the delegate for this tab view.
|
||||
@param aDelegate the tab view's delegate
|
||||
*/
|
||||
@@ -522,6 +531,55 @@ var CPTabViewDidSelectTabViewItemSelector = 1,
|
||||
|
||||
@end
|
||||
|
||||
var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
CPTabViewSelectedItemKey = "CPTabViewSelectedItemKey",
|
||||
CPTabViewTypeKey = "CPTabViewTypeKey",
|
||||
CPTabViewDelegateKey = "CPTabViewDelegateKey";
|
||||
|
||||
@implementation CPTabView (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
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];
|
||||
|
||||
var selected = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
||||
if (selected)
|
||||
[self selectTabViewItem:selected];
|
||||
|
||||
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (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 encodeConditionalObject:_delegate forKey:CPTabViewDelegateKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var _CPTabLabelsViewBackgroundColor = nil,
|
||||
_CPTabLabelsViewInsideMargin = 10.0,
|
||||
_CPTabLabelsViewOutsideMargin = 15.0;
|
||||
@@ -592,10 +650,9 @@ var _CPTabLabelsViewBackgroundColor = nil,
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
- (void)tabView:(CPTabView)aTabView didRemoveTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
- (void)tabView:(CPTabView)aTabView didRemoveTabViewItemAtIndex:(unsigned)index
|
||||
{
|
||||
var index = [aTabView indexOfTabViewItem:aTabViewItem],
|
||||
label = _tabLabels[index];
|
||||
var label = _tabLabels[index];
|
||||
|
||||
[_tabLabels removeObjectAtIndex:index];
|
||||
|
||||
|
||||
+63
-19
@@ -20,9 +20,10 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
import <AppKit/CPView.j>
|
||||
|
||||
/*
|
||||
The tab is currently selected.
|
||||
@@ -43,9 +44,12 @@ CPBackgroundTab = 1;
|
||||
*/
|
||||
CPPressedTab = 2;
|
||||
|
||||
/*
|
||||
The class representation of an item in a <objj>CPTabView</objj>. One tab view item
|
||||
can be shown at a time in a <objj>CPTabView</objj>.
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPTabViewItem
|
||||
|
||||
The class representation of an item in a CPTabView. One tab view item
|
||||
can be shown at a time in a CPTabView.
|
||||
*/
|
||||
@implementation CPTabViewItem : CPObject
|
||||
{
|
||||
@@ -56,9 +60,14 @@ CPPressedTab = 2;
|
||||
CPView _auxiliaryView;
|
||||
}
|
||||
|
||||
/*
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithIdentifier:@""];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the tab view item with the specified identifier.
|
||||
@return the initialized <objj>CPTabViewItem</objj>
|
||||
@return the initialized CPTabViewItem
|
||||
*/
|
||||
- (id)initWithIdentifier:(id)anIdentifier
|
||||
{
|
||||
@@ -71,8 +80,8 @@ CPPressedTab = 2;
|
||||
}
|
||||
|
||||
// Working With Labels
|
||||
/*
|
||||
Sets the <objj>CPTabViewItem</objj>'s label.
|
||||
/*!
|
||||
Sets the CPTabViewItem's label.
|
||||
@param aLabel the label for the item
|
||||
*/
|
||||
- (void)setLabel:(CPString)aLabel
|
||||
@@ -80,8 +89,8 @@ CPPressedTab = 2;
|
||||
_label = aLabel;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the <objj>CPTabViewItem</objj>'s label
|
||||
/*!
|
||||
Returns the CPTabViewItem's label
|
||||
*/
|
||||
- (CPString)label
|
||||
{
|
||||
@@ -89,7 +98,7 @@ CPPressedTab = 2;
|
||||
}
|
||||
|
||||
// Checking the Tab Display State
|
||||
/*
|
||||
/*!
|
||||
Returns the tab's current state.
|
||||
*/
|
||||
- (CPTabState)tabState
|
||||
@@ -98,7 +107,7 @@ CPPressedTab = 2;
|
||||
}
|
||||
|
||||
// Assigning an Identifier Object
|
||||
/*
|
||||
/*!
|
||||
Sets the item's identifier.
|
||||
@param anIdentifier the new identifier for the item
|
||||
*/
|
||||
@@ -107,7 +116,7 @@ CPPressedTab = 2;
|
||||
_identifier = anIdentifier;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the tab's identifier.
|
||||
*/
|
||||
- (id)identifier
|
||||
@@ -116,7 +125,7 @@ CPPressedTab = 2;
|
||||
}
|
||||
|
||||
// Assigning a View
|
||||
/*
|
||||
/*!
|
||||
Sets the view that gets displayed in this tab.
|
||||
*/
|
||||
- (void)setView:(CPView)aView
|
||||
@@ -124,7 +133,7 @@ CPPressedTab = 2;
|
||||
_view = aView;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the tab's view.
|
||||
*/
|
||||
- (CPView)view
|
||||
@@ -133,7 +142,7 @@ CPPressedTab = 2;
|
||||
}
|
||||
|
||||
// Assigning an Auxiliary View
|
||||
/*
|
||||
/*!
|
||||
Sets the tab's auxillary view.
|
||||
@param anAuxillaryView the new auxillary view
|
||||
*/
|
||||
@@ -142,7 +151,7 @@ CPPressedTab = 2;
|
||||
_auxiliaryView = anAuxiliaryView;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the tab's auxillary view
|
||||
*/
|
||||
- (CPView)auxiliaryView
|
||||
@@ -151,7 +160,7 @@ CPPressedTab = 2;
|
||||
}
|
||||
|
||||
// Accessing the Parent Tab View
|
||||
/*
|
||||
/*!
|
||||
Returns the tab view that contains this item.
|
||||
*/
|
||||
- (CPTabView)tabView
|
||||
@@ -160,3 +169,38 @@ CPPressedTab = 2;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||
CPTabViewItemLabelKey = "CPTabViewItemLabelKey",
|
||||
CPTabViewItemViewKey = "CPTabViewItemViewKey",
|
||||
CPTabViewItemAuxViewKey = "CPTabViewItemAuxViewKey";
|
||||
|
||||
|
||||
@implementation CPTabViewItem (CPCoding)
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:CPTabViewItemIdentifierKey];
|
||||
[aCoder encodeObject:_label forKey:CPTabViewItemLabelKey];
|
||||
|
||||
[aCoder encodeObject:_view forKey:CPTabViewItemViewKey];
|
||||
[aCoder encodeObject:_auxiliaryView forKey:CPTabViewItemAuxViewKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+310
-150
@@ -3,7 +3,7 @@
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* Copyright 2008, 280 North, Inc.
|
||||
* 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
|
||||
@@ -20,12 +20,13 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPSortDescriptor.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
import <Foundation/Foundation.j>
|
||||
@import "CPTableHeaderView.j"
|
||||
|
||||
/*
|
||||
@ignore
|
||||
*/
|
||||
|
||||
/*
|
||||
@global
|
||||
@@ -43,233 +44,392 @@ CPTableColumnAutoresizingMask = 1;
|
||||
*/
|
||||
CPTableColumnUserResizingMask = 2;
|
||||
|
||||
/*
|
||||
An <objj>CPTableColumn</objj> object mainly keeps information about the width of the column, its minimum and maximum width; whether the column can be edited or resized; and the cells used to draw the column header and the data in the column. You can change all these attributes of the column by calling the appropriate methods. Please note that the table column does not hold nor has access to the data to be displayed in the column; this data is maintained in the table view's data source.</p>
|
||||
|
||||
<p>Each <objj>CPTableColumn</objj> object is identified by a <objj>CPString</objj>, called the column identifier. The reason is that, after a column has been added to a table view, the user might move the columns around, so there is a need to identify the columns regardless of its position in the table.
|
||||
|
||||
@ignore
|
||||
*/
|
||||
@implementation CPTableColumn : CPObject
|
||||
{
|
||||
CPString _identifier;
|
||||
|
||||
CPTableView _tableView;
|
||||
|
||||
float _width;
|
||||
float _minWidth;
|
||||
float _maxWidth;
|
||||
|
||||
unsigned _resizingMask;
|
||||
CPTableView _tableView;
|
||||
CPView _headerView;
|
||||
CPView _dataView;
|
||||
Object _dataViewData;
|
||||
|
||||
float _width;
|
||||
float _minWidth;
|
||||
float _maxWidth;
|
||||
|
||||
id _identifier;
|
||||
BOOL _isEditable;
|
||||
CPSortDescriptor _sortDescriptorPrototype;
|
||||
BOOL _isHidden;
|
||||
CPString _headerToolTip;
|
||||
}
|
||||
|
||||
/*
|
||||
Initializes the table column with the specified identifier.
|
||||
@param anIdentifier the identifier
|
||||
@return the initialized table column
|
||||
*/
|
||||
- (id)initWithIdentifier:(CPString)anIdentifier
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithIdentifier:@""];
|
||||
}
|
||||
|
||||
- (id)initWithIdentifier:(id)anIdentifier
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_identifier = anIdentifier;
|
||||
|
||||
_width = 40.0;
|
||||
_minWidth = 8.0;
|
||||
_maxWidth = 1000.0;
|
||||
|
||||
// FIXME
|
||||
_dataCell = [[CPTextField alloc] initWithFrame:CPRectMakeZero()];
|
||||
_dataViewData = { };
|
||||
|
||||
_width = 100.0;
|
||||
_minWidth = 10.0;
|
||||
_maxWidth = 1000000.0;
|
||||
|
||||
[self setIdentifier:anIdentifier];
|
||||
[self setHeaderView:[CPTextField new]];
|
||||
[self setDataView:[CPTextField new]];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the table column's identifier
|
||||
@param anIdentifier the new identifier
|
||||
*/
|
||||
- (void)setIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
_identifier = anIdentifier;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the table column's identifier
|
||||
*/
|
||||
- (CPString)identifier
|
||||
{
|
||||
return _identifier;
|
||||
}
|
||||
|
||||
// Setting the CPTableView
|
||||
/*
|
||||
Sets the table's view. This is called automatically by Cappuccino.
|
||||
@param aTableView the new table view
|
||||
*/
|
||||
- (void)setTableView:(CPTableView)aTableView
|
||||
{
|
||||
_tableView = aTableView;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the column's table view.
|
||||
*/
|
||||
- (CPTableView)tableView
|
||||
{
|
||||
return _tableView;
|
||||
}
|
||||
|
||||
// Controlling size
|
||||
/*
|
||||
Sets the column's width.
|
||||
@param aWidth the new column width
|
||||
*/
|
||||
- (void)setWidth:(float)aWidth
|
||||
{
|
||||
_width = aWidth;
|
||||
aWidth = +aWidth;
|
||||
|
||||
if (_width === aWidth)
|
||||
return;
|
||||
|
||||
var newWidth = MIN(MAX(aWidth, [self minWidth]), [self maxWidth]);
|
||||
|
||||
if (_width === newWidth)
|
||||
return;
|
||||
|
||||
var oldWidth = _width;
|
||||
|
||||
_width = newWidth;
|
||||
|
||||
var tableView = [self tableView];
|
||||
|
||||
if (tableView)
|
||||
{
|
||||
var index = [[tableView tableColumns] indexOfObjectIdenticalTo:self];
|
||||
|
||||
// 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);
|
||||
|
||||
[tableView tile];
|
||||
|
||||
[[CPNotificationCenter defaultCenter]
|
||||
postNotificationName:CPTableViewColumnDidResizeNotification
|
||||
object:tableView
|
||||
userInfo:[CPDictionary dictionaryWithObjects:[self, oldWidth] forKeys:[@"CPTableColumn", "CPOldWidth"]]];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the column's width
|
||||
*/
|
||||
- (float)width
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets column's minimum width.
|
||||
@param aWidth the new minimum column width
|
||||
*/
|
||||
- (void)setMinWidth:(float)aWidth
|
||||
- (void)setMinWidth:(float)aMinWidth
|
||||
{
|
||||
if (_width < (_minWidth = aWidth))
|
||||
[self setWidth:_minWidth];
|
||||
aMinWidth = +aMinWidth;
|
||||
|
||||
if (_minWidth === aMinWidth)
|
||||
return;
|
||||
|
||||
_minWidth = aMinWidth;
|
||||
|
||||
var width = [self width],
|
||||
newWidth = MAX(width, [self minWidth]);
|
||||
|
||||
if (width !== newWidth)
|
||||
[self setWidth:newWidth];
|
||||
}
|
||||
|
||||
/*
|
||||
The column's minimum width
|
||||
*/
|
||||
- (float)minWidth
|
||||
{
|
||||
return _minWidth;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the column's maximum width.
|
||||
@param aWidth the new maximum width
|
||||
*/
|
||||
- (void)setMaxWidth:(float)aWidth
|
||||
- (void)setMaxWidth:(float)aMaxWidth
|
||||
{
|
||||
if (_width > (_maxmimumWidth = aWidth))
|
||||
[self setWidth:_maxWidth];
|
||||
aMaxWidth = +aMaxWidth;
|
||||
|
||||
if (_maxWidth === aMaxWidth)
|
||||
return;
|
||||
|
||||
_maxWidth = aMaxWidth;
|
||||
|
||||
var width = [self width],
|
||||
newWidth = MAX(width, [self maxWidth]);
|
||||
|
||||
if (width !== newWidth)
|
||||
[self setWidth:newWidth];
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the resizing mask. The mask is one of:
|
||||
<pre>
|
||||
CPTableColumnNoResizing;
|
||||
CPTableColumnAutoresizingMask;
|
||||
CPTableColumnUserResizingMask;
|
||||
</pre>
|
||||
@param aMask the new resizing mask
|
||||
*/
|
||||
- (void)setResizingMask:(unsigned)aMask
|
||||
- (float)maxWidth
|
||||
{
|
||||
_resizingMask = aMask;
|
||||
return _maxWidth;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the column's resizing mask. One of:
|
||||
<pre>
|
||||
CPTableColumnNoResizing;
|
||||
CPTableColumnAutoresizingMask;
|
||||
CPTableColumnUserResizingMask;
|
||||
</pre>
|
||||
*/
|
||||
- (unsigned)resizingMask
|
||||
- (void)setResizingMask:(unsigned)aResizingMask
|
||||
{
|
||||
_resizingMask = aResizingMask;
|
||||
}
|
||||
|
||||
- (float)resizingMask
|
||||
{
|
||||
return _resizingMask;
|
||||
}
|
||||
|
||||
/*
|
||||
Resizes the column according to the min, max and set width.
|
||||
*/
|
||||
- (void)sizeToFit
|
||||
{
|
||||
var width = CPRectGetWidth([_headerView frame]);
|
||||
|
||||
if (width < _minWidth)
|
||||
var width = _CGRectGetWidth([_headerView frame]);
|
||||
|
||||
if (width < [self minWidth])
|
||||
[self setMinWidth:width];
|
||||
else if (width > _maxWidth)
|
||||
else if (width > [self maxWidth])
|
||||
[self setMaxWidth:width]
|
||||
|
||||
if (_width != width)
|
||||
if (_width !== width)
|
||||
[self setWidth:width];
|
||||
}
|
||||
|
||||
/*
|
||||
Sets whether the column in this data is editable.
|
||||
@param aFlag <code>YES</code> means the column data is editable
|
||||
*/
|
||||
- (void)setEditable:(BOOL)aFlag
|
||||
//Setting Component Cells
|
||||
- (void)setHeaderView:(CPView)aView
|
||||
{
|
||||
_isEditable = aFlag;
|
||||
if (!aView)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"Attempt to set nil header view on " + [self description]];
|
||||
|
||||
_headerView = aView;
|
||||
|
||||
var tableHeaderView = [_tableView headerView];
|
||||
|
||||
[tableHeaderView setNeedsLayout];
|
||||
[tableHeaderView setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (CPView)headerView
|
||||
{
|
||||
return _headerView;
|
||||
}
|
||||
|
||||
- (void)setDataView:(CPView)aView
|
||||
{
|
||||
if (_dataView === aView)
|
||||
return;
|
||||
|
||||
if (_dataView)
|
||||
_dataViewData[[_dataView UID]] = nil;
|
||||
|
||||
_dataView = aView;
|
||||
_dataViewData[[aView UID]] = [CPKeyedArchiver archivedDataWithRootObject:aView];
|
||||
}
|
||||
|
||||
- (CPView)dataView
|
||||
{
|
||||
return _dataView;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the column data is editable.
|
||||
Returns the CPView object used by the CPTableView to draw values for the receiver.
|
||||
|
||||
By default, this method just calls dataView. Subclassers can override if they need to
|
||||
potentially use different cells for different rows. Subclasses should expect this method
|
||||
to be invoked with row equal to -1 in cases where no actual row is involved but the table
|
||||
view needs to get some generic cell info.
|
||||
*/
|
||||
- (id)dataViewForRow:(int)aRowIndex
|
||||
{
|
||||
return [self dataView];
|
||||
}
|
||||
|
||||
- (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();
|
||||
|
||||
// if we haven't cached an archive of the data view, do it now
|
||||
if (!_dataViewData[dataViewUID])
|
||||
_dataViewData[dataViewUID] = [CPKeyedArchiver archivedDataWithRootObject:dataView];
|
||||
|
||||
// unarchive the data view cache
|
||||
var newDataView = [CPKeyedUnarchiver unarchiveObjectWithData:_dataViewData[dataViewUID]];
|
||||
newDataView.identifier = dataViewUID;
|
||||
return newDataView;
|
||||
}
|
||||
|
||||
//Setting the Identifier
|
||||
|
||||
/*
|
||||
Sets the receiver identifier to anIdentifier.
|
||||
*/
|
||||
- (void)setIdentifier:(id)anIdentifier
|
||||
{
|
||||
_identifier = anIdentifier;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the object used by the data source to identify the attribute corresponding to the receiver.
|
||||
*/
|
||||
- (id)identifier
|
||||
{
|
||||
return _identifier;
|
||||
}
|
||||
|
||||
//Controlling Editability
|
||||
|
||||
/*
|
||||
Controls whether the user can edit cells in the receiver by double-clicking them.
|
||||
*/
|
||||
- (void)setEditable:(BOOL)shouldBeEditable
|
||||
{
|
||||
_isEditable = shouldBeEditable;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns YES if the user can edit cells associated with the receiver by double-clicking the
|
||||
column in the NSTableView, NO otherwise.
|
||||
*/
|
||||
- (BOOL)isEditable
|
||||
{
|
||||
return _isEditable;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the view that draws the column's header.
|
||||
@param aHeaderView the view that will draws the column header
|
||||
*/
|
||||
- (void)setHeaderView:(CPView)aHeaderView
|
||||
//Sorting
|
||||
- (void)setSortDescriptorPrototype:(CPSortDescriptor)aSortDescriptor
|
||||
{
|
||||
_headerView = aHeaderView;
|
||||
_sortDescriptorPrototype = aSortDescriptor;
|
||||
}
|
||||
|
||||
/*
|
||||
Return the view that draws the column's header
|
||||
*/
|
||||
- (CPView)headerView
|
||||
- (CPSortDescriptor)sortDescriptorPrototype
|
||||
{
|
||||
return _headerView;
|
||||
return _sortDescriptorPrototype;
|
||||
}
|
||||
|
||||
/*
|
||||
Sets the data cell that draws rows in this column.
|
||||
*/
|
||||
- (void)setDataCell:(CPCell)aDataCell
|
||||
//Setting Column Visibility
|
||||
|
||||
- (void)setHidden:(BOOL)shouldBeHidden
|
||||
{
|
||||
_dataCell = aDataCell;
|
||||
_isHidden = shouldBeHidden;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the data cell that draws rows in this column
|
||||
*/
|
||||
- (CPCell)dataCell
|
||||
- (BOOL)isHidden
|
||||
{
|
||||
return _dataCell;
|
||||
return _isHidden;
|
||||
}
|
||||
|
||||
//Setting Tool Tips
|
||||
|
||||
/*
|
||||
By default returns the value from <code>dataCell</code>. This can
|
||||
be overridden by a subclass to return different cells for different
|
||||
rows.
|
||||
@param aRowIndex the index of the row to obtain the cell for
|
||||
Sets the tooltip string that is displayed when the cursor pauses over the
|
||||
header cell of the receiver.
|
||||
*/
|
||||
- (CPCell)dataCellForRow:(int)aRowIndex
|
||||
- (void)setHeaderToolTip:(CPString)aToolTip
|
||||
{
|
||||
return [self dataCell];
|
||||
_headerToolTip = aToolTip;
|
||||
}
|
||||
|
||||
- (CPString)headerToolTip
|
||||
{
|
||||
return _headerToolTip;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTableColumnIdentifierKey = @"CPTableColumnIdentifierKey",
|
||||
CPTableColumnHeaderViewKey = @"CPTableColumnHeaderViewKey",
|
||||
CPTableColumnDataViewKey = @"CPTableColumnDataViewKey",
|
||||
CPTableColumnWidthKey = @"CPTableColumnWidthKey",
|
||||
CPTableColumnMinWidthKey = @"CPTableColumnMinWidthKey",
|
||||
CPTableColumnMaxWidthKey = @"CPTableColumnMaxWidthKey",
|
||||
CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey";
|
||||
|
||||
@implementation CPTableColumn (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_dataViewData = { };
|
||||
|
||||
_width = [aCoder decodeFloatForKey:CPTableColumnWidthKey];
|
||||
_minWidth = [aCoder decodeFloatForKey:CPTableColumnMinWidthKey];
|
||||
_maxWidth = [aCoder decodeFloatForKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
[self setIdentifier:[aCoder decodeObjectForKey:CPTableColumnIdentifierKey]];
|
||||
// [self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
|
||||
// [self setDataView:[aCoder decodeObjectForKey:CPTableColumnDataViewKey]];
|
||||
|
||||
[self setHeaderView:[CPTextField new]];
|
||||
[self setDataView:[CPTextField new]];
|
||||
|
||||
|
||||
// _resizingMask = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:CPTableColumnIdentifierKey];
|
||||
|
||||
[aCoder encodeObject:_width forKey:CPTableColumnWidthKey];
|
||||
[aCoder encodeObject:_minWidth forKey:CPTableColumnMinWidthKey];
|
||||
[aCoder encodeObject:_maxWidth forKey:CPTableColumnMaxWidthKey];
|
||||
|
||||
// [aCoder encodeObject:_headerView forKey:CPTableColumnHeaderViewKey];
|
||||
// [aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
|
||||
|
||||
// [aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTableColumn (NSInCompatibility)
|
||||
|
||||
- (void)setHeaderCell:(CPView)aView
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"setHeaderCell: is not supported. -setHeaderCell:aView instead."];
|
||||
}
|
||||
|
||||
- (CPView)headerCell
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"headCell is not supported. -headerView instead."];
|
||||
}
|
||||
|
||||
- (void)setDataCell:(CPView)aView
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"setDataCell: is not supported. Use -setHeaderCell:aView instead."];
|
||||
}
|
||||
|
||||
- (CPView)dataCell
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"dataCell is not supported. Use -dataCell instead."];
|
||||
}
|
||||
|
||||
- (id)dataCellForRow:(int)row
|
||||
{
|
||||
[CPException raise:CPUnsupportedMethodException
|
||||
reason:@"dataCellForRow: is not supported. Use -dataViewForRow:row instead."];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* CPTableHeaderView.j
|
||||
* AppKit
|
||||
*
|
||||
* 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 "CPTableColumn.j"
|
||||
@import "CPTableView.j"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
@implementation CPTableHeaderView : CPView
|
||||
{
|
||||
int _resizedColumn @accessors(readonly, property=resizedColumn);
|
||||
int _draggedColumn @accessors(readonly, property=draggedColumn);
|
||||
|
||||
float _draggedDistance @accessors(readonly, property=draggedDistance);
|
||||
|
||||
CPTableView _tableView @accessors(property=tableView);
|
||||
}
|
||||
|
||||
- (void)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_resizedColumn = CPNotFound;
|
||||
_draggedColumn = CPNotFound;
|
||||
_draggedDistance = 0.0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
- (CGRect)headerRectOfColumn:(int)aColumnIndex
|
||||
{
|
||||
var tableColumns = [_tableView tableColumns],
|
||||
tableSpacing = [_tableView intercellSpacing],
|
||||
bounds = [self bounds];
|
||||
|
||||
if (aColumnIndex < 0 || aColumnIndex > [tableColumns count])
|
||||
[CPException raise:"invalid" reason:"tried to get headerRectOfColumn: on invalid column"];
|
||||
|
||||
bounds.size.width = [tableColumns[aColumnIndex] width] + tableSpacing.width;
|
||||
|
||||
while (--aColumnIndex >= 0)
|
||||
bounds.origin.x += [tableColumns[aColumnIndex] width] + tableSpacing.width;
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
var tableColumns = [_tableView tableColumns],
|
||||
count = [tableColumns count],
|
||||
columnRect = [self bounds],
|
||||
spacing = [_tableView intercellSpacing];
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
var column = [tableColumns objectAtIndex:i],
|
||||
headerView = [column headerView];
|
||||
|
||||
columnRect.size.width = [column width] + spacing.width;
|
||||
|
||||
[headerView setFrame:columnRect];
|
||||
|
||||
columnRect.origin.x += [column width] + spacing.width;
|
||||
|
||||
[self addSubview:headerView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
[[_tableView gridColor] setStroke];
|
||||
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
exposedColumnIndexes = exposedColumnIndexes = [_tableView columnIndexesInRect:aRect],
|
||||
columnsArray = [];
|
||||
|
||||
[exposedColumnIndexes getIndexes:columnsArray maxCount:-1 inIndexRange:nil];
|
||||
|
||||
var columnArrayIndex = 0,
|
||||
columnArrayCount = columnsArray.length;
|
||||
|
||||
for(; columnArrayIndex < columnArrayCount; ++columnArrayIndex)
|
||||
{
|
||||
// grab each column rect and add horizontal lines
|
||||
var columnToStroke = [self headerRectOfColumn:columnArrayIndex];
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
+1779
-363
File diff suppressed because it is too large
Load Diff
+766
-325
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,588 @@
|
||||
/*
|
||||
* CPTheme.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
|
||||
var CPThemesByName = { },
|
||||
CPThemeDefaultTheme = nil;
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
|
||||
@implementation CPTheme : CPObject
|
||||
{
|
||||
CPString _name;
|
||||
CPDictionary _attributes;
|
||||
}
|
||||
|
||||
+ (void)setDefaultTheme:(CPTheme)aTheme
|
||||
{
|
||||
CPThemeDefaultTheme = aTheme;
|
||||
}
|
||||
|
||||
+ (CPTheme)defaultTheme
|
||||
{
|
||||
return CPThemeDefaultTheme;
|
||||
}
|
||||
|
||||
+ (CPTheme)themeNamed:(CPString)aName
|
||||
{
|
||||
return CPThemesByName[aName];
|
||||
}
|
||||
|
||||
- (id)initWithName:(CPString)aName
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_name = aName;
|
||||
_attributes = [CPDictionary dictionary];
|
||||
|
||||
CPThemesByName[_name] = self;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPString)name
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
- (_CPThemeAttribute)_attributeWithName:(CPString)aName forClass:(CPString)aClass
|
||||
{
|
||||
var attributes = [_attributes objectForKey:aClass];
|
||||
|
||||
if (!attributes)
|
||||
return nil;
|
||||
|
||||
return [attributes objectForKey:aName];
|
||||
}
|
||||
|
||||
- (void)takeThemeFromObject:(id)anObject
|
||||
{
|
||||
var attributes = [anObject _themeAttributeDictionary],
|
||||
attributeName = nil,
|
||||
attributeNames = [attributes keyEnumerator],
|
||||
objectThemeClass = [[anObject class] themeClass];
|
||||
|
||||
while (attributeName = [attributeNames nextObject])
|
||||
[self _recordAttribute:[attributes objectForKey:attributeName] forClass:objectThemeClass];
|
||||
}
|
||||
|
||||
- (void)_recordAttribute:(_CPThemeAttribute)anAttribute forClass:(CPString)aClass
|
||||
{
|
||||
if (![anAttribute hasValues])
|
||||
return;
|
||||
|
||||
var attributes = [_attributes objectForKey:aClass];
|
||||
|
||||
if (!attributes)
|
||||
{
|
||||
attributes = [CPDictionary dictionary];
|
||||
|
||||
[_attributes setObject:attributes forKey:aClass];
|
||||
}
|
||||
|
||||
var name = [anAttribute name],
|
||||
existingAttribute = [attributes objectForKey:name];
|
||||
|
||||
if (existingAttribute)
|
||||
[attributes setObject:[existingAttribute attributeMergedWithAttribute:anAttribute] forKey:name];
|
||||
else
|
||||
[attributes setObject:anAttribute forKey:name];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPThemeNameKey = @"CPThemeNameKey",
|
||||
CPThemeAttributesKey = @"CPThemeAttributesKey";
|
||||
|
||||
@implementation CPTheme (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_name = [aCoder decodeObjectForKey:CPThemeNameKey];
|
||||
_attributes = [aCoder decodeObjectForKey:CPThemeAttributesKey];
|
||||
|
||||
CPThemesByName[_name] = self;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_name forKey:CPThemeNameKey];
|
||||
[aCoder encodeObject:_attributes forKey:CPThemeAttributesKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPThemeKeyedUnarchiver : CPKeyedUnarchiver
|
||||
{
|
||||
CPBundle _bundle;
|
||||
}
|
||||
|
||||
- (id)initForReadingWithData:(CPData)data bundle:(CPBundle)aBundle
|
||||
{
|
||||
self = [super initForReadingWithData:data];
|
||||
|
||||
if (self)
|
||||
_bundle = aBundle;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPBundle)bundle
|
||||
{
|
||||
return _bundle;
|
||||
}
|
||||
|
||||
- (BOOL)awakenCustomResources
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPThemeStates = {},
|
||||
CPThemeStateNames = {},
|
||||
CPThemeStateCount = 0;
|
||||
|
||||
function CPThemeState(aStateName)
|
||||
{
|
||||
var state = CPThemeStates[aStateName];
|
||||
|
||||
if (state === undefined)
|
||||
{
|
||||
if (aStateName.indexOf('+') === -1)
|
||||
state = 1 << CPThemeStateCount++;
|
||||
else
|
||||
{
|
||||
var state = 0,
|
||||
states = aStateName.split('+'),
|
||||
count = states.length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var stateName = states[count],
|
||||
individualState = CPThemeStates[stateName];
|
||||
|
||||
if (individualState === undefined)
|
||||
{
|
||||
individualState = 1 << CPThemeStateCount++;
|
||||
CPThemeStates[stateName] = individualState;
|
||||
CPThemeStateNames[individualState] = stateName;
|
||||
}
|
||||
|
||||
state |= individualState;
|
||||
}
|
||||
}
|
||||
|
||||
CPThemeStates[aStateName] = state;
|
||||
CPThemeStateNames[state] = aStateName;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
function CPThemeStateName(aState)
|
||||
{
|
||||
var name = CPThemeStateNames[aState];
|
||||
|
||||
if (name !== undefined)
|
||||
return name;
|
||||
|
||||
if (!(aState & (aState - 1)))
|
||||
return "";
|
||||
|
||||
var state = 1,
|
||||
name = "";
|
||||
|
||||
for (; state < aState; state <<= 1)
|
||||
if (aState & state)
|
||||
name += (name.length === 0 ? '' : '+') + CPThemeStateNames[state];
|
||||
|
||||
CPThemeStateNames[aState] = name;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
CPThemeStateNames[0] = "normal";
|
||||
CPThemeStateNormal = CPThemeStates["normal"] = 0;
|
||||
CPThemeStateDisabled = CPThemeState("disabled");
|
||||
CPThemeStateHighlighted = CPThemeState("highlighted");
|
||||
CPThemeStateSelected = CPThemeState("selected");
|
||||
CPThemeStateBezeled = CPThemeState("bezeled");
|
||||
CPThemeStateBordered = CPThemeState("bordered");
|
||||
CPThemeStateEditable = CPThemeState("editable");
|
||||
CPThemeStateEditing = CPThemeState("editing");
|
||||
CPThemeStateVertical = CPThemeState("vertical");
|
||||
CPThemeStateDefault = CPThemeState("default");
|
||||
CPThemeStateCircular = CPThemeState("circular");
|
||||
|
||||
@implementation _CPThemeAttribute : CPObject
|
||||
{
|
||||
CPString _name;
|
||||
id _defaultValue;
|
||||
CPDictionary _values;
|
||||
|
||||
JSObject _cache;
|
||||
CPThemeAttribute _parentAttribute;
|
||||
}
|
||||
|
||||
- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_cache = { };
|
||||
_name = aName;
|
||||
_defaultValue = aDefaultValue;
|
||||
_values = [CPDictionary dictionary];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPString)name
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
- (id)defaultValue
|
||||
{
|
||||
return _defaultValue;
|
||||
}
|
||||
|
||||
- (BOOL)hasValues
|
||||
{
|
||||
return [_values count] > 0;
|
||||
}
|
||||
|
||||
- (BOOL)isTrivial
|
||||
{
|
||||
return ([_values count] === 1) && (Number([_values allKeys][0]) === CPThemeStateNormal);
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue
|
||||
{
|
||||
_cache = {};
|
||||
|
||||
if (aValue === undefined || aValue === nil)
|
||||
_values = [CPDictionary dictionary];
|
||||
else
|
||||
_values = [CPDictionary dictionaryWithObject:aValue forKey:String(CPThemeStateNormal)];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forState:(CPThemeState)aState
|
||||
{
|
||||
_cache = { };
|
||||
|
||||
if ((aValue === undefined) || (aValue === nil))
|
||||
[_values removeObjectForKey:String(aState)];
|
||||
else
|
||||
[_values setObject:aValue forKey:String(aState)];
|
||||
}
|
||||
|
||||
- (id)value
|
||||
{
|
||||
return [self valueForState:CPThemeStateNormal];
|
||||
}
|
||||
|
||||
- (id)valueForState:(CPThemeState)aState
|
||||
{
|
||||
var value = _cache[aState];
|
||||
|
||||
// This can be nil.
|
||||
if (value !== undefined)
|
||||
return value;
|
||||
|
||||
value = [_values objectForKey:String(aState)];
|
||||
|
||||
// If we don't have a value, and we have a non-normal state...
|
||||
if ((value === undefined || value === nil) && aState !== CPThemeStateNormal)
|
||||
{
|
||||
// If this is a composite state (not a power of 2), find the closest partial subset match.
|
||||
if (aState & (aState - 1))
|
||||
{
|
||||
var highestOneCount = 0,
|
||||
states = [_values allKeys],
|
||||
count = states.length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
// states[count] is a string!
|
||||
var state = Number(states[count]);
|
||||
|
||||
// A & B = A iff A < B
|
||||
if ((state & aState) === state)
|
||||
{
|
||||
var oneCount = cachedNumberOfOnes[state];
|
||||
|
||||
if (oneCount === undefined)
|
||||
oneCount = numberOfOnes(state);
|
||||
|
||||
if (oneCount > highestOneCount)
|
||||
{
|
||||
highestOneCount = oneCount;
|
||||
value = [_values objectForKey:String(state)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Still don't have a value? OK, let's use the normal value.
|
||||
if (value === undefined || value === nil)
|
||||
value = [_values objectForKey:String(CPThemeStateNormal)];
|
||||
}
|
||||
|
||||
if (value === undefined || value === nil)
|
||||
value = [_parentAttribute valueForState:aState];
|
||||
|
||||
if (value === undefined || value === nil)
|
||||
value = _defaultValue;
|
||||
|
||||
_cache[aState] = value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void)setParentAttribute:(CPThemeAttribute)anAttribute
|
||||
{
|
||||
if (_parentAttribute === anAttribute)
|
||||
return;
|
||||
|
||||
_cache = { };
|
||||
_parentAttribute = anAttribute;
|
||||
}
|
||||
|
||||
- (CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
|
||||
{
|
||||
var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue];
|
||||
|
||||
mergedAttribute._values = [_values copy];
|
||||
[mergedAttribute._values addEntriesFromDictionary:anAttribute._values];
|
||||
|
||||
return mergedAttribute;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPThemeAttribute (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_cache = {};
|
||||
|
||||
_name = [aCoder decodeObjectForKey:@"name"];
|
||||
_values = [CPDictionary dictionary];
|
||||
|
||||
if ([aCoder containsValueForKey:@"value"])
|
||||
{
|
||||
var state = CPThemeStateNormal;
|
||||
|
||||
if ([aCoder containsValueForKey:@"state"])
|
||||
state = CPThemeState([aCoder decodeObjectForKey:@"state"]);
|
||||
|
||||
[_values setObject:[aCoder decodeObjectForKey:"value"] forKey:state];
|
||||
}
|
||||
else
|
||||
{
|
||||
var encodedValues = [aCoder decodeObjectForKey:@"values"],
|
||||
keys = [encodedValues allKeys],
|
||||
count = keys.length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var key = keys[count];
|
||||
|
||||
[_values setObject:[encodedValues objectForKey:key] forKey:CPThemeState(key)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_name forKey:@"name"];
|
||||
|
||||
var keys = [_values allKeys],
|
||||
count = keys.length;
|
||||
|
||||
if (count === 1)
|
||||
{
|
||||
var onlyKey = keys[0];
|
||||
|
||||
if (Number(onlyKey) !== CPThemeStateNormal)
|
||||
[aCoder encodeObject:CPThemeStateName(Number(onlyKey)) forKey:@"state"];
|
||||
|
||||
[aCoder encodeObject:[_values objectForKey:onlyKey] forKey:@"value"];
|
||||
}
|
||||
else
|
||||
{
|
||||
var encodedValues = [CPDictionary dictionary];
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var key = keys[count];
|
||||
|
||||
[encodedValues setObject:[_values objectForKey:key] forKey:CPThemeStateName(Number(key))];
|
||||
}
|
||||
|
||||
[aCoder encodeObject:encodedValues forKey:@"values"];
|
||||
}
|
||||
}
|
||||
|
||||
@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*/,
|
||||
6 /*111111*/ ];
|
||||
|
||||
var numberOfOnes = function(aNumber)
|
||||
{
|
||||
var count = 0,
|
||||
slot = aNumber;
|
||||
|
||||
while (aNumber)
|
||||
{
|
||||
++count;
|
||||
aNumber &= (aNumber - 1);
|
||||
}
|
||||
|
||||
cachedNumberOfOnes[slot] = count;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
numberOfOnes.displayName = "numberOfOnes";
|
||||
|
||||
function CPThemeAttributeEncode(aCoder, aThemeAttribute)
|
||||
{
|
||||
var values = aThemeAttribute._values,
|
||||
count = [values count],
|
||||
key = "$a" + [aThemeAttribute name];
|
||||
|
||||
if (count === 1)
|
||||
{
|
||||
var state = [values allKeys][0];
|
||||
|
||||
if (Number(state) === 0)
|
||||
{
|
||||
[aCoder encodeObject:[values objectForKey:state] forKey:key];
|
||||
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (count >= 1)
|
||||
{
|
||||
[aCoder encodeObject:aThemeAttribute forKey:key];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme, aClass)
|
||||
{
|
||||
var key = "$a" + anAttributeName;
|
||||
|
||||
if (![aCoder containsValueForKey:key])
|
||||
var attribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
|
||||
|
||||
else
|
||||
{
|
||||
var attribute = [aCoder decodeObjectForKey:key];
|
||||
|
||||
if (!attribute.isa || ![attribute isKindOfClass:[_CPThemeAttribute class]])
|
||||
{
|
||||
var themeAttribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
|
||||
|
||||
[themeAttribute setValue:attribute];
|
||||
|
||||
attribute = themeAttribute;
|
||||
}
|
||||
}
|
||||
|
||||
if (aTheme && aClass)
|
||||
[attribute setParentAttribute:[aTheme _attributeWithName:anAttributeName forClass:aClass]];
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
/* TO AUTO CREATE THESE:
|
||||
function bit_count(bits)
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
while (bits)
|
||||
{
|
||||
++count;
|
||||
bits &= (bits - 1);
|
||||
}
|
||||
|
||||
return count ;
|
||||
}
|
||||
|
||||
zeros = "000000000";
|
||||
|
||||
function pad(string, digits)
|
||||
{
|
||||
return zeros.substr(0, digits - string.length) + string;
|
||||
}
|
||||
|
||||
var str = ""
|
||||
str += '[';
|
||||
for (i = 0;i < Math.pow(2,6);++i)
|
||||
{
|
||||
str += bit_count(i) + " /*" + pad(i.toString(2),6) + "*" + "/, ";
|
||||
}
|
||||
print(str+']');
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* CPThemeBlend.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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/CPObject.j>
|
||||
|
||||
@import <AppKit/CPTheme.j>
|
||||
@import <AppKit/_CPCibCustomResource.j>
|
||||
@import <AppKit/_CPCibKeyedUnarchiver.j>
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
|
||||
@implementation CPThemeBlend : CPObject
|
||||
{
|
||||
CPBundle _bundle;
|
||||
CPArray _themes @accessors(readonly, getter=themes);
|
||||
id _loadDelegate;
|
||||
}
|
||||
|
||||
- (id)initWithContentsOfURL:(CPURL)aURL
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_bundle = [[CPBundle alloc] initWithPath:aURL + "/Info.plist"];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadWithDelegate:(id)aDelegate
|
||||
{
|
||||
_loadDelegate = aDelegate;
|
||||
|
||||
[_bundle loadWithDelegate:self];
|
||||
}
|
||||
|
||||
- (void)bundleDidFinishLoading:(CPBundle)aBundle
|
||||
{
|
||||
var themes = [_bundle objectForInfoDictionaryKey:@"CPKeyedThemes"],
|
||||
count = themes.length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var path = [aBundle pathForResource:themes[count]],
|
||||
unarchiver = [[_CPThemeKeyedUnarchiver alloc]
|
||||
initForReadingWithData:[CPData dataWithString:objj_files[path].contents]
|
||||
bundle:_bundle];
|
||||
|
||||
[unarchiver decodeObjectForKey:@"root"];
|
||||
|
||||
[unarchiver finishDecoding];
|
||||
}
|
||||
|
||||
[_loadDelegate blendDidFinishLoading:self];
|
||||
}
|
||||
|
||||
@end
|
||||
+608
-275
File diff suppressed because it is too large
Load Diff
+180
-82
@@ -20,11 +20,12 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
@import <AppKit/CPImage.j>
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
import <AppKit/CPImage.j>
|
||||
import <AppKit/CPView.j>
|
||||
|
||||
/*
|
||||
@global
|
||||
@@ -55,17 +56,18 @@ CPToolbarShowFontsItemIdentifier = @"CPToolbarShowFontsItemIdentifier";
|
||||
CPToolbarCustomizeToolbarItemIdentifier = @"CPToolbarCustomizeToolbarItemIdentifier";
|
||||
CPToolbarPrintItemIdentifier = @"CPToolbarPrintItemIdentifier";
|
||||
|
||||
var _CPToolbarSeparatorItemView = nil;
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPToolbarItem
|
||||
|
||||
/*
|
||||
A representation of an item in a <objj>CPToolbar</objj>.
|
||||
A representation of an item in a CPToolbar.
|
||||
*/
|
||||
@implementation CPToolbarItem : CPObject
|
||||
{
|
||||
CPString _itemIdentifier;
|
||||
|
||||
|
||||
CPToolbar _toolbar;
|
||||
|
||||
|
||||
CPString _label;
|
||||
CPString _paletteLabel;
|
||||
CPString _toolTip;
|
||||
@@ -75,15 +77,24 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
BOOL _isEnabled;
|
||||
CPImage _image;
|
||||
CPImage _alternateImage;
|
||||
|
||||
CPView _view;
|
||||
|
||||
CGSize _minSize;
|
||||
CGSize _maxSize;
|
||||
|
||||
|
||||
int _visibilityPriority;
|
||||
|
||||
BOOL _autovalidates;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithItemIdentifier:@""];
|
||||
}
|
||||
|
||||
// Creating a Toolbar Item
|
||||
/*
|
||||
/*!
|
||||
Initializes the toolbar item with a specified identifier.
|
||||
@param anItemIdentifier the item's identifier
|
||||
@return the initialized toolbar item
|
||||
@@ -96,6 +107,9 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
{
|
||||
_itemIdentifier = anItemIdentifier;
|
||||
|
||||
_tag = 0;
|
||||
_isEnabled = YES;
|
||||
|
||||
_minSize = CGSizeMakeZero();
|
||||
_maxSize = CGSizeMakeZero();
|
||||
|
||||
@@ -106,7 +120,7 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
}
|
||||
|
||||
// Managing Attributes
|
||||
/*
|
||||
/*!
|
||||
Returns the item's identifier.
|
||||
*/
|
||||
- (CPString)itemIdentifier
|
||||
@@ -114,7 +128,7 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
return _itemIdentifier;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the toolbar of which this item is a part.
|
||||
*/
|
||||
- (CPToolbar)toolbar
|
||||
@@ -122,7 +136,7 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
return _toolbar;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the item's label
|
||||
*/
|
||||
- (CPString)label
|
||||
@@ -130,7 +144,7 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
return _label;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the item's label.
|
||||
@param aLabel the new label for the item
|
||||
*/
|
||||
@@ -139,7 +153,7 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
_label = aLabel;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the palette label.
|
||||
*/
|
||||
- (CPString)paletteLabel
|
||||
@@ -147,7 +161,7 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
return _paletteLabel;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the palette label
|
||||
@param aPaletteLabel the new palette label
|
||||
*/
|
||||
@@ -156,148 +170,188 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
_paletteLabel = aPaletteLabel;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the item's tooltip. A tooltip pops up
|
||||
next to the cursor when the user hovers over
|
||||
the item with the mouse.
|
||||
*/
|
||||
- (CPString)toolTip
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(toolTip)])
|
||||
return [_view toolTip];
|
||||
|
||||
return _toolTip;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the item's tooltip. A tooltip pops up next to the cursor when the user hovers over the item with the mouse.
|
||||
@param aToolTip the new item tool tip
|
||||
*/
|
||||
- (void)setToolTip:(CPString)aToolTip
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(setToolTip:)])
|
||||
[view setToolTip:aToolTip];
|
||||
|
||||
_toolTip = aToolTip;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the item's tag.
|
||||
*/
|
||||
- (int)tag
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(tag)])
|
||||
return [_view tag];
|
||||
|
||||
return _tag;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the item's tag.
|
||||
@param aTag the new tag for the item
|
||||
*/
|
||||
- (void)setTag:(int)aTag
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(setTag:)])
|
||||
[_view setTag:aTag];
|
||||
|
||||
_tag = aTag;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the item's action target.
|
||||
*/
|
||||
- (id)target
|
||||
{
|
||||
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. <code>nil</code> 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
|
||||
*/
|
||||
- (void)setTarget:(id)aTarget
|
||||
{
|
||||
_target = aTarget;
|
||||
if (!_view)
|
||||
_target = aTarget;
|
||||
|
||||
else if ([_view respondsToSelector:@selector(setTarget:)])
|
||||
[_view setTarget:aTarget];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the action that is triggered when the user clicks this item.
|
||||
*/
|
||||
- (SEL)action
|
||||
{
|
||||
if (_view)
|
||||
return [_view respondsToSelector:@selector(action)] ? [_view action] : nil;
|
||||
|
||||
return _action;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the action that is triggered when the user clicks this item.
|
||||
@param anAction the new action
|
||||
*/
|
||||
- (void)setAction:(SEL)anAction
|
||||
{
|
||||
_action = anAction;
|
||||
if (!_view)
|
||||
_action = anAction;
|
||||
|
||||
else if ([_view respondsToSelector:@selector(setAction:)])
|
||||
[_view setAction:anAction];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the item is enabled.
|
||||
/*!
|
||||
Returns \c YES if the item is enabled.
|
||||
*/
|
||||
- (BOOL)isEnabled
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(isEnabled)])
|
||||
return [_view isEnabled];
|
||||
|
||||
return _isEnabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets whether the item is enabled.
|
||||
@param aFlag <code>YES</code> enables the item
|
||||
@param aFlag \c YES enables the item
|
||||
*/
|
||||
- (void)setEnabled:(BOOL)aFlag
|
||||
- (void)setEnabled:(BOOL)shouldBeEnabled
|
||||
{
|
||||
_isEnabled = aFlag;
|
||||
if ([_view respondsToSelector:@selector(setEnabled:)])
|
||||
[_view setEnabled:shouldBeEnabled];
|
||||
|
||||
_isEnabled = shouldBeEnabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the item's image
|
||||
*/
|
||||
- (CPImage)image
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(image)])
|
||||
return [_view image];
|
||||
|
||||
return _image;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the item's image.
|
||||
@param anImage the new item image
|
||||
*/
|
||||
- (void)setImage:(CPImage)anImage
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(setImage:)])
|
||||
[_view setImage:anImage];
|
||||
|
||||
_image = anImage;
|
||||
|
||||
// FIXME: We can't keep assuming this.
|
||||
[_view setImage:anImage];
|
||||
|
||||
if (!_image)
|
||||
return;
|
||||
|
||||
var imageSize = [_image size];
|
||||
|
||||
if (_minSize.width == 0 && _minSize.height == 0 &&
|
||||
_maxSize.width == 0 && _maxSize.height == 0 &&
|
||||
(imageSize.width > 0 || imageSize.height > 0))
|
||||
_maxSize.width == 0 && _maxSize.height == 0)
|
||||
{
|
||||
[self setMinSize:imageSize];
|
||||
[self setMaxSize:imageSize];
|
||||
var imageSize = [_image size];
|
||||
|
||||
if (imageSize.width > 0 || imageSize.height > 0)
|
||||
{
|
||||
[self setMinSize:imageSize];
|
||||
[self setMaxSize:imageSize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the alternate image. This image is displayed on the item when the user is clicking it.
|
||||
@param anImage the new alternate image
|
||||
*/
|
||||
- (void)setAlternateImage:(CPImage)anImage
|
||||
{
|
||||
_alternateImage = anImage;
|
||||
if ([_view respondsToSelector:@selector(setAlternateImage:)])
|
||||
[_view setAlternateImage:anImage];
|
||||
|
||||
// FIXME: We can't keep assuming this.
|
||||
[_view setAlternateImage:anImage];
|
||||
_alternateImage = anImage;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the alternate image. This image is displayed on the item when the user is clicking it.
|
||||
*/
|
||||
- (CPImage)alternateImage
|
||||
{
|
||||
if ([_view respondsToSelector:@selector(alternateIamge)])
|
||||
return [_view alternateImage];
|
||||
|
||||
return _alternateImage;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the item's view.
|
||||
*/
|
||||
- (CPView)view
|
||||
@@ -305,16 +359,29 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
return _view;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the item's view
|
||||
@param aView the item's new view
|
||||
*/
|
||||
- (void)setView:(CPView)aView
|
||||
{
|
||||
if (_view == aView)
|
||||
return;
|
||||
|
||||
_view = aView;
|
||||
|
||||
if (_view)
|
||||
{
|
||||
// Tags get forwarded.
|
||||
if (_tag !== 0 && [_view respondsToSelector:@selector(setTag:)])
|
||||
[_view setTag:_tag];
|
||||
|
||||
_target = nil;
|
||||
_action = nil;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the item's minimum size.
|
||||
*/
|
||||
- (CGSize)minSize
|
||||
@@ -322,7 +389,7 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
return _minSize;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the item's minimum size.
|
||||
@param aMinSize the new minimum size
|
||||
*/
|
||||
@@ -334,7 +401,7 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
_maxSize = CGSizeMake(MAX(_minSize.width, _maxSize.width), MAX(_minSize.height, _maxSize.height));
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the item's maximum size.
|
||||
*/
|
||||
- (CGSize)maxSize
|
||||
@@ -342,7 +409,7 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
return _maxSize;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the item's new maximum size.
|
||||
@param aMaxSize the new maximum size
|
||||
*/
|
||||
@@ -355,7 +422,7 @@ var _CPToolbarSeparatorItemView = nil;
|
||||
}
|
||||
|
||||
// Visibility Priority
|
||||
/*
|
||||
/*!
|
||||
Returns the item's visibility priority. The value will be one of:
|
||||
<pre>
|
||||
CPToolbarItemVisibilityPriorityStandard
|
||||
@@ -369,7 +436,7 @@ CPToolbarItemVisibilityPriorityUser
|
||||
return _visibilityPriority;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the item's visibility priority. The value must be one of:
|
||||
<pre>
|
||||
CPToolbarItemVisibilityPriorityStandard
|
||||
@@ -384,6 +451,48 @@ CPToolbarItemVisibilityPriorityUser
|
||||
_visibilityPriority = aVisibilityPriority;
|
||||
}
|
||||
|
||||
- (void)validate
|
||||
{
|
||||
// View items do not do any target-action analysis.
|
||||
if (_view)
|
||||
{
|
||||
if ([target respondsToSelector:@selector(validateToolbarItem:)])
|
||||
[self setEnabled:[target validateToolbarItem:self]];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var action = [self action];
|
||||
|
||||
if (!action)
|
||||
return [self setEnabled:NO];
|
||||
|
||||
var target = [self target];
|
||||
|
||||
if (target && ![target respondsToSelector:action])
|
||||
return [self setEnabled:NO];
|
||||
|
||||
target = [CPApp targetForAction:action to:target from:self];
|
||||
|
||||
if (!target)
|
||||
return [self setEnabled:NO];
|
||||
|
||||
if ([target respondsToSelector:@selector(validateToolbarItem:)])
|
||||
[self setEnabled:[target validateToolbarItem:self]];
|
||||
else
|
||||
[self setEnabled:YES];
|
||||
}
|
||||
|
||||
- (BOOL)autovalidates
|
||||
{
|
||||
return _autovalidates;
|
||||
}
|
||||
|
||||
- (void)setAutovalidates:(BOOL)shouldAutovalidate
|
||||
{
|
||||
_autovalidates = !!shouldAutovalidate;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPToolbarItem (CPCopying)
|
||||
@@ -392,20 +501,21 @@ CPToolbarItemVisibilityPriorityUser
|
||||
{
|
||||
var copy = [[[self class] alloc] initWithItemIdentifier:_itemIdentifier];
|
||||
|
||||
[copy setLabel:_label];
|
||||
[copy setPaletteLabel:_paletteLabel];
|
||||
[copy setToolTip:_toolTip];
|
||||
|
||||
[copy setTag:_tag];
|
||||
[copy setTarget:_target];
|
||||
[copy setAction:_action];
|
||||
|
||||
[copy setEnabled:_isEnabled];
|
||||
[copy setImage:_image];
|
||||
[copy setAlternateImage:_alternateImage];
|
||||
|
||||
if (_view)
|
||||
[copy setView:[CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:_view]]];
|
||||
|
||||
[copy setLabel:_label];
|
||||
[copy setPaletteLabel:_paletteLabel];
|
||||
[copy setToolTip:[self toolTip]];
|
||||
|
||||
[copy setTag:[self tag]];
|
||||
[copy setTarget:[self target]];
|
||||
[copy setAction:[self action]];
|
||||
|
||||
[copy setEnabled:[self isEnabled]];
|
||||
|
||||
[copy setImage:[self image]];
|
||||
[copy setAlternateImage:[self alternateImage]];
|
||||
|
||||
[copy setMinSize:_minSize];
|
||||
[copy setMaxSize:_maxSize];
|
||||
@@ -431,18 +541,6 @@ CPToolbarItemVisibilityPriorityUser
|
||||
case CPToolbarSeparatorItemIdentifier: [item setMinSize:CGSizeMake(2.0, 0.0)];
|
||||
[item setMaxSize:CGSizeMake(2.0, 100000.0)];
|
||||
|
||||
if (!_CPToolbarSeparatorItemView)
|
||||
{
|
||||
_CPToolbarSeparatorItemView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 2.0, 32.0)];
|
||||
|
||||
sizes = {};
|
||||
sizes[@"CPToolbarItemSeparator"] = [CGSizeMake(2.0, 26.0), CGSizeMake(2.0, 1.0), CGSizeMake(2.0, 26.0)];
|
||||
[_CPToolbarSeparatorItemView setBackgroundColor:_CPControlThreePartImagePattern(YES, sizes, @"CPToolbarItem", @"Separator")];
|
||||
}
|
||||
|
||||
[item setView:_CPToolbarSeparatorItemView];
|
||||
|
||||
|
||||
return item;
|
||||
|
||||
case CPToolbarSpaceItemIdentifier: [item setMinSize:CGSizeMake(32.0, 32.0)];
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
@implementation CPTreeNode : CPObject
|
||||
{
|
||||
id _representedObject @accessors(readonly, property=representedObject);
|
||||
|
||||
CPTreeNode _parentNode @accessors(readonly, property=parentNode);
|
||||
CPMutableArray _childNodes @accessors(readonly, property=childNodes);
|
||||
}
|
||||
|
||||
+ (id)treeNodeWithRepresentedObject:(id)anObject
|
||||
{
|
||||
return [[self alloc] initWithRepresentedObject:anObject];
|
||||
}
|
||||
|
||||
- (id)initWithRepresentedObject:(id)anObject
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_representedObject = anObject;
|
||||
_childNodes = [];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isLeaf
|
||||
{
|
||||
return [_childNodes count] <= 0;
|
||||
}
|
||||
|
||||
- (CPMutableArray)mutableChildNodes
|
||||
{
|
||||
return [self mutableArrayValueForKey:@"childNodes"];
|
||||
}
|
||||
|
||||
- (void)insertObject:(id)anObject inChildNodesAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
anObject._parentNode = self;
|
||||
|
||||
[_childNodes addObject:anObject];
|
||||
}
|
||||
|
||||
- (void)removeObjectFromChildNodesAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
[_childNodes objectAtIndex:anIndex]._parentNode = nil;
|
||||
|
||||
[_childNodes removeObjectAtIndex:anIndex];
|
||||
}
|
||||
|
||||
- (void)replaceObjectFromChildNodesAtIndex:(CPInteger)anIndex withObject:(id)anObject
|
||||
{
|
||||
var oldObject = [_childNodes objectAtIndex:anIndex];
|
||||
|
||||
oldObject._parentNode = nil;
|
||||
|
||||
[_childNodes replaceObjectAtIndex:anIndex withObject:anObject];
|
||||
}
|
||||
|
||||
- (id)objectInChildNodesAtIndex:(CPInteger)anIndex
|
||||
{
|
||||
return _childNodes[anIndex];
|
||||
}
|
||||
|
||||
- (void)sortWithSortDescriptors:(CPArray)sortDescriptors recursively:(BOOL)shouldSortRecursively
|
||||
{
|
||||
[_childNodes sortUsingDescriptors:sortDescriptors];
|
||||
|
||||
if (!shouldSortRecursively)
|
||||
return;
|
||||
|
||||
var count = [_childNodes count];
|
||||
|
||||
while (count--)
|
||||
[_childNodes[count] sortWithSortDescriptors:sortDescriptors recursively:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
+965
-316
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* CPViewController.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Nicholas Small and Francisco Tolmasky.
|
||||
* 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 <AppKit/CPResponder.j>
|
||||
|
||||
|
||||
/*! @class CPViewController
|
||||
The CPViewController class provides the fundamental view-management controller for Cappuccino applications.
|
||||
The basic view controller class supports the presentation of an associated view in addition to basic support
|
||||
for managing modal views and, in the future, animations. Subclasses such as CPNavigationController and
|
||||
CPTabBarController provide additional behavior for managing complex hierarchies of view controllers and views.
|
||||
|
||||
You use each instance of CPViewController to manage a single view (and hierarchy). For a simple view controller,
|
||||
this entails managing the view hierarchy responsible for presenting your application content.
|
||||
A typical view hierarchy consists of a root viewŃa reference to which is available in the view property of this classŃ
|
||||
and one or more subviews presenting the actual content. In the case of navigation and tab bar controllers, the view
|
||||
controller manages not only the high-level view hierarchy (which provides the navigation controls) but also one
|
||||
or more additional view controllers that handle the presentation of the application content.
|
||||
|
||||
Unlike UIViewController in Cocoa Touch, a CPViewController does not represent an entire screen of content. You
|
||||
will add your root view to an existing view or window's content view. You can manage many view controllers
|
||||
on screen at once. CPViewController is also the preferred way of working with Cibs.
|
||||
|
||||
Subclasses can override -loadView to create their custom view hierarchy, or specify a cib name to be loaded automatically.
|
||||
It has methods that are called when a view appears or disappears.
|
||||
This class is also a good place for delegate & datasource methods, and other controller stuff.
|
||||
*/
|
||||
@implementation CPViewController : CPResponder
|
||||
{
|
||||
CPView _view;
|
||||
|
||||
id _representedObject @accessors(property=representedObject);
|
||||
CPString _title @accessors(property=title);
|
||||
|
||||
CPString _cibName @accessors(property=cibName, readonly);
|
||||
CPBundle _cibBundle @accessors(property=cibBundle, readonly);
|
||||
CPDictionary _cibExternalNameTable @accessors(property=cibExternalNameTable, readonly);
|
||||
}
|
||||
|
||||
/*!
|
||||
Convenience initializer calls -initWithCibName:bundle: with nil for both parameters.
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithCibName:nil bundle:nil];
|
||||
}
|
||||
|
||||
- (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil
|
||||
{
|
||||
return [self initWithCibName:aCibNameOrNil bundle:aCibBundleOrNil externalNameTable:nil];
|
||||
}
|
||||
|
||||
- (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil owner:(id)anOwner
|
||||
{
|
||||
return [self initWithCibName:aCibNameOrNil bundle:aCibBundleOrNil externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner]];
|
||||
}
|
||||
|
||||
/*!
|
||||
The designated initializer. If you subclass CPViewController, you must call the super implementation of this method, even if you aren't using a Cib.
|
||||
In the specified Cib, the File's Owner proxy should have its class set to your view controller subclass, with the view outlet connected to the main view.
|
||||
If you pass in a nil Cib name, then you must either call -setView: before -view is invoked, or override -loadView to set up your views.
|
||||
|
||||
@param cibNameOrNil The path to the cib to load for the root view or nil to programmatically create views.
|
||||
@param cibBundleOrNil The bundle that the cib is located in or nil for the main bundle.
|
||||
*/
|
||||
- (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil externalNameTable:(CPDictionary)anExternalNameTable
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
// Don't load the cib until someone actually requests the view. The user may just be intending to use setView:.
|
||||
_cibName = aCibNameOrNil;
|
||||
_cibBundle = aCibBundleOrNil || [CPBundle mainBundle];
|
||||
_cibExternalNameTable = anExternalNameTable || [CPDictionary dictionaryWithObject:self forKey:CPCibOwner];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Programmatically creates the view that the controller manages.
|
||||
You should never call this method directly. The view controller calls this method when the view property is requested but is nil.
|
||||
|
||||
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.
|
||||
|
||||
Note: The cib loading system is currently asynchronous.
|
||||
*/
|
||||
- (void)loadView
|
||||
{
|
||||
if (_view)
|
||||
return;
|
||||
|
||||
// if (_cibName)
|
||||
// [CPException raise: reason:];
|
||||
|
||||
var cib = [[CPCib alloc] initWithContentsOfURL:[_cibBundle pathForResource:_cibName + @".cib"]];
|
||||
|
||||
[cib instantiateCibWithExternalNameTable:_cibExternalNameTable];
|
||||
}
|
||||
|
||||
/*!
|
||||
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)
|
||||
{
|
||||
var cibOwner = [_cibExternalNameTable objectForKey:CPCibOwner];
|
||||
|
||||
if ([cibOwner respondsToSelector:@selector(viewControllerWillLoadCib:)])
|
||||
[cibOwner viewControllerWillLoadCib:self];
|
||||
|
||||
[self loadView];
|
||||
|
||||
if (_view === nil && [cibOwner isKindOfClass:[CPDocument class]])
|
||||
[self setView:[cibOwner valueForKey:@"view"]];
|
||||
|
||||
if ([cibOwner respondsToSelector:@selector(viewControllerDidLoadCib:)])
|
||||
[cibOwner viewControllerDidLoadCib:self];
|
||||
}
|
||||
|
||||
return _view;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Manually sets the view that the controller manages.
|
||||
Setting to nil will cause -loadView to be called on all subsequent calls of -view.
|
||||
|
||||
@param aView The view this controller should represent.
|
||||
*/
|
||||
- (void)setView:(CPView)aView
|
||||
{
|
||||
_view = aView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPViewControllerViewKey = @"CPViewControllerViewKey",
|
||||
CPViewControllerTitleKey = @"CPViewControllerTitleKey",
|
||||
CPViewControllerCibNameKey = @"CPViewControllerCibNameKey",
|
||||
CPViewControllerBundleKey = @"CPViewControllerBundleKey";
|
||||
|
||||
@implementation CPViewController (CPCoding)
|
||||
|
||||
/*!
|
||||
Initializes the view controller by unarchiving data from a coder.
|
||||
@param aCoder the coder from which the data will be unarchived
|
||||
@return the initialized view controller
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_view = [aCoder decodeObjectForKey:CPViewControllerViewKey];
|
||||
_title = [aCoder decodeObjectForKey:CPViewControllerTitleKey];
|
||||
_cibName = [aCoder decodeObjectForKey:CPViewControllerCibNameKey];
|
||||
|
||||
var bundlePath = [aCoder decodeObjectForKey:CPViewControllerBundleKey];
|
||||
_cibBundle = bundlePath ? [CPBundle bundleWithPath:bundlePath] : [CPBundle mainBundle];
|
||||
|
||||
_cibExternalNameTable = [CPDictionary dictionaryWithObject:self forKey:CPCibOwner];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Archives the view controller to the provided coder.
|
||||
@param aCoder the coder to which the view controller should be archived
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeObject:_view forKey:CPViewControllerViewKey];
|
||||
[aCoder encodeObject:_title forKey:CPViewControllerTitleKey];
|
||||
[aCoder encodeObject:_cibName forKey:CPViewControllerCibNameKey];
|
||||
[aCoder encodeObject:[_cibBundle bundlePath] forKey:CPViewControllerBundleKey];
|
||||
}
|
||||
|
||||
@end
|
||||
+328
-87
@@ -20,7 +20,10 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPView.j"
|
||||
@import <AppKit/CPView.j>
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
|
||||
|
||||
// FIXME: implement these where possible:
|
||||
/*
|
||||
@@ -34,16 +37,27 @@ CPWebViewProgressEstimateChangedNotification = "CPWebViewProgressEstimateChan
|
||||
CPWebViewProgressStartedNotification = "CPWebViewProgressStartedNotification";
|
||||
CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNotification";
|
||||
|
||||
CPWebViewScrollAppKit = 1;
|
||||
CPWebViewScrollNative = 2;
|
||||
|
||||
// FIXME: somehow make CPWebView work with CPScrollView instead of native scrollbars (is this even possible?)
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
|
||||
@implementation CPWebView : CPView
|
||||
{
|
||||
CPScrollView _scrollView;
|
||||
CPView _frameView;
|
||||
|
||||
IFrame _iframe;
|
||||
CPString _mainFrameURL;
|
||||
CPArray _backwardStack;
|
||||
CPArray _forwardStack;
|
||||
|
||||
BOOL _ignoreLoadEvent;
|
||||
BOOL _ignoreLoadStart;
|
||||
BOOL _ignoreLoadEnd;
|
||||
|
||||
id _downloadDelegate;
|
||||
id _frameLoadDelegate;
|
||||
@@ -52,6 +66,14 @@ CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNoti
|
||||
id _UIDelegate;
|
||||
|
||||
CPWebScriptObject _wso;
|
||||
|
||||
CPString _url;
|
||||
CPString _html;
|
||||
|
||||
Function _loadCallback;
|
||||
|
||||
int _scrollMode;
|
||||
CGSize _scrollSize;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)frameRect frameName:(CPString)frameName groupName:(CPString)groupName
|
||||
@@ -67,69 +89,218 @@ CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNoti
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_mainFrameURL = nil;
|
||||
_backwardStack = [];
|
||||
_forwardStack = [];
|
||||
_ignoreLoadEvent = NO;
|
||||
_mainFrameURL = nil;
|
||||
_backwardStack = [];
|
||||
_forwardStack = [];
|
||||
_scrollMode = CPWebViewScrollNative;
|
||||
|
||||
_iframe = document.createElement("iframe");
|
||||
_iframe.name = "iframe_" + Math.floor(Math.random()*10000);
|
||||
_iframe.style.width = "100%";
|
||||
_iframe.style.height = "100%";
|
||||
_iframe.style.borderWidth = "0px";
|
||||
|
||||
var loadCallback = function() {
|
||||
// HACK: this block handles the case where we don't know about loads initiated by the user clicking a link
|
||||
if (!_ignoreLoadEvent)
|
||||
{
|
||||
// post the start load notification
|
||||
[self _startedLoading];
|
||||
|
||||
if (_mainFrameURL)
|
||||
[_backwardStack addObject:_mainFrameURL];
|
||||
|
||||
// FIXME: this doesn't actually get the right URL for different domains. Probably not be possible due to browser security restrictions.
|
||||
_mainFrameURL = _iframe.src;
|
||||
|
||||
[_forwardStack removeAllObjects];
|
||||
}
|
||||
_ignoreLoadEvent = NO;
|
||||
|
||||
[self _finishedLoading]
|
||||
}
|
||||
|
||||
if (_iframe.addEventListener)
|
||||
_iframe.addEventListener("load", loadCallback, false);
|
||||
else if (_iframe.attachEvent)
|
||||
_iframe.attachEvent("onload", loadCallback);
|
||||
|
||||
_DOMElement.appendChild(_iframe);
|
||||
[self _initDOMWithFrame:aFrame];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)drawsBackground
|
||||
- (id)_initDOMWithFrame:(CPRect)aFrame
|
||||
{
|
||||
return _iframe.style.backgroundColor != "";
|
||||
_ignoreLoadStart = YES;
|
||||
_ignoreLoadEnd = YES;
|
||||
|
||||
_iframe = document.createElement("iframe");
|
||||
_iframe.name = "iframe_" + Math.floor(Math.random()*10000);
|
||||
_iframe.style.width = "100%";
|
||||
_iframe.style.height = "100%";
|
||||
_iframe.style.borderWidth = "0px";
|
||||
|
||||
[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.
|
||||
_mainFrameURL = _iframe.src;
|
||||
_mainFrameURL = _iframe.src;
|
||||
|
||||
// clear the forward
|
||||
[_forwardStack removeAllObjects];
|
||||
}
|
||||
else
|
||||
_ignoreLoadStart = NO;
|
||||
|
||||
if (!_ignoreLoadEnd)
|
||||
{
|
||||
[self _finishedLoading];
|
||||
}
|
||||
else
|
||||
_ignoreLoadEnd = NO;
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
if (_iframe.addEventListener)
|
||||
_iframe.addEventListener("load", _loadCallback, false);
|
||||
else if (_iframe.attachEvent)
|
||||
_iframe.attachEvent("onload", _loadCallback);
|
||||
|
||||
|
||||
_frameView = [[CPView alloc] initWithFrame:[self bounds]];
|
||||
|
||||
_scrollView = [[CPScrollView alloc] initWithFrame:[self bounds]];
|
||||
[_scrollView setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
|
||||
[_scrollView setDocumentView:_frameView];
|
||||
|
||||
_frameView._DOMElement.appendChild(_iframe);
|
||||
|
||||
[self _setScrollMode:_scrollMode];
|
||||
|
||||
[self addSubview:_scrollView];
|
||||
}
|
||||
|
||||
- (void)setDrawsBackground:(BOOL)drawsBackround
|
||||
{
|
||||
_iframe.style.backgroundColor = drawsBackround ? "white" : "";
|
||||
|
||||
- (void)setFrameSize:(CPSize)aSize
|
||||
{
|
||||
[super setFrameSize:aSize];
|
||||
|
||||
[self _resizeWebFrame];
|
||||
}
|
||||
|
||||
- (CPString)mainFrameURL
|
||||
- (BOOL)_resizeWebFrame
|
||||
{
|
||||
return _mainFrameURL;
|
||||
if (_scrollMode === CPWebViewScrollAppKit)
|
||||
{
|
||||
if (_scrollSize)
|
||||
{
|
||||
[_frameView setFrameSize:_scrollSize];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_frameView setFrameSize:[_scrollView bounds].size];
|
||||
|
||||
// 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)
|
||||
{
|
||||
var width = win.document.body.scrollWidth,
|
||||
height = win.document.body.scrollHeight;
|
||||
|
||||
_iframe.setAttribute("width", width);
|
||||
_iframe.setAttribute("height", height);
|
||||
|
||||
[_frameView setFrameSize:CGSizeMake(width, height)];
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLog.warn("using default size 800*1600");
|
||||
|
||||
[_frameView setFrameSize:CGSizeMake(800, 1600)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setScrollMode:(int)aScrollMode
|
||||
{
|
||||
if (_scrollMode == aScrollMode)
|
||||
return;
|
||||
|
||||
[self _setScrollMode:aScrollMode];
|
||||
}
|
||||
|
||||
- (void)_setScrollMode:(int)aScrollMode
|
||||
{
|
||||
_scrollMode = aScrollMode;
|
||||
|
||||
_ignoreLoadStart = YES;
|
||||
_ignoreLoadEnd = YES;
|
||||
|
||||
var parent = _iframe.parentNode;
|
||||
parent.removeChild(_iframe);
|
||||
|
||||
if (_scrollMode === CPWebViewScrollAppKit)
|
||||
{
|
||||
[_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];
|
||||
}
|
||||
|
||||
parent.appendChild(_iframe);
|
||||
}
|
||||
|
||||
- (void)loadHTMLString:(CPString)aString
|
||||
{
|
||||
[self loadHTMLString:aString baseURL:nil];
|
||||
}
|
||||
|
||||
- (void)loadHTMLString:(CPString)aString baseURL:(CPURL)URL
|
||||
{
|
||||
// FIXME: do something with baseURL?
|
||||
|
||||
[self _setScrollMode:CPWebViewScrollAppKit];
|
||||
|
||||
[self _startedLoading];
|
||||
|
||||
_ignoreLoadStart = YES;
|
||||
_ignoreLoadEnd = NO;
|
||||
|
||||
_url = null;
|
||||
_html = aString;
|
||||
|
||||
[self _load];
|
||||
}
|
||||
|
||||
- (void)_loadMainFrameURL
|
||||
{
|
||||
[self _setScrollMode:CPWebViewScrollNative];
|
||||
|
||||
[self _startedLoading];
|
||||
|
||||
_ignoreLoadEvent = YES;
|
||||
_iframe.src = _mainFrameURL;
|
||||
_ignoreLoadStart = YES;
|
||||
_ignoreLoadEnd = NO;
|
||||
|
||||
_url = _mainFrameURL;
|
||||
_html = null;
|
||||
|
||||
[self _load];
|
||||
}
|
||||
|
||||
- (void)_load
|
||||
{
|
||||
if (_url)
|
||||
{
|
||||
_iframe.src = _url;
|
||||
}
|
||||
else if (_html)
|
||||
{
|
||||
// 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() {
|
||||
var win = [self DOMWindow];
|
||||
|
||||
win.document.write(_html);
|
||||
|
||||
window.setTimeout(_loadCallback, 1);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_startedLoading
|
||||
@@ -142,25 +313,27 @@ CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNoti
|
||||
|
||||
- (void)_finishedLoading
|
||||
{
|
||||
[self _resizeWebFrame];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPWebViewProgressFinishedNotification object:self];
|
||||
|
||||
if ([_frameLoadDelegate respondsToSelector:@selector(webView:didFinishLoadForFrame:)])
|
||||
[_frameLoadDelegate webView:self didFinishLoadForFrame:nil]; // FIXME: give this a frame somehow?
|
||||
}
|
||||
|
||||
- (CPString)mainFrameURL
|
||||
{
|
||||
return _mainFrameURL;
|
||||
}
|
||||
|
||||
- (void)setMainFrameURL:(CPString)URLString
|
||||
{
|
||||
if (_mainFrameURL)
|
||||
[_backwardStack addObject:_mainFrameURL];
|
||||
_mainFrameURL = URLString;
|
||||
[_forwardStack removeAllObjects];
|
||||
|
||||
[self _loadMainFrameURL];
|
||||
}
|
||||
|
||||
- (IBAction)takeStringURLFrom:(id)sender
|
||||
{
|
||||
[self setMainFrameURL:[sender stringValue]];
|
||||
[self _loadMainFrameURL];
|
||||
}
|
||||
|
||||
- (BOOL)goBack
|
||||
@@ -195,16 +368,6 @@ CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNoti
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (IBAction)goBack:(id)sender
|
||||
{
|
||||
[self goBack];
|
||||
}
|
||||
|
||||
- (IBAction)goForward:(id)sender
|
||||
{
|
||||
[self goForward];
|
||||
}
|
||||
|
||||
- (BOOL)canGoBack
|
||||
{
|
||||
return (_backwardStack.length > 0);
|
||||
@@ -223,17 +386,17 @@ CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNoti
|
||||
|
||||
- (void)close
|
||||
{
|
||||
_DOMElement.removeChild(_iframe);
|
||||
_iframe.parentNode.removeChild(_iframe);
|
||||
}
|
||||
|
||||
- (Window)window
|
||||
- (DOMWindow)DOMWindow
|
||||
{
|
||||
return (_iframe.contentDocument && _iframe.contentDocument.defaultView) || _iframe.contentWindow;
|
||||
}
|
||||
|
||||
- (CPWebScriptObject)windowScriptObject
|
||||
{
|
||||
var win = [self window];
|
||||
var win = [self DOMWindow];
|
||||
if (!_wso || win != [_wso window])
|
||||
{
|
||||
if (win)
|
||||
@@ -266,6 +429,60 @@ CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNoti
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (BOOL)drawsBackground
|
||||
{
|
||||
return _iframe.style.backgroundColor != "";
|
||||
}
|
||||
|
||||
- (void)setDrawsBackground:(BOOL)drawsBackround
|
||||
{
|
||||
_iframe.style.backgroundColor = drawsBackround ? "white" : "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
// IBActions
|
||||
|
||||
- (IBAction)takeStringURLFrom:(id)sender
|
||||
{
|
||||
[self setMainFrameURL:[sender stringValue]];
|
||||
}
|
||||
|
||||
- (IBAction)goBack:(id)sender
|
||||
{
|
||||
[self goBack];
|
||||
}
|
||||
|
||||
- (IBAction)goForward:(id)sender
|
||||
{
|
||||
[self goForward];
|
||||
}
|
||||
|
||||
- (IBAction)stopLoading:(id)sender
|
||||
{
|
||||
// FIXME: what to do?
|
||||
}
|
||||
|
||||
- (IBAction)reload:(id)sender
|
||||
{
|
||||
[self _loadMainFrameURL];
|
||||
}
|
||||
|
||||
- (IBAction)print:(id)sender
|
||||
{
|
||||
try
|
||||
{
|
||||
[self DOMWindow].print();
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
alert('Please click the webpage and select "Print" from the "File" menu');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Delegates:
|
||||
|
||||
// FIXME: implement more delegates, though most of these will likely never work with the iframe implementation
|
||||
@@ -311,26 +528,6 @@ CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNoti
|
||||
_UIDelegate = anObject;
|
||||
}
|
||||
|
||||
|
||||
- (void)loadHTMLString:(CPString)aString
|
||||
{
|
||||
[self loadHTMLString:aString baseURL:nil];
|
||||
}
|
||||
|
||||
- (void)loadHTMLString:(CPString)aString baseURL:(CPURL)URL
|
||||
{
|
||||
// FIXME: do something with baseURL?
|
||||
|
||||
// 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() {
|
||||
var win = [self window];
|
||||
win.document.write(aString);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -343,7 +540,7 @@ CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNoti
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_window = aWindow
|
||||
_window = aWindow;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -376,3 +573,47 @@ CPWebViewProgressFinishedNotification = "CPWebViewProgressFinishedNoti
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPWebView (CPCoding)
|
||||
|
||||
/*!
|
||||
Initializes the web view from the data in a coder.
|
||||
@param aCoder the coder from which to read the data
|
||||
@return the initialized web view
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
// FIXME: encode/decode these?
|
||||
_mainFrameURL = nil;
|
||||
_backwardStack = [];
|
||||
_forwardStack = [];
|
||||
_scrollMode = CPWebViewScrollNative;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
[self _initDOMWithFrame:[self frame]];
|
||||
#endif
|
||||
|
||||
[self setBackgroundColor:[CPColor whiteColor]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Writes out the web view's instance information to a coder.
|
||||
@param aCoder the coder to which to write the data
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
var actualSubviews = _subviews;
|
||||
_subviews = [];
|
||||
[super encodeWithCoder:aCoder];
|
||||
_subviews = actualSubviews;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
-1753
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* _CPBorderlessWindowView.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 "_CPWindowView.j"
|
||||
|
||||
|
||||
var _CPToolbarViewBackgroundColor = nil;
|
||||
|
||||
@implementation _CPBorderlessBridgeWindowView : _CPWindowView
|
||||
{
|
||||
CPView _toolbarBackgroundView;
|
||||
}
|
||||
|
||||
+ (CPColor)toolbarBackgroundColor
|
||||
{
|
||||
if (!_CPToolbarViewBackgroundColor)
|
||||
_CPToolbarViewBackgroundColor = [CPColor colorWithPatternImage:[[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[_CPBorderlessBridgeWindowView class]] pathForResource:@"_CPToolbarView/_CPToolbarViewBackground.png"] size:CGSizeMake(1.0, 59.0)]];
|
||||
|
||||
return _CPToolbarViewBackgroundColor;
|
||||
}
|
||||
|
||||
- (void)setShowsResizeIndicator:(BOOL)shouldShowResizeIndicator
|
||||
{
|
||||
// We don't ever want to show the resize indicator.
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
{
|
||||
[super tile];
|
||||
|
||||
var theWindow = [self window],
|
||||
bounds = [self bounds];
|
||||
|
||||
[[theWindow contentView] setFrame:CGRectMake(0.0, [self toolbarMaxY], CGRectGetWidth(bounds), CGRectGetHeight(bounds) - [self toolbarMaxY])];
|
||||
|
||||
if (![[theWindow toolbar] isVisible])
|
||||
{
|
||||
[_toolbarBackgroundView removeFromSuperview];
|
||||
|
||||
_toolbarBackgroundView = nil;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_toolbarBackgroundView)
|
||||
{
|
||||
_toolbarBackgroundView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
|
||||
[_toolbarBackgroundView setBackgroundColor:[[self class] toolbarBackgroundColor]];
|
||||
[_toolbarBackgroundView setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[self addSubview:_toolbarBackgroundView positioned:CPWindowBelow relativeTo:nil];
|
||||
}
|
||||
|
||||
var frame = CGRectMakeZero();
|
||||
|
||||
frame.origin = CGPointMakeCopy([self toolbarOffset]);
|
||||
frame.size = [_toolbarView frame].size;
|
||||
|
||||
[_toolbarBackgroundView setFrame:frame];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* _CPBorderlessWindowView.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 "_CPWindowView.j"
|
||||
|
||||
|
||||
@implementation _CPBorderlessWindowView : _CPWindowView
|
||||
{
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
@import "_CPWindowView.j"
|
||||
|
||||
var _CPStandardWindowViewBodyBackgroundColor = nil;
|
||||
|
||||
@implementation _CPDocModalWindowView : _CPWindowView
|
||||
{
|
||||
CPView _bodyView;
|
||||
}
|
||||
|
||||
+ (CPColor)bodyBackgroundColor
|
||||
{
|
||||
if (!_CPStandardWindowViewBodyBackgroundColor)
|
||||
_CPStandardWindowViewBodyBackgroundColor = [CPColor colorWithWhite:0.96 alpha:0.9];
|
||||
|
||||
return _CPStandardWindowViewBodyBackgroundColor;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask
|
||||
{
|
||||
self = [super initWithFrame:aFrame styleMask:aStyleMask];
|
||||
|
||||
if (self)
|
||||
{
|
||||
var theClass = [self class],
|
||||
bounds = [self 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];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
{
|
||||
return aFrameRect;
|
||||
}
|
||||
|
||||
- (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
||||
{
|
||||
return aContentRect;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* _CPHUDWindowView.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
|
||||
*/
|
||||
|
||||
var _CPHUDWindowViewBackgroundColor = nil,
|
||||
|
||||
CPHUDCloseButtonImage = nil;
|
||||
|
||||
var HUD_TITLEBAR_HEIGHT = 26.0;
|
||||
|
||||
@implementation _CPHUDWindowView : _CPWindowView
|
||||
{
|
||||
CPView _toolbarView;
|
||||
|
||||
CPTextField _titleField;
|
||||
CPButton _closeButton;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
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)]
|
||||
]]];
|
||||
|
||||
_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)];
|
||||
}
|
||||
|
||||
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
{
|
||||
var contentRect = CGRectMakeCopy(aFrameRect),
|
||||
titleBarHeight = HUD_TITLEBAR_HEIGHT;
|
||||
|
||||
contentRect.origin.y += titleBarHeight;
|
||||
contentRect.size.height -= titleBarHeight;
|
||||
|
||||
return contentRect;
|
||||
}
|
||||
|
||||
+ (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
||||
{
|
||||
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 setBordered:NO];
|
||||
|
||||
[_closeButton setImage:_CPHUDWindowViewCloseImage];
|
||||
[_closeButton setAlternateImage:_CPHUDWindowViewCloseActiveImage];
|
||||
|
||||
[self addSubview:_closeButton];
|
||||
}
|
||||
|
||||
[self setResizeIndicatorOffset:CGSizeMake(5.0, 5.0)];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
[_closeButton setTarget:[self window]];
|
||||
[_closeButton setAction:@selector(performClose:)];
|
||||
}
|
||||
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
{
|
||||
[_titleField setStringValue:aTitle];
|
||||
}
|
||||
|
||||
- (_CPToolbarView)toolbarView
|
||||
{
|
||||
return _toolbarView;
|
||||
}
|
||||
|
||||
- (CPColor)toolbarLabelColor
|
||||
{
|
||||
return [CPColor whiteColor];
|
||||
}
|
||||
|
||||
- (CPColor)toolbarLabelShadowColor
|
||||
{
|
||||
return [CPColor blackColor];
|
||||
}
|
||||
|
||||
- (CGSize)toolbarOffset
|
||||
{
|
||||
return CGSizeMake(0.0, HUD_TITLEBAR_HEIGHT);
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
{
|
||||
[super tile];
|
||||
|
||||
var theWindow = [self window],
|
||||
bounds = [self bounds],
|
||||
width = CGRectGetWidth(bounds);
|
||||
|
||||
[_titleField setFrame:CGRectMake(20.0, 3.0, width - 40.0, CGRectGetHeight([_titleField frame]))];
|
||||
|
||||
var maxY = [self toolbarMaxY];
|
||||
|
||||
[[theWindow contentView] setFrameOrigin:CGPointMake(0.0, maxY, width, CGRectGetHeight(bounds) - maxY)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* _CPStandardWindowView.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 "_CPWindowView.j"
|
||||
|
||||
var GRADIENT_HEIGHT = 41.0;
|
||||
|
||||
var _CPTexturedWindowHeadGradientColor = nil,
|
||||
_CPTexturedWindowHeadSolidColor = nil;
|
||||
|
||||
@implementation _CPTexturedWindowHeadView : CPView
|
||||
{
|
||||
CPView _gradientView;
|
||||
CPView _solidView;
|
||||
CPView _dividerView;
|
||||
}
|
||||
|
||||
+ (CPColor)gradientColor
|
||||
{
|
||||
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)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardTop1.png"] size:CGSizeMake(1.0, 41.0)],
|
||||
[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardTop2.png"] size:CGSizeMake(6.0, 41.0)]
|
||||
]
|
||||
isVertical:NO
|
||||
]];
|
||||
}
|
||||
|
||||
return _CPTexturedWindowHeadGradientColor;
|
||||
}
|
||||
|
||||
+ (CPColor)solidColor
|
||||
{
|
||||
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)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPStandardWindowViewBodyBackgroundColor = nil,
|
||||
_CPStandardWindowViewDividerBackgroundColor = nil,
|
||||
_CPStandardWindowViewTitleBackgroundColor = nil,
|
||||
_CPStandardWindowViewCloseButtonImage = nil,
|
||||
_CPStandardWindowViewCloseButtonHighlightedImage = nil,
|
||||
_CPStandardWindowViewCloseButtonUnsavedImage = nil,
|
||||
_CPStandardWindowViewCloseButtonUnsavedHighlightedImage = nil,
|
||||
_CPStandardWindowViewMinimizeButtonImage = nil,
|
||||
_CPStandardWindowViewMinimizeButtonHighlightedImage = nil;
|
||||
|
||||
var STANDARD_GRADIENT_HEIGHT = 41.0;
|
||||
STANDARD_TITLEBAR_HEIGHT = 25.0;
|
||||
|
||||
@implementation _CPStandardWindowView : _CPWindowView
|
||||
{
|
||||
_CPTexturedWindowHeadView _headView;
|
||||
CPView _dividerView;
|
||||
CPView _bodyView;
|
||||
CPView _toolbarView;
|
||||
|
||||
CPTextField _titleField;
|
||||
CPButton _closeButton;
|
||||
CPButton _minimizeButton;
|
||||
|
||||
BOOL _isDocumentEdited;
|
||||
}
|
||||
|
||||
+ (CPColor)bodyBackgroundColor
|
||||
{
|
||||
if (!_CPStandardWindowViewBodyBackgroundColor)
|
||||
_CPStandardWindowViewBodyBackgroundColor = [CPColor colorWithWhite:0.96 alpha:1.0];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
+ (CPColor)titleColor
|
||||
{
|
||||
if (!_CPStandardWindowViewTitleBackgroundColor)
|
||||
_CPStandardWindowViewTitleBackgroundColor = [CPColor colorWithCalibratedRed:44.0 / 255.0 green:44.0 / 255.0 blue:44.0 / 255.0 alpha:1.0];
|
||||
|
||||
return _CPStandardWindowViewTitleBackgroundColor;
|
||||
}
|
||||
|
||||
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
{
|
||||
var contentRect = CGRectMakeCopy(aFrameRect),
|
||||
titleBarHeight = [self titleBarHeight] + 1.0;
|
||||
|
||||
contentRect.origin.y += titleBarHeight;
|
||||
contentRect.size.height -= titleBarHeight;
|
||||
|
||||
return contentRect;
|
||||
}
|
||||
|
||||
+ (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
||||
{
|
||||
var frameRect = CGRectMakeCopy(aContentRect),
|
||||
titleBarHeight = [self titleBarHeight] + 1.0;
|
||||
|
||||
frameRect.origin.y -= titleBarHeight;
|
||||
frameRect.size.height += titleBarHeight;
|
||||
|
||||
return frameRect;
|
||||
}
|
||||
|
||||
+ (float)titleBarHeight
|
||||
{
|
||||
return STANDARD_TITLEBAR_HEIGHT;
|
||||
}
|
||||
|
||||
- (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
- (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
||||
{
|
||||
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)
|
||||
{
|
||||
var bundle = [CPBundle bundleForClass:[CPWindow class]];
|
||||
|
||||
_CPStandardWindowViewCloseButtonImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButton.png"] size:CGSizeMake(16.0, 16.0)];
|
||||
_CPStandardWindowViewCloseButtonHighlightedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButtonHighlighted.png"] size:CGSizeMake(16.0, 16.0)];
|
||||
_CPStandardWindowViewCloseButtonUnsavedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButtonUnsaved.png"] size:CGSizeMake(16.0, 16.0)];
|
||||
_CPStandardWindowViewCloseButtonUnsavedHighlightedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardCloseButtonUnsavedHighlighted.png"] size:CGSizeMake(16.0, 16.0)];
|
||||
}
|
||||
|
||||
_closeButton = [[CPButton alloc] initWithFrame:CGRectMake(8.0, 7.0, 16.0, 16.0)];
|
||||
|
||||
[_closeButton setBordered:NO];
|
||||
[self _updateCloseButton];
|
||||
|
||||
[self addSubview:_closeButton];
|
||||
}
|
||||
|
||||
if (_styleMask & CPMiniaturizableWindowMask && ![CPPlatform isBrowser])
|
||||
{
|
||||
if (!_CPStandardWindowViewMinimizeButtonImage)
|
||||
{
|
||||
var bundle = [CPBundle bundleForClass:[CPWindow class]];
|
||||
|
||||
_CPStandardWindowViewMinimizeButtonImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardMinimizeButton.png"] size:CGSizeMake(16.0, 16.0)];
|
||||
_CPStandardWindowViewMinimizeButtonHighlightedImage = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPWindow/Standard/CPWindowStandardMinimizeButtonHighlighted.png"] size:CGSizeMake(16.0, 16.0)];
|
||||
}
|
||||
|
||||
_minimizeButton = [[CPButton alloc] initWithFrame:CGRectMake(27.0, 7.0, 16.0, 16.0)];
|
||||
|
||||
[_minimizeButton setBordered:NO];
|
||||
|
||||
[_minimizeButton setImage:_CPStandardWindowViewMinimizeButtonImage];
|
||||
[_minimizeButton setAlternateImage:_CPStandardWindowViewMinimizeButtonHighlightedImage];
|
||||
|
||||
[self addSubview:_minimizeButton];
|
||||
}
|
||||
|
||||
[self tile];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
[_closeButton setTarget:[self window]];
|
||||
[_closeButton setAction:@selector(performClose:)];
|
||||
|
||||
[_minimizeButton setTarget:[self window]];
|
||||
[_minimizeButton setAction:@selector(performMiniaturize:)];
|
||||
}
|
||||
|
||||
- (CGSize)toolbarOffset
|
||||
{
|
||||
return CGSizeMake(0.0, [[self class] titleBarHeight]);
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
{
|
||||
[super tile];
|
||||
|
||||
var theWindow = [self window],
|
||||
bounds = [self bounds],
|
||||
width = CGRectGetWidth(bounds);
|
||||
|
||||
[_headView setFrameSize:CGSizeMake(width, [self toolbarMaxY])];
|
||||
[_dividerView setFrame:CGRectMake(0.0, CGRectGetMaxY([_headView frame]), width, 1.0)];
|
||||
|
||||
var dividerMaxY = CGRectGetMaxY([_dividerView frame]);
|
||||
|
||||
[_bodyView setFrame:CGRectMake(0.0, dividerMaxY, width, CGRectGetHeight(bounds) - dividerMaxY)];
|
||||
|
||||
var leftOffset = 8;
|
||||
|
||||
if (_closeButton)
|
||||
leftOffset += 19.0;
|
||||
if (_minimizeButton)
|
||||
leftOffset += 19.0;
|
||||
|
||||
[_titleField setFrame:CGRectMake(leftOffset, 5.0, width - leftOffset*2.0, CGRectGetHeight([_titleField frame]))];
|
||||
|
||||
[[theWindow contentView] setFrameOrigin:CGPointMake(0.0, CGRectGetMaxY([_dividerView frame]))];
|
||||
}
|
||||
/*
|
||||
- (void)setAnimatingToolbar:(BOOL)isAnimatingToolbar
|
||||
{
|
||||
[super setAnimatingToolbar:isAnimatingToolbar];
|
||||
|
||||
if ([self isAnimatingToolbar])
|
||||
{
|
||||
[[self toolbarView] setAutoresizingMask:CPViewHeightSizable];
|
||||
|
||||
[_headView setAutoresizingMask:CPViewHeightSizable];
|
||||
[_dividerView setAutoresizingMask:CPViewMinYMargin];
|
||||
[_bodyView setAutoresizingMask:CPViewMinYMargin];
|
||||
|
||||
[[[self window] contentView] setAutoresizingMask:CPViewNotSizable];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[self toolbarView] setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
[_headView setAutoresizingMask:CPViewWidthSizable];
|
||||
[_dividerView setAutoresizingMask:CPViewWidthSizable];
|
||||
[_bodyView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
[[[self window] contentView] setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
- (void)_updateCloseButton
|
||||
{
|
||||
if (_isDocumentEdited)
|
||||
{
|
||||
[_closeButton setImage:_CPStandardWindowViewCloseButtonUnsavedImage];
|
||||
[_closeButton setAlternateImage:_CPStandardWindowViewCloseButtonUnsavedHighlightedImage];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_closeButton setImage:_CPStandardWindowViewCloseButtonImage];
|
||||
[_closeButton setAlternateImage:_CPStandardWindowViewCloseButtonHighlightedImage];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setDocumentEdited:(BOOL)isEdited
|
||||
{
|
||||
_isDocumentEdited = isEdited;
|
||||
[self _updateCloseButton];
|
||||
}
|
||||
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
{
|
||||
[_titleField setStringValue:aTitle];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if (CGRectContainsPoint([_headView frame], [self convertPoint:[anEvent locationInWindow] fromView:nil]))
|
||||
return [self trackMoveWithEvent:anEvent];
|
||||
|
||||
[super mouseDown:anEvent];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* _CPWindowView.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 "CPView.j"
|
||||
@import "CPImageView.j"
|
||||
|
||||
#include "../CoreGraphics/CGGeometry.h"
|
||||
|
||||
|
||||
var _CPWindowViewResizeIndicatorImage = nil;
|
||||
|
||||
@implementation _CPWindowView : CPView
|
||||
{
|
||||
unsigned _styleMask;
|
||||
|
||||
CPImageView _resizeIndicator;
|
||||
CGSize _resizeIndicatorOffset;
|
||||
|
||||
CPView _toolbarView;
|
||||
// BOOL _isAnimatingToolbar;
|
||||
|
||||
CGRect _resizeFrame;
|
||||
CGPoint _mouseDraggedPoint;
|
||||
|
||||
CGRect _cachedScreenFrame;
|
||||
}
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [_CPWindowView class])
|
||||
return;
|
||||
|
||||
_CPWindowViewResizeIndicatorImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPWindow class]] pathForResource:@"_CPWindowView/_CPWindowViewResizeIndicator.png"] size:CGSizeMake(12.0, 12.0)];
|
||||
}
|
||||
|
||||
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
{
|
||||
return CGRectMakeCopy(aFrameRect);
|
||||
}
|
||||
|
||||
+ (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
||||
{
|
||||
return CGRectMakeCopy(aContentRect);
|
||||
}
|
||||
|
||||
- (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
{
|
||||
return [[self class] contentRectForFrameRect:aFrameRect];
|
||||
}
|
||||
|
||||
- (CGRect)frameRectForContentRect:(CGRect)aContentRect
|
||||
{
|
||||
return [[self class] frameRectForContentRect:aContentRect];
|
||||
}
|
||||
|
||||
- (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);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setDocumentEdited:(BOOL)isEdited
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setTitle:(CPString)aTitle
|
||||
{
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
var theWindow = [self window];
|
||||
|
||||
if ((_styleMask & CPResizableWindowMask) && _resizeIndicator)
|
||||
{
|
||||
// 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];
|
||||
}
|
||||
|
||||
- (void)trackResizeWithEvent:(CPEvent)anEvent
|
||||
{
|
||||
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));
|
||||
|
||||
if (theWindow._isSheet && theWindow._parentView && (theWindow._frame.size.width !== newSize.width))
|
||||
[theWindow._parentView _setAttachedSheetFrameOrigin];
|
||||
|
||||
[theWindow setFrameSize:newSize];
|
||||
}
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackResizeWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
}
|
||||
|
||||
- (CGPoint)_pointWithinScreenFrame:(CGPoint)aPoint
|
||||
{
|
||||
// FIXME: this is WRONG, all of this is WRONG
|
||||
if (![CPPlatform isBrowser])
|
||||
return aPoint;
|
||||
|
||||
var visibleFrame = _cachedScreenFrame;
|
||||
|
||||
if (!visibleFrame)
|
||||
visibleFrame = [[CPPlatformWindow primaryPlatformWindow] visibleFrame];
|
||||
|
||||
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);
|
||||
|
||||
return restrictedPoint;
|
||||
}
|
||||
|
||||
- (void)trackMoveWithEvent:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type];
|
||||
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
_cachedScreenFrame = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
else if (type === CPLeftMouseDown)
|
||||
{
|
||||
_mouseDraggedPoint = [[self window] convertBaseToGlobal:[anEvent locationInWindow]];
|
||||
_cachedScreenFrame = [[CPPlatformWindow primaryPlatformWindow] visibleFrame];
|
||||
}
|
||||
|
||||
else if (type === CPLeftMouseDragged)
|
||||
{
|
||||
var theWindow = [self window],
|
||||
frame = [theWindow frame],
|
||||
location = [theWindow convertBaseToGlobal:[anEvent locationInWindow]],
|
||||
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];
|
||||
}
|
||||
|
||||
- (void)setShowsResizeIndicator:(BOOL)shouldShowResizeIndicator
|
||||
{
|
||||
if (shouldShowResizeIndicator)
|
||||
{
|
||||
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];
|
||||
|
||||
[self addSubview:_resizeIndicator];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_resizeIndicator removeFromSuperview];
|
||||
|
||||
_resizeIndicator = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (CPImage)showsResizeIndicator
|
||||
{
|
||||
return _resizeIndicator !== nil;
|
||||
}
|
||||
|
||||
- (void)setResizeIndicatorOffset:(CGSize)anOffset
|
||||
{
|
||||
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)];
|
||||
}
|
||||
|
||||
- (CGSize)resizeIndicatorOffset
|
||||
{
|
||||
return _resizeIndicatorOffset;
|
||||
}
|
||||
|
||||
- (void)windowDidChangeDocumentEdited
|
||||
{
|
||||
}
|
||||
|
||||
- (void)windowDidChangeDocumentSaving
|
||||
{
|
||||
}
|
||||
|
||||
- (BOOL)showsToolbar
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CGSize)toolbarOffset
|
||||
{
|
||||
return CGSizeMakeZero();
|
||||
}
|
||||
|
||||
- (CPColor)toolbarLabelColor
|
||||
{
|
||||
return [CPColor blackColor];
|
||||
}
|
||||
|
||||
- (float)toolbarMaxY
|
||||
{
|
||||
if (!_toolbarView || [_toolbarView isHidden])
|
||||
return [self toolbarOffset].height;
|
||||
|
||||
return CGRectGetMaxY([_toolbarView frame]);
|
||||
}
|
||||
|
||||
- (_CPToolbarView)toolbarView
|
||||
{
|
||||
return _toolbarView;
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
{
|
||||
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)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)noteToolbarChanged
|
||||
{
|
||||
var theWindow = [self window],
|
||||
toolbar = [theWindow toolbar],
|
||||
toolbarView = [toolbar _toolbarView];
|
||||
|
||||
if (_toolbarView !== toolbarView)
|
||||
{
|
||||
[_toolbarView removeFromSuperview];
|
||||
|
||||
if (toolbarView)
|
||||
{
|
||||
[toolbarView removeFromSuperview];
|
||||
[toolbarView FIXME_setIsHUD:_styleMask & CPHUDBackgroundWindowMask];
|
||||
|
||||
[self addSubview:toolbarView];
|
||||
}
|
||||
|
||||
_toolbarView = toolbarView;
|
||||
}
|
||||
|
||||
[toolbarView setHidden:![self showsToolbar] || ![toolbar isVisible]];
|
||||
|
||||
if (theWindow)
|
||||
{
|
||||
var contentRect = [self convertRect:[[theWindow contentView] frame] toView:nil];
|
||||
|
||||
contentRect.origin = [theWindow convertBaseToGlobal:contentRect.origin];
|
||||
|
||||
[self setAutoresizesSubviews:NO];
|
||||
[theWindow setFrame:[theWindow frameRectForContentRect:contentRect]];
|
||||
[self setAutoresizesSubviews:YES];
|
||||
}
|
||||
|
||||
[self tile];
|
||||
}
|
||||
/*
|
||||
- (void)setAnimatingToolbar:(BOOL)isAnimatingToolbar
|
||||
{
|
||||
_isAnimatingToolbar = isAnimatingToolbar;
|
||||
}
|
||||
|
||||
- (BOOL)isAnimatingToolbar
|
||||
{
|
||||
return _isAnimatingToolbar;
|
||||
}
|
||||
*/
|
||||
|
||||
- (void)didAddSubview:(CPView)aView
|
||||
{
|
||||
if (!_resizeIndicator || aView === _resizeIndicator)
|
||||
return;
|
||||
|
||||
[self addSubview:_resizeIndicator];
|
||||
}
|
||||
|
||||
@end
|
||||
+247
-64
@@ -20,30 +20,48 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
import "CPResponder.j"
|
||||
import "CPWindow.j"
|
||||
import "CPDocument.j"
|
||||
@import "CPResponder.j"
|
||||
@import "CPWindow.j"
|
||||
@import "CPDocument.j"
|
||||
|
||||
#include "Platform/Platform.h"
|
||||
|
||||
/*
|
||||
An instance of a <objj>CPWindowController</objj> manages a <objj>CPWindow</objj>. It has methods
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPWindowController
|
||||
|
||||
An instance of a CPWindowController manages a CPWindow. It has methods
|
||||
that get called when the window is loading, and after the window has loaded. In the
|
||||
Model-View-Controller method of program design, the <objj>CPWindowController</objj> would be
|
||||
considered the 'Controller' and the <objj>CPWindow</objj> the 'Model.'
|
||||
Model-View-Controller method of program design, the CPWindowController would be
|
||||
considered the 'Controller' and the CPWindow the 'Model.'
|
||||
*/
|
||||
@implementation CPWindowController : CPResponder
|
||||
{
|
||||
id _owner;
|
||||
CPWindow _window;
|
||||
CPDocument _document;
|
||||
CPString _windowCibName;
|
||||
CPWindow _window;
|
||||
|
||||
CPArray _documents;
|
||||
CPDocument _document;
|
||||
BOOL _shouldCloseDocument;
|
||||
BOOL _supportsMultipleDocuments;
|
||||
|
||||
id _cibOwner;
|
||||
CPString _windowCibName;
|
||||
CPString _windowCibPath;
|
||||
|
||||
CPViewController _viewController;
|
||||
CPView _viewControllerContainerView;
|
||||
}
|
||||
|
||||
/*
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithWindow:nil];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the controller with a window.
|
||||
@param aWindow the window to control
|
||||
@return the initialzed window controller
|
||||
@@ -51,18 +69,21 @@ import "CPDocument.j"
|
||||
- (id)initWithWindow:(CPWindow)aWindow
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setWindow:aWindow];
|
||||
|
||||
[self setShouldCloseDocument:NO];
|
||||
|
||||
[self setNextResponder:CPApp];
|
||||
|
||||
_documents = [];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the controller with a Capppuccino Interface Builder name.
|
||||
@param aWindowCibName the cib name of the window to control
|
||||
@return the initialized window controller
|
||||
@@ -72,7 +93,7 @@ import "CPDocument.j"
|
||||
return [self initWithWindowCibName:aWindowCibName owner:self];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Initializes the controller with a cafe name.
|
||||
@param aWindowCibName the cib name of the window to control
|
||||
@param anOwner the owner of the cib file
|
||||
@@ -80,36 +101,46 @@ import "CPDocument.j"
|
||||
*/
|
||||
- (id)initWithWindowCibName:(CPString)aWindowCibName owner:(id)anOwner
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
self = [self initWithWindow:nil];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_owner = anOwner;
|
||||
_cibOwner = anOwner;
|
||||
_windowCibName = aWindowCibName;
|
||||
|
||||
[self setNextResponder:CPApp];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
- (id)initWithWindowCibPath:(CPString)aWindowCibPath owner:(id)anOwner
|
||||
{
|
||||
self = [self initWithWindow:nil];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_cibOwner = anOwner;
|
||||
_windowCibPath = aWindowCibPath;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Loads the window
|
||||
*/
|
||||
- (void)loadWindow
|
||||
{
|
||||
[self windowWillLoad];
|
||||
//FIXME: ACTUALLY LOAD WINDOW!!!
|
||||
[self setWindow:CPApp._keyWindow = [[CPWindow alloc] initWithContentRect:CPRectMakeZero() styleMask:CPBorderlessBridgeWindowMask|CPTitledWindowMask|CPClosableWindowMask|CPResizableWindowMask]];
|
||||
|
||||
[self windowDidLoad];
|
||||
if (_window)
|
||||
return;
|
||||
|
||||
[[CPBundle bundleForClass:[_cibOwner class]] loadCibFile:[self windowCibPath] externalNameTable:[CPDictionary dictionaryWithObject:_cibOwner forKey:CPCibOwner]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Shows the window.
|
||||
@param aSender the object requesting the show
|
||||
*/
|
||||
- (CFAction)showWindow:(id)aSender
|
||||
- (@action)showWindow:(id)aSender
|
||||
{
|
||||
var theWindow = [self window];
|
||||
|
||||
@@ -119,73 +150,87 @@ import "CPDocument.j"
|
||||
[theWindow makeKeyAndOrderFront:aSender];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the window has been loaded. Specifically,
|
||||
if <objj>loadWindow</objj> has been called.
|
||||
/*!
|
||||
Returns \c YES if the window has been loaded. Specifically,
|
||||
if loadWindow has been called.
|
||||
*/
|
||||
- (BOOL)isWindowLoaded
|
||||
{
|
||||
return _window;
|
||||
return _window !== nil;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the window this object controls.
|
||||
*/
|
||||
- (CPWindow)window
|
||||
{
|
||||
if (!_window)
|
||||
[self loadWindow];
|
||||
{
|
||||
[self windowWillLoad];
|
||||
[_document windowControllerWillLoadCib:self];
|
||||
|
||||
[self loadWindow];
|
||||
|
||||
if (_window === nil && [_cibOwner isKindOfClass:[CPDocument class]])
|
||||
[self setWindow:[_cibOwner valueForKey:@"window"]];
|
||||
|
||||
[self windowDidLoad];
|
||||
[_document windowControllerDidLoadCib:self];
|
||||
|
||||
[self synchronizeWindowTitleWithDocumentName];
|
||||
}
|
||||
|
||||
return _window;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the window to be controlled.
|
||||
@param aWindow the new window to control
|
||||
*/
|
||||
- (void)setWindow:(CPWindow)aWindow
|
||||
{
|
||||
[_window setWindowController:nil];
|
||||
|
||||
_window = aWindow;
|
||||
|
||||
|
||||
[_window setWindowController:self];
|
||||
[_window setNextResponder:self];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
The method notifies the controller that it's window has loaded.
|
||||
*/
|
||||
- (void)windowDidLoad
|
||||
{
|
||||
[_document windowControllerDidLoadNib:self];
|
||||
|
||||
[self synchronizeWindowTitleWithDocumentName];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
The method notifies the controller that it's window is about to load.
|
||||
*/
|
||||
- (void)windowWillLoad
|
||||
{
|
||||
[_document windowControllerWillLoadNib:self];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the document that is inside the controlled window.
|
||||
@param aDocument the document in the controlled window
|
||||
*/
|
||||
- (void)setDocument:(CPDocument)aDocument
|
||||
{
|
||||
if (_document == aDocument)
|
||||
if (_document === aDocument)
|
||||
return;
|
||||
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter];
|
||||
|
||||
|
||||
if (_document)
|
||||
{
|
||||
if (![self supportsMultipleDocuments])
|
||||
[self removeDocument:_document];
|
||||
|
||||
[defaultCenter removeObserver:self
|
||||
name:CPDocumentWillSaveNotification
|
||||
object:_document];
|
||||
|
||||
|
||||
[defaultCenter removeObserver:self
|
||||
name:CPDocumentDidSaveNotification
|
||||
object:_document];
|
||||
@@ -194,16 +239,18 @@ import "CPDocument.j"
|
||||
name:CPDocumentDidFailToSaveNotification
|
||||
object:_document];
|
||||
}
|
||||
|
||||
|
||||
_document = aDocument;
|
||||
|
||||
|
||||
if (_document)
|
||||
{
|
||||
[self addDocument:_document];
|
||||
|
||||
[defaultCenter addObserver:self
|
||||
selector:@selector(_documentWillSave:)
|
||||
name:CPDocumentWillSaveNotification
|
||||
object:_document];
|
||||
|
||||
|
||||
[defaultCenter addObserver:self
|
||||
selector:@selector(_documentDidSave:)
|
||||
name:CPDocumentDidSaveNotification
|
||||
@@ -213,13 +260,113 @@ import "CPDocument.j"
|
||||
selector:@selector(_documentDidFailToSave:)
|
||||
name:CPDocumentDidFailToSaveNotification
|
||||
object:_document];
|
||||
|
||||
|
||||
[self setDocumentEdited:[_document isDocumentEdited]];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var viewController = [_document viewControllerForWindowController:self];
|
||||
|
||||
if (viewController)
|
||||
[self setViewController:viewController];
|
||||
|
||||
[self synchronizeWindowTitleWithDocumentName];
|
||||
}
|
||||
|
||||
- (void)setSupportsMultipleDocuments:(BOOL)shouldSupportMultipleDocuments
|
||||
{
|
||||
_supportsMultipleDocuments = shouldSupportMultipleDocuments;
|
||||
}
|
||||
|
||||
- (BOOL)supportsMultipleDocuments
|
||||
{
|
||||
return _supportsMultipleDocuments;
|
||||
}
|
||||
|
||||
- (void)addDocument:(CPDocument)aDocument
|
||||
{
|
||||
if (aDocument && ![_documents containsObject:aDocument])
|
||||
[_documents addObject:aDocument];
|
||||
}
|
||||
|
||||
- (void)removeDocument:(CPDocument)aDocument
|
||||
{
|
||||
var index = [_documents indexOfObjectIdenticalTo:aDocument];
|
||||
|
||||
if (index === CPNotFound)
|
||||
return;
|
||||
|
||||
[_documents removeObjectAtIndex:index];
|
||||
|
||||
if (_document === aDocument && [_documents count])
|
||||
[self setDocument:[_documents objectAtIndex:MIN(index, [_documents count] - 1)]];
|
||||
}
|
||||
|
||||
- (void)removeDocumentAndCloseIfNecessary:(CPDocument)aDocument
|
||||
{
|
||||
[self removeDocument:aDocument];
|
||||
|
||||
if (![_documents count])
|
||||
[self close];
|
||||
}
|
||||
|
||||
- (CPArray)documents
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
- (void)viewControllerContainerView
|
||||
{
|
||||
return _viewControllerContainerView;
|
||||
}
|
||||
|
||||
- (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];
|
||||
}
|
||||
}
|
||||
|
||||
- (CPViewController)viewController
|
||||
{
|
||||
return _viewController;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (void)_documentWillSave:(CPNotification)aNotification
|
||||
{
|
||||
@@ -238,7 +385,7 @@ import "CPDocument.j"
|
||||
[[self window] setDocumentSaving:NO];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the document in the controlled window.
|
||||
*/
|
||||
- (CPDocument)document
|
||||
@@ -246,30 +393,66 @@ import "CPDocument.j"
|
||||
return _document;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets whether the document has unsaved changes. The window can use this as a hint to
|
||||
@param isEdited <code>YES</code> means the document has unsaved changes.
|
||||
@param isEdited \c YES means the document has unsaved changes.
|
||||
*/
|
||||
- (void)setDocumentEdited:(BOOL)isEdited
|
||||
{
|
||||
[[self window] setDocumentEdited:isEdited];
|
||||
}
|
||||
|
||||
- (void)close
|
||||
{
|
||||
[[self window] close];
|
||||
}
|
||||
|
||||
- (void)setShouldCloseDocument:(BOOL)shouldCloseDocument
|
||||
{
|
||||
_shouldCloseDocument = shouldCloseDocument;
|
||||
}
|
||||
|
||||
- (BOOL)shouldCloseDocument
|
||||
{
|
||||
return _shouldCloseDocument;
|
||||
}
|
||||
|
||||
- (id)owner
|
||||
{
|
||||
return _cibOwner;
|
||||
}
|
||||
|
||||
- (CPString)windowCibName
|
||||
{
|
||||
if (_windowCibName)
|
||||
return _windowCibName;
|
||||
|
||||
return [[_windowCibPath lastPathComponent] stringByDeletingPathExtension];
|
||||
}
|
||||
|
||||
- (CPString)windowCibPath
|
||||
{
|
||||
if (_windowCibPath)
|
||||
return _windowCibPath;
|
||||
|
||||
return [[CPBundle bundleForClass:[_cibOwner class]] pathForResource:_windowCibName + @".cib"];
|
||||
}
|
||||
|
||||
// Setting and Getting Window Attributes
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the title of the window as the name of the document.
|
||||
*/
|
||||
- (void)synchronizeWindowTitleWithDocumentName
|
||||
{
|
||||
if (!_document || !_window)
|
||||
return;
|
||||
|
||||
|
||||
// [_window setRepresentedFilename:];
|
||||
[_window setTitle:[self windowTitleForDocumentDisplayName:[_document displayName]]];
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the window title based on the document's name.
|
||||
@param aDisplayName the document's filename
|
||||
*/
|
||||
|
||||
+138
-66
@@ -20,101 +20,173 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
import <Foundation/CPURLConnection.j>
|
||||
import <Foundation/CPURLRequest.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPURLConnection.j>
|
||||
@import <Foundation/CPURLRequest.j>
|
||||
|
||||
@import "_CPCibClassSwapper.j"
|
||||
@import "_CPCibCustomObject.j"
|
||||
@import "_CPCibCustomResource.j"
|
||||
@import "_CPCibCustomView.j"
|
||||
@import "_CPCibKeyedUnarchiver.j"
|
||||
@import "_CPCibObjectData.j"
|
||||
@import "_CPCibProxyObject.j"
|
||||
@import "_CPCibWindowTemplate.j"
|
||||
|
||||
import "_CPCibCustomObject.j"
|
||||
import "_CPCibCustomView.j"
|
||||
import "_CPCibKeyedUnarchiver.j"
|
||||
import "_CPCibObjectData.j"
|
||||
import "_CPCibWindowTemplate.j"
|
||||
|
||||
CPCibOwner = @"CPCibOwner",
|
||||
CPCibTopLevelObjects = @"CPCibTopLevelObjects";
|
||||
CPCibTopLevelObjects = @"CPCibTopLevelObjects",
|
||||
CPCibReplacementClasses = @"CPCibReplacementClasses",
|
||||
CPCibExternalObjects = @"CPCibExternalObjects";
|
||||
|
||||
var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
|
||||
@implementation CPCib : CPObject
|
||||
{
|
||||
CPData _data;
|
||||
CPData _data;
|
||||
CPBundle _bundle;
|
||||
BOOL _awakenCustomResources;
|
||||
|
||||
id _loadDelegate;
|
||||
}
|
||||
|
||||
- (id)initWithContentsOfURL:(CPURL)aURL
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_data = [CPURLConnection sendSynchronousRequest:[CPURLRequest requestWithURL:aURL] returningResponse:nil error:nil];
|
||||
|
||||
_awakenCustomResources = YES;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithContentsOfURL:(CPURL)aURL loadDelegate:(id)aLoadDelegate
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:aURL] delegate:self];
|
||||
|
||||
_awakenCustomResources = YES;
|
||||
|
||||
_loadDelegate = aLoadDelegate;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCibNamed:(CPString)aName bundle:(CPBundle)aBundle
|
||||
{
|
||||
if (![aName hasSuffix:@".cib"])
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
|
||||
self = [self initWithContentsOfURL:[aBundle || [CPBundle mainBundle] pathForResource:aName]];
|
||||
|
||||
if (self)
|
||||
_bundle = aBundle;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCibNamed:(CPString)aName bundle:(CPBundle)aBundle loadDelegate:(id)aLoadDelegate
|
||||
{
|
||||
if (![aName hasSuffix:@".cib"])
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
|
||||
self = [self initWithContentsOfURL:[aBundle || [CPBundle mainBundle] pathForResource:aName] loadDelegate:aLoadDelegate];
|
||||
|
||||
if (self)
|
||||
_bundle = aBundle;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_setAwakenCustomResources:(BOOL)shouldAwakenCustomResources
|
||||
{
|
||||
_awakenCustomResources = shouldAwakenCustomResources;
|
||||
}
|
||||
|
||||
- (BOOL)_awakenCustomResources
|
||||
{
|
||||
return _awakenCustomResources;
|
||||
}
|
||||
|
||||
- (BOOL)instantiateCibWithExternalNameTable:(CPDictionary)anExternalNameTable
|
||||
{
|
||||
var unarchiver = [[_CPCibKeyedUnarchiver alloc] initForReadingWithData:_data],
|
||||
objectData = [unarchiver decodeObjectForKey:CPCibObjectDataKey];
|
||||
var bundle = _bundle,
|
||||
owner = [anExternalNameTable objectForKey:CPCibOwner];
|
||||
|
||||
if (!bundle && owner)
|
||||
bundle = [CPBundle bundleForClass:[owner class]];
|
||||
|
||||
var unarchiver = [[_CPCibKeyedUnarchiver alloc] initForReadingWithData:_data bundle:bundle awakenCustomResources:_awakenCustomResources],
|
||||
replacementClasses = [anExternalNameTable objectForKey:CPCibReplacementClasses];
|
||||
|
||||
if (replacementClasses)
|
||||
{
|
||||
var key = nil,
|
||||
keyEnumerator = [replacementClasses keyEnumerator];
|
||||
|
||||
while (key = [keyEnumerator nextObject])
|
||||
[unarchiver setClass:[replacementClasses objectForKey:key] forClassName:key];
|
||||
}
|
||||
|
||||
[unarchiver setExternalObjectsForProxyIdentifiers:[anExternalNameTable objectForKey:CPCibExternalObjects]];
|
||||
|
||||
var objectData = [unarchiver decodeObjectForKey:CPCibObjectDataKey];
|
||||
|
||||
if (!objectData || ![objectData isKindOfClass:[_CPCibObjectData class]])
|
||||
return NO;
|
||||
|
||||
[objectData establishConnectionsWithExternalNameTable:anExternalNameTable];
|
||||
|
||||
var owner = [anExternalNameTable objectForKey:CPCibOwner],
|
||||
topLevelObjects = [anExternalNameTable objectForKey:CPCibTopLevelObjects];
|
||||
/*
|
||||
// [objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects];
|
||||
// [objectData cibInstantiateWithOwner:owner topLevelObjects:topLevelObjects];
|
||||
// alert([objectData description]);
|
||||
// alert([unarchiver._archive description]);
|
||||
|
||||
NSMutableArray *t;
|
||||
NSEnumerator *e;
|
||||
id o;
|
||||
id owner;
|
||||
id rootObject;
|
||||
#if 0
|
||||
NSLog(@"instantiateCibWithExternalNameTable=%@", table);
|
||||
#endif
|
||||
if(![decoded isKindOfClass:[NSIBObjectData class]])
|
||||
return NO;
|
||||
owner=[table objectForKey:NSCibOwner];
|
||||
rootObject=[decoded rootObject];
|
||||
#if 0
|
||||
NSLog(@"establishConnections");
|
||||
#endif
|
||||
[decoded establishConnectionsWithExternalNameTable:table];
|
||||
#if 0
|
||||
NSLog(@"awakeFromCib %d objects", [decodedObjects count]);
|
||||
#endif
|
||||
#if 0
|
||||
NSLog(@"objects 2=%@", decodedObjects);
|
||||
#endif
|
||||
|
||||
// FIXME: shouldn't be accessing private variables.
|
||||
var objects = unarchiver._objects,
|
||||
count = [objects count];
|
||||
|
||||
// The order in which objects receive the awakeFromCib message is not guaranteed.
|
||||
while (count--)
|
||||
{
|
||||
var object = objects[count];
|
||||
|
||||
if ([object respondsToSelector:@selector(awakeFromCib)])
|
||||
[object awakeFromCib];
|
||||
}
|
||||
var topLevelObjects = [anExternalNameTable objectForKey:CPCibTopLevelObjects];
|
||||
|
||||
// if ([owner respondsToSelector:@selector(awakeFromCib)])
|
||||
// [owner awakeFromCib];
|
||||
// Display visible windows.
|
||||
|
||||
return YES;*/
|
||||
[objectData instantiateWithOwner:owner topLevelObjects:topLevelObjects]
|
||||
[objectData establishConnectionsWithOwner:owner topLevelObjects:topLevelObjects];
|
||||
[objectData awakeWithOwner:owner topLevelObjects:topLevelObjects];
|
||||
|
||||
// Display Visible Windows.
|
||||
[objectData displayVisibleWindows];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)instantiateCibWithOwner:(id)anOwner topLevelObjects:(CPArray)topLevelObjects
|
||||
{
|
||||
[CPDictionary dictionaryWithObjectsAndKeys:anOwner, CPCibOwner, topLevelObjects, CPCibTopLevelObjects];
|
||||
return [self instantiate];
|
||||
return [self instantiateCibWithExternalNameTable:[CPDictionary dictionaryWithObjectsAndKeys:anOwner, CPCibOwner, topLevelObjects, CPCibTopLevelObjects]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPCib (CPURLConnectionDelegate)
|
||||
|
||||
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)data
|
||||
{
|
||||
_data = [CPData dataWithString:data];
|
||||
}
|
||||
|
||||
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
|
||||
{
|
||||
alert("cib: connection failed.");
|
||||
|
||||
_loadDelegate = nil;
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(CPURLConnection)aConnection
|
||||
{
|
||||
if ([_loadDelegate respondsToSelector:@selector(cibDidFinishLoading:)])
|
||||
[_loadDelegate cibDidFinishLoading:self];
|
||||
|
||||
_loadDelegate = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* CPCibConnector.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 <Foundation/CPObject.j>
|
||||
@import <Foundation/CPKeyValueCoding.j>
|
||||
|
||||
|
||||
var _CPCibConnectorSourceKey = @"_CPCibConnectorSourceKey",
|
||||
_CPCibConnectorDestinationKey = @"_CPCibConnectorDestinationKey",
|
||||
_CPCibConnectorLabelKey = @"_CPCibConnectorLabelKey";
|
||||
|
||||
@implementation CPCibConnector : CPObject
|
||||
{
|
||||
id _source @accessors(property=source);
|
||||
id _destination @accessors(property=destination);
|
||||
CPString _label @accessors(property=label);
|
||||
}
|
||||
|
||||
- (void)replaceObject:(id)anObject withObject:(id)anotherObject
|
||||
{
|
||||
if (_source === anObject)
|
||||
_source = anotherObject;
|
||||
|
||||
if (_destination === anObject)
|
||||
_destination = anotherObject;
|
||||
}
|
||||
|
||||
- (void)replaceObjects:(Object)replacementObjects
|
||||
{
|
||||
var replacement = replacementObjects[[_source UID]];
|
||||
|
||||
if (replacement !== undefined)
|
||||
_source = replacement;
|
||||
|
||||
replacement = replacementObjects[[_destination UID]];
|
||||
|
||||
if (replacement !== undefined)
|
||||
_destination = replacement;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPCibConnector (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_source = [aCoder decodeObjectForKey:_CPCibConnectorSourceKey];
|
||||
_destination = [aCoder decodeObjectForKey:_CPCibConnectorDestinationKey];
|
||||
_label = [aCoder decodeObjectForKey:_CPCibConnectorLabelKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_source forKey:_CPCibConnectorSourceKey];
|
||||
[aCoder encodeObject:_destination forKey:_CPCibConnectorDestinationKey];
|
||||
[aCoder encodeObject:_label forKey:_CPCibConnectorLabelKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// For backwards compatibility.
|
||||
@implementation _CPCibConnector : CPCibConnector { } @end
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* CPCibControlConnector.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 "CPCibConnector.j"
|
||||
|
||||
|
||||
@implementation CPCibControlConnector : CPCibConnector
|
||||
{
|
||||
}
|
||||
|
||||
- (void)establishConnection
|
||||
{
|
||||
var selectorName = _label,
|
||||
selectorNameLength = [selectorName length];
|
||||
|
||||
if (selectorNameLength && selectorName[selectorNameLength - 1] !== ':')
|
||||
selectorName += ':';
|
||||
|
||||
var selector = CPSelectorFromString(selectorName);
|
||||
|
||||
// Not having a selector is a fatal error.
|
||||
if (!selector)
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] selector " + selectorName + @" does not exist."];
|
||||
|
||||
// If the destination doesn't respond to this selector, warn but don't die.
|
||||
if (_destination && ![_destination respondsToSelector:selector])
|
||||
{
|
||||
CPLog.warn(@"Could not connect the action " + selector + @" to target of class " + [_destination className]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Not being able to set the action is a fatal error.
|
||||
if ([_source respondsToSelector:@selector(setAction:)])
|
||||
objj_msgSend(_source, @selector(setAction:), selector);
|
||||
|
||||
else
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + " does not respond to setAction:"];
|
||||
|
||||
// Not being able to set the target is a fatal error.
|
||||
if ([_source respondsToSelector:@selector(setTarget:)])
|
||||
objj_msgSend(_source, @selector(setTarget:), _destination);
|
||||
|
||||
else
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + " does not respond to setTarget:"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibControlConnector : CPCibControlConnector { } @end
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* _CPCibLoading.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 <Foundation/CPBundle.j>
|
||||
@import <Foundation/CPDictionary.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
|
||||
var CPCibOwner = @"CPCibOwner";
|
||||
|
||||
var LoadInfoForCib = {};
|
||||
|
||||
@implementation CPObject (CPCibLoading)
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPBundle (CPCibLoading)
|
||||
|
||||
+ (void)loadCibFile:(CPString)anAbsolutePath externalNameTable:(CPDictionary)aNameTable
|
||||
{
|
||||
[[[CPCib alloc] initWithContentsOfURL:anAbsolutePath] instantiateCibWithExternalNameTable:aNameTable];
|
||||
}
|
||||
|
||||
+ (void)loadCibNamed:(CPString)aName owner:(id)anOwner
|
||||
{
|
||||
if (![aName hasSuffix:@".cib"])
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// Path is based solely on anOwner:
|
||||
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
|
||||
path = [bundle pathForResource:aName];
|
||||
|
||||
[self loadCibFile:path externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner]];
|
||||
}
|
||||
|
||||
- (void)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable
|
||||
{
|
||||
[[[CPCib alloc] initWithContentsOfURL:aFileName] instantiateCibWithExternalNameTable:aNameTable];
|
||||
}
|
||||
|
||||
+ (void)loadCibFile:(CPString)anAbsolutePath externalNameTable:(CPDictionary)aNameTable loadDelegate:aDelegate
|
||||
{
|
||||
var cib = [[CPCib alloc] initWithContentsOfURL:anAbsolutePath loadDelegate:self];
|
||||
|
||||
LoadInfoForCib[[cib UID]] = { loadDelegate:aDelegate, externalNameTable:aNameTable };
|
||||
}
|
||||
|
||||
+ (void)loadCibNamed:(CPString)aName owner:(id)anOwner loadDelegate:(id)aDelegate
|
||||
{
|
||||
if (![aName hasSuffix:@".cib"])
|
||||
aName = [aName stringByAppendingString:@".cib"];
|
||||
|
||||
// Path is based solely on anOwner:
|
||||
var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
|
||||
path = [bundle pathForResource:aName];
|
||||
|
||||
[self loadCibFile:path externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner] loadDelegate:aDelegate];
|
||||
}
|
||||
|
||||
- (void)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable loadDelegate:(id)aDelegate
|
||||
{
|
||||
var cib = [[CPCib alloc] initWithCibNamed:aFileName bundle:self loadDelegate:[self class]];
|
||||
|
||||
LoadInfoForCib[[cib UID]] = { loadDelegate:aDelegate, externalNameTable:aNameTable };
|
||||
}
|
||||
|
||||
+ (void)cibDidFinishLoading:(CPCib)aCib
|
||||
{
|
||||
var loadInfo = LoadInfoForCib[[aCib UID]];
|
||||
|
||||
delete LoadInfoForCib[[aCib UID]];
|
||||
|
||||
[aCib instantiateCibWithExternalNameTable:loadInfo.externalNameTable];
|
||||
|
||||
[loadInfo.loadDelegate cibDidFinishLoading:aCib];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* CPCibOutletConnector.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 "CPCibConnector.j"
|
||||
|
||||
|
||||
@implementation CPCibOutletConnector : CPCibConnector
|
||||
{
|
||||
}
|
||||
|
||||
- (void)establishConnection
|
||||
{
|
||||
try
|
||||
{
|
||||
[_source setValue:_destination forKey:_label];
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
if ([anException name] === CPUndefinedKeyException)
|
||||
CPLog.warn(@"Could not connect the outlet " + _label + @" of target of class " + [_source className]);
|
||||
|
||||
else
|
||||
throw anException;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibOutletConnector : CPCibOutletConnector { } @end
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* _CPCibClassSwapper.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
|
||||
var _CPCibClassSwapperClassNameKey = @"_CPCibClassSwapperClassNameKey",
|
||||
_CPCibClassSwapperOriginalClassNameKey = @"_CPCibClassSwapperOriginalClassNameKey";
|
||||
|
||||
@implementation _CPCibClassSwapper : CPObject
|
||||
{
|
||||
}
|
||||
|
||||
+ (void)allocObjectWithCoder:(CPCoder)aCoder className:(CPString)aClassName
|
||||
{
|
||||
// FIXME: Also check class classForClassName:
|
||||
var theClass = [aCoder classForClassName:aClassName];
|
||||
|
||||
if (!theClass)
|
||||
{
|
||||
theClass = objj_lookUpClass(aClassName);
|
||||
|
||||
if (!theClass)
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [theClass alloc];
|
||||
}
|
||||
|
||||
+ (id)allocWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if ([aCoder respondsToSelector:@selector(usesOriginalClasses)] && [aCoder usesOriginalClasses])
|
||||
{
|
||||
var theClassName = [aCoder decodeObjectForKey:_CPCibClassSwapperOriginalClassNameKey],
|
||||
object = [self allocObjectWithCoder:aCoder className:theClassName];
|
||||
}
|
||||
else
|
||||
{
|
||||
var theClassName = [aCoder decodeObjectForKey:_CPCibClassSwapperClassNameKey],
|
||||
object = [self allocObjectWithCoder:aCoder className:theClassName];
|
||||
|
||||
if (!object)
|
||||
{
|
||||
CPLog.error("Unable to find class " + theClassName + " in cib file.");
|
||||
|
||||
object = [self allocObjectWithCoder:aCoder className:[aCoder decodeObjectForKey:_CPCibClassSwapperOriginalClassNameKey]];
|
||||
}
|
||||
}
|
||||
|
||||
if (!object)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"Unable to find class " + theClassName + " in cib file."];
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,112 +0,0 @@
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
import <Foundation/CPKeyValueCoding.j>
|
||||
|
||||
|
||||
var _CPCibConnectorSourceKey = @"_CPCibConnectorSourceKey",
|
||||
_CPCibConnectorDestinationKey = @"_CPCibConnectorDestinationKey",
|
||||
_CPCibConnectorLabelKey = @"_CPCibConnectorLabelKey";
|
||||
|
||||
@implementation _CPCibConnector : CPObject
|
||||
{
|
||||
id _source;
|
||||
id _destination;
|
||||
CPString _label;
|
||||
}
|
||||
|
||||
- (void)replaceObject:(id)anObject withObject:(id)anotherObject
|
||||
{
|
||||
if (_source == anObject)
|
||||
_source = anotherObject;
|
||||
|
||||
else if (_destination == anObject)
|
||||
_destination = anotherObject;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibConnector (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_source = [aCoder decodeObjectForKey:_CPCibConnectorSourceKey];
|
||||
_destination = [aCoder decodeObjectForKey:_CPCibConnectorDestinationKey];
|
||||
_label = [aCoder decodeObjectForKey:_CPCibConnectorLabelKey];
|
||||
|
||||
#if DEBUG
|
||||
CPLog("Connection from " + [_source description] + " to " + [_destination description] + " and label " + _label + " decoded.");
|
||||
#endif
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_source forKey:_CPCibConnectorSourceKey];
|
||||
[aCoder encodeObject:_destination forKey:_CPCibConnectorDestinationKey];
|
||||
[aCoder encodeObject:_label forKey:_CPCibConnectorLabelKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibControlConnector : _CPCibConnector
|
||||
{
|
||||
}
|
||||
|
||||
- (void)establishConnection
|
||||
{
|
||||
#if DEBUG
|
||||
CPLog('[' + [_source description] + " setTarget: " + [_destination description] + ']');
|
||||
CPLog('[' + [_source description] + " setAction: " + _label + ']');
|
||||
#endif
|
||||
|
||||
var selectorName = _label;
|
||||
|
||||
if (![selectorName hasSuffix:@":"])
|
||||
selectorName += ':';
|
||||
|
||||
var selector = CPSelectorFromString(selectorName);
|
||||
|
||||
if (!selector)
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] selector " + selectorName + @" does not exist."];
|
||||
|
||||
if ([_source respondsToSelector:@selector(setAction:)])
|
||||
objj_msgSend(_source, @selector(setAction:), selector);
|
||||
|
||||
else
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + " does not respond to setAction:"];
|
||||
|
||||
if ([_source respondsToSelector:@selector(setTarget:)])
|
||||
objj_msgSend(_source, @selector(setTarget:), _destination);
|
||||
|
||||
else
|
||||
[CPException
|
||||
raise:CPInvalidArgumentException
|
||||
reason:@"-[" + [self className] + ' ' + _cmd + @"] " + [_source description] + " does not respond to setTarget:"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibOutletConnector : _CPCibConnector
|
||||
{
|
||||
}
|
||||
|
||||
- (void)establishConnection
|
||||
{
|
||||
#if DEBUG
|
||||
CPLog([_source description] + " setValue: " + [_destination description] + " forKey: " + _label);
|
||||
#endif
|
||||
|
||||
[_source setValue:_destination forKey:_label];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
var _CPCibCustomObjectClassName = @"_CPCibCustomObjectClassName";
|
||||
@@ -9,6 +9,21 @@ var _CPCibCustomObjectClassName = @"_CPCibCustomObjectClassName";
|
||||
CPString _className;
|
||||
}
|
||||
|
||||
- (CPString)customClassName
|
||||
{
|
||||
return _className;
|
||||
}
|
||||
|
||||
- (void)setCustomClassName:(CPString)aClassName
|
||||
{
|
||||
_className = aClassName;
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [super description] + " (" + [self customClassName] + ')';
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibCustomObject (CPCoding)
|
||||
@@ -31,9 +46,25 @@ var _CPCibCustomObjectClassName = @"_CPCibCustomObjectClassName";
|
||||
- (id)_cibInstantiate
|
||||
{
|
||||
var theClass = CPClassFromString(_className);
|
||||
|
||||
|
||||
// Hey this is us!
|
||||
if (theClass === [self class])
|
||||
{
|
||||
_className = @"CPObject";
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
if (!theClass)
|
||||
{
|
||||
#if DEBUG
|
||||
CPLog("Unknown class \"" + _className + "\" in cib file");
|
||||
#endif
|
||||
theClass = [CPObject class];
|
||||
}
|
||||
|
||||
if (theClass === [CPApplication class])
|
||||
return [CPApplication sharedApplication];
|
||||
|
||||
return [[theClass alloc] init];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* _CPCibCustomResource.j
|
||||
* AppKit
|
||||
*
|
||||
* Portions based on NSCustomResource.m (01/08/2009) in Cocotron (http://www.cocotron.org/)
|
||||
* Copyright (c) 2006-2007 Christopher J. W. Lloyd
|
||||
*
|
||||
* Created by Francisco Tolmasky.
|
||||
* 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/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
|
||||
var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
|
||||
_CPCibCustomResourceResourceNameKey = @"_CPCibCustomResourceResourceNameKey",
|
||||
_CPCibCustomResourcePropertiesKey = @"_CPCibCustomResourcePropertiesKey";
|
||||
|
||||
@implementation _CPCibCustomResource : CPObject
|
||||
{
|
||||
CPString _className;
|
||||
CPString _resourceName;
|
||||
CPDictionary _properties;
|
||||
}
|
||||
|
||||
+ (id)imageResourceWithName:(CPString)aResourceName size:(CGSize)aSize
|
||||
{
|
||||
return [[self alloc] initWithClassName:@"CPImage" resourceName:aResourceName properties:[CPDictionary dictionaryWithObject:aSize forKey:@"size"]];
|
||||
}
|
||||
|
||||
- (id)initWithClassName:(CPString)aClassName resourceName:(CPString)aResourceName properties:(CPDictionary)properties
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_className = aClassName;
|
||||
_resourceName = aResourceName;
|
||||
_properties = properties;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_className = [aCoder decodeObjectForKey:_CPCibCustomResourceClassNameKey];
|
||||
_resourceName = [aCoder decodeObjectForKey:_CPCibCustomResourceResourceNameKey];
|
||||
_properties = [aCoder decodeObjectForKey:_CPCibCustomResourcePropertiesKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_className forKey:_CPCibCustomResourceClassNameKey];
|
||||
[aCoder encodeObject:_resourceName forKey:_CPCibCustomResourceResourceNameKey];
|
||||
[aCoder encodeObject:_properties forKey:_CPCibCustomResourcePropertiesKey];
|
||||
}
|
||||
|
||||
- (id)awakeAfterUsingCoder:(CPCoder)aCoder
|
||||
{
|
||||
if ([aCoder respondsToSelector:@selector(bundle)] &&
|
||||
(![aCoder respondsToSelector:@selector(awakenCustomResources)] || [aCoder awakenCustomResources]))
|
||||
if (_className === @"CPImage")
|
||||
return [[CPImage alloc] initWithContentsOfFile:[[aCoder bundle] pathForResource:_resourceName] size:[_properties objectForKey:@"size"]];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibCustomResource (CPImage)
|
||||
|
||||
- (CPString)filename
|
||||
{
|
||||
return [[CPBundle mainBundle] pathForResource:_resourceName];
|
||||
}
|
||||
|
||||
- (CGSize)size
|
||||
{
|
||||
return [_properties objectForKey:@"size"];
|
||||
}
|
||||
|
||||
- (BOOL)isThreePartImage
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isNinePartImage
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CPView.j"
|
||||
@import "CPView.j"
|
||||
|
||||
|
||||
/* @ignore */
|
||||
@@ -30,6 +30,16 @@ import "CPView.j"
|
||||
CPString _className;
|
||||
}
|
||||
|
||||
- (CPString)customClassName
|
||||
{
|
||||
return _className;
|
||||
}
|
||||
|
||||
- (void)setCustomClassName:(CPString)aClassName
|
||||
{
|
||||
_className = aClassName;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPCibCustomViewClassNameKey = @"_CPCibCustomViewClassNameKey";
|
||||
@@ -49,23 +59,37 @@ var _CPCibCustomViewClassNameKey = @"_CPCibCustomViewClassNameKey";
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
|
||||
[aCoder encodeObject:_className forKey:_CPCibCustomViewClassNameKey];
|
||||
}
|
||||
|
||||
- (CPString)customClassName
|
||||
{
|
||||
return _className;
|
||||
}
|
||||
|
||||
- (id)_cibInstantiate
|
||||
{
|
||||
var theClass = CPClassFromString(_className);
|
||||
|
||||
|
||||
// If we don't have this class, just use CPView.
|
||||
// FIXME: Should we instead throw an exception?
|
||||
if (!theClass)
|
||||
{
|
||||
#if DEBUG
|
||||
CPLog("Unknown class \"" + _className + "\" in cib file, using CPView instead.");
|
||||
|
||||
#endif
|
||||
theClass = [CPView class];
|
||||
}
|
||||
|
||||
// Hey this is us!
|
||||
if (theClass === [self class])
|
||||
{
|
||||
_className = @"CPView";
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
var view = [[theClass alloc] initWithFrame:[self frame]];
|
||||
|
||||
if (view)
|
||||
@@ -73,7 +97,9 @@ var _CPCibCustomViewClassNameKey = @"_CPCibCustomViewClassNameKey";
|
||||
[view setBounds:[self bounds]];
|
||||
|
||||
// Since the object replacement logic hasn't had a chance to kick in yet, we need to do it manually:
|
||||
var subviews = [self subviews],
|
||||
|
||||
// we need to copy subviews since each time we add a subview to a different view its removed from the original subviews array
|
||||
var subviews = [[self subviews] copy],
|
||||
index = 0,
|
||||
count = subviews.length;
|
||||
|
||||
@@ -82,14 +108,16 @@ var _CPCibCustomViewClassNameKey = @"_CPCibCustomViewClassNameKey";
|
||||
|
||||
[view setAutoresizingMask:[self autoresizingMask]];
|
||||
[view setAutoresizesSubviews:[self autoresizesSubviews]];
|
||||
|
||||
|
||||
[view setHitTests:[self hitTests]];
|
||||
[view setHidden:[self isHidden]];
|
||||
[view setAlphaValue:[self alphaValue]];
|
||||
|
||||
|
||||
[_superview replaceSubview:self with:view];
|
||||
|
||||
[view setBackgroundColor:[self backgroundColor]];
|
||||
}
|
||||
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,47 @@
|
||||
|
||||
import <Foundation/CPKeyedUnarchiver.j>
|
||||
@import <Foundation/CPKeyedUnarchiver.j>
|
||||
|
||||
|
||||
@implementation _CPCibKeyedUnarchiver : CPKeyedUnarchiver
|
||||
{
|
||||
CPBundle _bundle;
|
||||
BOOL _awakenCustomResources;
|
||||
CPDictionary _externalObjectsForProxyIdentifiers;
|
||||
}
|
||||
|
||||
- (id)initForReadingWithData:(CPData)data
|
||||
- (id)initForReadingWithData:(CPData)data bundle:(CPBundle)aBundle awakenCustomResources:(BOOL)shouldAwakenCustomResources
|
||||
{
|
||||
self = [super initForReadingWithData:data];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_bundle = aBundle;
|
||||
_awakenCustomResources = shouldAwakenCustomResources;
|
||||
|
||||
[self setDelegate:self];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)unarchiver:(CPKeyedUnarchiver)aKeyedUnarchiver didDecodeObject:(id)anObject
|
||||
- (CPBundle)bundle
|
||||
{
|
||||
if ([anObject respondsToSelector:@selector(_cibInstantiate)])
|
||||
return [anObject _cibInstantiate];
|
||||
return _bundle;
|
||||
}
|
||||
|
||||
return anObject;
|
||||
- (BOOL)awakenCustomResources
|
||||
{
|
||||
return _awakenCustomResources;
|
||||
}
|
||||
|
||||
- (void)setExternalObjectsForProxyIdentifiers:(CPDictionary)externalObjectsForProxyIdentifiers
|
||||
{
|
||||
_externalObjectsForProxyIdentifiers = externalObjectsForProxyIdentifiers;
|
||||
}
|
||||
|
||||
- (id)externalObjectForProxyIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
return [_externalObjectsForProxyIdentifiers objectForKey:anIdentifier];
|
||||
}
|
||||
|
||||
- (void)replaceObjectAtUID:(int)aUID withObject:(id)anObject
|
||||
@@ -34,4 +54,4 @@ import <Foundation/CPKeyedUnarchiver.j>
|
||||
return _objects[aUID];
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* _CPCibLoading.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 <Foundation/CPBundle.j>
|
||||
import <Foundation/CPDictionary.j>
|
||||
import <Foundation/CPString.j>
|
||||
|
||||
|
||||
var CPCibOwner = @"CPCibOwner";
|
||||
|
||||
@implementation CPObject (CPCibLoading)
|
||||
|
||||
-(void)awakeFromCib
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPBundle (CPCibLoading)
|
||||
|
||||
+ (BOOL)loadCibFile:(CPString)aPath externalNameTable:(CPDictionary)aNameTable
|
||||
{
|
||||
return [[[CPCib alloc] initWithContentsOfFile:aPath] instantiateCibWithExternalNameTable:aNameTable];
|
||||
}
|
||||
|
||||
+ (BOOL)loadCibNamed:(CPString)aName owner:(id)anOwner
|
||||
{
|
||||
var bundle = [CPBundle bundleForClass:[anOwner class]];
|
||||
|
||||
/*NSString *path;
|
||||
|
||||
path=[bundle pathForResource:name ofType:@"cib"];
|
||||
if(path==nil)
|
||||
path=[[NSBundle mainBundle] pathForResource:name ofType:@"cib"];
|
||||
|
||||
if(path==nil)
|
||||
return NO;
|
||||
*/
|
||||
var path = [bundle pathForResource:aName];
|
||||
|
||||
return [CPBundle loadCibFile:aPath externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner]];
|
||||
}
|
||||
|
||||
- (BOOL)loadCibFile:(CPString)aPath externalNameTable:(CPDictionary)aNameTable
|
||||
{
|
||||
return [[[CPCib alloc] initWithContentsOfFile:aPath] instantiateCibWithExternalNameTable:aNameTable];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+132
-39
@@ -20,12 +20,14 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPArray.j>
|
||||
import <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
@import <Foundation/CPArray.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
import "CPCib.j"
|
||||
import "_CPCibConnector.j"
|
||||
@import "CPCib.j"
|
||||
@import "CPCibConnector.j"
|
||||
@import "CPCibControlConnector.j"
|
||||
@import "CPCibOutletConnector.j"
|
||||
|
||||
|
||||
@implementation _CPCibObjectData : CPObject
|
||||
@@ -57,27 +59,54 @@ import "_CPCibConnector.j"
|
||||
_CPCibCustomObject _fileOwner;
|
||||
|
||||
CPSet _visibleWindows;
|
||||
|
||||
JSObject _replacementObjects;
|
||||
}
|
||||
|
||||
- (CPArray)topLevelObjects
|
||||
- (id)init
|
||||
{
|
||||
var count = [_objectsValues count],
|
||||
topLevelObjects = [];
|
||||
|
||||
while (count--)
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
var eachObject = _objectsValues[count];
|
||||
|
||||
if (eachObject == _fileOwner)
|
||||
{
|
||||
var anObject = _objectsKeys[count];
|
||||
|
||||
if (anObject != _fileOwner)
|
||||
topLevelObjects.push(anObject);
|
||||
}
|
||||
_namesKeys = [];
|
||||
_namesValues = [];
|
||||
|
||||
//CPArray _accessibilityConnectors;
|
||||
//CPArray _accessibilityOidsKeys;
|
||||
//CPArray _accessibilityOidsValues;
|
||||
|
||||
_classesKeys = [];
|
||||
_classesValues = [];
|
||||
|
||||
_connections = [];
|
||||
//id _fontManager;
|
||||
|
||||
_framework = @"";
|
||||
|
||||
_nextOid = [];
|
||||
|
||||
_objectsKeys = [];
|
||||
_objectsValues = [];
|
||||
|
||||
_oidKeys = [];
|
||||
_oidValues = [];
|
||||
|
||||
_fileOwner = nil;
|
||||
|
||||
_visibleWindows = [CPSet set];
|
||||
}
|
||||
|
||||
return topLevelObjects;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)displayVisibleWindows
|
||||
{
|
||||
var object = nil,
|
||||
objectEnumerator = [_visibleWindows objectEnumerator];
|
||||
|
||||
while (object = [objectEnumerator nextObject])
|
||||
[_replacementObjects[[object UID]] makeKeyAndOrderFront:self];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -114,9 +143,11 @@ var _CPCibObjectDataNamesKeysKey = @"_CPCibObjectDataNamesKeysKey
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
_replacementObjects = {};
|
||||
|
||||
_namesKeys = [aCoder decodeObjectForKey:_CPCibObjectDataNamesKeysKey];
|
||||
_namesValues = [aCoder decodeObjectForKey:_CPCibObjectDataNamesValuesKey];
|
||||
|
||||
@@ -129,22 +160,22 @@ var _CPCibObjectDataNamesKeysKey = @"_CPCibObjectDataNamesKeysKey
|
||||
|
||||
_connections = [aCoder decodeObjectForKey:_CPCibObjectDataConnectionsKey];
|
||||
//id _fontManager;
|
||||
|
||||
|
||||
_framework = [aCoder decodeObjectForKey:_CPCibObjectDataFrameworkKey];
|
||||
|
||||
|
||||
_nextOid = [aCoder decodeIntForKey:_CPCibObjectDataNextOidKey];
|
||||
|
||||
|
||||
_objectsKeys = [aCoder decodeObjectForKey:_CPCibObjectDataObjectsKeysKey];
|
||||
_objectsValues = [aCoder decodeObjectForKey:_CPCibObjectDataObjectsValuesKey];
|
||||
|
||||
|
||||
_oidKeys = [aCoder decodeObjectForKey:_CPCibObjectDataOidKeysKey];
|
||||
_oidValues = [aCoder decodeObjectForKey:_CPCibObjectDataOidValuesKey];
|
||||
|
||||
_fileOwner = [aCoder decodeObjectForKey:_CPCibObjectDataFileOwnerKey];
|
||||
|
||||
// CPSet _visibleWindows;
|
||||
|
||||
_visibleWindows = [aCoder decodeObjectForKey:_CPCibObjectDataVisibleWindowsKey];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -152,16 +183,16 @@ var _CPCibObjectDataNamesKeysKey = @"_CPCibObjectDataNamesKeysKey
|
||||
{
|
||||
[aCoder encodeObject:_namesKeys forKey:_CPCibObjectDataNamesKeysKey];
|
||||
[aCoder encodeObject:_namesValues forKey:_CPCibObjectDataNamesValuesKey];
|
||||
|
||||
|
||||
//CPArray _accessibilityConnectors;
|
||||
//CPArray _accessibilityOidsKeys;
|
||||
//CPArray _accessibilityOidsValues;
|
||||
|
||||
|
||||
[aCoder encodeObject:_classesKeys forKey:_CPCibObjectDataClassesKeysKey];
|
||||
[aCoder encodeObject:_classesValues forKey:_CPCibObjectDataClassesValuesKey];
|
||||
|
||||
|
||||
[aCoder encodeObject:_connections forKey:_CPCibObjectDataConnectionsKey];
|
||||
|
||||
|
||||
//id _fontManager;
|
||||
|
||||
[aCoder encodeObject:_framework forKey:_CPCibObjectDataFrameworkKey];
|
||||
@@ -177,22 +208,84 @@ var _CPCibObjectDataNamesKeysKey = @"_CPCibObjectDataNamesKeysKey
|
||||
[aCoder encodeObject:_fileOwner forKey:_CPCibObjectDataFileOwnerKey];
|
||||
// CPCustomObject _fileOwner;
|
||||
|
||||
// CPSet _visibleWindows;
|
||||
[aCoder encodeObject:_visibleWindows forKey:_CPCibObjectDataVisibleWindowsKey];
|
||||
}
|
||||
|
||||
- (void)establishConnectionsWithExternalNameTable:(CPDictionary)anExternalNameTable
|
||||
- (void)instantiateWithOwner:(id)anOwner topLevelObjects:(CPMutableArray)topLevelObjects
|
||||
{
|
||||
// _objectsValues -> parent
|
||||
// _objectsKeys -> child
|
||||
var count = [_objectsKeys count];
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var object = _objectsKeys[count],
|
||||
parent = _objectsValues[count],
|
||||
instantiatedObject = object;
|
||||
|
||||
if ([object respondsToSelector:@selector(_cibInstantiate)])
|
||||
{
|
||||
var instantiatedObject = [object _cibInstantiate];
|
||||
|
||||
if (instantiatedObject !== object)
|
||||
{
|
||||
_replacementObjects[[object UID]] = instantiatedObject;
|
||||
|
||||
if ([instantiatedObject isKindOfClass:[CPView class]])
|
||||
{
|
||||
var clipView = [instantiatedObject superview];
|
||||
|
||||
if ([clipView isKindOfClass:[CPClipView class]])
|
||||
{
|
||||
var scrollView = [clipView superview];
|
||||
|
||||
if ([scrollView isKindOfClass:[CPScrollView class]])
|
||||
[scrollView setDocumentView:instantiatedObject];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (topLevelObjects && parent === _fileOwner && object !== _fileOwner)
|
||||
topLevelObjects.push(instantiatedObject);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)establishConnectionsWithOwner:(id)anOwner topLevelObjects:(CPMutableArray)topLevelObjects
|
||||
{
|
||||
_replacementObjects[[_fileOwner UID]] = anOwner;
|
||||
|
||||
var index = 0,
|
||||
count = _connections.length,
|
||||
cibOwner = [anExternalNameTable objectForKey:CPCibOwner];
|
||||
count = _connections.length;
|
||||
|
||||
for (; index < count; ++index)
|
||||
{
|
||||
var connection = _connections[index];
|
||||
|
||||
[connection replaceObject:_fileOwner withObject:cibOwner];
|
||||
|
||||
[connection replaceObjects:_replacementObjects];
|
||||
[connection establishConnection];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)awakeWithOwner:(id)anOwner topLevelObjects:(CPMutableArray)topLevelObjects
|
||||
{
|
||||
var count = [_objectsKeys count];
|
||||
|
||||
while (count--)
|
||||
{
|
||||
var object = _objectsKeys[count],
|
||||
instantiatedObject = _replacementObjects[[object UID]];
|
||||
|
||||
if (instantiatedObject)
|
||||
object = instantiatedObject;
|
||||
|
||||
// Don't use _fileOwner, by this point its been replaced with anOwner.
|
||||
if (object !== anOwner && [object respondsToSelector:@selector(awakeFromCib)])
|
||||
[object awakeFromCib];
|
||||
}
|
||||
|
||||
if ([anOwner respondsToSelector:@selector(awakeFromCib)])
|
||||
[anOwner awakeFromCib];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
@implementation _CPCibProxyObject : CPObject
|
||||
{
|
||||
CPString _identifier;
|
||||
}
|
||||
@end
|
||||
|
||||
var _CPCibProxyObjectIdentifierKey = @"CPIdentifier";
|
||||
|
||||
@implementation _CPCibProxyObject (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_identifier = [aCoder decodeObjectForKey:_CPCibProxyObjectIdentifierKey];
|
||||
|
||||
if ([aCoder respondsToSelector:@selector(externalObjectForProxyIdentifier:)])
|
||||
return [aCoder externalObjectForProxyIdentifier:_identifier];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:_CPCibProxyObjectIdentifierKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,31 +1,57 @@
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
|
||||
var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeKey",
|
||||
_CPCibWindowTemplateMaxSizeKey = @"_CPCibWindowTemplateMaxSizeKey",
|
||||
var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeKey",
|
||||
_CPCibWindowTemplateMaxSizeKey = @"_CPCibWindowTemplateMaxSizeKey",
|
||||
|
||||
_CPCibWindowTemplateViewClassKey = @"_CPCibWindowTemplateWindowClassKey",
|
||||
_CPCibWindowTemplateViewClassKey = @"_CPCibWindowTemplateViewClassKey",
|
||||
_CPCibWindowTemplateWindowClassKey = @"_CPCibWindowTemplateWindowClassKey",
|
||||
|
||||
_CPCibWindowTemplateWindowRectKey = @"_CPCibWindowTemplateWindowRectKey",
|
||||
_CPCibWindowTemplateWindowStyleMaskKey = @"_CPCibWindowTempatStyleMaskKey",
|
||||
_CPCibWindowTemplateWindowTitleKey = @"_CPCibWindowTemplateWindowTitleKey",
|
||||
_CPCibWindowTemplateWindowViewKey = @"_CPCibWindowTemplateWindowViewKey";
|
||||
_CPCibWindowTemplateWindowRectKey = @"_CPCibWindowTemplateWindowRectKey",
|
||||
_CPCibWindowTemplateWindowStyleMaskKey = @"_CPCibWindowTempatStyleMaskKey",
|
||||
_CPCibWindowTemplateWindowTitleKey = @"_CPCibWindowTemplateWindowTitleKey",
|
||||
_CPCibWindowTemplateWindowViewKey = @"_CPCibWindowTemplateWindowViewKey",
|
||||
|
||||
_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop = @"_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop";
|
||||
_CPCibWindowTemplateWindowIsFullPlatformWindowKey = @"_CPCibWindowTemplateWindowIsFullPlatformWindowKey";
|
||||
|
||||
@implementation _CPCibWindowTemplate : CPObject
|
||||
{
|
||||
CGSize _minSize;
|
||||
CGSize _maxSize;
|
||||
//CGSize _screenRect;
|
||||
|
||||
CPString _viewClass;
|
||||
|
||||
id _viewClass;
|
||||
//unsigned _wtFlags;
|
||||
CPString _windowClass;
|
||||
CGRect _windowRect;
|
||||
unsigned _windowStyleMask;
|
||||
|
||||
|
||||
CPString _windowTitle;
|
||||
CPView _windowView;
|
||||
|
||||
BOOL _windowAutorecalculatesKeyViewLoop;
|
||||
BOOL _windowIsFullPlatformWindow;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_windowClass = @"CPWindow";
|
||||
_windowRect = CGRectMake(0.0, 0.0, 400.0, 200.0);
|
||||
_windowStyleMask = CPTitledWindowMask | CPClosableWindowMask | CPMiniaturizableWindowMask | CPResizableWindowMask;
|
||||
|
||||
_windowTitle = @"Window";
|
||||
_windowView = [[CPView alloc] initWithFrame:CGRectMake(0.0, 0.0, 400.0, 200.0)];
|
||||
|
||||
_windowIsFullPlatformWindow = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
@@ -34,17 +60,22 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeKey",
|
||||
|
||||
if (self)
|
||||
{
|
||||
_minSize = [aCoder decodeSizeForKey:_CPCibWindowTemplateMinSizeKey];
|
||||
_maxSize = [aCoder decodeSizeForKey:_CPCibWindowTemplateMaxSizeKey];
|
||||
if ([aCoder containsValueForKey:_CPCibWindowTemplateMinSizeKey])
|
||||
_minSize = [aCoder decodeSizeForKey:_CPCibWindowTemplateMinSizeKey];
|
||||
if ([aCoder containsValueForKey:_CPCibWindowTemplateMaxSizeKey])
|
||||
_maxSize = [aCoder decodeSizeForKey:_CPCibWindowTemplateMaxSizeKey];
|
||||
|
||||
_viewClass = [aCoder decodeObjectForKey:_CPCibWindowTemplateViewClassKey];
|
||||
|
||||
_windowClass = [aCoder decodeObjectForKey:_CPCibWindowTemplateViewClassKey];
|
||||
_windowClass = [aCoder decodeObjectForKey:_CPCibWindowTemplateWindowClassKey];
|
||||
_windowRect = [aCoder decodeRectForKey:_CPCibWindowTemplateWindowRectKey];
|
||||
_windowStyleMask = [aCoder decodeIntForKey:_CPCibWindowTemplateWindowStyleMaskKey];
|
||||
|
||||
_windowTitle = [aCoder decodeObjectForKey:_CPCibWindowTemplateWindowTitleKey];
|
||||
_windowView = [aCoder decodeObjectForKey:_CPCibWindowTemplateWindowViewKey];
|
||||
|
||||
_windowAutorecalculatesKeyViewLoop = !![aCoder decodeObjectForKey:_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop];
|
||||
_windowIsFullPlatformWindow = !![aCoder decodeObjectForKey:_CPCibWindowTemplateWindowIsFullPlatformWindowKey];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -52,33 +83,57 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeKey",
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeSize:_minSize forKey:_CPCibWindowTemplateMinSizeKey];
|
||||
[aCoder encodeSize:_maxSize forKey:_CPCibWindowTemplateMaxSizeKey];
|
||||
if (_minSize)
|
||||
[aCoder encodeSize:_minSize forKey:_CPCibWindowTemplateMinSizeKey];
|
||||
if (_maxSize)
|
||||
[aCoder encodeSize:_maxSize forKey:_CPCibWindowTemplateMaxSizeKey];
|
||||
|
||||
[aCoder encodeObject:_viewClass forKey:_CPCibWindowTemplateViewClassKey];
|
||||
|
||||
[aCoder encodeObject:_windowClass forKey:_CPCibWindowTemplateViewClassKey];
|
||||
[aCoder encodeObject:_windowClass forKey:_CPCibWindowTemplateWindowClassKey];
|
||||
[aCoder encodeRect:_windowRect forKey:_CPCibWindowTemplateWindowRectKey];
|
||||
[aCoder encodeInt:_windowStyleMask forKey:_CPCibWindowTemplateWindowStyleMaskKey];
|
||||
|
||||
[aCoder encodeObject:_windowTitle forKey:_CPCibWindowTemplateWindowTitleKey];
|
||||
[aCoder encodeObject:_windowView forKey:_CPCibWindowTemplateWindowViewKey];
|
||||
|
||||
if (_windowAutorecalculatesKeyViewLoop)
|
||||
[aCoder encodeObject:_windowAutorecalculatesKeyViewLoop forKey:_CPCibWindowTemplateWindowAutorecalculatesKeyViewLoop];
|
||||
|
||||
if (_windowIsFullPlatformWindow)
|
||||
[aCoder encodeObject:_windowIsFullPlatformWindow forKey:_CPCibWindowTemplateWindowIsFullPlatformWindowKey];
|
||||
}
|
||||
|
||||
- (CPString)customClassName
|
||||
{
|
||||
return _windowClass;
|
||||
}
|
||||
|
||||
|
||||
- (void)setCustomClassName:(CPString)aClassName
|
||||
{
|
||||
_windowClass = aClassName;
|
||||
}
|
||||
|
||||
- (CPString)windowClass
|
||||
{
|
||||
return _windowClass;
|
||||
}
|
||||
|
||||
- (id)_cibInstantiate
|
||||
{
|
||||
var windowClass = CPClassFromString(_windowClass);
|
||||
var windowClass = CPClassFromString([self windowClass]);
|
||||
|
||||
/* if (!windowClass)
|
||||
[NSException raise:NSInvalidArgumentException format:@"Unable to locate NSWindow class %@, using NSWindow",_windowClass];
|
||||
class=[NSWindow class];*/
|
||||
|
||||
var theWindow = [[windowClass alloc] initWithContentRect:_windowRect styleMask:CPHUDBackgroundWindowMask | CPClosableWindowMask];//styleMask:_windowStyleMask];
|
||||
// alert(CPStringFromRect(_windowRect));
|
||||
// alert(CPStringFromSize(_minSize));
|
||||
// alert(CPStringFromSize(_maxSize));
|
||||
//[theWindow setMinSize:_minSize];
|
||||
//[theWindow setMaxSize:_maxSize];
|
||||
var theWindow = [[windowClass alloc] initWithContentRect:_windowRect styleMask:_windowStyleMask];
|
||||
|
||||
if (_minSize)
|
||||
[theWindow setMinSize:_minSize];
|
||||
if (_maxSize)
|
||||
[theWindow setMaxSize:_maxSize];
|
||||
[theWindow setLevel:CPFloatingWindowLevel];
|
||||
|
||||
//[result setHidesOnDeactivate:(_wtFlags&0x80000000)?YES:NO];
|
||||
@@ -91,6 +146,14 @@ var _CPCibWindowTemplateMinSizeKey = @"_CPCibWindowTemplateMinSizeKey",
|
||||
|
||||
[_windowView setAutoresizesSubviews:YES];
|
||||
|
||||
if ([_viewClass isKindOfClass:[CPToolbar class]])
|
||||
{
|
||||
[theWindow setToolbar:_viewClass];
|
||||
}
|
||||
|
||||
[theWindow setAutorecalculatesKeyViewLoop:_windowAutorecalculatesKeyViewLoop];
|
||||
[theWindow setFullBridge:_windowIsFullPlatformWindow];
|
||||
|
||||
return theWindow;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import <Foundation/CPObject.j>
|
||||
import <Foundation/CPString.j>
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPString.j>
|
||||
|
||||
import "CAMediaTimingFunction.j"
|
||||
@import "CAMediaTimingFunction.j"
|
||||
|
||||
/*
|
||||
This is an animation class.
|
||||
@@ -31,11 +31,12 @@ import "CAMediaTimingFunction.j"
|
||||
@implementation CAAnimation : CPObject
|
||||
{
|
||||
BOOL _isRemovedOnCompletion;
|
||||
id _delegate;
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a new <objj>CAAnimation</objj> instance
|
||||
@return a new <objj>CAAnimation</objj> instance
|
||||
/*!
|
||||
Creates a new CAAnimation instance
|
||||
@return a new CAAnimation instance
|
||||
*/
|
||||
+ (id)animation
|
||||
{
|
||||
@@ -52,51 +53,51 @@ import "CAMediaTimingFunction.j"
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code>
|
||||
@return <code>YES</code>
|
||||
/*!
|
||||
Returns \c YES
|
||||
@return \c YES
|
||||
*/
|
||||
- (void)shouldArchiveValueForKey:(CPString)aKey
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>nil</code>
|
||||
@return <code>nil</code>
|
||||
/*!
|
||||
Returns \c nil
|
||||
@return \c nil
|
||||
*/
|
||||
+ (id)defaultValueForKey:(CPString)aKey
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Specifies whether this animation should be removed after it has completed.
|
||||
@param <code>YES</code> means the animation should be removed
|
||||
@param \c YES means the animation should be removed
|
||||
*/
|
||||
- (void)setRemovedOnCompletion:(BOOL)isRemovedOnCompletion
|
||||
{
|
||||
_isRemovedOnCompletion = isRemovedOnCompletion;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the animation is removed after completion
|
||||
/*!
|
||||
Returns \c YES if the animation is removed after completion
|
||||
*/
|
||||
- (BOOL)removedOnCompletion
|
||||
{
|
||||
return _isRemovedOnCompletion;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns <code>YES</code> if the animation is removed after completion
|
||||
/*!
|
||||
Returns \c YES if the animation is removed after completion
|
||||
*/
|
||||
- (BOOL)isRemovedOnCompletion
|
||||
{
|
||||
return _isRemovedOnCompletion;
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the animation's timing function. If <code>nil</code>, then it has a linear pacing.
|
||||
/*!
|
||||
Returns the animation's timing function. If \c nil, then it has a linear pacing.
|
||||
*/
|
||||
- (CAMediaTimingFunction)timingFunction
|
||||
{
|
||||
@@ -104,7 +105,7 @@ import "CAMediaTimingFunction.j"
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the animation delegate
|
||||
@param aDelegate the new delegate
|
||||
*/
|
||||
@@ -113,7 +114,7 @@ import "CAMediaTimingFunction.j"
|
||||
_delegate = aDelegate;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the animation's delegate
|
||||
*/
|
||||
- (id)delegate
|
||||
@@ -143,7 +144,7 @@ import "CAMediaTimingFunction.j"
|
||||
{
|
||||
var animation = [self animation];
|
||||
|
||||
[animation setKeypath:aKeyPath];
|
||||
[animation setKeyPath:aKeyPath];
|
||||
|
||||
return animation;
|
||||
}
|
||||
@@ -190,9 +191,9 @@ import "CAMediaTimingFunction.j"
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
A <objj>CABasicAnimation</objj> is a simple animation that moves a
|
||||
<objj>CALayer</objj> from one point to another over a specified
|
||||
/*!
|
||||
A CABasicAnimation is a simple animation that moves a
|
||||
CALayer from one point to another over a specified
|
||||
period of time.
|
||||
*/
|
||||
@implementation CABasicAnimation : CAPropertyAnimation
|
||||
@@ -202,7 +203,7 @@ import "CAMediaTimingFunction.j"
|
||||
id _byValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the starting position for the animation.
|
||||
@param aValue the animation starting position
|
||||
*/
|
||||
@@ -211,7 +212,7 @@ import "CAMediaTimingFunction.j"
|
||||
_fromValue = aValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the animation's starting position.
|
||||
*/
|
||||
- (id)fromValue
|
||||
@@ -219,7 +220,7 @@ import "CAMediaTimingFunction.j"
|
||||
return _fromValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the ending position for the animation.
|
||||
@param aValue the animation ending position
|
||||
*/
|
||||
@@ -228,7 +229,7 @@ import "CAMediaTimingFunction.j"
|
||||
_toValue = aValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the animation's ending position.
|
||||
*/
|
||||
- (id)toValue
|
||||
@@ -236,7 +237,7 @@ import "CAMediaTimingFunction.j"
|
||||
return _toValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Sets the optional byValue for animation interpolation.
|
||||
@param aValue the byValue
|
||||
*/
|
||||
@@ -245,7 +246,7 @@ import "CAMediaTimingFunction.j"
|
||||
_byValue = aValue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
Returns the animation's byValue.
|
||||
*/
|
||||
- (id)byValue
|
||||
|
||||
@@ -20,11 +20,12 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CGGeometry.j"
|
||||
import "CPCompatibility.j"
|
||||
@import "CGGeometry.j"
|
||||
@import "CPCompatibility.j"
|
||||
|
||||
#define PIXEL(pixels) pixels + "px";
|
||||
|
||||
|
||||
function CABackingStoreGetContext(aBackingStore)
|
||||
{
|
||||
return aBackingStore.context;
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
import "CALayer.j"
|
||||
import "CPFlashMovie.j"
|
||||
@import "CALayer.j"
|
||||
@import "CPFlashMovie.j"
|
||||
|
||||
|
||||
@implementation CAFlashLayer : CALayer
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user