mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-09 12:17:13 +00:00
Compare commits
1574
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb56955fff | ||
|
|
cbb7cc93e9 | ||
|
|
8b608c5cce | ||
|
|
163fb14d0e | ||
|
|
e4f87bd805 | ||
|
|
864c7d96f1 | ||
|
|
e35e87fb05 | ||
|
|
3246bdbc8b | ||
|
|
710bb5fe1c | ||
|
|
53a52da709 | ||
|
|
4afbfc6e57 | ||
|
|
f7aa9efe13 | ||
|
|
1fce0ee551 | ||
|
|
ee695f9000 | ||
|
|
8f99ebb577 | ||
|
|
5bfafaf241 | ||
|
|
64e1a2ce4b | ||
|
|
27883c5beb | ||
|
|
2772725fe3 | ||
|
|
3f58a64827 | ||
|
|
cb37c7d218 | ||
|
|
45d951071f | ||
|
|
2ac60684fa | ||
|
|
af71742110 | ||
|
|
1adf27427a | ||
|
|
082f78eefa | ||
|
|
a8a9ec0023 | ||
|
|
df4f945bc6 | ||
|
|
ad756a5b86 | ||
|
|
2ca99ba669 | ||
|
|
6443575114 | ||
|
|
52a51d81f1 | ||
|
|
7d20b06e52 | ||
|
|
ad9d8a7200 | ||
|
|
44da699343 | ||
|
|
0a542f9be9 | ||
|
|
8f1601ca55 | ||
|
|
ff0ce164da | ||
|
|
3650c38aa9 | ||
|
|
61d97338e2 | ||
|
|
e27bb4de50 | ||
|
|
4462d8bf22 | ||
|
|
f23a8ac928 | ||
|
|
9e1750ee13 | ||
|
|
5cd284cb36 | ||
|
|
5ea58430fe | ||
|
|
acfbda0d91 | ||
|
|
437e5a62f1 | ||
|
|
bbb5aea77d | ||
|
|
b5d04f84e7 | ||
|
|
93b12eb14f | ||
|
|
0aa67c4cf2 | ||
|
|
1c1affac5c | ||
|
|
0919797edc | ||
|
|
94ac8fd837 | ||
|
|
ecc82b5b51 | ||
|
|
add021fd67 | ||
|
|
2daf309d7d | ||
|
|
20be4a40ea | ||
|
|
7de91da87a | ||
|
|
b8b48b533b | ||
|
|
535f981739 | ||
|
|
0404ac0395 | ||
|
|
f961625f03 | ||
|
|
7909bae065 | ||
|
|
7e6e117b17 | ||
|
|
314e64d7b1 | ||
|
|
77eb6b358f | ||
|
|
25f86e2542 | ||
|
|
8bc0461463 | ||
|
|
c9dd1ea71d | ||
|
|
85d3529bc3 | ||
|
|
3aac8bb072 | ||
|
|
e9a7e21aca | ||
|
|
fc0e4da33c | ||
|
|
ef3c1315dd | ||
|
|
5c12890faf | ||
|
|
c4fd5e0999 | ||
|
|
d3c56d635d | ||
|
|
c53cb49034 | ||
|
|
d38463c5d9 | ||
|
|
bc4b424f0e | ||
|
|
6bd4f3b7b4 | ||
|
|
d91d83f115 | ||
|
|
584b6ab219 | ||
|
|
04b155dbfb | ||
|
|
ddc5488db7 | ||
|
|
5c6906d5fd | ||
|
|
56fee5d286 | ||
|
|
c4dcab04b6 | ||
|
|
8dd9a96769 | ||
|
|
6864015af6 | ||
|
|
ebaa60e929 | ||
|
|
ac9962c624 | ||
|
|
ccd1773643 | ||
|
|
ac970ff768 | ||
|
|
53ff45425d | ||
|
|
de435c741f | ||
|
|
1dc9a44aec | ||
|
|
0206379315 | ||
|
|
c39adbbbc5 | ||
|
|
061a15d4f7 | ||
|
|
7c2518f027 | ||
|
|
a07459454b | ||
|
|
752f9d2694 | ||
|
|
0a180f9e20 | ||
|
|
6fff2e1c2f | ||
|
|
4266900d2e | ||
|
|
a2a1d84bc0 | ||
|
|
7648c562f1 | ||
|
|
403fd30926 | ||
|
|
17ccddf541 | ||
|
|
c24366fcec | ||
|
|
9a258a6265 | ||
|
|
4542ccbdc2 | ||
|
|
41d8ad2b86 | ||
|
|
a3a2ee118e | ||
|
|
f285efc277 | ||
|
|
fbaf87ec6c | ||
|
|
a005a468a7 | ||
|
|
c8d01549ba | ||
|
|
23af4505dd | ||
|
|
53cb6a5b3e | ||
|
|
765384ace1 | ||
|
|
d39e3e2f69 | ||
|
|
d75124066e | ||
|
|
f4400876da | ||
|
|
c222c5127f | ||
|
|
90d04956e6 | ||
|
|
5d3b918339 | ||
|
|
eba0091764 | ||
|
|
7999cbbea3 | ||
|
|
cee7987b35 | ||
|
|
d475c08024 | ||
|
|
d78f83ae49 | ||
|
|
df694f01e7 | ||
|
|
f5d9cbb11b | ||
|
|
d802588abe | ||
|
|
927d6d2fc6 | ||
|
|
bab7595078 | ||
|
|
1ea9a6c57f | ||
|
|
f479d415d8 | ||
|
|
f41e7be726 | ||
|
|
c1fe28124f | ||
|
|
01a1e506e4 | ||
|
|
e61aebb55d | ||
|
|
bae5bafc62 | ||
|
|
be6cb266de | ||
|
|
6f72edc2d9 | ||
|
|
29f66bdfed | ||
|
|
c1c6f5682c | ||
|
|
a8a74a0246 | ||
|
|
f9b867c3eb | ||
|
|
6c07f9a048 | ||
|
|
573e0acafa | ||
|
|
530c664967 | ||
|
|
12c29c8387 | ||
|
|
6c7f61d90f | ||
|
|
eb6d8d0bb3 | ||
|
|
ef462973c7 | ||
|
|
aa7e49cfde | ||
|
|
186048676a | ||
|
|
0f40721cff | ||
|
|
3799dd92fd | ||
|
|
9ea1a2b1de | ||
|
|
d8b27d122e | ||
|
|
15c3a6eae4 | ||
|
|
4fcc90d53e | ||
|
|
2619a50976 | ||
|
|
de116c95d4 | ||
|
|
47594c9b07 | ||
|
|
1b9098b564 | ||
|
|
b0306eac87 | ||
|
|
125e813d25 | ||
|
|
8e0d4a70f2 | ||
|
|
0556fc68b0 | ||
|
|
e9e2313159 | ||
|
|
dbb2f03a43 | ||
|
|
ed0dc2d887 | ||
|
|
0a93f2d0b0 | ||
|
|
fd581cd65c | ||
|
|
36702bc971 | ||
|
|
0fc3eb38f1 | ||
|
|
73c8e945e9 | ||
|
|
b8420303ba | ||
|
|
532bfcf937 | ||
|
|
f6d892023e | ||
|
|
7f2977f4af | ||
|
|
e246a2753b | ||
|
|
6d06a26c31 | ||
|
|
7cda21b60f | ||
|
|
1db740039b | ||
|
|
df462774aa | ||
|
|
f55fa789c4 | ||
|
|
488541f26e | ||
|
|
b404b1cf50 | ||
|
|
69370aa806 | ||
|
|
b3ddd85b09 | ||
|
|
033221fd01 | ||
|
|
1201fa38a8 | ||
|
|
32059c376e | ||
|
|
511d393c78 | ||
|
|
237601cf29 | ||
|
|
928f14872e | ||
|
|
c6a9adf96c | ||
|
|
cdd00e0bd4 | ||
|
|
2503d13402 | ||
|
|
2d97970d24 | ||
|
|
d8945e53ba | ||
|
|
f18012d07b | ||
|
|
104f8603b9 | ||
|
|
19bb083030 | ||
|
|
ae575a19a0 | ||
|
|
5f94d0a5c2 | ||
|
|
f2fe1a7c17 | ||
|
|
618e6a1604 | ||
|
|
cef294513a | ||
|
|
8550abadef | ||
|
|
25c8b2d631 | ||
|
|
e7b690205e | ||
|
|
2403f8833a | ||
|
|
9f25743b4d | ||
|
|
f1c76f65cf | ||
|
|
7b39533410 | ||
|
|
1fe9c39dd5 | ||
|
|
2b7be2e3f1 | ||
|
|
1ee9a3f279 | ||
|
|
679acfb239 | ||
|
|
3c7ab4b49b | ||
|
|
3f062e1c0a | ||
|
|
0a497d8c3f | ||
|
|
462351f485 | ||
|
|
c4cbb631a9 | ||
|
|
76f2d15b1b | ||
|
|
5d63865ae5 | ||
|
|
ad7c88fb45 | ||
|
|
1af3877758 | ||
|
|
146ce3b12b | ||
|
|
bf67721ac4 | ||
|
|
436f736a8e | ||
|
|
1446e90dd9 | ||
|
|
54716c03d1 | ||
|
|
fad5e97e0a | ||
|
|
8cf79590c5 | ||
|
|
c6aafac6c8 | ||
|
|
94a5995197 | ||
|
|
74081dc981 | ||
|
|
7a593b13a7 | ||
|
|
25dc38b2b1 | ||
|
|
403b12f7b7 | ||
|
|
cd223be88a | ||
|
|
a6a3fca1e7 | ||
|
|
63815eb5c2 | ||
|
|
4444264830 | ||
|
|
255f91f7ca | ||
|
|
700207d29b | ||
|
|
72d96e9a3b | ||
|
|
a55b6de1b4 | ||
|
|
9faac05666 | ||
|
|
7bf408aba0 | ||
|
|
70ee391b33 | ||
|
|
f5eb1158db | ||
|
|
81916d0f45 | ||
|
|
76d77cc244 | ||
|
|
03dccbe613 | ||
|
|
1ccec65619 | ||
|
|
b78e9062a1 | ||
|
|
dc87354702 | ||
|
|
033e8583db | ||
|
|
d46b51fa9a | ||
|
|
dd1dfdcfeb | ||
|
|
c0233c803d | ||
|
|
1fa8c88f6b | ||
|
|
195b109a36 | ||
|
|
994ac3fc8e | ||
|
|
a8ec5cf5e9 | ||
|
|
b4263aa3ec | ||
|
|
6456809a72 | ||
|
|
024c0a3186 | ||
|
|
560b1d48bc | ||
|
|
bb2d860807 | ||
|
|
6f1c0fa4a8 | ||
|
|
35808e7bde | ||
|
|
1b4eeb35b3 | ||
|
|
cee032bbc9 | ||
|
|
9547b70743 | ||
|
|
5df8939d84 | ||
|
|
0b9c689273 | ||
|
|
ac193cd97a | ||
|
|
7355a60f16 | ||
|
|
fb6f6325b5 | ||
|
|
0f767d9dfc | ||
|
|
d1addcab91 | ||
|
|
7a02cd794b | ||
|
|
258cd9209f | ||
|
|
700f6022d9 | ||
|
|
a0f6b815e8 | ||
|
|
54477b8b8f | ||
|
|
6183f8d4e9 | ||
|
|
d98e247665 | ||
|
|
d0cd976246 | ||
|
|
15f6f6a90a | ||
|
|
f18074bf49 | ||
|
|
3f64b74453 | ||
|
|
1c86256f83 | ||
|
|
98105ad4dc | ||
|
|
6bf2d6c1ab | ||
|
|
cebd51aac1 | ||
|
|
42b8b3aa75 | ||
|
|
c0eaf79d25 | ||
|
|
fb2fe3eb63 | ||
|
|
184d63f7eb | ||
|
|
14ebd58c96 | ||
|
|
3edddb8e91 | ||
|
|
d2340dff1a | ||
|
|
d30c6aec7d | ||
|
|
4854b3e08f | ||
|
|
facc748090 | ||
|
|
5c71612e34 | ||
|
|
f00a5fbaf4 | ||
|
|
34769da9cc | ||
|
|
458e6ed531 | ||
|
|
05814e9351 | ||
|
|
b6c0b1b396 | ||
|
|
1a299d85b6 | ||
|
|
1816274ebe | ||
|
|
b02e4c5308 | ||
|
|
12ed42e163 | ||
|
|
6c9c135062 | ||
|
|
6aea0160df | ||
|
|
30edd9e8dd | ||
|
|
7734402e8e | ||
|
|
ace1332ca5 | ||
|
|
baa313e8f8 | ||
|
|
3d9aa24f3c | ||
|
|
c287d457d6 | ||
|
|
7537b27a58 | ||
|
|
1f560c92fc | ||
|
|
74cd11c763 | ||
|
|
ee7d89331b | ||
|
|
5b51077823 | ||
|
|
d2f94d1945 | ||
|
|
0170a099bb | ||
|
|
2b893d0ea1 | ||
|
|
a08da57cbb | ||
|
|
a313fe5a86 | ||
|
|
544de699f6 | ||
|
|
d9d35b7f50 | ||
|
|
7dbec00f0e | ||
|
|
51b9b40c7e | ||
|
|
b2383a084a | ||
|
|
61848c5e24 | ||
|
|
60b2f7d243 | ||
|
|
d2a5e126ae | ||
|
|
9d7d8ddde9 | ||
|
|
249ebeb4aa | ||
|
|
378c14d790 | ||
|
|
30c1093c2f | ||
|
|
b966c8cb0b | ||
|
|
38cddf029b | ||
|
|
6652a27781 | ||
|
|
08b067cfc5 | ||
|
|
4959b84b4c | ||
|
|
d42562fdcb | ||
|
|
1e0aacd811 | ||
|
|
329469fe84 | ||
|
|
93b0c5a7e5 | ||
|
|
9e456fd315 | ||
|
|
8203c71b03 | ||
|
|
80d1e9a9a7 | ||
|
|
3c27e57b19 | ||
|
|
b46b2f048d | ||
|
|
8d403f579c | ||
|
|
12b274ab43 | ||
|
|
986cccb800 | ||
|
|
5da8728d6c | ||
|
|
66c9a1e90a | ||
|
|
a1925e1d0b | ||
|
|
da0910b1e5 | ||
|
|
8dbbe34dfc | ||
|
|
947d552509 | ||
|
|
037bc4ea2b | ||
|
|
cfbef141d8 | ||
|
|
410ad0dc09 | ||
|
|
3d9b42f0c2 | ||
|
|
eeea21c882 | ||
|
|
d94e5480c9 | ||
|
|
a5c8e87e6d | ||
|
|
08f74201b6 | ||
|
|
3a25c683b6 | ||
|
|
fb43f8bf1a | ||
|
|
bf082600c3 | ||
|
|
7596c66703 | ||
|
|
e1bcf6c47e | ||
|
|
ef1d6c78ac | ||
|
|
f36406af08 | ||
|
|
4b7d215d9d | ||
|
|
e09f12dc0b | ||
|
|
efa7406741 | ||
|
|
5a2181c064 | ||
|
|
dda81b12da | ||
|
|
c4bf67d133 | ||
|
|
f355c8679d | ||
|
|
095864de39 | ||
|
|
0eb196e852 | ||
|
|
62e5d8d841 | ||
|
|
2f82fb842d | ||
|
|
f9fa6abbad | ||
|
|
4b55a69150 | ||
|
|
42e12fae24 | ||
|
|
2f55d5130a | ||
|
|
cd4b47b397 | ||
|
|
126cba1e94 | ||
|
|
e4e894fa54 | ||
|
|
65f701d459 | ||
|
|
c2b7cc7964 | ||
|
|
969d1dff2a | ||
|
|
e0fab808b9 | ||
|
|
77031d7082 | ||
|
|
5a0f7ea706 | ||
|
|
9d041c4b62 | ||
|
|
ec4d29c327 | ||
|
|
5f8d8988e3 | ||
|
|
a31912f710 | ||
|
|
e0d42962e7 | ||
|
|
47688bf016 | ||
|
|
39a695959d | ||
|
|
71d45a67b7 | ||
|
|
8dc1369ba3 | ||
|
|
1206654d98 | ||
|
|
1492763d0c | ||
|
|
285fafc464 | ||
|
|
b15cc59b95 | ||
|
|
55cfe31dfe | ||
|
|
dafa6e0310 | ||
|
|
4befe17cab | ||
|
|
2217b560cb | ||
|
|
39d01a1da8 | ||
|
|
4e14ad7821 | ||
|
|
aee5a2cbe4 | ||
|
|
de2d5aec1a | ||
|
|
bea03de562 | ||
|
|
0aeccbb02c | ||
|
|
4e11152e76 | ||
|
|
5059028e8a | ||
|
|
070e6cf848 | ||
|
|
4187fe7261 | ||
|
|
bdec147874 | ||
|
|
189fc76dad | ||
|
|
c0a68005d4 | ||
|
|
8187e4cdc2 | ||
|
|
cc98fd4167 | ||
|
|
422e014eff | ||
|
|
dd4e044608 | ||
|
|
13fad1ca47 | ||
|
|
70aaa29016 | ||
|
|
7dcec9afcf | ||
|
|
608642fc25 | ||
|
|
dd62c08b09 | ||
|
|
315fc21eca | ||
|
|
8cfd3aeda9 | ||
|
|
6061323c36 | ||
|
|
17da8b04bf | ||
|
|
3a948406d5 | ||
|
|
91c52f8ec8 | ||
|
|
6076bbb554 | ||
|
|
f70d3359c4 | ||
|
|
2dbfbe36c3 | ||
|
|
a25bd03566 | ||
|
|
109da1c2cc | ||
|
|
9a48e25bb1 | ||
|
|
a3ade200a9 | ||
|
|
eb1901f03d | ||
|
|
f8c7cdfc28 | ||
|
|
ea58d84b55 | ||
|
|
1360caaa81 | ||
|
|
315a7b9e6a | ||
|
|
79311d107b | ||
|
|
77435fcbd7 | ||
|
|
4395c07648 | ||
|
|
f9cc77382a | ||
|
|
e4fce08eff | ||
|
|
47196cbc55 | ||
|
|
c0bf301434 | ||
|
|
f6671f8561 | ||
|
|
26aab2961b | ||
|
|
c233abe3a4 | ||
|
|
beb3646bef | ||
|
|
eb8330a420 | ||
|
|
7f3c2176f8 | ||
|
|
1d0fdd68f4 | ||
|
|
b26c78828e | ||
|
|
85648c7f11 | ||
|
|
8588f446aa | ||
|
|
d723c5c1c7 | ||
|
|
7897f2489c | ||
|
|
86e2e0a2c3 | ||
|
|
e2349019a3 | ||
|
|
e485044cfb | ||
|
|
72d80e2857 | ||
|
|
79a4733698 | ||
|
|
c64e1daa99 | ||
|
|
d6b4933b05 | ||
|
|
bcf9cee0f8 | ||
|
|
a2960a7997 | ||
|
|
dac9874895 | ||
|
|
a4d374ecb6 | ||
|
|
3e56c8ed8b | ||
|
|
f4c11d14e7 | ||
|
|
dde805931d | ||
|
|
fedc721432 | ||
|
|
b09cb24804 | ||
|
|
80bfeb1a29 | ||
|
|
63ad51a637 | ||
|
|
5ee6232bfc | ||
|
|
b7d399faf7 | ||
|
|
28691bb8a0 | ||
|
|
fd61f35f78 | ||
|
|
0505b59781 | ||
|
|
a3601e5167 | ||
|
|
99f63ab64d | ||
|
|
bb2a9fe180 | ||
|
|
e9c7c7ee4c | ||
|
|
ff7d4f237a | ||
|
|
8bf9f8057e | ||
|
|
27b0f531d7 | ||
|
|
01e96d3086 | ||
|
|
95e7a7c8eb | ||
|
|
bbc2f4766e | ||
|
|
12dde5154f | ||
|
|
5bc3609ad7 | ||
|
|
5c1cdea3c6 | ||
|
|
3da5b3a278 | ||
|
|
9f6460ddae | ||
|
|
f1dcf85d7b | ||
|
|
7b78edf38f | ||
|
|
4a726a0c75 | ||
|
|
5afa7e9fc3 | ||
|
|
75c102f304 | ||
|
|
68f3c10055 | ||
|
|
e9aeda2a34 | ||
|
|
45c7a110b6 | ||
|
|
e438e6aedb | ||
|
|
8dd888bcbd | ||
|
|
5f12291ac4 | ||
|
|
e206d78a52 | ||
|
|
d94a82b0a7 | ||
|
|
3bee3d1701 | ||
|
|
631352e1ef | ||
|
|
f2d5dee49e | ||
|
|
2d578c7cd9 | ||
|
|
b07ff100d1 | ||
|
|
b0c10b2248 | ||
|
|
7b8249442d | ||
|
|
8221339318 | ||
|
|
b773af96b8 | ||
|
|
dbc7808c70 | ||
|
|
77c5000f16 | ||
|
|
56928b6ca0 | ||
|
|
8178dfe7bd | ||
|
|
0db6c667c6 | ||
|
|
2b0a642827 | ||
|
|
f71c0f8cb8 | ||
|
|
3782372f65 | ||
|
|
9e35e42030 | ||
|
|
7a8195e54c | ||
|
|
b50ee28693 | ||
|
|
e134f910aa | ||
|
|
17eb6e47eb | ||
|
|
82ce626d8a | ||
|
|
e063ff5e54 | ||
|
|
5aa98966bc | ||
|
|
fca2746a82 | ||
|
|
7faffc2de7 | ||
|
|
86a8a9dc85 | ||
|
|
c13a0c1c26 | ||
|
|
6ce3c070b8 | ||
|
|
e1cb11da22 | ||
|
|
c628ea34b7 | ||
|
|
33b7c726f6 | ||
|
|
6f3fd424e1 | ||
|
|
b048ce9c8e | ||
|
|
6a1040d659 | ||
|
|
fc05c28231 | ||
|
|
5d165bc6df | ||
|
|
c139b5eabb | ||
|
|
d6dd0e551d | ||
|
|
a84a7bb7c3 | ||
|
|
7195ae6e46 | ||
|
|
3c70b2f033 | ||
|
|
c46340a830 | ||
|
|
8d703b1459 | ||
|
|
f1145ef169 | ||
|
|
a28f9ceeba | ||
|
|
1927bb4798 | ||
|
|
d4c77f9786 | ||
|
|
021f8d0331 | ||
|
|
e7d25550ee | ||
|
|
8227276437 | ||
|
|
38ef76bcdc | ||
|
|
7faa0f60b4 | ||
|
|
faebbb2b8a | ||
|
|
bc9beb1e87 | ||
|
|
f161f723d7 | ||
|
|
6c94550de3 | ||
|
|
b39a45781a | ||
|
|
9273c9ee10 | ||
|
|
e6285914b0 | ||
|
|
7d2eb83a1b | ||
|
|
7e622f1153 | ||
|
|
9b30a634b1 | ||
|
|
e897d2ef81 | ||
|
|
f5b177dda8 | ||
|
|
5f51b5d565 | ||
|
|
8d7753fb40 | ||
|
|
4c6f5744d8 | ||
|
|
43eb2932ba | ||
|
|
59de0e7a4a | ||
|
|
7b204cf910 | ||
|
|
b587a19a0f | ||
|
|
e58420b031 | ||
|
|
24313d6709 | ||
|
|
7acb30b981 | ||
|
|
7454620aa2 | ||
|
|
abb0799817 | ||
|
|
aebd28dbef | ||
|
|
152db9fb8e | ||
|
|
980cd520cd | ||
|
|
373ec8b5a1 | ||
|
|
bacb78e9e8 | ||
|
|
333e112e37 | ||
|
|
818d5d782f | ||
|
|
8774090086 | ||
|
|
0ca65c6d61 | ||
|
|
c38daac646 | ||
|
|
c31f5b1d59 | ||
|
|
2320a56a63 | ||
|
|
07bd91b456 | ||
|
|
803f7c3a4b | ||
|
|
a1926982c6 | ||
|
|
b3306d50eb | ||
|
|
3e6e5f95ec | ||
|
|
df71fb42ac | ||
|
|
fe81a354a3 | ||
|
|
67ba2e49c3 | ||
|
|
8dec42f295 | ||
|
|
5942553370 | ||
|
|
ae5d31746e | ||
|
|
1c1976abbf | ||
|
|
b3465ddf89 | ||
|
|
f67fafdb8e | ||
|
|
ffcac677e0 | ||
|
|
a9d643aa55 | ||
|
|
fa3647e8e5 | ||
|
|
db839f6d6d | ||
|
|
3a85d5b89f | ||
|
|
1575c2bc10 | ||
|
|
57189183d6 | ||
|
|
01c5cf98df | ||
|
|
9ad2995168 | ||
|
|
51fc6518e1 | ||
|
|
530db4e904 | ||
|
|
cd92ebd69c | ||
|
|
5c58552d91 | ||
|
|
584aebf76b | ||
|
|
43a2480699 | ||
|
|
699503042f | ||
|
|
02e71c23de | ||
|
|
c24c3868bb | ||
|
|
5c60819668 | ||
|
|
480a125a20 | ||
|
|
598a6ebaed | ||
|
|
185ad5e747 | ||
|
|
42d7e4312b | ||
|
|
41a741724f | ||
|
|
087e811586 | ||
|
|
fa07bc143e | ||
|
|
e13f5ed730 | ||
|
|
48108cebc9 | ||
|
|
ecac17a00a | ||
|
|
3ba215887c | ||
|
|
edbc1baf32 | ||
|
|
500fa7f314 | ||
|
|
a10dfa4ab4 | ||
|
|
b208571ed7 | ||
|
|
c1ec27045c | ||
|
|
111b12161c | ||
|
|
d23d18933c | ||
|
|
f0e736962d | ||
|
|
b12d1491d4 | ||
|
|
6d1a094b15 | ||
|
|
88d14792a4 | ||
|
|
7780a98abe | ||
|
|
a1248db3e7 | ||
|
|
6fd2d12d3f | ||
|
|
9fab52c044 | ||
|
|
a32f55d64e | ||
|
|
96d2b001ea | ||
|
|
e38d6c9f9e | ||
|
|
be129d243b | ||
|
|
c85355986b | ||
|
|
310c0e8379 | ||
|
|
f0dbffe4f8 | ||
|
|
598a482538 | ||
|
|
a995f3ab52 | ||
|
|
ec5454de44 | ||
|
|
ec55bddbd4 | ||
|
|
808591df56 | ||
|
|
96ae3da1e0 | ||
|
|
4f13df69c7 | ||
|
|
6d305e4a84 | ||
|
|
d3dab982a1 | ||
|
|
2f6adbae3c | ||
|
|
ccdc1eed5a | ||
|
|
65dee154f7 | ||
|
|
662db009de | ||
|
|
d07d8f6857 | ||
|
|
0327f6b1ea | ||
|
|
16dea2ee09 | ||
|
|
5e3fe139fc | ||
|
|
55d41fca05 | ||
|
|
0b7bb29557 | ||
|
|
6b683bd02d | ||
|
|
8f1a498953 | ||
|
|
293ed9065d | ||
|
|
8908fb7ee0 | ||
|
|
2877178b34 | ||
|
|
d527139f48 | ||
|
|
b3d8180541 | ||
|
|
3411c340ab | ||
|
|
4482089712 | ||
|
|
c8ce2343d7 | ||
|
|
d547d04277 | ||
|
|
8f1191733b | ||
|
|
00bdbcf857 | ||
|
|
bf8070c42d | ||
|
|
346a0b84ae | ||
|
|
ad48381744 | ||
|
|
0f30d7744d | ||
|
|
095a978120 | ||
|
|
1b72646772 | ||
|
|
a7b129147f | ||
|
|
6496a6643f | ||
|
|
e172217427 | ||
|
|
8c67eee4c8 | ||
|
|
ca9039b8d5 | ||
|
|
e260632a45 | ||
|
|
678f4fca8a | ||
|
|
5d88d07edc | ||
|
|
08aa591c65 | ||
|
|
f2a8edb29f | ||
|
|
50d7e8dc3d | ||
|
|
8403e36d4d | ||
|
|
2dda326cc4 | ||
|
|
9002a8db04 | ||
|
|
5ec5198dda | ||
|
|
0326b7d7ac | ||
|
|
2f5e7f3b2d | ||
|
|
0e17658269 | ||
|
|
87edda5972 | ||
|
|
ab8c3c2b75 | ||
|
|
44cef002ed | ||
|
|
bc80fe693a | ||
|
|
1898b51dc2 | ||
|
|
a5dcf5958e | ||
|
|
9fba72cbab | ||
|
|
912fdeaf09 | ||
|
|
36c417458d | ||
|
|
f1591e593a | ||
|
|
117ac0f5d6 | ||
|
|
e72a42fd2d | ||
|
|
e6e2f4c22c | ||
|
|
07b6e2b9f1 | ||
|
|
5ab10f4024 | ||
|
|
0ac08456bc | ||
|
|
80a3549ef5 | ||
|
|
770d5eb257 | ||
|
|
b32499f6ec | ||
|
|
d9819d8b3d | ||
|
|
932b825818 | ||
|
|
a0cbc5822e | ||
|
|
2ab50c6b61 | ||
|
|
d7426f6d35 | ||
|
|
8f15266f0a | ||
|
|
936c7190c7 | ||
|
|
3452984b8e | ||
|
|
3e17890528 | ||
|
|
2a7c50d356 | ||
|
|
05bca368c2 | ||
|
|
89c78f6030 | ||
|
|
b59b0259ca | ||
|
|
e46e1b20de | ||
|
|
7089abcef2 | ||
|
|
e6148a97a3 | ||
|
|
9e40033a92 | ||
|
|
08d4e19851 | ||
|
|
dcc0768411 | ||
|
|
f14062c721 | ||
|
|
0508b70658 | ||
|
|
04f8545397 | ||
|
|
633f985360 | ||
|
|
a649a6ebac | ||
|
|
634a8b4532 | ||
|
|
f3709a41e3 | ||
|
|
2bfebfee3c | ||
|
|
76ab5fbf78 | ||
|
|
c0b0c74638 | ||
|
|
6c0b771758 | ||
|
|
2f983fc305 | ||
|
|
3cf0d60f5d | ||
|
|
751e76b453 | ||
|
|
c2f95f7953 | ||
|
|
924b994df2 | ||
|
|
b10de5e82c | ||
|
|
65ffce8db2 | ||
|
|
58aeb2e40f | ||
|
|
141d737ca5 | ||
|
|
d0fd53a2ca | ||
|
|
3e0a6ea2cf | ||
|
|
758ad3eb06 | ||
|
|
b132a66707 | ||
|
|
f241f12ddb | ||
|
|
e1daf9467c | ||
|
|
5457ddb44f | ||
|
|
6fee13b0a5 | ||
|
|
2b17e173f5 | ||
|
|
569d4a5d8b | ||
|
|
51a88a23ac | ||
|
|
f41504ca57 | ||
|
|
bacb7018cb | ||
|
|
671ed3c2fe | ||
|
|
372e994617 | ||
|
|
e4560df62f | ||
|
|
920daf7fde | ||
|
|
ad1a15faaa | ||
|
|
459cf2e380 | ||
|
|
8d058a53fd | ||
|
|
d864ae9fef | ||
|
|
f1eeb61264 | ||
|
|
1418dd8b29 | ||
|
|
0297980ca3 | ||
|
|
5e94aaa2cb | ||
|
|
b929a8fe2e | ||
|
|
8c2f0bacf1 | ||
|
|
0bc02de012 | ||
|
|
411a282063 | ||
|
|
6c4d55622b | ||
|
|
e214bf3581 | ||
|
|
78e29d2bbf | ||
|
|
a93c10b794 | ||
|
|
3240d6f11a | ||
|
|
d5f38fe2f2 | ||
|
|
d4b1680727 | ||
|
|
b04e5760fb | ||
|
|
42c629ca45 | ||
|
|
68f45beb20 | ||
|
|
ea95897bbd | ||
|
|
4d1dc0c949 | ||
|
|
9fc892f723 | ||
|
|
5541381afe | ||
|
|
ed12bae6aa | ||
|
|
1520c1ebab | ||
|
|
790190e5c3 | ||
|
|
24889f7dcd | ||
|
|
8397685430 | ||
|
|
fc411c5c1c | ||
|
|
3a91f43bbb | ||
|
|
5e1f1675b8 | ||
|
|
9946987f69 | ||
|
|
526181a76b | ||
|
|
ce8f8c05e1 | ||
|
|
3844b88f7e | ||
|
|
db3c232173 | ||
|
|
22b77af9d3 | ||
|
|
3e29732e46 | ||
|
|
54e1efd794 | ||
|
|
8809c1634f | ||
|
|
13e1be1f74 | ||
|
|
26a123f5a8 | ||
|
|
f062fda2f7 | ||
|
|
e45e31d223 | ||
|
|
101261ca63 | ||
|
|
4ba9a19b5c | ||
|
|
f8eab7d611 | ||
|
|
40a9f19352 | ||
|
|
4b81e0d8af | ||
|
|
ea37bf8b45 | ||
|
|
c30f8b09b0 | ||
|
|
686f931fb4 | ||
|
|
1fe1e6f265 | ||
|
|
860decdac5 | ||
|
|
d5b90c91f5 | ||
|
|
20d6e365a1 | ||
|
|
e5e50c2220 | ||
|
|
e8da7baee2 | ||
|
|
8ab9e6bac7 | ||
|
|
9129301c89 | ||
|
|
9545e0145f | ||
|
|
15d3e4023b | ||
|
|
205e4dedd1 | ||
|
|
324d796aca | ||
|
|
5b31e8e9d8 | ||
|
|
0afb62ee88 | ||
|
|
1b0d053ab9 | ||
|
|
5871c4f092 | ||
|
|
5d415e6679 | ||
|
|
3a7384ec26 | ||
|
|
1d3a8bcacd | ||
|
|
dd187aef08 | ||
|
|
78b2c4d094 | ||
|
|
32b7a314b1 | ||
|
|
c8d768c59a | ||
|
|
3ef1c9167f | ||
|
|
36cdcbc3c1 | ||
|
|
83876532d0 | ||
|
|
edc824d05e | ||
|
|
9a7869dd40 | ||
|
|
1aa247d4da | ||
|
|
5db9f28b33 | ||
|
|
8599ad4a3c | ||
|
|
448c2a27d4 | ||
|
|
f2f4fa1b98 | ||
|
|
6f43a189c0 | ||
|
|
dd4de9822f | ||
|
|
c9e83e0517 | ||
|
|
4ac63950ad | ||
|
|
7a0b2c958b | ||
|
|
3fcc37e086 | ||
|
|
55181cec8c | ||
|
|
41d259006e | ||
|
|
93e8ec7f06 | ||
|
|
ba1d1c3bac | ||
|
|
fbaff2b75b | ||
|
|
9774e8807b | ||
|
|
b30103c790 | ||
|
|
bb9408b5d0 | ||
|
|
42c3c7bc48 | ||
|
|
464040895c | ||
|
|
525747dc53 | ||
|
|
c61d992559 | ||
|
|
2f79b079fd | ||
|
|
655bc39d85 | ||
|
|
1eb12d4fa0 | ||
|
|
2ab9186259 | ||
|
|
ab61aef985 | ||
|
|
65b3e9f8c1 | ||
|
|
1f57aa1ea5 | ||
|
|
340d827377 | ||
|
|
5cd25e24e4 | ||
|
|
58afc92a26 | ||
|
|
acb9931fe6 | ||
|
|
d4b035ac1a | ||
|
|
403dd5c005 | ||
|
|
7e4e1ec68f | ||
|
|
547764e3c1 | ||
|
|
541a7974e0 | ||
|
|
6bf6f82cad | ||
|
|
7be2325a95 | ||
|
|
fa1f739458 | ||
|
|
20289ff81b | ||
|
|
6d67d30aee | ||
|
|
bd0bd2e087 | ||
|
|
3d79799450 | ||
|
|
c99f9205b7 | ||
|
|
26bb54472f | ||
|
|
73e13fee37 | ||
|
|
205c1ac3b6 | ||
|
|
d317744f05 | ||
|
|
47611b7615 | ||
|
|
6420a51dd0 | ||
|
|
7c17d5bac0 | ||
|
|
0efd7c93c5 | ||
|
|
806be9551c | ||
|
|
b4dd3a9889 | ||
|
|
6b4a1b6758 | ||
|
|
4510b69798 | ||
|
|
84a3ee209a | ||
|
|
d6b0fccbda | ||
|
|
d1e31da2e5 | ||
|
|
923d32ee6d | ||
|
|
4b0fe879cb | ||
|
|
b87aeffcd8 | ||
|
|
88aafec6e3 | ||
|
|
6fe9dae3d6 | ||
|
|
2cb6ddf3ca | ||
|
|
799622a932 | ||
|
|
4c1e22b0f6 | ||
|
|
2283652704 | ||
|
|
33fb2daabf | ||
|
|
c79f59ec05 | ||
|
|
845aaad891 | ||
|
|
3c0b061220 | ||
|
|
af7de73f5c | ||
|
|
1a8a9f0047 | ||
|
|
a3b4e8db44 | ||
|
|
f9e8875803 | ||
|
|
e4c11d9a88 | ||
|
|
6d1d8da101 | ||
|
|
77303e668f | ||
|
|
72725b91c7 | ||
|
|
46f50f55c2 | ||
|
|
5e880f6bfa | ||
|
|
058a73f8a0 | ||
|
|
1ad8d34ecb | ||
|
|
3f9f926c8e | ||
|
|
80cf88dcc1 | ||
|
|
dddc7f348f | ||
|
|
ce45f2e114 | ||
|
|
d22a7c0a63 | ||
|
|
59162155de | ||
|
|
0f347cd0b4 | ||
|
|
1f983db47c | ||
|
|
dc50793733 | ||
|
|
b98329f91c | ||
|
|
587d8fbfa9 | ||
|
|
26a2e9ef53 | ||
|
|
e5ca98a1d3 | ||
|
|
5bd5e9000e | ||
|
|
d566642c98 | ||
|
|
f81ddc60ec | ||
|
|
6e0b20c921 | ||
|
|
0c308b38c5 | ||
|
|
8e0fdf68e4 | ||
|
|
9c7a9b5674 | ||
|
|
0a49e31b30 | ||
|
|
9478206202 | ||
|
|
c86e6f5b88 | ||
|
|
87868acecd | ||
|
|
6d877754da | ||
|
|
dd5521a07c | ||
|
|
3a6a84917b | ||
|
|
cca142ce86 | ||
|
|
fa3567cb9f | ||
|
|
7d22230b13 | ||
|
|
9694c796e3 | ||
|
|
1b7f684ab2 | ||
|
|
fef8f4d29c | ||
|
|
0d7e945623 | ||
|
|
13ea8eae77 | ||
|
|
cdb2ef3ac7 | ||
|
|
f216e0198f | ||
|
|
c5c3698f43 | ||
|
|
299d3b9f60 | ||
|
|
eaaebeaebb | ||
|
|
2ef2d2b9c5 | ||
|
|
da6698b216 | ||
|
|
c9f391dfc1 | ||
|
|
0f62926623 | ||
|
|
a856b4027b | ||
|
|
e2f72ecffc | ||
|
|
d888bcd473 | ||
|
|
0bfd5fa9a3 | ||
|
|
9c05ccef69 | ||
|
|
c7eb2ffe5c | ||
|
|
5281b6df77 | ||
|
|
cd5bb6a309 | ||
|
|
94a44d7af4 | ||
|
|
662050b89f | ||
|
|
3f7f3140eb | ||
|
|
a419694f70 | ||
|
|
0a20f77b08 | ||
|
|
dc11d18161 | ||
|
|
cde83e9d7d | ||
|
|
1476fda014 | ||
|
|
7a875f2db4 | ||
|
|
b868f37da5 | ||
|
|
9bef5a62dd | ||
|
|
d1aff30c9e | ||
|
|
a356dafde0 | ||
|
|
09477384fe | ||
|
|
6b923542e8 | ||
|
|
9722cf718c | ||
|
|
702b973c87 | ||
|
|
472372c9ed | ||
|
|
d358f0d27b | ||
|
|
d66bbd95f0 | ||
|
|
aae498509b | ||
|
|
4987fb5f15 | ||
|
|
b1ed299286 | ||
|
|
679ac3e5b1 | ||
|
|
181ef788a3 | ||
|
|
d2caa0953f | ||
|
|
3f88e02587 | ||
|
|
245da9b422 | ||
|
|
8cc075aff3 | ||
|
|
128a243166 | ||
|
|
0e41365020 | ||
|
|
bd6d5ffbb9 | ||
|
|
de7951f4db | ||
|
|
bdbf1fbb43 | ||
|
|
a7b6010ce2 | ||
|
|
5bba109873 | ||
|
|
d69ae60bcb | ||
|
|
a0277aba2e | ||
|
|
caba631db9 | ||
|
|
5095620005 | ||
|
|
abcdc0875c | ||
|
|
a4e41a57d7 | ||
|
|
e8bb3864e3 | ||
|
|
3e03d52fce | ||
|
|
4f64d466fb | ||
|
|
3102c545ce | ||
|
|
3cd1066a8d | ||
|
|
6494b7ea89 | ||
|
|
f232b327c0 | ||
|
|
204ed1f257 | ||
|
|
2f5d13ac10 | ||
|
|
65a379c8da | ||
|
|
99df5fadbc | ||
|
|
c8e6dca263 | ||
|
|
9dec40eca7 | ||
|
|
92cdb27e23 | ||
|
|
4896043599 | ||
|
|
5f0ddeedd0 | ||
|
|
e679e760ee | ||
|
|
a439688b3c | ||
|
|
0d17757729 | ||
|
|
899bbc2dbc | ||
|
|
57b68880db | ||
|
|
06c59763bc | ||
|
|
71c1d16c06 | ||
|
|
64c1b72481 | ||
|
|
e5742e057d | ||
|
|
a9d6176fd2 | ||
|
|
1b0aa2a9b1 | ||
|
|
a49c230bb2 | ||
|
|
b65749cf70 | ||
|
|
0cf54a6a03 | ||
|
|
4d77277f2d | ||
|
|
8b81506ed3 | ||
|
|
24c6509a61 | ||
|
|
3c32c4160d | ||
|
|
1f912d86e1 | ||
|
|
9c93ab0b6d | ||
|
|
6646a20a5d | ||
|
|
92f471c476 | ||
|
|
4c7ce97650 | ||
|
|
4d1fc994c0 | ||
|
|
c4310c554e | ||
|
|
c603326e58 | ||
|
|
a93e04a44e | ||
|
|
db9e649f6e | ||
|
|
c26aca4686 | ||
|
|
f822268ae1 | ||
|
|
4ebcf40619 | ||
|
|
a884b94ac0 | ||
|
|
194e705982 | ||
|
|
c77ec3bf20 | ||
|
|
0a1857249b | ||
|
|
3be3a1f2e6 | ||
|
|
62f9488829 | ||
|
|
9bcaef192c | ||
|
|
bcb1630f20 | ||
|
|
5f7922d7fd | ||
|
|
66b1d297ba | ||
|
|
eb4780a952 | ||
|
|
34f724a4a4 | ||
|
|
c5b250236f | ||
|
|
e5b122ee61 | ||
|
|
3b20021981 | ||
|
|
a7d1f180a4 | ||
|
|
68374e7c62 | ||
|
|
ba26a97ddc | ||
|
|
2db7ce8da1 | ||
|
|
6958da1adb | ||
|
|
cf48cd0ae5 | ||
|
|
b1b3966d41 | ||
|
|
c171a37f24 | ||
|
|
c13f1991c9 | ||
|
|
da15cc4de7 | ||
|
|
44c686f494 | ||
|
|
03bd5ad426 | ||
|
|
914a3d71c2 | ||
|
|
5c2e0935c6 | ||
|
|
e1ec604655 | ||
|
|
0dad10d6ab | ||
|
|
136138daab | ||
|
|
6efe563a69 | ||
|
|
fd068443aa | ||
|
|
82d0f62d73 | ||
|
|
01ba6ddecd | ||
|
|
769b379a8b | ||
|
|
1b981e0695 | ||
|
|
9798b9fd50 | ||
|
|
10792c0bc6 | ||
|
|
1c61afd3ee | ||
|
|
737ef583ee | ||
|
|
7b4365bd8a | ||
|
|
041fce743e | ||
|
|
ad4904a0ec | ||
|
|
42a1bca64d | ||
|
|
492a5bbb38 | ||
|
|
c40ef22c20 | ||
|
|
783aac561a | ||
|
|
02245c150c | ||
|
|
54a93775e4 | ||
|
|
ca778aac70 | ||
|
|
007ca4c6b0 | ||
|
|
b3dfce7b5a | ||
|
|
c75f1685c5 | ||
|
|
db5b103ab3 | ||
|
|
1925b7b324 | ||
|
|
b2f1c9e832 | ||
|
|
cff4eebb74 | ||
|
|
a3aab5aeca | ||
|
|
63e5d8f7ea | ||
|
|
e4df7b10f6 | ||
|
|
f39a1e7d03 | ||
|
|
a473f6aa16 | ||
|
|
6907c24690 | ||
|
|
ef3d8a9277 | ||
|
|
1a183b7f17 | ||
|
|
88f7b30128 | ||
|
|
4b38389dc8 | ||
|
|
865a45044f | ||
|
|
7378d06bfd | ||
|
|
55e19de8a1 | ||
|
|
4d7cb06481 | ||
|
|
7179a2995d | ||
|
|
2b853df75b | ||
|
|
ae6bcc2987 | ||
|
|
ed61e5c7f7 | ||
|
|
3581ac5ebd | ||
|
|
1f369b47d7 | ||
|
|
bb9049bef4 | ||
|
|
94f15aba40 | ||
|
|
b16b8388ef | ||
|
|
b1e89e7904 | ||
|
|
f48e2f3b18 | ||
|
|
bd673a3f26 | ||
|
|
51ec699649 | ||
|
|
ed6be510fb | ||
|
|
55f05ba839 | ||
|
|
17c9382a21 | ||
|
|
43c0eda2be | ||
|
|
f2c5515317 | ||
|
|
c1441eb758 | ||
|
|
cf92fb921d | ||
|
|
559ca3e97b | ||
|
|
7c095175ab | ||
|
|
3b2635014c | ||
|
|
755040444a | ||
|
|
59afb012b6 | ||
|
|
7d9a52301c | ||
|
|
50a53181dc | ||
|
|
36f6fec99f | ||
|
|
83eeb488f9 | ||
|
|
5394a35e1a | ||
|
|
0cd68ff8f8 | ||
|
|
e9ae8063db | ||
|
|
1c4e23512b | ||
|
|
51bdfa44b1 | ||
|
|
98d0a397a3 | ||
|
|
1dc060123d | ||
|
|
14349cb583 | ||
|
|
eca959034c | ||
|
|
9844550d6c | ||
|
|
82fcf08d84 | ||
|
|
845a35a9b8 | ||
|
|
0e4751ebee | ||
|
|
56badd44f3 | ||
|
|
399b3fd198 | ||
|
|
43a7ca7fe8 | ||
|
|
b951624ed3 | ||
|
|
1b21f2d1c7 | ||
|
|
43ea0effa2 | ||
|
|
6df9b95dea | ||
|
|
630aa53d98 | ||
|
|
40c81b88d4 | ||
|
|
027a317291 | ||
|
|
81b538556d | ||
|
|
6aece1cf8c | ||
|
|
cd7b9d31c9 | ||
|
|
03a6e6fc6a | ||
|
|
1d1aae03f3 | ||
|
|
b47ad50d42 | ||
|
|
a924fd0e7e | ||
|
|
82ae908e6a | ||
|
|
8e497e2a48 | ||
|
|
cbb4565a48 | ||
|
|
471fbee090 | ||
|
|
d66de937d9 | ||
|
|
3ea230fd17 | ||
|
|
34bc571f80 | ||
|
|
db46804bfe | ||
|
|
2bd0faac2e | ||
|
|
a9a6e58d60 | ||
|
|
b40f980c62 | ||
|
|
12ac781ef8 | ||
|
|
8373ffbf10 | ||
|
|
0233dca884 | ||
|
|
c1a3162a54 | ||
|
|
fc2f5c3615 | ||
|
|
3a5ac9df10 | ||
|
|
63f634af00 | ||
|
|
9cb3eefdc4 | ||
|
|
1c9e9da10f | ||
|
|
71c0ba63ca | ||
|
|
c04d3b2ace | ||
|
|
e8497bb0cc | ||
|
|
3430349dd1 | ||
|
|
fa6a5f75af | ||
|
|
7b9eb4c87c | ||
|
|
808411bc0b | ||
|
|
c3ab510b57 | ||
|
|
cbc653be37 | ||
|
|
e9efe5bbb7 | ||
|
|
a7c234b272 | ||
|
|
bb64d0dd54 | ||
|
|
f82b478a77 | ||
|
|
2028642efe | ||
|
|
9af9bd0c4c | ||
|
|
662bf83a88 | ||
|
|
2d474ccbda | ||
|
|
cec746d632 | ||
|
|
8371ac5b0d | ||
|
|
f248788d14 | ||
|
|
bf202f25dd | ||
|
|
2d2472dbe9 | ||
|
|
3adfa3c2a8 | ||
|
|
bf05155c5e | ||
|
|
d50df6a6fc | ||
|
|
a2b38c8d05 | ||
|
|
d546da8b5f | ||
|
|
7c60ffa88d | ||
|
|
6b235ab083 | ||
|
|
7c4cdef66d | ||
|
|
c0a2da443a | ||
|
|
e53658759b | ||
|
|
6dc4c1c03a | ||
|
|
818c3b6092 | ||
|
|
6e5dee13e1 | ||
|
|
cae78622f6 | ||
|
|
c5db677663 | ||
|
|
c6942daf74 | ||
|
|
ea644a2293 | ||
|
|
01e69ce2b0 | ||
|
|
187a017c4d | ||
|
|
f9ce1d17c8 | ||
|
|
ef4cd6a4a7 | ||
|
|
80c8dcacb3 | ||
|
|
0ad2dea722 | ||
|
|
dec3c45f6c | ||
|
|
32a73ad4b9 | ||
|
|
452e03b894 | ||
|
|
01bdddaee4 | ||
|
|
f7c82b0920 | ||
|
|
820eeb7752 | ||
|
|
2e25ef6f81 | ||
|
|
5736d2c9cc | ||
|
|
056a1f6a33 | ||
|
|
f96e211cd6 | ||
|
|
0d473cbe18 | ||
|
|
95514b05ca | ||
|
|
dc622a9bd5 | ||
|
|
3ddd1d5f21 | ||
|
|
ea15f0690c | ||
|
|
8751ef7439 | ||
|
|
8c393e3144 | ||
|
|
a3ef2a43ac | ||
|
|
d2b3029f67 | ||
|
|
3e800ec7c8 | ||
|
|
47d963b585 | ||
|
|
397ae3407e | ||
|
|
3f9e2d5d39 | ||
|
|
31449552d1 | ||
|
|
5f2fee5a99 | ||
|
|
db63c91f99 | ||
|
|
9c87c36644 | ||
|
|
be9f5ad951 | ||
|
|
d065a0cbc4 | ||
|
|
02dfa638ca | ||
|
|
bd71e779c3 | ||
|
|
18d83b8c50 | ||
|
|
fc2e16dd4e | ||
|
|
e156badef6 | ||
|
|
ae6d379cce | ||
|
|
1a414ac69c | ||
|
|
f82099143f | ||
|
|
93743b11b2 | ||
|
|
a94696e037 | ||
|
|
0255e8980b | ||
|
|
eff9c69069 | ||
|
|
2dccd2f30b | ||
|
|
b775cbb3bf | ||
|
|
79ef6f2f75 | ||
|
|
451ac297d4 | ||
|
|
0fd8a4507c | ||
|
|
49c7e7ce2d | ||
|
|
1af7d01d96 | ||
|
|
66c6938df0 | ||
|
|
bb9fb04788 | ||
|
|
f5cf2b2b55 | ||
|
|
3e8afaeae4 | ||
|
|
43169ced69 | ||
|
|
708844857d | ||
|
|
abe2e544bb | ||
|
|
ce777348b3 | ||
|
|
c052325487 | ||
|
|
ce69410f49 | ||
|
|
5f0be0cc7f | ||
|
|
dca6347ea9 | ||
|
|
94c4db9072 | ||
|
|
7f63be2cd2 | ||
|
|
1236dea4c5 | ||
|
|
c5c46df613 | ||
|
|
a96269150a | ||
|
|
75e347e8e3 | ||
|
|
d641cc5057 | ||
|
|
cf64145ff3 | ||
|
|
195eca6b09 | ||
|
|
b0ef077e48 | ||
|
|
e40a698d1a | ||
|
|
69c34b1b27 | ||
|
|
4700c2583d | ||
|
|
6cf79a3c73 | ||
|
|
2c110f7796 | ||
|
|
aebea0b57f | ||
|
|
8684455ea5 | ||
|
|
7cbb103376 | ||
|
|
9cf8ec5dd7 | ||
|
|
f5187b1533 | ||
|
|
6230a65a79 | ||
|
|
f13c96839e | ||
|
|
776f68c9dd | ||
|
|
714ed89a4c | ||
|
|
0b2b29b7cf | ||
|
|
a41c0a0519 | ||
|
|
026e28114e | ||
|
|
0840719289 | ||
|
|
aad875e113 | ||
|
|
6b059d94ba | ||
|
|
39eeaea7e7 | ||
|
|
81a8e91180 | ||
|
|
4fc6f6f59b | ||
|
|
dd2f573f27 | ||
|
|
ff8ddc431b | ||
|
|
d703945aa3 | ||
|
|
f4aba5f9ea | ||
|
|
393113c3e3 | ||
|
|
f2db18820a | ||
|
|
e04d300e18 | ||
|
|
685f836f41 | ||
|
|
d4f5ad641a | ||
|
|
007d437ee8 | ||
|
|
00e380e8d8 | ||
|
|
56749d9494 | ||
|
|
cf10d37c90 | ||
|
|
f9de34e36d | ||
|
|
aa889be140 | ||
|
|
6da6838bde | ||
|
|
07a0be303f | ||
|
|
ddbe0af290 | ||
|
|
e71f0f44a4 | ||
|
|
39f3bc7100 | ||
|
|
211a145e58 | ||
|
|
d2339b915f | ||
|
|
24c2651745 | ||
|
|
36e95b93b4 | ||
|
|
e7a44e0b15 | ||
|
|
fbdb21ac72 | ||
|
|
c9522cf6ae | ||
|
|
7bf9d334ad | ||
|
|
422ff93dcd | ||
|
|
eee1ea7a09 | ||
|
|
b90e482add | ||
|
|
875b51720b | ||
|
|
5272d4f9e3 | ||
|
|
c980f4f6e3 | ||
|
|
89525330c4 | ||
|
|
806db24c0e | ||
|
|
ba7d6df5a4 | ||
|
|
112301e0e0 | ||
|
|
c07065e457 | ||
|
|
b9e1afae17 | ||
|
|
549b4a9ebf | ||
|
|
fa9a3fb7c4 | ||
|
|
ccb15f278a | ||
|
|
f0fb4b8842 | ||
|
|
dacaf293b2 | ||
|
|
627d0e4e6f | ||
|
|
f32b9e26e0 | ||
|
|
db2b1a0a35 | ||
|
|
f734a60c62 | ||
|
|
9aa815c1ed | ||
|
|
e0772500c9 | ||
|
|
78c5522966 | ||
|
|
718f79775e | ||
|
|
4261c1768a | ||
|
|
01603185c3 | ||
|
|
f1adf24f3b | ||
|
|
856765a775 | ||
|
|
075da34cdb | ||
|
|
5ce543002d | ||
|
|
700b1c50af | ||
|
|
9e1c363a0a | ||
|
|
bbe3065f19 | ||
|
|
1716a0ebca | ||
|
|
ed66339e94 | ||
|
|
441774e61c | ||
|
|
c6fd14afd2 | ||
|
|
27ed9ca7fd | ||
|
|
50fda906bd | ||
|
|
6c7c60037b | ||
|
|
91a429ea77 | ||
|
|
2dbe01e90a | ||
|
|
3d7dd41223 | ||
|
|
91495c8595 | ||
|
|
c3e997168a | ||
|
|
c3e6f4f7b6 | ||
|
|
e600ec95ed | ||
|
|
cbd5853389 | ||
|
|
4e3a086993 | ||
|
|
5f23dfb034 | ||
|
|
99f59490a3 | ||
|
|
da82887cc0 | ||
|
|
45a59f9aec | ||
|
|
57408d18ae | ||
|
|
3a97db804f | ||
|
|
4a44b35ea5 | ||
|
|
47aafe9042 | ||
|
|
53d94a1d3d | ||
|
|
5dd3c7c8e8 | ||
|
|
0c2a13f27f | ||
|
|
da0d3133df | ||
|
|
7abd9a3db9 | ||
|
|
98b9f57e0e | ||
|
|
e064ccd585 | ||
|
|
b5a63ed3fc | ||
|
|
147edddb90 | ||
|
|
4b8a91fb8c | ||
|
|
4fb677ed23 | ||
|
|
4f0bd7f461 | ||
|
|
01eecdb698 | ||
|
|
5ca41a6fff | ||
|
|
e4f71420fc | ||
|
|
d5456ed1db | ||
|
|
e3335c5843 | ||
|
|
067e6bf93d | ||
|
|
13dc31dc1f | ||
|
|
7f6bf21270 | ||
|
|
d647258856 | ||
|
|
1eb7ed5e9c | ||
|
|
e78f1b05a7 | ||
|
|
e45140b7ea | ||
|
|
41747a7a04 | ||
|
|
e448430d8a | ||
|
|
394b1a1602 | ||
|
|
85abd5a8c3 | ||
|
|
b9e0eb5b76 | ||
|
|
a8eae73b66 | ||
|
|
9edd8d6f66 | ||
|
|
4d8f7c67d4 | ||
|
|
e53337bcdc | ||
|
|
e5d883f97e | ||
|
|
548a55792c | ||
|
|
43f0f2a0fd | ||
|
|
7b1c696810 | ||
|
|
8d69c3764d | ||
|
|
87acd9d7ee | ||
|
|
06391c9de6 | ||
|
|
b42b41216b | ||
|
|
0a1d215560 | ||
|
|
44b6136ce2 | ||
|
|
ba3c6023a8 | ||
|
|
be7f6ae7d1 | ||
|
|
984c7a67f5 | ||
|
|
a4bf6c1f7a | ||
|
|
690ae1a7b6 | ||
|
|
eae9a10a7d | ||
|
|
7c6765b969 | ||
|
|
a6c143a1c7 | ||
|
|
714da0ab3b | ||
|
|
377ec37257 |
@@ -17,3 +17,4 @@ Tests/Manual/**/*.xcodeproj
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
*.tm_properties
|
||||
*.idea
|
||||
+8
-1
@@ -20,12 +20,15 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import "_CPObject+Theme.j"
|
||||
@import "_CPToolTip.j"
|
||||
@import "CALayer.j"
|
||||
@import "CGGeometry.j"
|
||||
@import "CPAccordionView.j"
|
||||
@import "CPAlert.j"
|
||||
@import "CPAnimation.j"
|
||||
@import "CPAnimationContext.j"
|
||||
@import "CPAppearance.j"
|
||||
@import "CPApplication.j"
|
||||
@import "CPArrayController.j"
|
||||
@import "CPBezierPath.j"
|
||||
@@ -98,15 +101,19 @@
|
||||
@import "CPTabView.j"
|
||||
@import "CPText.j"
|
||||
@import "CPTextField.j"
|
||||
@import "CPTextView.j"
|
||||
@import "CPTokenField.j"
|
||||
@import "CPToolbar.j"
|
||||
@import "CPToolbarItem.j"
|
||||
@import "CPTrackingArea.j"
|
||||
@import "CPTreeNode.j"
|
||||
@import "CPUserDefaultsController.j"
|
||||
@import "CPView.j"
|
||||
@import "CPViewAnimator.j"
|
||||
@import "CPViewAnimation.j"
|
||||
@import "CPViewController.j"
|
||||
@import "CPVisualEffectView.j"
|
||||
@import "CPWebView.j"
|
||||
@import "CPWindow.j"
|
||||
@import "CPWindowController.j"
|
||||
@import "CPWorkspace.j"
|
||||
@import "CPWorkspace.j"
|
||||
+2
-2
@@ -821,12 +821,12 @@ var bottomHeight = 71;
|
||||
else if (_modalDelegate)
|
||||
{
|
||||
if (_didEndSelector)
|
||||
objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
|
||||
_modalDelegate.isa.objj_msgSend3(_modalDelegate, _didEndSelector, self, returnCode, contextInfo);
|
||||
}
|
||||
else if (_delegate)
|
||||
{
|
||||
if (_didEndSelector)
|
||||
objj_msgSend(_delegate, _didEndSelector, self, returnCode);
|
||||
_delegate.isa.objj_msgSend2(_delegate, _didEndSelector, self, returnCode);
|
||||
else
|
||||
[self _sendDelegateAlertDidEndReturnCode:returnCode];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* CPAppearance.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Antoine Mercadal.
|
||||
* Copyright 2015, Cappuccino Project.
|
||||
*
|
||||
* 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 "CPTheme.j"
|
||||
|
||||
CPAppearanceNameAqua = @"CPAppearanceNameAqua";
|
||||
CPAppearanceNameLightContent = @"CPAppearanceNameLightContent";
|
||||
CPAppearanceNameVibrantDark = @"CPAppearanceNameVibrantDark";
|
||||
CPAppearanceNameVibrantLight = @"CPAppearanceNameVibrantLight";
|
||||
|
||||
var _CPAppearanceCurrent = nil,
|
||||
_CPAppearancesRegistry = @{};
|
||||
|
||||
|
||||
@protocol CPAppearanceCustomization <CPObject>
|
||||
|
||||
@required
|
||||
- (CPAppearance)appearance;
|
||||
- (void)setAppearance:(CPAppearance)appearance;
|
||||
- (CPAppearance)effectiveAppearance;
|
||||
- (void)setEffectiveAppearance:(CPAppearance)appearance;
|
||||
|
||||
@end
|
||||
|
||||
CPThemeStateAppearanceAqua = CPThemeState("appearance-aqua");
|
||||
CPThemeStateAppearanceLightContent = CPThemeState("appearance-light-content");
|
||||
CPThemeStateAppearanceVibrantLight = CPThemeState("appearance-vibrant-light");
|
||||
CPThemeStateAppearanceVibrantDark = CPThemeState("appearance-vibrant-dark");
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
|
||||
A CPAppareance represents the appearance of an to a subset of UI elements.
|
||||
This is a very lightweight implementation of the NSAppearance system, but
|
||||
We are using it for compliance, and especially for the CPVisualEffectView
|
||||
*/
|
||||
@implementation CPAppearance : CPObject
|
||||
{
|
||||
BOOL _allowsVibrancy @accessors(property=allowsVibrancy);
|
||||
|
||||
CPString _name;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class Methods
|
||||
|
||||
/*! Returns the current default CPAppearance
|
||||
*/
|
||||
+ (CPAppearance)currentAppearance
|
||||
{
|
||||
if (!_CPAppearanceCurrent)
|
||||
_CPAppearanceCurrent = [CPAppearance appearanceNamed:CPAppearanceNameAqua];
|
||||
|
||||
return _CPAppearanceCurrent;
|
||||
}
|
||||
|
||||
/*! Sets the current default CPAppearance
|
||||
@param appearance the new current appearance
|
||||
*/
|
||||
+ (void)setCurrentAppearance:(CPAppearance)anAppearance
|
||||
{
|
||||
_CPAppearanceCurrent = anAppearance;
|
||||
}
|
||||
|
||||
/*! Returns the CPAppearance object with the given name
|
||||
@param name the name of the appearance
|
||||
*/
|
||||
+ (CPAppearance)appearanceNamed:(CPString)aName
|
||||
{
|
||||
if (![_CPAppearancesRegistry containsKey:aName])
|
||||
{
|
||||
[_CPAppearancesRegistry setObject:[[CPAppearance alloc] initWithAppearanceNamed:aName bundle:nil]
|
||||
forKey:aName];
|
||||
}
|
||||
|
||||
return [_CPAppearancesRegistry objectForKey:aName];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
/*! Creates a CPAppearance object initialized to the specified appearance file in the specified bundle
|
||||
This method does actually nothing special. It just creates a default appearance object
|
||||
*/
|
||||
- (id)initWithAppearanceNamed:(CPString)aName bundle:(CPBundle)bundle
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_name = aName;
|
||||
_allowsVibrancy = YES;
|
||||
|
||||
if ([_CPAppearancesRegistry containsKey:aName])
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Appearance with name '" + aName + "' is already declared."];
|
||||
|
||||
[_CPAppearancesRegistry setObject:self forKey:aName];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Implementation
|
||||
|
||||
- (BOOL)isEqual:(id)anObject
|
||||
{
|
||||
if (![anObject isKindOfClass:CPAppearance])
|
||||
return NO;
|
||||
|
||||
return self._name == anObject._name;
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return @"<CPAppearance @" + [self UID] + @" name: " + _name + ">";
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CPCoding
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_name = [aCoder decodeObjectForKey:@"_name"];
|
||||
_allowsVibrancy = [aCoder decodeBoolForKey:@"_allowsVibrancy"];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_name forKey:@"_name"];
|
||||
[aCoder encodeBool:_allowsVibrancy forKey:@"_allowsVibrancy"];
|
||||
}
|
||||
|
||||
@end
|
||||
+49
-8
@@ -47,6 +47,8 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
@protocol CPApplicationDelegate <CPObject>
|
||||
|
||||
@optional
|
||||
- (CPApplicationTerminateReply)applicationShouldTerminate:(CPApplication)sender;
|
||||
- (CPString)applicationShouldTerminateMessage:(CPApplication)sender;
|
||||
- (void)applicationDidBecomeActive:(CPNotification)aNotification;
|
||||
- (void)applicationDidChangeScreenParameters:(CPNotification)aNotification;
|
||||
- (void)applicationDidFinishLaunching:(CPNotification)aNotification;
|
||||
@@ -58,6 +60,9 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
|
||||
@end
|
||||
|
||||
var CPApplicationDelegate_applicationShouldTerminate_ = 1 << 0,
|
||||
CPApplicationDelegate_applicationShouldTerminateMessage_ = 1 << 1;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPApplication
|
||||
@@ -103,6 +108,8 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
|
||||
//
|
||||
id <CPApplicationDelegate> _delegate;
|
||||
CPInteger _implementedDelegateMethods;
|
||||
|
||||
BOOL _finishedLaunching;
|
||||
BOOL _isActive;
|
||||
|
||||
@@ -165,6 +172,8 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
if (_delegate == aDelegate)
|
||||
return;
|
||||
|
||||
_implementedDelegateMethods = 0;
|
||||
|
||||
var defaultCenter = [CPNotificationCenter defaultCenter],
|
||||
delegateNotifications =
|
||||
[
|
||||
@@ -205,6 +214,12 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
if ([_delegate respondsToSelector:selector])
|
||||
[defaultCenter addObserver:_delegate selector:selector name:notificationName object:self];
|
||||
}
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
|
||||
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminate_;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(applicationShouldTerminateMessage:)])
|
||||
_implementedDelegateMethods |= CPApplicationDelegate_applicationShouldTerminateMessage_
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -426,12 +441,7 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
{
|
||||
// callback method for terminate:
|
||||
if (didCloseAll)
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(applicationShouldTerminate:)])
|
||||
[self replyToApplicationShouldTerminate:[_delegate applicationShouldTerminate:self]];
|
||||
else
|
||||
[self replyToApplicationShouldTerminate:YES];
|
||||
}
|
||||
[self replyToApplicationShouldTerminate:[self _sendDelegateApplicationShouldTerminate]];
|
||||
}
|
||||
|
||||
- (void)replyToApplicationShouldTerminate:(BOOL)terminate
|
||||
@@ -445,6 +455,9 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
|
||||
- (void)activateIgnoringOtherApps:(BOOL)shouldIgnoreOtherApps
|
||||
{
|
||||
if (_isActive)
|
||||
return;
|
||||
|
||||
[self _willBecomeActive];
|
||||
|
||||
[CPPlatform activateIgnoringOtherApps:shouldIgnoreOtherApps];
|
||||
@@ -455,6 +468,9 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
|
||||
- (void)deactivate
|
||||
{
|
||||
if (!_isActive)
|
||||
return;
|
||||
|
||||
[self _willResignActive];
|
||||
|
||||
[CPPlatform deactivate];
|
||||
@@ -480,6 +496,15 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
- (void)run
|
||||
{
|
||||
[self finishLaunching];
|
||||
[self sendEvent:[CPEvent otherEventWithType:CPAppKitDefined
|
||||
location:CGPointMakeZero()
|
||||
modifierFlags:0
|
||||
timestamp:[CPEvent currentTimestamp]
|
||||
windowNumber:[_keyWindow windowNumber]
|
||||
context:nil
|
||||
subtype:nil
|
||||
data1:nil
|
||||
data2:nil]];
|
||||
}
|
||||
|
||||
// Managing the Event Loop
|
||||
@@ -959,7 +984,7 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
*/
|
||||
- (void)setTarget:(id)aTarget selector:(SEL)aSelector forNextEventMatchingMask:(unsigned int)aMask untilDate:(CPDate)anExpiration inMode:(CPString)aMode dequeue:(BOOL)shouldDequeue
|
||||
{
|
||||
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, function (anEvent) { objj_msgSend(aTarget, aSelector, anEvent); }, shouldDequeue));
|
||||
_eventListeners.splice(_eventListenerInsertionIndex++, 0, _CPEventListenerMake(aMask, function (anEvent) { if (aTarget != null) aTarget.isa.objj_msgSend1(aTarget, aSelector, anEvent); }, shouldDequeue));
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1195,6 +1220,22 @@ var CPMainCibFile = @"CPMainCibFile",
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
- (BOOL)_sendDelegateApplicationShouldTerminate
|
||||
{
|
||||
if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminate_))
|
||||
return YES;
|
||||
|
||||
return [_delegate applicationShouldTerminate:self];
|
||||
}
|
||||
|
||||
- (CPString)_sendDelegateApplicationShouldTerminateMessage
|
||||
{
|
||||
if (!(_implementedDelegateMethods & CPApplicationDelegate_applicationShouldTerminateMessage_))
|
||||
return @"You have attempted to leave this page. Are you sure you want to exit this page?";
|
||||
|
||||
return [_delegate applicationShouldTerminateMessage:self];
|
||||
}
|
||||
|
||||
- (void)_didResignActive
|
||||
{
|
||||
if (self._activeMenu)
|
||||
@@ -1317,7 +1358,7 @@ var _CPAppBootstrapperActions = nil;
|
||||
{
|
||||
var action = _CPAppBootstrapperActions.shift();
|
||||
|
||||
if (objj_msgSend(self, action))
|
||||
if (self.isa.objj_msgSend0(self, action))
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ CPApplicationWillResignActiveNotification = @"CPApplicationWillResignA
|
||||
CPApplicationDidResignActiveNotification = @"CPApplicationDidResignActiveNotification";
|
||||
CPApplicationDidChangeScreenParametersNotification = @"CPApplicationDidChangeScreenParametersNotification";
|
||||
|
||||
@typedef CPApplicationTerminateReply
|
||||
CPTerminateNow = YES;
|
||||
CPTerminateCancel = NO;
|
||||
CPTerminateLater = -1; // not currently supported
|
||||
|
||||
@@ -563,7 +563,7 @@
|
||||
*/
|
||||
- (BOOL)setSelectionIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
[self _selectionWillChange]
|
||||
[self _selectionWillChange];
|
||||
var r = [self __setSelectionIndexes:indexes avoidEmpty:NO];
|
||||
[self _selectionDidChange];
|
||||
return r;
|
||||
|
||||
+21
-2
@@ -598,7 +598,8 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
CPBoxBorderTypeKey = @"CPBoxBorderTypeKey",
|
||||
CPBoxTitle = @"CPBoxTitle",
|
||||
CPBoxTitlePosition = @"CPBoxTitlePosition",
|
||||
CPBoxTitleView = @"CPBoxTitleView";
|
||||
CPBoxTitleView = @"CPBoxTitleView",
|
||||
CPBoxContentView = @"CPBoxContentView";
|
||||
|
||||
@implementation CPBox (CPCoding)
|
||||
|
||||
@@ -615,8 +616,25 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
_titlePosition = [aCoder decodeIntForKey:CPBoxTitlePosition];
|
||||
_titleView = [aCoder decodeObjectForKey:CPBoxTitleView] || [CPTextField labelWithTitle:_title];
|
||||
|
||||
_contentView = [self subviews][0];
|
||||
if (_boxType != CPBoxSeparator)
|
||||
{
|
||||
// FIXME: we have a problem with CIB decoding here.
|
||||
// We should be able to simply add : _contentView = [self subviews][0]
|
||||
// but first box subview seems to be malformed (badly decoded).
|
||||
// For example, when deployed, this view doesn't have its _trackingAreas array initialized.
|
||||
// As a (temporary) workaround, we encode/decode the _contentView property. We then transfer the subview hierarchy
|
||||
// and replace the first (and only) box subview with this _contentView
|
||||
|
||||
_contentView = [aCoder decodeObjectForKey:CPBoxContentView] || [[CPView alloc] initWithFrame:[self bounds]];
|
||||
var malformedContentView = [self subviews][0];
|
||||
[_contentView setSubviews:[malformedContentView subviews]];
|
||||
[self replaceSubview:malformedContentView with:_contentView];
|
||||
}
|
||||
else
|
||||
{
|
||||
_titlePosition = CPNoTitle;
|
||||
}
|
||||
|
||||
[self setAutoresizesSubviews:YES];
|
||||
[_contentView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
|
||||
|
||||
@@ -635,6 +653,7 @@ var CPBoxTypeKey = @"CPBoxTypeKey",
|
||||
[aCoder encodeObject:_title forKey:CPBoxTitle];
|
||||
[aCoder encodeInt:_titlePosition forKey:CPBoxTitlePosition];
|
||||
[aCoder encodeObject:_titleView forKey:CPBoxTitleView];
|
||||
[aCoder encodeObject:_contentView forKey:CPBoxContentView];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+4
-4
@@ -280,7 +280,7 @@ CPButtonImageOffset = 3.0;
|
||||
break;
|
||||
|
||||
case CPOffState:
|
||||
[self unsetThemeState:[CPThemeStateSelected, CPButtonStateMixed, CPThemeStateHighlighted]];
|
||||
[self unsetThemeStates:[CPThemeStateSelected, CPButtonStateMixed, CPThemeStateHighlighted]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ CPButtonImageOffset = 3.0;
|
||||
|
||||
- (void)setImage:(CPImage)anImage
|
||||
{
|
||||
[self setValue:anImage forThemeAttribute:@"image"];
|
||||
[self setValue:anImage forThemeAttribute:@"image" inState:CPThemeStateNormal];
|
||||
}
|
||||
|
||||
- (CPImage)image
|
||||
@@ -665,10 +665,10 @@ CPButtonImageOffset = 3.0;
|
||||
*/
|
||||
- (void)sizeToFit
|
||||
{
|
||||
[self setFrameSize:[self _minimumFrameSize]];
|
||||
|
||||
[self layoutSubviews];
|
||||
|
||||
[self setFrameSize:[self _minimumFrameSize]];
|
||||
|
||||
if ([self ephemeralSubviewNamed:@"content-view"])
|
||||
[self layoutSubviews];
|
||||
}
|
||||
|
||||
@@ -112,6 +112,8 @@
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
[super awakeFromCib];
|
||||
|
||||
var view = [self superview],
|
||||
subview = self;
|
||||
|
||||
@@ -270,15 +272,15 @@
|
||||
currentButtonOffset += width - 1;
|
||||
}
|
||||
|
||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered]];
|
||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, ]];
|
||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered]];
|
||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered]];
|
||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered, ]];
|
||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered]];
|
||||
[button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered];
|
||||
|
||||
// FIXME shouldn't need this
|
||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
[button setValue:normalColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateNormal, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
[button setValue:highlightedColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateHighlighted, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
[button setValue:disabledColor forThemeAttribute:@"bezel-color" inStates:[CPThemeStateDisabled, CPThemeStateBordered, CPPopUpButtonStatePullsDown]];
|
||||
|
||||
[self addSubview:button];
|
||||
}
|
||||
|
||||
+1
-1
@@ -195,7 +195,7 @@
|
||||
|
||||
- (CGRect)documentVisibleRect
|
||||
{
|
||||
return [self convertRect:[self bounds] fromView:_documentView];
|
||||
return [_documentView visibleRect];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+45
-27
@@ -31,6 +31,7 @@
|
||||
@import "CPDragServer_Constants.j"
|
||||
@import "CPPasteboard.j"
|
||||
@import "CPView.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
|
||||
@class _CPCollectionViewDropIndicator
|
||||
|
||||
@@ -55,11 +56,13 @@ var CPCollectionViewDelegate_collectionView_acceptDrop_index_dropOperation_
|
||||
- (CPData)collectionView:(CPCollectionView)collectionView dataForItemsAtIndexes:(CPIndexSet)indices forType:(CPString)aType;
|
||||
- (CPDragOperation)collectionView:(CPCollectionView)collectionView validateDrop:(id)draggingInfo proposedIndex:(CPInteger)proposedDropIndex dropOperation:(CPCollectionViewDropOperation)proposedDropOperation;
|
||||
- (CPMenu)collectionView:(CPCollectionView)collectionView menuForItemAtIndex:(CPInteger)anIndex;
|
||||
- (CPView)collectionView:(CPCollectionView)collectionView dragginViewForItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset;
|
||||
- (CPView)collectionView:(CPCollectionView)collectionView draggingViewForItemsAtIndexes:(CPIndexSet)indexes withEvent:(CPEvent)event offset:(CGPoint)dragImageOffset;
|
||||
- (void)collectionView:(CPCollectionView)collectionView didDoubleClickOnItemAtIndex:(int)index;
|
||||
|
||||
@end
|
||||
|
||||
var HORIZONTAL_MARGIN = 2;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPCollectionView
|
||||
@@ -88,9 +91,6 @@ var CPCollectionViewDelegate_collectionView_acceptDrop_index_dropOperation_
|
||||
@param indices the indices to obtain drag types
|
||||
@return an array of drag types (CPString)
|
||||
*/
|
||||
|
||||
var HORIZONTAL_MARGIN = 2;
|
||||
|
||||
@implementation CPCollectionView : CPView
|
||||
{
|
||||
CPArray _content;
|
||||
@@ -133,7 +133,6 @@ var HORIZONTAL_MARGIN = 2;
|
||||
CGSize _storedFrameSize;
|
||||
|
||||
BOOL _uniformSubviewsResizing @accessors(property=uniformSubviewsResizing);
|
||||
BOOL _lockResizing;
|
||||
|
||||
CPInteger _currentDropIndex;
|
||||
CPDragOperation _currentDragOperation;
|
||||
@@ -141,6 +140,14 @@ var HORIZONTAL_MARGIN = 2;
|
||||
_CPCollectionViewDropIndicator _dropView;
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding == CPContentBinding)
|
||||
return [_CPCollectionViewContentBinder class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
self = [super initWithFrame:aFrame];
|
||||
@@ -183,7 +190,7 @@ var HORIZONTAL_MARGIN = 2;
|
||||
|
||||
_needsMinMaxItemSizeUpdate = YES;
|
||||
_uniformSubviewsResizing = NO;
|
||||
_lockResizing = NO;
|
||||
_inLiveResize = NO;
|
||||
|
||||
_currentDropIndex = -1;
|
||||
_currentDragOperation = CPDragOperationNone;
|
||||
@@ -385,14 +392,7 @@ var HORIZONTAL_MARGIN = 2;
|
||||
_isSelectable = isSelectable;
|
||||
|
||||
if (!_isSelectable)
|
||||
{
|
||||
var index = CPNotFound,
|
||||
itemCount = [_items count];
|
||||
|
||||
// Be wary of invalid selection ranges since setContent: does not clear selection indexes.
|
||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound && index < itemCount)
|
||||
[_items[index] setSelected:NO];
|
||||
}
|
||||
[self _applySelectionToItems:NO];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -446,22 +446,15 @@ var HORIZONTAL_MARGIN = 2;
|
||||
{
|
||||
if (!anIndexSet)
|
||||
anIndexSet = [CPIndexSet indexSet];
|
||||
|
||||
if (!_isSelectable || [_selectionIndexes isEqual:anIndexSet])
|
||||
return;
|
||||
|
||||
var index = CPNotFound,
|
||||
itemCount = [_items count];
|
||||
|
||||
// Be wary of invalid selection ranges since setContent: does not clear selection indexes.
|
||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) !== CPNotFound && index < itemCount)
|
||||
[_items[index] setSelected:NO];
|
||||
[self _applySelectionToItems:NO];
|
||||
|
||||
_selectionIndexes = anIndexSet;
|
||||
|
||||
var index = CPNotFound;
|
||||
|
||||
while ((index = [_selectionIndexes indexGreaterThanIndex:index]) !== CPNotFound)
|
||||
[_items[index] setSelected:YES];
|
||||
[self _applySelectionToItems:YES];
|
||||
|
||||
var binderClass = [[self class] _binderClassForBinding:@"selectionIndexes"];
|
||||
[[binderClass getBinding:@"selectionIndexes" forObject:self] reverseSetValueFor:@"selectionIndexes"];
|
||||
@@ -526,14 +519,14 @@ var HORIZONTAL_MARGIN = 2;
|
||||
|
||||
- (void)resizeWithOldSuperviewSize:(CGSize)oldBoundsSize
|
||||
{
|
||||
if (_lockResizing)
|
||||
if (_inLiveResize)
|
||||
return;
|
||||
|
||||
_lockResizing = YES;
|
||||
_inLiveResize = YES;
|
||||
|
||||
[self tile];
|
||||
|
||||
_lockResizing = NO;
|
||||
_inLiveResize = NO;
|
||||
}
|
||||
|
||||
- (void)tile
|
||||
@@ -973,6 +966,20 @@ var HORIZONTAL_MARGIN = 2;
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (void)_applySelectionToItems:(BOOL)select
|
||||
{
|
||||
var numberOfItems = [_items count];
|
||||
|
||||
[_selectionIndexes enumerateIndexesUsingBlock:function(idx, stop)
|
||||
{
|
||||
if (idx < numberOfItems)
|
||||
[[_items objectAtIndex:idx] setSelected:select];
|
||||
else {
|
||||
stop(YES);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPCollectionView (DragAndDrop)
|
||||
@@ -1587,3 +1594,14 @@ var CPCollectionViewMinItemSizeKey = @"CPCollectionViewMinItemSizeK
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCollectionViewContentBinder : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
||||
{
|
||||
[_source setContent:aValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+294
-9
@@ -25,6 +25,7 @@
|
||||
|
||||
@import "CGColor.j"
|
||||
|
||||
@import "_CPObject+Theme.j"
|
||||
@import "CPCompatibility.j"
|
||||
@import "CPImage.j"
|
||||
|
||||
@@ -65,7 +66,8 @@ var cachedBlackColor,
|
||||
cachedOrangeColor,
|
||||
cachedPurpleColor,
|
||||
cachedShadowColor,
|
||||
cachedClearColor;
|
||||
cachedClearColor,
|
||||
cachedThemeColor;
|
||||
|
||||
/// @endcond
|
||||
|
||||
@@ -78,7 +80,7 @@ var cachedBlackColor,
|
||||
<p>It also provides some class helper methods that
|
||||
returns instances of commonly used colors.</p>
|
||||
*/
|
||||
@implementation CPColor : CPObject
|
||||
@implementation CPColor : CPObject <CPTheme>
|
||||
{
|
||||
CPArray _components;
|
||||
|
||||
@@ -86,6 +88,30 @@ var cachedBlackColor,
|
||||
CPString _cssString;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Theming
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return "color";
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
{
|
||||
return @{
|
||||
@"alternate-selected-control-color": [CPNull null],
|
||||
@"secondary-selected-control-color": [CPNull null],
|
||||
@"selected-text-background-color": [CPNull null],
|
||||
@"selected-text-inactive-background-color": [CPNull null],
|
||||
@"css-based": NO
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Static methods
|
||||
|
||||
/*!
|
||||
Creates a color in the RGB colorspace, with an alpha value.
|
||||
Each component should be between the range of 0.0 to 1.0. For
|
||||
@@ -436,14 +462,22 @@ var cachedBlackColor,
|
||||
return cachedClearColor;
|
||||
}
|
||||
|
||||
+ (CPColor)_cachedThemeColor
|
||||
{
|
||||
if (!cachedThemeColor)
|
||||
cachedThemeColor = [self colorWithCalibratedWhite:0.0 alpha:0.0];
|
||||
|
||||
return cachedThemeColor;
|
||||
}
|
||||
|
||||
+ (CPColor)alternateSelectedControlColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.22, 0.46, 0.84, 1.0]];
|
||||
return [[self _cachedThemeColor] valueForThemeAttribute:@"alternate-selected-control-color"];
|
||||
}
|
||||
|
||||
+ (CPColor)secondarySelectedControlColor
|
||||
{
|
||||
return [[CPColor alloc] _initWithRGBA:[0.83, 0.83, 0.83, 1.0]];
|
||||
return [[self _cachedThemeColor] valueForThemeAttribute:@"secondary-selected-control-color"];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -467,6 +501,16 @@ var cachedBlackColor,
|
||||
return [[CPColor alloc] _initWithCSSString: aString];
|
||||
}
|
||||
|
||||
+ (CPColor)selectedTextBackgroundColor
|
||||
{
|
||||
return [[self _cachedThemeColor] valueForThemeAttribute:@"selected-text-background-color"] || [CPColor colorWithHexString:"99CCFF"];
|
||||
}
|
||||
|
||||
+ (CPColor)_selectedTextBackgroundColorUnfocussed
|
||||
{
|
||||
return [[self _cachedThemeColor] valueForThemeAttribute:@"selected-text-inactive-background-color"] || [CPColor colorWithHexString:"CCCCCC"];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (id)_initWithCSSString:(CPString)aString
|
||||
{
|
||||
@@ -489,6 +533,10 @@ var cachedBlackColor,
|
||||
// use it (issue #1413.)
|
||||
[self _initCSSStringFromComponents];
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeStateNormal;
|
||||
[self _loadThemeAttributes];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -502,6 +550,10 @@ var cachedBlackColor,
|
||||
_components = components;
|
||||
|
||||
[self _initCSSStringFromComponents];
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeStateNormal;
|
||||
[self _loadThemeAttributes];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -528,6 +580,10 @@ var cachedBlackColor,
|
||||
_patternImage = anImage;
|
||||
_cssString = "url(\"" + [_patternImage filename] + "\")";
|
||||
_components = [0.0, 0.0, 0.0, 1.0];
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeStateNormal;
|
||||
[self _loadThemeAttributes];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -821,9 +877,221 @@ url("data:image/png;base64,BASE64ENCODEDDATA") // if there is a pattern image
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CSS Theming
|
||||
|
||||
// The code below adds support for CSS theming with 100% compatibility with current theming system.
|
||||
// The idea is to extend CPColor (and CPImage) with CSS components and adapt low level UI components to
|
||||
// support this new kind of CPColor/CPImage. See CPImageView, CPView and _CPImageAndTextView.
|
||||
//
|
||||
// To create a CPColor that uses CSS, simply use the new class method :
|
||||
// + (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary
|
||||
// where beforeDictionary & afterDictionary are related to ::before & ::after pseudo-elements.
|
||||
// If you don't need them, just use the simplified class method :
|
||||
// + (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary
|
||||
//
|
||||
// Example :
|
||||
// buttonCssColor = [CPColor colorWithCSSDictionary:@{
|
||||
// @"background-color": A3ColorBackgroundWhite,
|
||||
// @"border-color": A3ColorActiveBorder,
|
||||
// @"border-style": @"solid",
|
||||
// @"border-width": @"1px",
|
||||
// @"border-radius": @"3px",
|
||||
// @"box-sizing": @"border-box"
|
||||
// }
|
||||
// beforeDictionary:@{
|
||||
// @"background-color": @"rgb(225,225,225)",
|
||||
// @"bottom": @"3px",
|
||||
// @"content": @"''",
|
||||
// @"position": @"absolute",
|
||||
// @"right": @"21px",
|
||||
// @"top": @"3px",
|
||||
// @"width": @"1px"
|
||||
// }
|
||||
// afterDictionary:@{
|
||||
// @"content": @"''",
|
||||
// @"bottom": @"3px",
|
||||
// @"right": @"6px",
|
||||
// @"top": @"1px",
|
||||
// @"position": @"absolute",
|
||||
// @"height": @"14px",
|
||||
// @"width": @"9px",
|
||||
// @"margin": @"2px 0px 2px 0px",
|
||||
// @"background-image": @"url(%%packed.png)",
|
||||
// @"background-position": @"0px -64px",
|
||||
// @"background-repeat": @"no-repeat",
|
||||
// @"background-size": @"100px 400px"
|
||||
// }];
|
||||
//
|
||||
// Remark : Please note the special URL of the background image used in this example : url(%%packed.png)
|
||||
// During theme loading, "%%" will be replaced by the path to the theme blend resources folder.
|
||||
// Typically, a CSS theme will use some (rare) images all packed together in a single image resource (see packed.png in Aristo3 theme)
|
||||
//
|
||||
// Also, please note that if you don't use one of the CSS components, you can either set it to nil (best solution) or to an empty dictionary, like :
|
||||
// aCssColor = [CPColor colorWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:@{ ... }];
|
||||
//
|
||||
// You can use -(BOOL)isCSSBased to determine how to cope with it in your code.
|
||||
// -(BOOL)hasCSSDictionary, -(BOOL)hasCSSBeforeDictionary and -(BOOL)hasCSSAfterDictionary are convience methods you can use.
|
||||
//
|
||||
// Remark : -(void)restorePreviousCSSState and -(DOMElement)applyCSSColorForView are meant to be used by low level UI widgets (like CPView) to implement
|
||||
// CSS theme support.
|
||||
|
||||
@implementation CPColor (CSSTheming)
|
||||
{
|
||||
CPDictionary _cssDictionary @accessors(property=cssDictionary);
|
||||
CPDictionary _cssBeforeDictionary @accessors(property=cssBeforeDictionary);
|
||||
CPDictionary _cssAfterDictionary @accessors(property=cssAfterDictionary);
|
||||
}
|
||||
|
||||
+ (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary
|
||||
{
|
||||
return [[CPColor alloc] _initWithCSSDictionary:aDictionary beforeDictionary:nil afterDictionary:nil];
|
||||
}
|
||||
|
||||
+ (CPColor)colorWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary
|
||||
{
|
||||
return [[CPColor alloc] _initWithCSSDictionary:aDictionary beforeDictionary:beforeDictionary afterDictionary:afterDictionary];
|
||||
}
|
||||
|
||||
- (id)_initWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_cssDictionary = aDictionary;
|
||||
_cssBeforeDictionary = beforeDictionary;
|
||||
_cssAfterDictionary = afterDictionary;
|
||||
_components = [0.0, 0.0, 0.0, 1.0];
|
||||
|
||||
_theme = [CPTheme defaultTheme];
|
||||
_themeState = CPThemeStateNormal;
|
||||
[self _loadThemeAttributes];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isCSSBased
|
||||
{
|
||||
return !!(_cssDictionary || _cssBeforeDictionary || _cssAfterDictionary);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSDictionary
|
||||
{
|
||||
return ([_cssDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSBeforeDictionary
|
||||
{
|
||||
return ([_cssBeforeDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSAfterDictionary
|
||||
{
|
||||
return ([_cssAfterDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (void)restorePreviousCSSState:(CPArrayRef)aPreviousStateRef forDOMElement:(DOMElement)aDOMElement
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var aPreviousState = @deref(aPreviousStateRef);
|
||||
|
||||
for (var i = 0, count = aPreviousState.length; i < count; i++)
|
||||
aDOMElement.style[aPreviousState[i][0]] = aPreviousState[i][1];
|
||||
|
||||
@deref(aPreviousStateRef) = @[];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (DOMElement)applyCSSColorForView:(CPView)aView onDOMElement:(DOMElement)aDOMElement styleNode:(DOMElement)aStyleNode previousState:(CPArrayRef)aPreviousStateRef
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
var aPreviousState = @deref(aPreviousStateRef);
|
||||
|
||||
[_cssDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
[aPreviousState addObject:@[aKey, aDOMElement.style[aKey]]];
|
||||
aDOMElement.style[aKey] = anObject;
|
||||
}];
|
||||
|
||||
if ([self hasCSSBeforeDictionary] || [self hasCSSAfterDictionary])
|
||||
{
|
||||
// We need to create a unique class name
|
||||
|
||||
var styleClassName = @".CP" + [aView UID],
|
||||
styleContent = @"";
|
||||
|
||||
if ([self hasCSSBeforeDictionary])
|
||||
{
|
||||
styleContent += styleClassName + @"::before { ";
|
||||
|
||||
[_cssBeforeDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
styleContent += aKey + ": " + anObject + "; ";
|
||||
}];
|
||||
|
||||
styleContent += "} ";
|
||||
}
|
||||
|
||||
if ([self hasCSSAfterDictionary])
|
||||
{
|
||||
styleContent += styleClassName + @"::after { ";
|
||||
|
||||
[_cssAfterDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
styleContent += aKey + ": " + anObject + "; ";
|
||||
}];
|
||||
|
||||
styleContent += "} ";
|
||||
}
|
||||
|
||||
var styleDescription = document.createTextNode(styleContent);
|
||||
|
||||
if (!aStyleNode)
|
||||
{
|
||||
aStyleNode = document.createElement("style");
|
||||
|
||||
aView._DOMElement.insertBefore(aStyleNode, aView._DOMElement.firstChild);
|
||||
|
||||
aStyleNode.appendChild(styleDescription);
|
||||
}
|
||||
else
|
||||
{
|
||||
aStyleNode.replaceChild(styleDescription, aStyleNode.firstChild);
|
||||
}
|
||||
|
||||
[aView setDOMClassName:@"CP"+[aView UID]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// no before/after so remove aStyleNode if existing
|
||||
|
||||
if (aStyleNode)
|
||||
{
|
||||
aView._DOMElement.removeChild(aStyleNode);
|
||||
aStyleNode = nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Return actualised values
|
||||
|
||||
@deref(aPreviousStateRef) = aPreviousState;
|
||||
|
||||
return aStyleNode;
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
/// @cond IGNORE
|
||||
var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
CPColorPatternImageKey = @"CPColorPatternImageKey";
|
||||
var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
CPColorPatternImageKey = @"CPColorPatternImageKey",
|
||||
CPColorCssDictionaryKey = @"CPColorCssDictionaryKey",
|
||||
CPColorCssBeforeDictionaryKey = @"CPColorCssBeforeDictionaryKey",
|
||||
CPColorCssAfterDictionaryKey = @"CPColorCssAfterDictionaryKey";
|
||||
/// @endcond
|
||||
|
||||
@implementation CPColor (CPCoding)
|
||||
@@ -835,9 +1103,17 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if ([aCoder containsValueForKey:CPColorPatternImageKey])
|
||||
return [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
|
||||
self = [self _initWithPatternImage:[aCoder decodeObjectForKey:CPColorPatternImageKey]];
|
||||
else if ([aCoder containsValueForKey:CPColorCssDictionaryKey])
|
||||
self = [self _initWithCSSDictionary:[aCoder decodeObjectForKey:CPColorCssDictionaryKey]
|
||||
beforeDictionary:[aCoder decodeObjectForKey:CPColorCssBeforeDictionaryKey]
|
||||
afterDictionary:[aCoder decodeObjectForKey:CPColorCssAfterDictionaryKey]];
|
||||
else
|
||||
self = [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
|
||||
|
||||
return [self _initWithRGBA:[aCoder decodeObjectForKey:CPColorComponentsKey]];
|
||||
[self _decodeThemeObjectsWithCoder:aCoder];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -848,8 +1124,16 @@ var CPColorComponentsKey = @"CPColorComponentsKey",
|
||||
{
|
||||
if (_patternImage)
|
||||
[aCoder encodeObject:_patternImage forKey:CPColorPatternImageKey];
|
||||
else if (_cssDictionary)
|
||||
{
|
||||
[aCoder encodeObject:_cssDictionary forKey:CPColorCssDictionaryKey];
|
||||
[aCoder encodeObject:_cssBeforeDictionary forKey:CPColorCssBeforeDictionaryKey];
|
||||
[aCoder encodeObject:_cssAfterDictionary forKey:CPColorCssAfterDictionaryKey];
|
||||
}
|
||||
else
|
||||
[aCoder encodeObject:_components forKey:CPColorComponentsKey];
|
||||
|
||||
[self _encodeThemeObjectsWithCoder:aCoder];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1186,7 +1470,8 @@ var patternColorsFromPattern = function(pattern, attributes, imageFactory)
|
||||
bottomHeight,
|
||||
centerWidthHeight,
|
||||
centerIsNil,
|
||||
numParts;
|
||||
numParts,
|
||||
isVertical;
|
||||
|
||||
// positions are mandatory
|
||||
if (pattern.indexOf("{position}") < 0)
|
||||
|
||||
@@ -71,7 +71,6 @@ var SharedColorPanel = nil,
|
||||
*/
|
||||
@implementation CPColorPanel : CPPanel
|
||||
{
|
||||
_CPColorPanelToolbar _toolbar;
|
||||
_CPColorPanelSwatches _swatchView;
|
||||
_CPColorPanelPreview _previewView;
|
||||
|
||||
@@ -484,13 +483,12 @@ var CPColorPanelSwatchesCookie = "CPColorPanelSwatchesCookie";
|
||||
];
|
||||
}
|
||||
|
||||
var cookieValue = eval(cookieValue),
|
||||
result = [];
|
||||
var cookieValue = eval(cookieValue);
|
||||
|
||||
for (var i = 0; i < cookieValue.length; i++)
|
||||
result.push([CPColor colorWithHexString:cookieValue[i]]);
|
||||
|
||||
return result;
|
||||
return [cookieValue arrayByApplyingBlock:function(value)
|
||||
{
|
||||
return [CPColor colorWithHexString:value];
|
||||
}];
|
||||
}
|
||||
|
||||
- (CPArray)saveColorList
|
||||
|
||||
@@ -125,7 +125,8 @@
|
||||
_brightnessSlider = [[CPSlider alloc] initWithFrame:CGRectMake(0, (aFrame.size.height - 34), aFrame.size.width, 15)];
|
||||
|
||||
[_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"];
|
||||
var brightnessImage = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPColorPicker class]] pathForResource:@"brightness_bar.png"] size:CGSizeMake(272, 20)];
|
||||
[_brightnessSlider setValue:[CPColor colorWithPatternImage:brightnessImage] forThemeAttribute:@"track-color"];
|
||||
|
||||
[_brightnessSlider setMinValue:0.0];
|
||||
[_brightnessSlider setMaxValue:100.0];
|
||||
@@ -246,7 +247,7 @@
|
||||
_blackWheelImage = new Image();
|
||||
_blackWheelImage.src = path;
|
||||
_blackWheelImage.style.opacity = "0";
|
||||
_blackWheelImage.style.filter = "alpha(opacity=0)"
|
||||
_blackWheelImage.style.filter = "alpha(opacity=0)";
|
||||
_blackWheelImage.style.position = "absolute";
|
||||
|
||||
_DOMElement.appendChild(_wheelImage);
|
||||
|
||||
@@ -255,6 +255,8 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
|
||||
|
||||
var colorPanel = [CPColorPanel sharedColorPanel];
|
||||
|
||||
[colorPanel setPlatformWindow:[[self window] platformWindow]];
|
||||
|
||||
[colorPanel setColor:_color];
|
||||
[colorPanel orderFront:self];
|
||||
}
|
||||
|
||||
+14
-6
@@ -74,7 +74,7 @@ var CPComboBoxTextSubview = @"text",
|
||||
BOOL _usesDataSource;
|
||||
CGSize _intercellSpacing;
|
||||
CPArray _items;
|
||||
id<CPComboBoxDataSource> _dataSource;
|
||||
id <CPComboBoxDataSource> _dataSource;
|
||||
CPInteger _implementedDelegateComboBoxMethods;
|
||||
CPString _selectedStringValue;
|
||||
float _itemHeight;
|
||||
@@ -843,7 +843,17 @@ var CPComboBoxTextSubview = @"text",
|
||||
// In FireFox this needs to be done in setTimeout, otherwise there is no caret
|
||||
// We have to save the input element now, when we lose focus it will change.
|
||||
var element = [self _inputElement];
|
||||
window.setTimeout(function() { element.focus(); }, 0);
|
||||
|
||||
[[CPRunLoop mainRunLoop] performBlock:function()
|
||||
{
|
||||
// This will prevent to jump to the focused element
|
||||
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
|
||||
|
||||
element.focus();
|
||||
|
||||
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
|
||||
|
||||
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
#endif
|
||||
|
||||
return NO;
|
||||
@@ -1132,11 +1142,9 @@ var CPComboBoxTextSubview = @"text",
|
||||
// Directly nuke _items, [_items removeAll] will trigger an extra call to setContent
|
||||
_items = [];
|
||||
|
||||
var values = [];
|
||||
|
||||
[anArray enumerateObjectsUsingBlock:function(object)
|
||||
var values = [anArray arrayByApplyingBlock:function(object)
|
||||
{
|
||||
values.push([object description]);
|
||||
return [object description];
|
||||
}];
|
||||
|
||||
[self addItemsWithObjectValues:values];
|
||||
|
||||
+58
-16
@@ -26,11 +26,12 @@
|
||||
|
||||
// Browser Engines
|
||||
CPUnknownBrowserEngine = 0;
|
||||
CPGeckoBrowserEngine = 1;
|
||||
CPInternetExplorerBrowserEngine = 2;
|
||||
CPKHTMLBrowserEngine = 3;
|
||||
CPOperaBrowserEngine = 4;
|
||||
CPWebKitBrowserEngine = 5;
|
||||
CPGeckoBrowserEngine = 1 << 0;
|
||||
CPInternetExplorerBrowserEngine = 1 << 1;
|
||||
CPKHTMLBrowserEngine = 1 << 2;
|
||||
CPOperaBrowserEngine = 1 << 3;
|
||||
CPWebKitBrowserEngine = 1 << 4; // Safari + Chrome
|
||||
CPBlinkBrowserEngine = 1 << 5; // Recent Chrome
|
||||
|
||||
// Operating Systems
|
||||
CPMacOperatingSystem = 0;
|
||||
@@ -80,18 +81,27 @@ CPFileAPIFeature = 31;
|
||||
|
||||
CPAltEnterTextAreaFeature = 32;
|
||||
|
||||
|
||||
CPCSSAnimationFeature = 33;
|
||||
|
||||
/*
|
||||
When an absolutely positioned div (CPView) with an absolutely positioned canvas in it (CPView with drawRect:) moves things on top of the canvas (subviews) don't redraw correctly. E.g. if you have a bunch of text fields in a CPBox in a sheet which animates in, some of the text fields might not be visible because the CPBox has a canvas at the bottom and the box moved form offscreen to onscreen. This bug is probably very related: https://bugs.webkit.org/show_bug.cgi?id=67203
|
||||
*/
|
||||
*/
|
||||
CPCanvasParentDrawErrorsOnMovementBug = 1 << 0;
|
||||
|
||||
// The paste event is only sent if an input or textarea has focus.
|
||||
CPJavaScriptPasteRequiresEditableTarget = 1 << 1;
|
||||
CPJavaScriptPasteRequiresEditableTarget = 1 << 1;
|
||||
// Redirecting the focus of the browser on keydown to an input for Cmd-V or Ctrl-V makes the paste fail.
|
||||
CPJavaScriptPasteCantRefocus = 1 << 2;
|
||||
|
||||
/*
|
||||
Safari calculates incorrect text size unless you set the canvas font even if it is already set
|
||||
You can see the bug after disabling the workaround and opening any panel while typing.
|
||||
You can use the font panel in the manual test for CPTextView.
|
||||
Look out for a displaced cursor, i.e. after typing letters of small width, such as the 'i'.
|
||||
https://bugs.webkit.org/show_bug.cgi?id=150224
|
||||
*/
|
||||
CPTextSizingAlwaysNeedsSetFontBug = 1 << 3;
|
||||
|
||||
|
||||
var USER_AGENT = "",
|
||||
PLATFORM_ENGINE = CPUnknownBrowserEngine,
|
||||
@@ -110,15 +120,15 @@ if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
|
||||
// Opera
|
||||
if (typeof window !== "undefined" && window.opera)
|
||||
{
|
||||
PLATFORM_ENGINE = CPOperaBrowserEngine;
|
||||
PLATFORM_ENGINE |= CPOperaBrowserEngine;
|
||||
|
||||
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
|
||||
}
|
||||
|
||||
// Internet Explorer
|
||||
else if (typeof window !== "undefined" && window.attachEvent) // Must follow Opera check.
|
||||
else if (typeof window !== "undefined" && (window.attachEvent || (!(window.ActiveXObject) && "ActiveXObject" in window))) // Must follow Opera check.
|
||||
{
|
||||
PLATFORM_ENGINE = CPInternetExplorerBrowserEngine;
|
||||
PLATFORM_ENGINE |= CPInternetExplorerBrowserEngine;
|
||||
|
||||
// Features we can only be sure of with IE (no known independent tests)
|
||||
PLATFORM_FEATURES[CPVMLFeature] = YES;
|
||||
@@ -136,10 +146,10 @@ else if (typeof window !== "undefined" && window.attachEvent) // Must follow Ope
|
||||
PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = YES;
|
||||
}
|
||||
|
||||
// WebKit
|
||||
// Safari + Chrome (WebKit and Blink)
|
||||
else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
{
|
||||
PLATFORM_ENGINE = CPWebKitBrowserEngine;
|
||||
PLATFORM_ENGINE |= CPWebKitBrowserEngine;
|
||||
|
||||
// Features we can only be sure of with WebKit (no known independent tests)
|
||||
PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
|
||||
@@ -179,7 +189,10 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
PLATFORM_BUGS |= CPJavaScriptPasteRequiresEditableTarget;
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=39689
|
||||
PLATFORM_BUGS |= CPJavaScriptPasteCantRefocus;
|
||||
PLATFORM_BUGS |= CPTextSizingAlwaysNeedsSetFontBug;
|
||||
}
|
||||
else if ((window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window)
|
||||
PLATFORM_ENGINE |= CPBlinkBrowserEngine;
|
||||
|
||||
// Assume this bug was introduced around Safari 5.1/Chrome 16. This could probably be tighter.
|
||||
if (majorVersion > 533)
|
||||
@@ -189,13 +202,13 @@ else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
||||
// KHTML
|
||||
else if (USER_AGENT.indexOf("KHTML") != -1) // Must follow WebKit check.
|
||||
{
|
||||
PLATFORM_ENGINE = CPKHTMLBrowserEngine;
|
||||
PLATFORM_ENGINE |= CPKHTMLBrowserEngine;
|
||||
}
|
||||
|
||||
// Gecko
|
||||
else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
|
||||
{
|
||||
PLATFORM_ENGINE = CPGeckoBrowserEngine;
|
||||
PLATFORM_ENGINE |= CPGeckoBrowserEngine;
|
||||
|
||||
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
|
||||
|
||||
@@ -286,7 +299,7 @@ function CPPlatformHasBug(aBug)
|
||||
|
||||
function CPBrowserIsEngine(anEngine)
|
||||
{
|
||||
return PLATFORM_ENGINE === anEngine;
|
||||
return PLATFORM_ENGINE & anEngine;
|
||||
}
|
||||
|
||||
function CPBrowserIsOperatingSystem(anOperatingSystem)
|
||||
@@ -322,6 +335,14 @@ else
|
||||
CPRedoKeyEquivalentModifierMask = CPControlKeyMask;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets a feature with the given value.
|
||||
*/
|
||||
function CPSetPlatformFeature(aFeature, aBool)
|
||||
{
|
||||
PLATFORM_FEATURES[aFeature] = aBool;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the properly prefixed JS property for the given name. E.g. in a webkit browser,
|
||||
CPBrowserStyleProperty('transition') -> WebkitTransition
|
||||
@@ -369,6 +390,18 @@ function CPBrowserStyleProperty(aProperty)
|
||||
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transform']] || nil;
|
||||
break;
|
||||
|
||||
case 'animationend':
|
||||
var candidates = {
|
||||
'WebkitAnimation' : 'webkitAnimationEnd',
|
||||
'MozAnimation' : 'animationend',
|
||||
'OAnimation' : 'oAnimationEnd',
|
||||
'msAnimation' : 'MSAnimationEnd',
|
||||
'animation' : 'animationend'
|
||||
};
|
||||
|
||||
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['animation']] || nil;
|
||||
break;
|
||||
|
||||
default:
|
||||
var prefixes = ["Webkit", "Moz", "O", "ms"],
|
||||
strippedProperty = aProperty.split('-').join(' '),
|
||||
@@ -438,3 +471,12 @@ function CPBrowserCSSProperty(aProperty)
|
||||
|
||||
return browserProperty.toLowerCase();
|
||||
}
|
||||
|
||||
function CPBrowserBackingStorePixelRatio(context)
|
||||
{
|
||||
return context.webkitBackingStorePixelRatio ||
|
||||
context.mozBackingStorePixelRatio ||
|
||||
context.msBackingStorePixelRatio ||
|
||||
context.oBackingStorePixelRatio ||
|
||||
context.backingStorePixelRatio || 1;
|
||||
}
|
||||
|
||||
+75
-11
@@ -25,8 +25,9 @@
|
||||
|
||||
@import "CPFont.j"
|
||||
@import "CPShadow.j"
|
||||
@import "CPView.j"
|
||||
@import "CPText.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
@import "CPTrackingArea.j"
|
||||
|
||||
@global CPApp
|
||||
|
||||
@@ -42,13 +43,6 @@
|
||||
|
||||
@end
|
||||
|
||||
@typedef CPTextAlignment
|
||||
CPLeftTextAlignment = 0;
|
||||
CPRightTextAlignment = 1;
|
||||
CPCenterTextAlignment = 2;
|
||||
CPJustifiedTextAlignment = 3;
|
||||
CPNaturalTextAlignment = 4;
|
||||
|
||||
@typedef CPControlSize
|
||||
CPRegularControlSize = 0;
|
||||
CPSmallControlSize = 1;
|
||||
@@ -126,6 +120,8 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
CGPoint _previousTrackingLocation;
|
||||
|
||||
CPControlSize _controlSize;
|
||||
|
||||
CPWritingDirection _baseWritingDirection @accessors(property=baseWritingDirection);
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
@@ -173,7 +169,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
}
|
||||
|
||||
/*!
|
||||
Reverse set the binding iff the CPContinuouslyUpdatesValueBindingOption is set.
|
||||
Reverse set the binding if the CPContinuouslyUpdatesValueBindingOption is set.
|
||||
*/
|
||||
- (void)_continuouslyReverseSetBinding
|
||||
{
|
||||
@@ -205,7 +201,6 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Control Size
|
||||
|
||||
@@ -240,7 +235,7 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
*/
|
||||
- (ThemeState)_controlSizeThemeState
|
||||
{
|
||||
switch(_controlSize)
|
||||
switch (_controlSize)
|
||||
{
|
||||
case CPSmallControlSize:
|
||||
return CPThemeStateControlSizeSmall;
|
||||
@@ -1016,6 +1011,69 @@ var CPControlBlackColor = [CPColor blackColor];
|
||||
return [self hasThemeState:CPThemeStateHighlighted];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Base writing direction
|
||||
|
||||
/*!
|
||||
Sets the initial writing direction of the receiver
|
||||
@param writingDirection - It could be CPWritingDirectionNatural, CPWritingDirectionLeftToRight, CPWritingDirectionRightToLeft
|
||||
*/
|
||||
- (void)setBaseWritingDirection:(CPWritingDirection)writingDirection
|
||||
{
|
||||
if (writingDirection == _baseWritingDirection)
|
||||
return;
|
||||
|
||||
[self willChangeValueForKey:@"baseWritingDirection"];
|
||||
_baseWritingDirection = writingDirection;
|
||||
[self didChangeValueForKey:@"baseWritingDirection"];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
var style;
|
||||
|
||||
switch (_baseWritingDirection)
|
||||
{
|
||||
case CPWritingDirectionNatural:
|
||||
style = "initial";
|
||||
break;
|
||||
|
||||
case CPWritingDirectionLeftToRight:
|
||||
style = "ltr";
|
||||
break;
|
||||
|
||||
case CPWritingDirectionRightToLeft:
|
||||
style = "rtl";
|
||||
break;
|
||||
|
||||
default:
|
||||
style = "initial";
|
||||
}
|
||||
|
||||
_DOMElement.style.direction = style;
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPControl (CPTrackingArea)
|
||||
{
|
||||
CPTrackingArea _controlTrackingArea;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (_controlTrackingArea)
|
||||
[self removeTrackingArea:_controlTrackingArea];
|
||||
|
||||
_controlTrackingArea = [[CPTrackingArea alloc] initWithRect:CGRectMakeZero()
|
||||
options:CPTrackingMouseEnteredAndExited | CPTrackingActiveInKeyWindow | CPTrackingInVisibleRect
|
||||
owner:self
|
||||
userInfo:nil];
|
||||
[self addTrackingArea:_controlTrackingArea];
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPControlActionKey = @"CPControlActionKey",
|
||||
@@ -1027,6 +1085,7 @@ var CPControlActionKey = @"CPControlActionKey",
|
||||
CPControlSendsActionOnEndEditingKey = @"CPControlSendsActionOnEndEditingKey",
|
||||
CPControlTargetKey = @"CPControlTargetKey",
|
||||
CPControlValueKey = @"CPControlValueKey",
|
||||
CPControlBaseWrittingDirectionKey = @"CPControlBaseWrittingDirectionKey";
|
||||
|
||||
__Deprecated__CPImageViewImageKey = @"CPImageViewImageKey";
|
||||
|
||||
@@ -1055,6 +1114,8 @@ var CPControlActionKey = @"CPControlActionKey",
|
||||
[self setFormatter:[aCoder decodeObjectForKey:CPControlFormatterKey]];
|
||||
|
||||
[self setControlSize:[aCoder decodeIntForKey:CPControlControlSizeKey]];
|
||||
|
||||
[self setBaseWritingDirection:[aCoder decodeIntForKey:CPControlBaseWrittingDirectionKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -1089,6 +1150,9 @@ var CPControlActionKey = @"CPControlActionKey",
|
||||
[aCoder encodeObject:_formatter forKey:CPControlFormatterKey];
|
||||
|
||||
[aCoder encodeInt:_controlSize forKey:CPControlControlSizeKey];
|
||||
|
||||
[aCoder encodeInt:_baseWritingDirection forKey:CPControlBaseWrittingDirectionKey];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -123,6 +123,9 @@ var currentCursor = nil,
|
||||
|
||||
- (void)set
|
||||
{
|
||||
if (currentCursor === self)
|
||||
return;
|
||||
|
||||
currentCursor = self;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
@@ -323,6 +323,14 @@ CPEraDatePickerElementFlag = 0x0100;
|
||||
*/
|
||||
- (void)_setDateValue:(CPDate)aDateValue timeInterval:(CPTimeInterval)aTimeInterval
|
||||
{
|
||||
// Make sure to have a valid date and avoid NaN values
|
||||
if (!isFinite(aDateValue))
|
||||
{
|
||||
[CPException raise:CPInvalidArgumentException
|
||||
reason:@"aDateValue is not valid"];
|
||||
return;
|
||||
}
|
||||
|
||||
if (_minDate)
|
||||
aDateValue = new Date (MAX(aDateValue, _minDate));
|
||||
|
||||
@@ -344,8 +352,8 @@ CPEraDatePickerElementFlag = 0x0100;
|
||||
if (_implementedCDatePickerDelegateMethods & CPDatePicker_validateProposedDateValue_timeInterval)
|
||||
{
|
||||
// constrain timeInterval also
|
||||
var aStartDateRef = function(x){if (typeof x == 'undefined') return aDateValue; aDateValue = x;}
|
||||
var aTimeIntervalRef = function(x){if (typeof x == 'undefined') return aTimeInterval; aTimeInterval = x;}
|
||||
var aStartDateRef = function(x){if (typeof x == 'undefined') return aDateValue; aDateValue = x;};
|
||||
var aTimeIntervalRef = function(x){if (typeof x == 'undefined') return aTimeInterval; aTimeInterval = x;};
|
||||
|
||||
[_delegate datePicker:self validateProposedDateValue:aStartDateRef timeInterval:aTimeIntervalRef];
|
||||
}
|
||||
@@ -622,14 +630,6 @@ CPEraDatePickerElementFlag = 0x0100;
|
||||
return [[_locale objectForKey:CPLocaleCountryCode] isEqualToString:@"US"];
|
||||
}
|
||||
|
||||
/*! Check if we are in the english format or not. Depending on the locale
|
||||
*/
|
||||
- (BOOL)_isEnglishFormat
|
||||
{
|
||||
return [[_locale objectForKey:CPLocaleLanguageCode] isEqualToString:@"en"];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Key event
|
||||
|
||||
@@ -694,7 +694,7 @@ var CPDatePickerModeKey = @"CPDatePickerModeKey",
|
||||
[aCoder encodeInt:_datePickerStyle forKey:CPDatePickerStyleKey];
|
||||
[aCoder encodeInt:_datePickerElements forKey:CPDatePickerElementsKey];
|
||||
[aCoder encodeObject:_minDate forKey:CPMinDateKey];
|
||||
[aCoder encodeObject:_maxDate forKey:CPMaxDateKey]
|
||||
[aCoder encodeObject:_maxDate forKey:CPMaxDateKey];
|
||||
[aCoder encodeObject:_dateValue forKey:CPDateValueKey];;
|
||||
[aCoder encodeObject:_textFont forKey:CPTextFontKey];
|
||||
[aCoder encodeObject:_locale forKey:CPLocaleKey];
|
||||
|
||||
@@ -85,7 +85,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_datePicker = aDatePicker
|
||||
_datePicker = aDatePicker;
|
||||
[self _init];
|
||||
}
|
||||
|
||||
@@ -435,7 +435,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
break;
|
||||
|
||||
default:
|
||||
return CPShortWeekDayNameArrayEn
|
||||
return CPShortWeekDayNameArrayEn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -464,7 +464,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
break;
|
||||
|
||||
default:
|
||||
return CPShortMonthNameArrayEn
|
||||
return CPShortMonthNameArrayEn;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -526,11 +526,11 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
var dayLabel = _dayLabels[i];
|
||||
|
||||
[dayLabel setStringValue:dayNames[i]];
|
||||
[dayLabel sizeToFit]
|
||||
[dayLabel sizeToFit];
|
||||
[dayLabel setFrameOrigin:CGPointMake(sizeTileWidth * (i + 1) - sizeTileWidth / 2 - [dayLabel frameSize].width / 2, 23)];
|
||||
|
||||
if (i == 0)
|
||||
firstDayTileX = sizeTileWidth * (i + 1) - sizeTileWidth / 2 - [dayLabel frameSize].width / 2
|
||||
firstDayTileX = sizeTileWidth * (i + 1) - sizeTileWidth / 2 - [dayLabel frameSize].width / 2;
|
||||
}
|
||||
|
||||
// Title
|
||||
@@ -769,7 +769,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
tileDate = [[tile date] copy],
|
||||
selected = NO;
|
||||
|
||||
[tileDate _resetToMidnight]
|
||||
[tileDate _resetToMidnight];
|
||||
|
||||
if (aStartDate)
|
||||
selected = tileDate >= aStartDate && tileDate <= endDate;
|
||||
@@ -845,7 +845,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
|
||||
// Very usefull to avoid to have a line of two pixels instead one
|
||||
if (!isBorderPair)
|
||||
y += 0.5
|
||||
y += 0.5;
|
||||
|
||||
CGContextMoveToPoint(context, 0, y);
|
||||
CGContextAddLineToPoint(context, [self bounds].size.width, y);
|
||||
@@ -857,7 +857,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
|
||||
// Very usefull to avoid to have a line of two pixels instead one
|
||||
if (!isBorderPair)
|
||||
x += 0.5
|
||||
x += 0.5;
|
||||
|
||||
CGContextMoveToPoint(context, x, 0);
|
||||
CGContextAddLineToPoint(context, x, [self bounds].size.height);
|
||||
@@ -869,7 +869,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
|
||||
// Very usefull to avoid to have a line of two pixels instead one
|
||||
if (!isBorderPair)
|
||||
y += 0.5
|
||||
y += 0.5;
|
||||
|
||||
CGContextMoveToPoint(context, 0, y);
|
||||
CGContextAddLineToPoint(context, [self bounds].size.width, y);
|
||||
@@ -897,7 +897,7 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
_clickDate = [dateTile copy];
|
||||
_dragDate = nil;
|
||||
_indexDayTile = -1;
|
||||
_eventDragged = nil
|
||||
_eventDragged = nil;
|
||||
|
||||
_datePicker._invokedByUserEvent = YES;
|
||||
|
||||
@@ -1114,30 +1114,30 @@ var CPShortWeekDayNameArrayEn = [@"Mo", @"Tu", @"We", @"Th", @"Fr", @"Sa", @"Su"
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateDisabled];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateDisabled] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateDisabled];
|
||||
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]]forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]]forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateSelected]];
|
||||
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:CPThemeStateHighlighted] forThemeAttribute:@"font" inState:CPThemeStateHighlighted];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-color" inState:CPThemeStateHighlighted];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateHighlighted];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:CPThemeStateHighlighted] forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateHighlighted];
|
||||
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted, CPThemeStateSelected]];
|
||||
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"font" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-color" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-offset" inState:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"font" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-color" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
[_textField setValue:[_datePicker valueForThemeAttribute:@"tile-text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]] forThemeAttribute:@"text-shadow-offset" inStates:[CPThemeStateDisabled, CPThemeStateHighlighted]];
|
||||
|
||||
[self addSubview:_textField];
|
||||
|
||||
|
||||
@@ -139,10 +139,10 @@ var RADIANS = Math.PI / 180;
|
||||
[_minuteHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"minute-hand-image"]];
|
||||
[_secondHandLayer setImage:[_datePicker currentValueForThemeAttribute:@"second-hand-image"]];
|
||||
|
||||
if ([_datePicker _isEnglishFormat])
|
||||
if ([_datePicker _isAmericanFormat])
|
||||
{
|
||||
if (dateValue.getHours() > 11)
|
||||
[_PMAMTextField setStringValue:@"PM"]
|
||||
[_PMAMTextField setStringValue:@"PM"];
|
||||
else
|
||||
[_PMAMTextField setStringValue:@"AM"];
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ var CPZeroKeyCode = 48,
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_datePicker = aDatePicker
|
||||
_datePicker = aDatePicker;
|
||||
[self _init];
|
||||
}
|
||||
return self;
|
||||
@@ -198,7 +198,6 @@ var CPZeroKeyCode = 48,
|
||||
else
|
||||
[self _selectTextField:_firstTextField];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*! Select a textField
|
||||
@@ -277,74 +276,99 @@ var CPZeroKeyCode = 48,
|
||||
}
|
||||
}
|
||||
|
||||
/*! performKeyEquivalent event
|
||||
Used for moving in the textField
|
||||
/*!
|
||||
PerformKeyEquivalent event
|
||||
We need to override that to handle the tab key
|
||||
*/
|
||||
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled] || !_currentTextField || [[self window] firstResponder] != _datePicker)
|
||||
return NO;
|
||||
|
||||
var key = [anEvent charactersIgnoringModifiers];
|
||||
|
||||
if (key == CPUpArrowFunctionKey)
|
||||
if ([anEvent charactersIgnoringModifiers] === CPTabCharacter)
|
||||
{
|
||||
[_currentTextField _invalidTimer];
|
||||
[_stepper setDoubleValue:[_currentTextField intValue]];
|
||||
[_stepper performClickUp:self];
|
||||
if ([anEvent modifierFlags] & CPShiftKeyMask)
|
||||
[self insertBacktab:self];
|
||||
else
|
||||
[self insertTab:self];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (key == CPDownArrowFunctionKey)
|
||||
else if ([anEvent charactersIgnoringModifiers] === CPBackTabCharacter)
|
||||
{
|
||||
[_currentTextField _invalidTimer];
|
||||
[_stepper setDoubleValue:[_currentTextField intValue]];
|
||||
[_stepper performClickDown:self];
|
||||
[self insertBacktab:self];
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (key == CPLeftArrowFunctionKey || [anEvent keyCode] == CPTabKeyCode && [anEvent modifierFlags] & CPShiftKeyMask)
|
||||
{
|
||||
if (_currentTextField == _firstTextField && [anEvent keyCode] == CPTabKeyCode)
|
||||
{
|
||||
var previousValidKeyView = [_datePicker previousValidKeyView];
|
||||
|
||||
if (previousValidKeyView)
|
||||
[[self window] makeFirstResponder:previousValidKeyView];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
[self _selectTextField:[_currentTextField previousTextField]];
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (key == CPRightArrowFunctionKey || [anEvent keyCode] == CPTabKeyCode)
|
||||
{
|
||||
if (_currentTextField == _lastTextField && [anEvent keyCode] == CPTabKeyCode)
|
||||
{
|
||||
var nextValidKeyView = [_datePicker nextValidKeyView];
|
||||
|
||||
if (nextValidKeyView)
|
||||
[[self window] makeFirstResponder:nextValidKeyView];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
[self _selectTextField:[_currentTextField nextTextField]];
|
||||
return YES;
|
||||
}
|
||||
|
||||
if ([anEvent keyCode] == CPReturnKeyCode)
|
||||
{
|
||||
[_currentTextField _endEditing];
|
||||
|
||||
return [super performKeyEquivalent:anEvent];
|
||||
}
|
||||
|
||||
return [super performKeyEquivalent:anEvent];
|
||||
}
|
||||
|
||||
- (void)insertTab:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
if (_currentTextField == _lastTextField)
|
||||
[[self window] selectNextKeyView:self];
|
||||
else
|
||||
[self moveRight:sender];
|
||||
}
|
||||
|
||||
- (void)moveRight:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
[self _selectTextField:[_currentTextField nextTextField]];
|
||||
}
|
||||
|
||||
- (void)insertBacktab:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
if (_currentTextField == _firstTextField)
|
||||
[[self window] selectPreviousKeyView:self];
|
||||
else
|
||||
[self moveLeft:sender];
|
||||
}
|
||||
|
||||
- (void)moveLeft:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
[self _selectTextField:[_currentTextField previousTextField]];
|
||||
}
|
||||
|
||||
- (void)moveDown:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
[_currentTextField _invalidTimer];
|
||||
[_stepper setDoubleValue:[_currentTextField intValue]];
|
||||
[_stepper performClickDown:self];
|
||||
}
|
||||
|
||||
- (void)moveUp:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
[_currentTextField _invalidTimer];
|
||||
[_stepper setDoubleValue:[_currentTextField intValue]];
|
||||
[_stepper performClickUp:self];
|
||||
}
|
||||
|
||||
- (void)insertNewline:(id)sender
|
||||
{
|
||||
if (!_currentTextField)
|
||||
return;
|
||||
|
||||
[_currentTextField _endEditing];
|
||||
}
|
||||
|
||||
/*! KeyDown event
|
||||
We just care care about the event A/P and every numbers
|
||||
*/
|
||||
@@ -353,7 +377,9 @@ var CPZeroKeyCode = 48,
|
||||
if (![self isEnabled])
|
||||
return;
|
||||
|
||||
if ([_datePicker _isEnglishFormat] && [_currentTextField dateType] == CPAMPMDateType && ([anEvent keyCode] == CPAKeyCode || [anEvent keyCode] == CPPKeyCode || [anEvent keyCode] == CPMajAKeyCode || [anEvent keyCode] == CPMajPKeyCode))
|
||||
[self interpretKeyEvents:[anEvent]];
|
||||
|
||||
if ([_datePicker _isAmericanFormat] && [_currentTextField dateType] == CPAMPMDateType && ([anEvent keyCode] == CPAKeyCode || [anEvent keyCode] == CPPKeyCode || [anEvent keyCode] == CPMajAKeyCode || [anEvent keyCode] == CPMajPKeyCode))
|
||||
{
|
||||
if ([anEvent keyCode] == CPAKeyCode || [anEvent keyCode] == CPMajAKeyCode)
|
||||
[_currentTextField setStringValue:@"AM"];
|
||||
@@ -368,6 +394,7 @@ var CPZeroKeyCode = 48,
|
||||
[_currentTextField setValueForKeyEvent:anEvent];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Layout methods
|
||||
|
||||
@@ -725,7 +752,7 @@ var CPZeroKeyCode = 48,
|
||||
|
||||
if (hour != currentHour)
|
||||
{
|
||||
if (([_datePicker _isEnglishFormat] || [_datePicker _isAmericanFormat]))
|
||||
if ([_datePicker _isAmericanFormat])
|
||||
{
|
||||
if (![self _isAMHour])
|
||||
{
|
||||
@@ -1050,7 +1077,7 @@ var CPZeroKeyCode = 48,
|
||||
- (void)_updateHiddenTextFields
|
||||
{
|
||||
var datePickerElements = [_datePicker datePickerElements],
|
||||
isEnglishFormat = [_datePicker _isEnglishFormat];
|
||||
isAmericanFormat = [_datePicker _isAmericanFormat];
|
||||
|
||||
if (datePickerElements & CPYearMonthDatePickerElementFlag)
|
||||
{
|
||||
@@ -1083,7 +1110,7 @@ var CPZeroKeyCode = 48,
|
||||
[_textFieldSeparatorThree setHidden:NO];
|
||||
[_textFieldSeparatorFour setHidden:YES];
|
||||
|
||||
if (isEnglishFormat)
|
||||
if (isAmericanFormat)
|
||||
[_textFieldPMAM setHidden:NO];
|
||||
else
|
||||
[_textFieldPMAM setHidden:YES];
|
||||
@@ -1115,9 +1142,9 @@ var CPZeroKeyCode = 48,
|
||||
verticalInset = contentInset.top - contentInset.bottom,
|
||||
firstTexField = _textFieldMonth,
|
||||
secondTextField = _textFieldDay,
|
||||
isEnglishFormat = [_datePicker _isEnglishFormat];
|
||||
isAmericanFormat = [_datePicker _isAmericanFormat];
|
||||
|
||||
if (!isEnglishFormat)
|
||||
if (!isAmericanFormat)
|
||||
{
|
||||
firstTexField = _textFieldDay;
|
||||
secondTextField = _textFieldMonth;
|
||||
@@ -1131,7 +1158,7 @@ var CPZeroKeyCode = 48,
|
||||
else
|
||||
[secondTextField setFrameOrigin:CGPointMake(CGRectGetMaxX([_textFieldSeparatorOne frame]) + separatorContentInset.right, verticalInset)];
|
||||
|
||||
if (isEnglishFormat && [secondTextField isHidden])
|
||||
if (isAmericanFormat && [secondTextField isHidden])
|
||||
[_textFieldSeparatorTwo setFrameOrigin:CGPointMake(CGRectGetMaxX([firstTexField frame]) + separatorContentInset.left, verticalInset)];
|
||||
else
|
||||
[_textFieldSeparatorTwo setFrameOrigin:CGPointMake(CGRectGetMaxX([secondTextField frame]) + separatorContentInset.left, verticalInset)];
|
||||
@@ -1206,7 +1233,7 @@ var CPZeroKeyCode = 48,
|
||||
{
|
||||
var datePickerElements = [_datePicker datePickerElements];
|
||||
|
||||
if ([_datePicker _isEnglishFormat])
|
||||
if ([_datePicker _isAmericanFormat])
|
||||
{
|
||||
if (datePickerElements & CPYearMonthDayDatePickerElementFlag || datePickerElements & CPYearMonthDatePickerElementFlag)
|
||||
[[self superview] setFirstTextField:_textFieldMonth];
|
||||
@@ -1249,9 +1276,9 @@ var CPZeroKeyCode = 48,
|
||||
var datePickerElements = [_datePicker datePickerElements],
|
||||
firstTexField = _textFieldMonth,
|
||||
secondTextField = _textFieldDay,
|
||||
isEnglishFormat = [_datePicker _isEnglishFormat];
|
||||
isAmericanFormat = [_datePicker _isAmericanFormat];
|
||||
|
||||
if (!isEnglishFormat)
|
||||
if (!isAmericanFormat)
|
||||
{
|
||||
firstTexField = _textFieldDay;
|
||||
secondTextField = _textFieldMonth;
|
||||
@@ -1266,7 +1293,7 @@ var CPZeroKeyCode = 48,
|
||||
|
||||
if (datePickerElements & CPHourMinuteSecondDatePickerElementFlag || datePickerElements & CPHourMinuteDatePickerElementFlag)
|
||||
[_textFieldYear setNextTextField:_textFieldHour];
|
||||
else if (isEnglishFormat || (datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
|
||||
else if (isAmericanFormat || (datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
|
||||
[_textFieldYear setNextTextField:firstTexField];
|
||||
else
|
||||
[_textFieldYear setNextTextField:secondTextField];
|
||||
@@ -1275,7 +1302,7 @@ var CPZeroKeyCode = 48,
|
||||
|
||||
if ((datePickerElements & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
|
||||
[_textFieldMinute setNextTextField:_textFieldSecond];
|
||||
else if (isEnglishFormat)
|
||||
else if (isAmericanFormat)
|
||||
[_textFieldMinute setNextTextField:_textFieldPMAM];
|
||||
else if ((datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
|
||||
[_textFieldMinute setNextTextField:firstTexField];
|
||||
@@ -1284,7 +1311,7 @@ var CPZeroKeyCode = 48,
|
||||
else
|
||||
[_textFieldMinute setNextTextField:_textFieldHour];
|
||||
|
||||
if (isEnglishFormat)
|
||||
if (isAmericanFormat)
|
||||
[_textFieldSecond setNextTextField:_textFieldPMAM];
|
||||
else if ((datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
|
||||
[_textFieldSecond setNextTextField:firstTexField];
|
||||
@@ -1304,9 +1331,9 @@ var CPZeroKeyCode = 48,
|
||||
var datePickerElements = [_datePicker datePickerElements],
|
||||
firstTexField = _textFieldMonth,
|
||||
secondTextField = _textFieldDay,
|
||||
isEnglishFormat = [_datePicker _isEnglishFormat];
|
||||
isAmericanFormat = [_datePicker _isAmericanFormat];
|
||||
|
||||
if (!isEnglishFormat)
|
||||
if (!isAmericanFormat)
|
||||
{
|
||||
firstTexField = _textFieldDay;
|
||||
secondTextField = _textFieldMonth;
|
||||
@@ -1322,14 +1349,14 @@ var CPZeroKeyCode = 48,
|
||||
|
||||
if (datePickerElements & CPYearMonthDatePickerElementFlag)
|
||||
[_textFieldHour setPreviousTextField:_textFieldYear];
|
||||
else if (isEnglishFormat)
|
||||
else if (isAmericanFormat)
|
||||
[_textFieldHour setPreviousTextField:_textFieldPMAM];
|
||||
else if ((datePickerElements & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
|
||||
[_textFieldHour setPreviousTextField:_textFieldSecond];
|
||||
else
|
||||
[_textFieldHour setPreviousTextField:_textFieldMinute];
|
||||
|
||||
if (!isEnglishFormat)
|
||||
if (!isAmericanFormat)
|
||||
[_textFieldYear setPreviousTextField:_textFieldMonth];
|
||||
else if ((datePickerElements & CPYearMonthDayDatePickerElementFlag) == CPYearMonthDayDatePickerElementFlag)
|
||||
[_textFieldYear setPreviousTextField:_textFieldDay];
|
||||
@@ -1338,7 +1365,7 @@ var CPZeroKeyCode = 48,
|
||||
|
||||
[secondTextField setPreviousTextField:firstTexField];
|
||||
|
||||
if (isEnglishFormat && datePickerElements & CPHourMinuteDatePickerElementFlag)
|
||||
if (isAmericanFormat && datePickerElements & CPHourMinuteDatePickerElementFlag)
|
||||
[firstTexField setPreviousTextField:_textFieldPMAM];
|
||||
else if ((datePickerElements & CPHourMinuteSecondDatePickerElementFlag) == CPHourMinuteSecondDatePickerElementFlag)
|
||||
[firstTexField setPreviousTextField:_textFieldSecond];
|
||||
@@ -1530,7 +1557,7 @@ var CPMonthDateType = 0,
|
||||
}
|
||||
}
|
||||
|
||||
if (parseInt(newValue) > [self _maxNumberWithMaxDate] || ([_datePicker _isEnglishFormat] && _dateType == CPHourDateType && parseInt(newValue) > 12))
|
||||
if (parseInt(newValue) > [self _maxNumberWithMaxDate] || ([_datePicker _isAmericanFormat] && _dateType == CPHourDateType && parseInt(newValue) > 12))
|
||||
return;
|
||||
|
||||
_firstEvent = NO;
|
||||
@@ -1549,7 +1576,7 @@ var CPMonthDateType = 0,
|
||||
|
||||
if ([stringValue length])
|
||||
{
|
||||
if ([_datePicker _isEnglishFormat] && [self dateType] == CPHourDateType)
|
||||
if ([_datePicker _isAmericanFormat] && [self dateType] == CPHourDateType)
|
||||
{
|
||||
var isAMHour = [[self superview] _isAMHour];
|
||||
|
||||
@@ -1593,7 +1620,7 @@ var CPMonthDateType = 0,
|
||||
if (![objectValue length])
|
||||
objectValue = [self objectValue];
|
||||
|
||||
if ([_datePicker _isEnglishFormat] && [self dateType] == CPHourDateType)
|
||||
if ([_datePicker _isAmericanFormat] && [self dateType] == CPHourDateType)
|
||||
{
|
||||
var isAMHour = [[self superview] _isAMHour];
|
||||
|
||||
@@ -1621,7 +1648,7 @@ var CPMonthDateType = 0,
|
||||
}
|
||||
else if (_dateType != CPAMPMDateType)
|
||||
{
|
||||
if (_dateType == CPHourDateType && [_datePicker _isEnglishFormat])
|
||||
if (_dateType == CPHourDateType && [_datePicker _isAmericanFormat])
|
||||
{
|
||||
var value = parseInt(aStringValue);
|
||||
|
||||
|
||||
+22
-13
@@ -515,7 +515,9 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
alert("There was an error retrieving the document.");
|
||||
|
||||
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||
var theDelegate = session.delegate;
|
||||
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -540,7 +542,9 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
_writeRequest = nil;
|
||||
|
||||
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||
var theDelegate = session.delegate;
|
||||
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||
[self _sendDocumentSavedNotification:NO];
|
||||
}
|
||||
}
|
||||
@@ -553,14 +557,15 @@ var CPDocumentUntitledCount = 0;
|
||||
*/
|
||||
- (void)connection:(CPURLConnection)aConnection didReceiveData:(CPString)aData
|
||||
{
|
||||
var session = aConnection.session;
|
||||
var session = aConnection.session,
|
||||
theDelegate = session.delegate;
|
||||
|
||||
// READ
|
||||
if (aConnection == _readConnection)
|
||||
{
|
||||
[self readFromData:[CPData dataWithRawString:aData] ofType:session.fileType error:nil];
|
||||
|
||||
objj_msgSend(session.delegate, session.didReadSelector, self, YES, session.contextInfo);
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, YES, session.contextInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -569,7 +574,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
_writeRequest = nil;
|
||||
|
||||
objj_msgSend(session.delegate, session.didSaveSelector, self, YES, session.contextInfo);
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, YES, session.contextInfo);
|
||||
[self _sendDocumentSavedNotification:YES];
|
||||
}
|
||||
}
|
||||
@@ -580,10 +585,11 @@ var CPDocumentUntitledCount = 0;
|
||||
*/
|
||||
- (void)connection:(CPURLConnection)aConnection didFailWithError:(CPError)anError
|
||||
{
|
||||
var session = aConnection.session;
|
||||
var session = aConnection.session,
|
||||
theDelegate = session.delegate;
|
||||
|
||||
if (_readConnection == aConnection)
|
||||
objj_msgSend(session.delegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didReadSelector, self, NO, session.contextInfo);
|
||||
|
||||
else
|
||||
{
|
||||
@@ -597,7 +603,7 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
alert("There was an error saving the document.");
|
||||
|
||||
objj_msgSend(session.delegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, session.didSaveSelector, self, NO, session.contextInfo);
|
||||
[self _sendDocumentSavedNotification:NO];
|
||||
}
|
||||
}
|
||||
@@ -858,21 +864,24 @@ var CPDocumentUntitledCount = 0;
|
||||
[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);
|
||||
delegate.isa.objj_msgSend3(delegate, selector, self, YES, info);
|
||||
}
|
||||
|
||||
- (void)_document:(CPDocument)aDocument shouldClose:(BOOL)shouldClose context:(Object)context
|
||||
{
|
||||
var theDelegate = context.delegate;
|
||||
|
||||
if (aDocument === self && shouldClose)
|
||||
[self close];
|
||||
|
||||
objj_msgSend(context.delegate, context.selector, aDocument, shouldClose, context.context);
|
||||
if (theDelegate != null)
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, 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);
|
||||
return [aDelegate respondsToSelector:aSelector] && aDelegate.isa.objj_msgSend3(aDelegate, aSelector, self, YES, context);
|
||||
|
||||
_canCloseAlert = [[CPAlert alloc] init];
|
||||
|
||||
@@ -901,8 +910,8 @@ var CPDocumentUntitledCount = 0;
|
||||
|
||||
if (returnCode === 0)
|
||||
[self saveDocumentWithDelegate:delegate didSaveSelector:selector contextInfo:context];
|
||||
else
|
||||
objj_msgSend(delegate, selector, self, returnCode === 2, context);
|
||||
else if (delegate != null)
|
||||
delegate.isa.objj_msgSend3(delegate, selector, self, returnCode === 2, context);
|
||||
|
||||
_canCloseAlert = nil;
|
||||
}
|
||||
|
||||
@@ -402,8 +402,10 @@ var CPSharedDocumentController = nil;
|
||||
}
|
||||
}
|
||||
|
||||
if ([context.delegate respondsToSelector:context.selector])
|
||||
objj_msgSend(context.delegate, context.selector, self, [[self documents] count] === 0, context.context);
|
||||
var theDelegate = context.delegate;
|
||||
|
||||
if ([theDelegate respondsToSelector:context.selector])
|
||||
theDelegate.isa.objj_msgSend3(theDelegate, context.selector, self, [[self documents] count] === 0, context.context);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+67
-2
@@ -28,13 +28,16 @@
|
||||
|
||||
@import "CPCompatibility.j"
|
||||
@import "CGGeometry.j"
|
||||
@import "CPText.j"
|
||||
@import "CPTrackingArea.j"
|
||||
|
||||
@class CPTextField
|
||||
@class CPWindow
|
||||
@class CPGraphicsContext
|
||||
|
||||
@global CPApp
|
||||
@global CPNewlineCharacter
|
||||
@global CPCarriageReturnCharacter
|
||||
@global CPEnterCharacter
|
||||
|
||||
@typedef DOMEvent
|
||||
@typedef CPEventType
|
||||
@@ -63,7 +66,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
CPWindow _window;
|
||||
Number _windowNumber;
|
||||
CPString _characters;
|
||||
CPString _charactersIgnoringModifiers
|
||||
CPString _charactersIgnoringModifiers;
|
||||
BOOL _isARepeat;
|
||||
unsigned _keyCode;
|
||||
DOMEvent _DOMEvent;
|
||||
@@ -82,6 +85,8 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
BOOL _suppressCappuccinoCut;
|
||||
BOOL _suppressCappuccinoPaste;
|
||||
#endif
|
||||
|
||||
CPTrackingArea _trackingArea;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -141,6 +146,27 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber clickCount:aClickCount pressure:aPressure];
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a new mouse tracking event.
|
||||
|
||||
@param anEventType the event type
|
||||
@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 CPWindow where the event occurred
|
||||
@param aGraphicsContext the graphics context where the event occurred
|
||||
@param anEventNumber a number for this event
|
||||
@param aTrackingArea the tracking area that triggered the event
|
||||
@throws CPInternalInconsistencyException if an invalid event type is provided
|
||||
@return the new mouse event
|
||||
*/
|
||||
+ (id)enterExitEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||
eventNumber:(int)anEventNumber trackingArea:(CPTrackingArea)aTrackingArea
|
||||
{
|
||||
return [[self alloc] _initEnterExitEventWithType:anEventType location:aPoint modifierFlags:modifierFlags timestamp:aTimestamp windowNumber:aWindowNumber context:aGraphicsContext eventNumber:anEventNumber trackingArea:aTrackingArea];
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a new custom event.
|
||||
|
||||
@@ -201,6 +227,28 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return self;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (id)_initEnterExitEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned)modifierFlags
|
||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||
eventNumber:(int)anEventNumber trackingArea:(CPTrackingArea)aTrackingArea
|
||||
{
|
||||
if ((anEventType != CPMouseEntered) && (anEventType != CPMouseExited) && (anEventType != CPCursorUpdate))
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Invalid event type"];
|
||||
|
||||
if (self = [self _initWithType:anEventType])
|
||||
{
|
||||
_location = CGPointCreateCopy(aPoint);
|
||||
_modifierFlags = modifierFlags;
|
||||
_timestamp = aTimestamp;
|
||||
_context = aGraphicsContext;
|
||||
_eventNumber = anEventNumber;
|
||||
_trackingArea = aTrackingArea;
|
||||
_window = [CPApp windowWithWindowNumber:aWindowNumber];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
- (id)_initKeyEventWithType:(CPEventType)anEventType location:(CGPoint)aPoint modifierFlags:(unsigned int)modifierFlags
|
||||
timestamp:(CPTimeInterval)aTimestamp windowNumber:(int)aWindowNumber context:(CPGraphicsContext)aGraphicsContext
|
||||
@@ -236,6 +284,7 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
_subtype = aSubtype;
|
||||
_data1 = aData1;
|
||||
_data2 = aData2;
|
||||
_windowNumber = aWindowNumber;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -289,6 +338,14 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
return _type;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the subtype of the event.
|
||||
*/
|
||||
- (CPEventType)subtype
|
||||
{
|
||||
return _subtype;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the event's associated window.
|
||||
*/
|
||||
@@ -582,6 +639,14 @@ var _CPEventPeriodicEventPeriod = 0,
|
||||
}
|
||||
}
|
||||
|
||||
- (CPTrackingArea)trackingArea
|
||||
{
|
||||
if ((_type !== CPMouseEntered) && (_type !== CPMouseExited) && (_type !== CPCursorUpdate))
|
||||
[CPException raise:CPInternalInconsistencyException format:@"You can't call trackingArea for events of type %#x", _type];
|
||||
|
||||
return _trackingArea;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
function _CPEventFirePeriodEvent()
|
||||
|
||||
@@ -43,6 +43,9 @@ var IEFlashCLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
|
||||
CPLog.warn("CPFlashView is not supported by Cappuccino, this is now deprecated and it will be removed in the version 1.1 of Cappuccino");
|
||||
|
||||
self = [super initWithFrame:aFrame];
|
||||
|
||||
if (self)
|
||||
|
||||
+39
-1
@@ -24,6 +24,7 @@
|
||||
@import <Foundation/CPBundle.j>
|
||||
|
||||
@import "CPView.j"
|
||||
@import "CPFontDescriptor.j"
|
||||
|
||||
CPFontDefaultSystemFontFace = @"Arial, sans-serif";
|
||||
CPFontDefaultSystemFontSize = 12;
|
||||
@@ -162,7 +163,7 @@ following:
|
||||
if (normalizedFaces === _CPFontSystemFontFace)
|
||||
return;
|
||||
|
||||
[self _invalidateSystemFontCache]
|
||||
[self _invalidateSystemFontCache];
|
||||
_CPFontSystemFontFace = aFace;
|
||||
}
|
||||
|
||||
@@ -433,6 +434,43 @@ following:
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPFont(DescriptorAdditions)
|
||||
|
||||
- (id)_initWithFontDescriptor:(CPFontDescriptor)fontDescriptor
|
||||
{
|
||||
var aName = [fontDescriptor objectForKey: CPFontNameAttribute] ,
|
||||
aSize = [fontDescriptor pointSize],
|
||||
isBold = [fontDescriptor symbolicTraits] & CPFontBoldTrait,
|
||||
isItalic = [fontDescriptor symbolicTraits] & CPFontItalicTrait;
|
||||
|
||||
return [self _initWithName:aName size:aSize bold:isBold italic:isItalic system:NO];
|
||||
}
|
||||
|
||||
+ (CPFont)fontWithDescriptor:(CPFontDescriptor)fontDescriptor size:(float)aSize
|
||||
{
|
||||
var aName = [fontDescriptor objectForKey: CPFontNameAttribute],
|
||||
isBold = [fontDescriptor symbolicTraits] & CPFontBoldTrait,
|
||||
isItalic = [fontDescriptor symbolicTraits] & CPFontItalicTrait;
|
||||
|
||||
return [self _fontWithName:aName size:aSize || [fontDescriptor pointSize] bold:isBold italic:isItalic];
|
||||
}
|
||||
|
||||
- (CPFontDescriptor)fontDescriptor
|
||||
{
|
||||
var traits = 0;
|
||||
|
||||
if ([self isBold])
|
||||
traits |= CPFontBoldTrait;
|
||||
|
||||
if ([self isItalic])
|
||||
traits |= CPFontItalicTrait;
|
||||
|
||||
return [[CPFontDescriptor fontDescriptorWithName:_name size:_size] fontDescriptorWithSymbolicTraits:traits];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPFontNameKey = @"CPFontNameKey",
|
||||
CPFontSizeKey = @"CPFontSizeKey",
|
||||
CPFontIsBoldKey = @"CPFontIsBoldKey",
|
||||
|
||||
+208
-1
@@ -22,9 +22,12 @@
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
|
||||
@import "CPControl.j"
|
||||
@import "CPFont.j"
|
||||
@import "CPFontDescriptor.j"
|
||||
|
||||
@global CPApp
|
||||
@class CPFontPanel
|
||||
|
||||
CPItalicFontMask = 1 << 0;
|
||||
CPBoldFontMask = 1 << 1;
|
||||
@@ -41,7 +44,20 @@ CPUnitalicFontMask = 1 << 24;
|
||||
|
||||
|
||||
var CPSharedFontManager = nil,
|
||||
CPFontManagerFactory = Nil;
|
||||
CPFontManagerFactory = nil,
|
||||
CPFontPanelFactory = nil;
|
||||
|
||||
/*
|
||||
modifyFont: sender's tag
|
||||
*/
|
||||
CPNoFontChangeAction = 0;
|
||||
CPViaPanelFontAction = 1;
|
||||
CPAddTraitFontAction = 2;
|
||||
CPSizeUpFontAction = 3;
|
||||
CPSizeDownFontAction = 4;
|
||||
CPHeavierFontAction = 5;
|
||||
CPLighterFontAction = 6;
|
||||
CPRemoveTraitFontAction = 7;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -59,6 +75,8 @@ var CPSharedFontManager = nil,
|
||||
BOOL _multiple @accessors(getter=isMultiple, setter=setMultiple:);
|
||||
|
||||
CPDictionary _activeChange;
|
||||
|
||||
unsigned _fontAction;
|
||||
}
|
||||
|
||||
// Getting the Shared Font Manager
|
||||
@@ -83,6 +101,15 @@ var CPSharedFontManager = nil,
|
||||
{
|
||||
CPFontManagerFactory = aClass;
|
||||
}
|
||||
/*!
|
||||
Sets the class that will be used to create the application's
|
||||
Font panel.
|
||||
*/
|
||||
+ (void)setFontPanelFactory:(Class)aClass
|
||||
{
|
||||
CPFontPanelFactory = aClass;
|
||||
}
|
||||
|
||||
|
||||
- (id)init
|
||||
{
|
||||
@@ -210,6 +237,7 @@ var CPSharedFontManager = nil,
|
||||
{
|
||||
var tag = [sender tag];
|
||||
_activeChange = tag === nil ? @{} : @{ @"addTraits": tag };
|
||||
_fontAction = CPAddTraitFontAction;
|
||||
|
||||
[self sendAction];
|
||||
}
|
||||
@@ -219,6 +247,185 @@ var CPSharedFontManager = nil,
|
||||
return [CPApp sendAction:_action to:_target from:self];
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
This method open the font panel, create it if necessary.
|
||||
@param sender The object that sent the message.
|
||||
*/
|
||||
- (CPFontPanel)fontPanel:(BOOL)createIt
|
||||
{
|
||||
var panel = nil,
|
||||
panelExists = [CPFontPanelFactory sharedFontPanelExists];
|
||||
|
||||
if ((panelExists) || (!panelExists && createIt))
|
||||
panel = [CPFontPanelFactory sharedFontPanel];
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
/*!
|
||||
Convert a font to have the specified Font traits. The font is unchanged expect for the specified Font traits.
|
||||
Using CPUnboldFontMask or CPUnitalicFontMask will respectively remove Bold and Italic traits.
|
||||
@param aFont The font to convert.
|
||||
@param fontTrait The new font traits mask.
|
||||
@result The converted font or \c aFont if the conversion failed.
|
||||
*/
|
||||
- (CPFont)convertFont:(CPFont)aFont toHaveTrait:(CPFontTraitMask)fontTrait
|
||||
{
|
||||
var attributes = [[[aFont fontDescriptor] fontAttributes] copy],
|
||||
symbolicTrait = [[aFont fontDescriptor] symbolicTraits];
|
||||
|
||||
if (fontTrait & CPBoldFontMask)
|
||||
symbolicTrait |= CPFontBoldTrait;
|
||||
|
||||
if (fontTrait & CPItalicFontMask)
|
||||
symbolicTrait |= CPFontItalicTrait;
|
||||
|
||||
if (fontTrait & CPUnboldFontMask) /* FIXME: this only change CPFontSymbolicTrait what about CPFontWeightTrait */
|
||||
symbolicTrait &= ~CPFontBoldTrait;
|
||||
|
||||
if (fontTrait & CPUnitalicFontMask)
|
||||
symbolicTrait &= ~CPFontItalicTrait;
|
||||
|
||||
if (fontTrait & CPExpandedFontMask)
|
||||
symbolicTrait |= CPFontExpandedTrait;
|
||||
|
||||
if (fontTrait & CPSmallCapsFontMask)
|
||||
symbolicTrait |= CPFontSmallCapsTrait;
|
||||
|
||||
if (![attributes containsKey:CPFontTraitsAttribute])
|
||||
[attributes setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
|
||||
forKey:CPFontSymbolicTrait]
|
||||
forKey:CPFontTraitsAttribute];
|
||||
else
|
||||
[[attributes objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
|
||||
forKey:CPFontSymbolicTrait];
|
||||
|
||||
return [[aFont class] fontWithDescriptor:[CPFontDescriptor fontDescriptorWithFontAttributes:attributes] size:0.0];
|
||||
}
|
||||
|
||||
/*!
|
||||
Convert a font to not have the specified Font traits. The font is unchanged expect for the specified Font traits.
|
||||
@param aFont The font to convert.
|
||||
@param fontTrait The font traits mask to remove.
|
||||
@result The converted font or \c aFont if the conversion failed.
|
||||
*/
|
||||
- (CPFont)convertFont:(CPFont)aFont toNotHaveTrait:(CPFontTraitMask)fontTrait
|
||||
{
|
||||
var attributes = [[[aFont fontDescriptor] fontAttributes] copy],
|
||||
symbolicTrait = [[aFont fontDescriptor] symbolicTraits];
|
||||
|
||||
if ((fontTrait & CPBoldFontMask) || (fontTrait & CPUnboldFontMask)) /* FIXME: see convertFont:toHaveTrait: about CPFontWeightTrait */
|
||||
symbolicTrait &= ~CPFontBoldTrait;
|
||||
|
||||
if ((fontTrait & CPItalicFontMask) || (fontTrait & CPUnitalicFontMask))
|
||||
symbolicTrait &= ~CPFontItalicTrait;
|
||||
|
||||
if (fontTrait & CPExpandedFontMask)
|
||||
symbolicTrait &= ~CPFontExpandedTrait;
|
||||
|
||||
if (fontTrait & CPSmallCapsFontMask)
|
||||
symbolicTrait &= ~CPFontSmallCapsTrait;
|
||||
|
||||
if (![attributes containsKey:CPFontTraitsAttribute])
|
||||
[attributes setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
|
||||
forKey:CPFontSymbolicTrait]
|
||||
forKey:CPFontTraitsAttribute];
|
||||
else
|
||||
[[attributes objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTrait]
|
||||
forKey:CPFontSymbolicTrait];
|
||||
|
||||
return [[aFont class] fontWithDescriptor:[CPFontDescriptor fontDescriptorWithFontAttributes:attributes] size:0.0];
|
||||
}
|
||||
|
||||
/*!
|
||||
Convert a font to have specified size. The font is unchanged expect for the specified size.
|
||||
@param aFont The font to convert.
|
||||
@param aSize The new font size.
|
||||
@result The converted font or \c aFont if the conversion failed.
|
||||
*/
|
||||
- (CPFont)convertFont:(CPFont)aFont toSize:(float)aSize
|
||||
{
|
||||
var descriptor = [aFont fontDescriptor];
|
||||
|
||||
return [[aFont class] fontWithDescriptor: descriptor size:aSize]
|
||||
}
|
||||
|
||||
- (void)orderFrontFontPanel:(id)sender
|
||||
{
|
||||
[[self fontPanel:YES] orderFront:sender];
|
||||
}
|
||||
|
||||
- (void)modifyFont:(id)sender
|
||||
{
|
||||
_fontAction = [sender tag];
|
||||
[self sendAction];
|
||||
|
||||
if (_selectedFont)
|
||||
[self setSelectedFont:[self convertFont:_selectedFont] isMultiple:NO];
|
||||
}
|
||||
|
||||
/*!
|
||||
This method causes the receiver to send its action message.
|
||||
@param sender The object that sent the message. (a Font panel)
|
||||
*/
|
||||
- (void)modifyFontViaPanel:(id)sender
|
||||
{
|
||||
_fontAction = CPViaPanelFontAction;
|
||||
if (_selectedFont)
|
||||
[self setSelectedFont:[self convertFont:_selectedFont] isMultiple:NO];
|
||||
|
||||
[self sendAction];
|
||||
}
|
||||
|
||||
/*!
|
||||
Convert a font according to current font changes, provided by the object that initiated the font change.
|
||||
@param aFont The font to convert.
|
||||
@result The converted font or \c aFont if the conversion failed.
|
||||
*/
|
||||
- (CPFont)convertFont:(CPFont)aFont
|
||||
{
|
||||
var newFont = nil;
|
||||
switch (_fontAction)
|
||||
{
|
||||
case CPNoFontChangeAction:
|
||||
newFont = aFont;
|
||||
break;
|
||||
|
||||
case CPViaPanelFontAction:
|
||||
newFont = [[self fontPanel:NO] panelConvertFont:aFont];
|
||||
break;
|
||||
|
||||
case CPAddTraitFontAction:
|
||||
newFont = aFont;
|
||||
if (!_activeChange)
|
||||
break;
|
||||
|
||||
var addTraits = [_activeChange valueForKey:@"addTraits"];
|
||||
|
||||
if (addTraits)
|
||||
newFont = [self convertFont:aFont toHaveTrait:addTraits];
|
||||
break;
|
||||
|
||||
case CPSizeUpFontAction:
|
||||
newFont = [self convertFont:aFont toSize:[aFont size] + 1.0]; /* any limit ? */
|
||||
break;
|
||||
|
||||
case CPSizeDownFontAction:
|
||||
if ([aFont size] > 1)
|
||||
newFont = [self convertFont:aFont toSize:[aFont size] - 1.0];
|
||||
/* else CPBeep() :-p */
|
||||
break;
|
||||
|
||||
default:
|
||||
CPLog.trace(@"-[" + [self className] + " " + _cmd + "] unsupported font action: " + _fontAction + " aFont unchanged");
|
||||
newFont = aFont;
|
||||
break;
|
||||
}
|
||||
|
||||
return newFont;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPFontDetectSpan,
|
||||
|
||||
+6
-5
@@ -70,11 +70,12 @@ CPGradientDrawsAfterEndingLocation = kCGGradientDrawsAfterEndLocation;
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
var cgColors = [],
|
||||
count = [someColors count],
|
||||
colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB;
|
||||
for (var i = 0; i < count; i++)
|
||||
cgColors.push(CGColorCreate(colorSpace, [someColors[i] components]));
|
||||
var colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB,
|
||||
cgColors = [someColors arrayByApplyingBlock:function(color)
|
||||
{
|
||||
return CGColorCreate(colorSpace, [color components])
|
||||
}];
|
||||
|
||||
_gradient = CGGradientCreateWithColors(colorSpace, cgColors, someLocations);
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -43,10 +43,10 @@ function CPDrawTiledRects(
|
||||
if (sides.length != grays.length)
|
||||
[CPException raise:CPInvalidArgumentException reason:@"sides (length: " + sides.length + ") and grays (length: " + grays.length + ") must have the same length."];
|
||||
|
||||
var colors = [];
|
||||
|
||||
for (var i = 0; i < grays.length; ++i)
|
||||
colors.push([CPColor colorWithCalibratedWhite:grays[i] alpha:1.0]);
|
||||
var colors = [grays arrayByApplyingBlock:function(gray)
|
||||
{
|
||||
return [CPColor colorWithCalibratedWhite:gray alpha:1.0];
|
||||
}];
|
||||
|
||||
return CPDrawColorTiledRects(boundsRect, clipRect, sides, colors);
|
||||
}
|
||||
|
||||
+220
-3
@@ -246,7 +246,7 @@ function CPAppKitImage(aFilename, aSize)
|
||||
var canvas = document.createElement("canvas"),
|
||||
ctx = canvas.getContext("2d");
|
||||
|
||||
canvas.width = _image.width,
|
||||
canvas.width = _image.width;
|
||||
canvas.height = _image.height;
|
||||
|
||||
ctx.drawImage(_image, 0, 0);
|
||||
@@ -275,7 +275,7 @@ function CPAppKitImage(aFilename, aSize)
|
||||
*/
|
||||
- (CGSize)size
|
||||
{
|
||||
return _size;
|
||||
return CGSizeMakeCopy(_size);
|
||||
}
|
||||
|
||||
+ (id)imageNamed:(CPString)aName
|
||||
@@ -509,6 +509,212 @@ function CPAppKitImage(aFilename, aSize)
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CSS Theming
|
||||
|
||||
// The code below adds support for CSS theming with 100% compatibility with current theming system.
|
||||
// The idea is to extend CPImage (and CPColor) with CSS components and adapt low level UI components to
|
||||
// support this new kind of CPColor/CPImage. See CPImageView, CPView and _CPImageAndTextView.
|
||||
//
|
||||
// To create a CPImage that uses CSS, simply use the new class method :
|
||||
// + (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
|
||||
// where beforeDictionary & afterDictionary are related to ::before & ::after pseudo-elements.
|
||||
// If you don't need them, just use the simplified class method :
|
||||
// + (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary size:(CGSize)aSize
|
||||
//
|
||||
// Examples :
|
||||
// regularImageNormal = [CPImage imageWithCSSDictionary:@{
|
||||
// @"border-color": A3ColorActiveBorder,
|
||||
// @"border-style": @"solid",
|
||||
// @"border-width": @"1px",
|
||||
// @"border-radius": @"50%",
|
||||
// @"box-sizing": @"border-box",
|
||||
// @"background-color": A3ColorBackgroundWhite,
|
||||
// @"transition-duration": @"0.35s",
|
||||
// @"transition-property": @"all",
|
||||
// @"transition-timing-function": @"ease"
|
||||
// }
|
||||
// size:CGSizeMake(16,16)];
|
||||
//
|
||||
// imageSearch = [CPImage imageWithCSSDictionary:@{
|
||||
// @"background-image": @"url(%%packed.png)",
|
||||
// @"background-position": @"-16px -32px",
|
||||
// @"background-repeat": @"no-repeat",
|
||||
// @"background-size": @"100px 400px"
|
||||
// }
|
||||
// size:CGSizeMake(16,16)];
|
||||
//
|
||||
// Remark : Please note the special URL of the background image used in this example : url(%%packed.png)
|
||||
// During theme loading, "%%" will be replaced by the path to the theme blend resources folder.
|
||||
// Typically, a CSS theme will use some (rare) images all packed together in a single image resource (see packed.png in Aristo3 theme)
|
||||
//
|
||||
// Also, please note that if you don't use one of the CSS components, you can either set it to nil (best solution) or to an empty dictionary, like :
|
||||
// aCssImage = [CPImage imageWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:@{ ... }];
|
||||
//
|
||||
// You can use -(BOOL)isCSSBased to determine how to cope with it in your code.
|
||||
// -(BOOL)hasCSSDictionary, -(BOOL)hasCSSBeforeDictionary and -(BOOL)hasCSSAfterDictionary are convience methods you can use.
|
||||
//
|
||||
// Remark : -(DOMElement)applyCSSImageForView is meant to be used by low level UI widgets (like CPImageView and _CPImageAndTextView) to implement CSS theme support.
|
||||
//
|
||||
// In some circumstances, you may have to clear a CSS image. You can do this easily by replacing your current image with the special dummy empty CSS image :
|
||||
// [CPImage dummyCSSImageOfSize:CGSizeMake(someWidth, someHeight)]
|
||||
|
||||
@implementation CPImage (CSSTheming)
|
||||
{
|
||||
CPDictionary _cssDictionary @accessors(property=cssDictionary);
|
||||
CPDictionary _cssBeforeDictionary @accessors(property=cssBeforeDictionary);
|
||||
CPDictionary _cssAfterDictionary @accessors(property=cssAfterDictionary);
|
||||
}
|
||||
|
||||
+ (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary size:(CGSize)aSize
|
||||
{
|
||||
return [[CPImage alloc] initWithCSSDictionary:aDictionary beforeDictionary:nil afterDictionary:nil size:aSize];
|
||||
}
|
||||
|
||||
+ (CPImage)imageWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
|
||||
{
|
||||
return [[CPImage alloc] initWithCSSDictionary:aDictionary beforeDictionary:beforeDictionary afterDictionary:afterDictionary size:aSize];
|
||||
}
|
||||
|
||||
+ (CPImage)dummyCSSImageOfSize:(CGSize)aSize
|
||||
{
|
||||
// This is used to clear a previous CSS image
|
||||
return [[CPImage alloc] initWithCSSDictionary:@{} beforeDictionary:nil afterDictionary:nil size:aSize];
|
||||
}
|
||||
|
||||
- (id)initWithCSSDictionary:(CPDictionary)aDictionary beforeDictionary:(CPDictionary)beforeDictionary afterDictionary:(CPDictionary)afterDictionary size:(CGSize)aSize
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_size = CGSizeMakeCopy(aSize);
|
||||
_filename = @"CSS image";
|
||||
_loadStatus = CPImageLoadStatusCompleted;
|
||||
_cssDictionary = aDictionary;
|
||||
_cssBeforeDictionary = beforeDictionary;
|
||||
_cssAfterDictionary = afterDictionary;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isCSSBased
|
||||
{
|
||||
return !!(_cssDictionary || _cssBeforeDictionary || _cssAfterDictionary);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSDictionary
|
||||
{
|
||||
return ([_cssDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSBeforeDictionary
|
||||
{
|
||||
return ([_cssBeforeDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSAfterDictionary
|
||||
{
|
||||
return ([_cssAfterDictionary count] > 0);
|
||||
}
|
||||
|
||||
- (DOMElement)applyCSSImageForView:(CPView)aView onDOMElement:(DOMElement)aDOMElement styleNode:(DOMElement)aStyleNode previousState:(CPArrayRef)aPreviousStateRef
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
// First, restore previous CSS styling before applying the new one
|
||||
|
||||
var aPreviousState = @deref(aPreviousStateRef);
|
||||
|
||||
for (var i = 0, count = aPreviousState.length; i < count; i++)
|
||||
aDOMElement.style[aPreviousState[i][0]] = aPreviousState[i][1];
|
||||
|
||||
aPreviousState = @[];
|
||||
|
||||
// Then apply new CSS styling
|
||||
|
||||
[_cssDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
[aPreviousState addObject:@[aKey, aDOMElement.style[aKey]]];
|
||||
aDOMElement.style[aKey] = anObject;
|
||||
}];
|
||||
|
||||
if (_cssBeforeDictionary || _cssAfterDictionary)
|
||||
{
|
||||
// We need to create a unique class name
|
||||
|
||||
var styleClassName = @".CP" + [aView UID],
|
||||
styleContent = @"";
|
||||
|
||||
if (_cssBeforeDictionary)
|
||||
{
|
||||
styleContent += styleClassName + @"::before { ";
|
||||
|
||||
[_cssBeforeDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
styleContent += aKey + ": " + anObject + "; ";
|
||||
}];
|
||||
|
||||
styleContent += "} ";
|
||||
}
|
||||
|
||||
if (_cssAfterDictionary)
|
||||
{
|
||||
styleContent += styleClassName + @"::after { ";
|
||||
|
||||
[_cssAfterDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
styleContent += aKey + ": " + anObject + "; ";
|
||||
}];
|
||||
|
||||
styleContent += "} ";
|
||||
}
|
||||
|
||||
var styleDescription = document.createTextNode(styleContent);
|
||||
|
||||
if (!aStyleNode)
|
||||
{
|
||||
aStyleNode = document.createElement("style");
|
||||
|
||||
aView._DOMElement.insertBefore(aStyleNode, aView._DOMElement.firstChild);
|
||||
|
||||
aStyleNode.appendChild(styleDescription);
|
||||
}
|
||||
else
|
||||
{
|
||||
aStyleNode.replaceChild(styleDescription, aStyleNode.firstChild);
|
||||
}
|
||||
|
||||
[aView setDOMClassName:@"CP"+[aView UID]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// no before/after so remove aStyleNode if existing
|
||||
|
||||
if (aStyleNode)
|
||||
{
|
||||
aView._DOMElement.removeChild(aStyleNode);
|
||||
aStyleNode = nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Return actualised values
|
||||
|
||||
@deref(aPreviousStateRef) = aPreviousState;
|
||||
|
||||
return aStyleNode;
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPImageCSSDictionaryKey = @"CPImageCSSDictionaryKey",
|
||||
CPImageCSSBeforeDictionaryKey = @"CPImageCSSBeforeDictionaryKey",
|
||||
CPImageCSSAfterDictionaryKey = @"CPImageCSSAfterDictionaryKey";
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation CPImage (CPCoding)
|
||||
|
||||
/*!
|
||||
@@ -518,7 +724,10 @@ function CPAppKitImage(aFilename, aSize)
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]];
|
||||
if ([aCoder containsValueForKey:CPImageCSSDictionaryKey])
|
||||
return [self initWithCSSDictionary:[aCoder decodeObjectForKey:CPImageCSSDictionaryKey] beforeDictionary:[aCoder decodeObjectForKey:CPImageCSSBeforeDictionaryKey] afterDictionary:[aCoder decodeObjectForKey:CPImageCSSAfterDictionaryKey] size:[aCoder decodeSizeForKey:@"CPSize"]];
|
||||
else
|
||||
return [self initWithContentsOfFile:[aCoder decodeObjectForKey:@"CPFilename"] size:[aCoder decodeSizeForKey:@"CPSize"]];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -529,6 +738,14 @@ function CPAppKitImage(aFilename, aSize)
|
||||
{
|
||||
[aCoder encodeObject:_filename forKey:@"CPFilename"];
|
||||
[aCoder encodeSize:_size forKey:@"CPSize"];
|
||||
|
||||
// CSS Styling
|
||||
if ([self isCSSBased])
|
||||
{
|
||||
[aCoder encodeObject:_cssDictionary forKey:CPImageCSSDictionaryKey];
|
||||
[aCoder encodeObject:_cssBeforeDictionary forKey:CPImageCSSBeforeDictionaryKey];
|
||||
[aCoder encodeObject:_cssAfterDictionary forKey:CPImageCSSAfterDictionaryKey];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+33
-6
@@ -98,10 +98,32 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
||||
- (void)_createDOMImageElement
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMImageElement)
|
||||
return;
|
||||
var image = [self objectValue],
|
||||
isCSSBasedImage = [image isCSSBased],
|
||||
isIMGImageElement = _DOMImageElement && (_DOMImageElement.nodeName == "IMG");
|
||||
|
||||
_DOMImageElement = document.createElement("img");
|
||||
// First, check if we need to destroy a current DOM image element. This is the case if :
|
||||
// - we have one but not the right one (that is a DIV but needing an IMG, and vice versa)
|
||||
|
||||
if (_DOMImageElement)
|
||||
{
|
||||
if ((isIMGImageElement && isCSSBasedImage) || (!isIMGImageElement && !isCSSBasedImage))
|
||||
{
|
||||
// OK, destroy it
|
||||
|
||||
_DOMElement.removeChild(_DOMImageElement);
|
||||
|
||||
_DOMImageElement = nil;
|
||||
|
||||
// CSS styling cleaning
|
||||
_cssStylePreviousState = @[];
|
||||
_cssStyleNode = nil;
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
_DOMImageElement = document.createElement(isCSSBasedImage ? "div" : "img");
|
||||
_DOMImageElement.style.position = "absolute";
|
||||
_DOMImageElement.style.left = "0px";
|
||||
_DOMImageElement.style.top = "0px";
|
||||
@@ -150,10 +172,15 @@ var CPImageViewEmptyPlaceholderImage = nil;
|
||||
var newImage = [self objectValue];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (!_DOMImageElement)
|
||||
[self _createDOMImageElement];
|
||||
[self _createDOMImageElement];
|
||||
|
||||
_DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
|
||||
if ([newImage isCSSBased])
|
||||
_cssStyleNode = [newImage applyCSSImageForView:self
|
||||
onDOMElement:_DOMImageElement
|
||||
styleNode:_cssStyleNode
|
||||
previousState:@ref(_cssStylePreviousState)];
|
||||
else
|
||||
_DOMImageElement.src = newImage ? [newImage filename] : [CPImageViewEmptyPlaceholderImage filename];
|
||||
#endif
|
||||
|
||||
var size = [newImage size];
|
||||
|
||||
@@ -182,7 +182,7 @@ var CPBindingOperationAnd = 0,
|
||||
allBindings = [bindingsForObject allKeys],
|
||||
count = [allBindings count];
|
||||
|
||||
while(count--)
|
||||
while (count--)
|
||||
{
|
||||
if ([[anObject class] isBindingExclusive:allBindings[count]])
|
||||
return NO;
|
||||
@@ -857,7 +857,7 @@ var CPBindingOperationAnd = 0,
|
||||
- (id)initWithName:(CPString)aName
|
||||
{
|
||||
if (self = [super init])
|
||||
_name = aName
|
||||
_name = aName;
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -918,6 +918,7 @@ CPTextColorBinding = @"textColor";
|
||||
CPTitleBinding = @"title";
|
||||
CPToolTipBinding = @"toolTip";
|
||||
CPValueBinding = @"value";
|
||||
CPAttributedStringBinding = @"attributedString";
|
||||
CPValueURLBinding = @"valueURL";
|
||||
CPValuePathBinding = @"valuePath";
|
||||
CPDataBinding = @"data";
|
||||
|
||||
@@ -292,7 +292,7 @@ CPRatingLevelIndicatorStyle = 3;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setWarningValue:(double)warningValue;
|
||||
- (void)setWarningValue:(double)warningValue
|
||||
{
|
||||
if (_warningValue === warningValue)
|
||||
return;
|
||||
@@ -301,7 +301,7 @@ CPRatingLevelIndicatorStyle = 3;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setCriticalValue:(double)criticalValue;
|
||||
- (void)setCriticalValue:(double)criticalValue
|
||||
{
|
||||
if (_criticalValue === criticalValue)
|
||||
return;
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
|
||||
@implementation _CPMenuBarWindow : CPPanel
|
||||
{
|
||||
CPMenu _menu;
|
||||
CPView _highlightView;
|
||||
CPArray _menuItemViews;
|
||||
|
||||
|
||||
@@ -505,7 +505,7 @@ CPOffState
|
||||
if (_submenu)
|
||||
{
|
||||
[_submenu setSupermenu:_menu];
|
||||
[_submenu setTitle:[self title]]
|
||||
[_submenu setTitle:[self title]];
|
||||
|
||||
[self setTarget:_menu];
|
||||
[self setAction:@selector(submenuAction:)];
|
||||
@@ -843,7 +843,7 @@ CPControlKeyMask
|
||||
[item setTarget:_target];
|
||||
[item setAction:_action];
|
||||
[item setEnabled:_isEnabled];
|
||||
[item setHidden:_isHidden]
|
||||
[item setHidden:_isHidden];
|
||||
[item setTag:_tag];
|
||||
[item setState:_state];
|
||||
[item setImage:_image];
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
@"menu-item-selection-color": [CPNull null],
|
||||
@"menu-item-text-shadow-color": [CPNull null],
|
||||
@"menu-item-text-color": [CPNull null],
|
||||
@"menu-item-disabled-text-color": [CPColor lightGrayColor],
|
||||
@"menu-item-default-off-state-image": [CPNull null],
|
||||
@"menu-item-default-off-state-highlighted-image": [CPNull null],
|
||||
@"menu-item-default-on-state-image": [CPNull null],
|
||||
@@ -124,7 +125,7 @@
|
||||
- (CPColor)textColor
|
||||
{
|
||||
if (![_menuItem isEnabled])
|
||||
return [CPColor lightGrayColor];
|
||||
return [self valueForThemeAttribute:@"menu-item-disabled-text-color"];
|
||||
|
||||
if (_highlighted)
|
||||
return [CPColor whiteColor];
|
||||
|
||||
+106
-4
@@ -27,6 +27,9 @@
|
||||
@import "CPController.j"
|
||||
@import "CPKeyValueBinding.j"
|
||||
|
||||
@class _CPManagedProxy
|
||||
@class CPPredicate;
|
||||
|
||||
/*!
|
||||
@class
|
||||
|
||||
@@ -47,6 +50,9 @@
|
||||
|
||||
BOOL _isEditable;
|
||||
BOOL _automaticallyPreparesContent;
|
||||
BOOL _usesLazyFetching @accessors(getter=usesLazyFetching, setter=setUsesLazyFetching:);
|
||||
BOOL _isUsingManagedProxy;
|
||||
_CPManagedProxy _managedProxy;
|
||||
|
||||
CPCountedSet _observedKeys;
|
||||
}
|
||||
@@ -179,6 +185,52 @@
|
||||
return _automaticallyPreparesContent;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the entity name the controller handles.
|
||||
|
||||
@param CPString newEntityName - The new entity name.
|
||||
*/
|
||||
- (void)setEntityName:(CPString)newEntityName
|
||||
{
|
||||
if (!_managedProxy)
|
||||
{
|
||||
_managedProxy = [[_CPManagedProxy alloc] init];
|
||||
_isUsingManagedProxy = YES;
|
||||
}
|
||||
|
||||
[_managedProxy setEntityName:newEntityName];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the entity name.
|
||||
|
||||
@return CPString - The name of the entity.
|
||||
*/
|
||||
- (CPString)entityName
|
||||
{
|
||||
return [_managedProxy entityName];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the predicate used to fetch content.
|
||||
|
||||
@param CPPredicate newPredicate - The fetch predicate.
|
||||
*/
|
||||
- (void)setFetchPredicate:(CPPredicate)newPredicate
|
||||
{
|
||||
[_managedProxy setFetchPredicate:newPredicate];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the fetch predicate.
|
||||
|
||||
@return CPPredicate - The predicate used to fetch content.
|
||||
*/
|
||||
- (CPPredicate)fetchPredicate
|
||||
{
|
||||
return [_managedProxy fetchPredicate];
|
||||
}
|
||||
|
||||
/*!
|
||||
Overridden by a subclass that require control over the creation of new objects.
|
||||
*/
|
||||
@@ -189,6 +241,7 @@
|
||||
|
||||
/*!
|
||||
Sets the object class when creating new objects.
|
||||
|
||||
@param Class - the class of new objects that will be created.
|
||||
*/
|
||||
- (void)setObjectClass:(Class)aClass
|
||||
@@ -363,7 +416,10 @@
|
||||
var CPObjectControllerContentKey = @"CPObjectControllerContentKey",
|
||||
CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
|
||||
CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
|
||||
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey";
|
||||
CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey",
|
||||
CPObjectControllerUsesLazyFetchingKey = @"CPObjectControllerUsesLazyFetchingKey",
|
||||
CPObjectControllerIsUsingManagedProxyKey = @"CPObjectControllerIsUsingManagedProxyKey",
|
||||
CPObjectControllerManagedProxyKey = @"CPObjectControllerManagedProxyKey";
|
||||
|
||||
@implementation CPObjectController (CPCoding)
|
||||
|
||||
@@ -374,12 +430,18 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
if (self)
|
||||
{
|
||||
var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey],
|
||||
objectClass = CPClassFromString(objectClassName);
|
||||
objectClass = CPClassFromString(objectClassName),
|
||||
content = [aCoder decodeObjectForKey:CPObjectControllerContentKey];
|
||||
|
||||
[self setObjectClass:objectClass || [CPMutableDictionary class]];
|
||||
[self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]];
|
||||
[self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey]];
|
||||
[self setContent:[aCoder decodeObjectForKey:CPObjectControllerContentKey]];
|
||||
[self setUsesLazyFetching:[aCoder decodeBoolForKey:CPObjectControllerUsesLazyFetchingKey]];
|
||||
_isUsingManagedProxy = [aCoder decodeBoolForKey:CPObjectControllerIsUsingManagedProxyKey];
|
||||
_managedProxy = [aCoder decodeObjectForKey:CPObjectControllerManagedProxyKey];
|
||||
|
||||
if (content != nil)
|
||||
[self setContent:content];
|
||||
|
||||
_observedKeys = [[CPCountedSet alloc] init];
|
||||
}
|
||||
@@ -398,6 +460,11 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
|
||||
[aCoder encodeBool:[self isEditable] forKey:CPObjectControllerIsEditableKey];
|
||||
[aCoder encodeBool:[self automaticallyPreparesContent] forKey:CPObjectControllerAutomaticallyPreparesContentKey];
|
||||
[aCoder encodeBool:[self usesLazyFetching] forKey:CPObjectControllerUsesLazyFetchingKey];
|
||||
[aCoder encodeBool:_isUsingManagedProxy forKey:CPObjectControllerIsUsingManagedProxyKey];
|
||||
|
||||
if (_managedProxy)
|
||||
[aCoder encodeObject:_managedProxy forKey:CPObjectControllerManagedProxyKey];
|
||||
}
|
||||
|
||||
- (void)awakeFromCib
|
||||
@@ -749,7 +816,7 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
|
||||
- (void)setValue:(id)theValue forKey:(CPString)theKeyPath
|
||||
{
|
||||
[self setValue:theKeyPath forKeyPath:theKeyPath];
|
||||
[self setValue:theValue forKeyPath:theKeyPath];
|
||||
}
|
||||
|
||||
- (unsigned)count
|
||||
@@ -825,3 +892,38 @@ var CPObjectControllerContentKey = @"CPObjectControllerCo
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation _CPManagedProxy : CPObject
|
||||
{
|
||||
CPString _entityName @accessors(getter=entityName, setter=setEntityName:);
|
||||
CPPredicate _fetchPredicate @accessors(getter=fetchPredicate, setter=setFetchPredicate:);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPManagedProxyEntityNameKey = @"CPManagedProxyEntityNameKey",
|
||||
CPManagedProxyFetchPredicateKey = @"CPManagedProxyFetchPredicateKey";
|
||||
|
||||
@implementation _CPManagedProxy (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setEntityName:[aCoder decodeObjectForKey:CPManagedProxyEntityNameKey]];
|
||||
[self setFetchPredicate:[aCoder decodeObjectForKey:CPManagedProxyFetchPredicateKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:[self entityName] forKey:CPManagedProxyEntityNameKey];
|
||||
[aCoder encodeObject:[self fetchPredicate] forKey:CPManagedProxyFetchPredicateKey];
|
||||
}
|
||||
|
||||
@end
|
||||
+60
-99
@@ -223,6 +223,22 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_initSubclass
|
||||
{
|
||||
_BlockDeselectView = function(view, row, column)
|
||||
{
|
||||
[view unsetThemeState:CPThemeStateSelectedDataView];
|
||||
[_disclosureControlsForRows[row] unsetThemeState:CPThemeStateSelected];
|
||||
};
|
||||
|
||||
_BlockSelectView = function(view, row, column)
|
||||
{
|
||||
[view setThemeState:CPThemeStateSelectedDataView];
|
||||
[_disclosureControlsForRows[row] setThemeState:CPThemeStateSelected];
|
||||
};
|
||||
}
|
||||
|
||||
/*!
|
||||
In addition to standard delegation, the outline view also supports data
|
||||
source delegation. This method sets the data source object. Just like the
|
||||
@@ -679,7 +695,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
|
||||
[self _cleanPendingItem];
|
||||
|
||||
[super reloadData];
|
||||
[super _reloadDataViews];
|
||||
}
|
||||
|
||||
- (void)_reloadItem:(id)anItem
|
||||
@@ -1308,7 +1324,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
/*!
|
||||
Reloads all the data of the outlineview.
|
||||
*/
|
||||
- (void)reloadData
|
||||
- (void)_reloadDataViews
|
||||
{
|
||||
[self reloadItem:nil reloadChildren:YES];
|
||||
}
|
||||
@@ -1340,6 +1356,25 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
else
|
||||
[super removeTableColumn:aTableColumn];
|
||||
}
|
||||
|
||||
- (void)_addDraggedDataView:(CPView)aDataView toView:(CPView)aSuperview forColumn:(CPInteger)column row:(CPInteger)row offset:(CGPoint)offset
|
||||
{
|
||||
var control;
|
||||
|
||||
[super _addDraggedDataView:aDataView toView:aSuperview forColumn:column row:row offset:offset];
|
||||
|
||||
if (_tableColumns[column] === _outlineTableColumn && (control = _disclosureControlsForRows[row]))
|
||||
{
|
||||
var controlFrame = [self frameOfOutlineDisclosureControlAtRow:row];
|
||||
|
||||
controlFrame.origin.x -= offset.x;
|
||||
controlFrame.origin.y -= offset.y;
|
||||
|
||||
[control setFrame:controlFrame];
|
||||
[aSuperview addSubview:control];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
We override this because we need a special behavior for the outline
|
||||
@@ -1355,67 +1390,6 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
return [super frameOfDataViewAtColumn:aColumn row:aRow];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
We need to offset the dataview and add the disclosure triangle.
|
||||
*/
|
||||
- (CPView)_dragViewForColumn:(CPInteger)theColumnIndex event:(CPEvent)theDragEvent offset:(CGPoint)theDragViewOffset
|
||||
{
|
||||
var dragView = [[_CPColumnDragView alloc] initWithLineColor:[self gridColor]],
|
||||
tableColumn = [[self tableColumns] objectAtIndex:theColumnIndex],
|
||||
defaultRowHeight = [self valueForThemeAttribute:@"default-row-height"],
|
||||
bounds = CGRectMake(0.0, 0.0, [tableColumn width], CGRectGetHeight([self exposedRect]) + defaultRowHeight),
|
||||
columnRect = [self rectOfColumn:theColumnIndex],
|
||||
headerView = [tableColumn headerView],
|
||||
row = [_exposedRows firstIndex];
|
||||
|
||||
while (row !== CPNotFound)
|
||||
{
|
||||
var dataView = [self _newDataViewForRow:row tableColumn:tableColumn],
|
||||
dataViewFrame = [self frameOfDataViewAtColumn:theColumnIndex row:row];
|
||||
|
||||
// Only one column is ever dragged so we just place the view at
|
||||
dataViewFrame.origin.x = 0.0;
|
||||
|
||||
// Offset by table header height - scroll position
|
||||
dataViewFrame.origin.y = (CGRectGetMinY(dataViewFrame) - CGRectGetMinY([self exposedRect])) + defaultRowHeight;
|
||||
[dataView setFrame:dataViewFrame];
|
||||
|
||||
[dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]];
|
||||
|
||||
|
||||
if (tableColumn === _outlineTableColumn)
|
||||
{
|
||||
// first inset the dragview
|
||||
var indentationWidth = ([self levelForRow:row] + 1) * [self indentationPerLevel];
|
||||
|
||||
dataViewFrame.origin.x += indentationWidth;
|
||||
dataViewFrame.size.width -= indentationWidth;
|
||||
|
||||
[dataView setFrame:dataViewFrame];
|
||||
}
|
||||
|
||||
[dragView addSubview:dataView];
|
||||
|
||||
row = [_exposedRows indexGreaterThanIndex:row];
|
||||
}
|
||||
|
||||
// Add the column header view
|
||||
var headerFrame = [headerView frame];
|
||||
headerFrame.origin = CGPointMakeZero();
|
||||
|
||||
var columnHeaderView = [[_CPTableColumnHeaderView alloc] initWithFrame:headerFrame];
|
||||
[columnHeaderView setStringValue:[headerView stringValue]];
|
||||
[columnHeaderView setThemeState:[headerView themeState]];
|
||||
[dragView addSubview:columnHeaderView];
|
||||
|
||||
[dragView setBackgroundColor:[CPColor whiteColor]];
|
||||
[dragView setAlphaValue:0.7];
|
||||
[dragView setFrame:bounds];
|
||||
|
||||
return dragView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Retargets the drop item for the outlineview.
|
||||
|
||||
@@ -1443,7 +1417,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
if (_dropItem)
|
||||
{
|
||||
[_dropOperationFeedbackView blink];
|
||||
[CPTimer scheduledTimerWithTimeInterval:.3 callback:objj_msgSend(self, "expandItem:", _dropItem) repeats:NO];
|
||||
[CPTimer scheduledTimerWithTimeInterval:.3 callback:[self expandItem:_dropItem] repeats:NO];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1539,43 +1513,22 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
@ignore
|
||||
We need to move the disclosure control too.
|
||||
*/
|
||||
- (void)_layoutDataViewsInRows:(CPIndexSet)rows columns:(CPIndexSet)columns
|
||||
- (void)_layoutViewsForRowIndexes:(CPIndexSet)rowIndexes columnIndexes:(CPIndexSet)columnIndexes
|
||||
{
|
||||
var rowArray = [],
|
||||
columnArray = [];
|
||||
|
||||
[rows getIndexes:rowArray maxCount:-1 inIndexRange:nil];
|
||||
[columns getIndexes:columnArray maxCount:-1 inIndexRange:nil];
|
||||
|
||||
var columnIndex = 0,
|
||||
columnsCount = columnArray.length;
|
||||
|
||||
for (; columnIndex < columnsCount; ++columnIndex)
|
||||
[self _enumerateViewsInRows:rowIndexes columns:columnIndexes usingBlock:function(view, row, column, stop)
|
||||
{
|
||||
var column = columnArray[columnIndex],
|
||||
tableColumn = _tableColumns[column],
|
||||
tableColumnUID = [tableColumn UID],
|
||||
dataViewsForTableColumn = _dataViewsForTableColumns[tableColumnUID],
|
||||
rowIndex = 0,
|
||||
rowsCount = rowArray.length;
|
||||
var control;
|
||||
|
||||
for (; rowIndex < rowsCount; ++rowIndex)
|
||||
[view setFrame:[self frameOfDataViewAtColumn:column row:row]];
|
||||
|
||||
if (_tableColumns[column] === _outlineTableColumn && (control = _disclosureControlsForRows[row]))
|
||||
{
|
||||
var row = rowArray[rowIndex],
|
||||
dataView = dataViewsForTableColumn[row],
|
||||
dataViewFrame = [self frameOfDataViewAtColumn:column row:row];
|
||||
|
||||
[dataView setFrame:dataViewFrame];
|
||||
|
||||
if (tableColumn === _outlineTableColumn)
|
||||
{
|
||||
var control = _disclosureControlsForRows[row],
|
||||
frame = [self frameOfOutlineDisclosureControlAtRow:row];
|
||||
|
||||
[control setFrame:frame];
|
||||
}
|
||||
var frame = [self frameOfOutlineDisclosureControlAtRow:row];
|
||||
[control setFrame:frame];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1587,7 +1540,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
|
||||
var outlineColumn = [[self tableColumns] indexOfObjectIdenticalTo:[self outlineTableColumn]];
|
||||
|
||||
if (![columns containsIndex:outlineColumn] || [self outlineTableColumn] === _draggedColumn)
|
||||
if (![columns containsIndex:outlineColumn] || outlineColumn === _draggedColumnIndex)
|
||||
return;
|
||||
|
||||
var rowArray = [];
|
||||
@@ -1890,6 +1843,14 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
return _implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_dataViewForTableColumn_item_;
|
||||
}
|
||||
|
||||
- (id)_hitTest:(CPView)aView
|
||||
{
|
||||
if ([aView isKindOfClass:[CPDisclosureButton class]])
|
||||
return aView;
|
||||
|
||||
return [super _hitTest:aView];
|
||||
}
|
||||
|
||||
- (BOOL)_delegateRespondsToShouldExpandItem
|
||||
{
|
||||
return _implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_shouldExpandItem_;
|
||||
@@ -1962,7 +1923,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
|
||||
while (index !== CPNotFound)
|
||||
{
|
||||
[items addObject:[_outlineView itemAtRow:index]]
|
||||
[items addObject:[_outlineView itemAtRow:index]];
|
||||
index = [theIndexes indexGreaterThanIndex:index];
|
||||
}
|
||||
|
||||
@@ -1996,7 +1957,7 @@ var CPOutlineViewCoalesceSelectionNotificationStateOff = 0,
|
||||
- (void)_parentItemForDropOperation:(CPTableViewDropOperation)theDropOperation row:(CPInteger)theRow offset:(CGPoint)theOffset
|
||||
{
|
||||
if (theDropOperation === CPTableViewDropAbove)
|
||||
return [_outlineView _parentItemForUpperRow:theRow - 1 andLowerRow:theRow atMouseOffset:theOffset]
|
||||
return [_outlineView _parentItemForUpperRow:theRow - 1 andLowerRow:theRow atMouseOffset:theOffset];
|
||||
|
||||
return [_outlineView itemAtRow:theRow];
|
||||
}
|
||||
|
||||
+3
-2
@@ -22,10 +22,13 @@
|
||||
|
||||
@import "CPWindow.j"
|
||||
|
||||
@global CPApp
|
||||
|
||||
CPOKButton = 1;
|
||||
CPCancelButton = 0;
|
||||
|
||||
CPDocModalWindowMask = 1 << 6;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPPanel
|
||||
@@ -51,8 +54,6 @@ CPCancelButton = 0;
|
||||
@global
|
||||
@class CPWindow
|
||||
*/
|
||||
CPDocModalWindowMask = 1 << 6;
|
||||
|
||||
@implementation CPPanel : CPWindow
|
||||
{
|
||||
BOOL _becomesKeyOnlyIfNeeded;
|
||||
|
||||
+3
-69
@@ -44,6 +44,8 @@ CPStringPboardType = @"CPStringPboardType";
|
||||
CPURLPboardType = @"CPURLPboardType";
|
||||
CPImagesPboardType = @"CPImagesPboardType";
|
||||
CPVideosPboardType = @"CPVideosPboardType";
|
||||
CPRTFPboardType = @"CPRTFPboardType";
|
||||
_CPSmartPboardType = @"_CPSmartPboardType";
|
||||
|
||||
UTF8PboardType = @"public.utf8-plain-text";
|
||||
|
||||
@@ -51,8 +53,7 @@ UTF8PboardType = @"public.utf8-plain-text";
|
||||
CPImagePboardType = @"CPImagePboardType";
|
||||
|
||||
|
||||
var CPPasteboards = nil,
|
||||
supportsNativePasteboard = NO;
|
||||
var CPPasteboards = nil;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -68,8 +69,6 @@ var CPPasteboards = nil,
|
||||
|
||||
unsigned _changeCount;
|
||||
CPString _stateUID;
|
||||
|
||||
CPWebScriptObject _nativePasteboard;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -83,9 +82,6 @@ var CPPasteboards = nil,
|
||||
[self setVersion:1.0];
|
||||
|
||||
CPPasteboards = @{};
|
||||
|
||||
if (typeof window.cpPasteboardWithName !== "undefined")
|
||||
supportsNativePasteboard = YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -128,12 +124,6 @@ var CPPasteboards = nil,
|
||||
_provided = @{};
|
||||
|
||||
_changeCount = 0;
|
||||
|
||||
if (supportsNativePasteboard)
|
||||
{
|
||||
_nativePasteboard = window.cpPasteboardWithName(aName);
|
||||
[self _synchronizePasteboard];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -163,15 +153,6 @@ var CPPasteboards = nil,
|
||||
[_owners setObject:anOwner forKey:type];
|
||||
}
|
||||
|
||||
if (_nativePasteboard)
|
||||
{
|
||||
var nativeTypes = [types copy];
|
||||
if ([types containsObject:CPStringPboardType])
|
||||
nativeTypes.push(UTF8PboardType);
|
||||
|
||||
_nativePasteboard.addTypes_(nativeTypes);
|
||||
}
|
||||
|
||||
return ++_changeCount;
|
||||
}
|
||||
|
||||
@@ -182,12 +163,6 @@ var CPPasteboards = nil,
|
||||
@return the pasteboard's change count
|
||||
*/
|
||||
- (unsigned)declareTypes:(CPArray)types owner:(id)anOwner
|
||||
{
|
||||
[self _declareTypes:types owner:anOwner updateNativePasteboard:YES];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (unsigned)_declareTypes:(CPArray)types owner:(id)anOwner updateNativePasteboard:(BOOL)shouldUpdate
|
||||
{
|
||||
[_types setArray:types];
|
||||
|
||||
@@ -201,16 +176,6 @@ var CPPasteboards = nil,
|
||||
[_owners setObject:anOwner forKey:_types[count]];
|
||||
}
|
||||
|
||||
if (_nativePasteboard && shouldUpdate)
|
||||
{
|
||||
var nativeTypes = [types copy];
|
||||
if ([types containsObject:CPStringPboardType])
|
||||
nativeTypes.push(UTF8PboardType);
|
||||
|
||||
_nativePasteboard.declareTypes_(nativeTypes);
|
||||
_changeCount = _nativePasteboard.changeCount();
|
||||
}
|
||||
|
||||
return ++_changeCount;
|
||||
}
|
||||
|
||||
@@ -273,7 +238,6 @@ var CPPasteboards = nil,
|
||||
*/
|
||||
- (CPArray)types
|
||||
{
|
||||
[self _synchronizePasteboard];
|
||||
return _types;
|
||||
}
|
||||
|
||||
@@ -312,36 +276,6 @@ var CPPasteboards = nil,
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)_synchronizePasteboard
|
||||
{
|
||||
if (_nativePasteboard && _nativePasteboard.changeCount() > _changeCount)
|
||||
{
|
||||
var nativeTypes = [_nativePasteboard.types() copy];
|
||||
if ([nativeTypes containsObject:UTF8PboardType])
|
||||
nativeTypes.push(CPStringPboardType);
|
||||
|
||||
[self _declareTypes:nativeTypes owner:self updateNativePasteboard:NO];
|
||||
|
||||
_changeCount = _nativePasteboard.changeCount();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @ignore
|
||||
method provided for integration with native pasteboard
|
||||
*/
|
||||
- (void)pasteboard:(CPPasteboard)aPasteboard provideDataForType:(CPString)aType
|
||||
{
|
||||
if (aType === CPStringPboardType)
|
||||
{
|
||||
var string = _nativePasteboard.stringForType_(UTF8PboardType);
|
||||
|
||||
[self setString:string forType:CPStringPboardType];
|
||||
[self setString:string forType:UTF8PboardType];
|
||||
}
|
||||
else
|
||||
[self setString:_nativePasteboard.stringForType_(aType) forType:aType];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the property list for the specified data type
|
||||
@param aType the requested data type
|
||||
|
||||
+27
-12
@@ -361,15 +361,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
*/
|
||||
- (CPArray)itemTitles
|
||||
{
|
||||
var titles = [],
|
||||
items = [self itemArray],
|
||||
index = 0,
|
||||
count = [items count];
|
||||
|
||||
for (; index < count; ++index)
|
||||
titles.push([items[index] title]);
|
||||
|
||||
return titles;
|
||||
return [[self itemArray] arrayByApplyingBlock:function(item)
|
||||
{
|
||||
return [item title];
|
||||
}];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -852,7 +847,17 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
{
|
||||
var count = [aValue count],
|
||||
options = [_info objectForKey:CPOptionsKey],
|
||||
offset = [self _getInsertNullOffset];
|
||||
offset = [self _getInsertNullOffset],
|
||||
selectedBindingInfo = [_source infoForBinding:CPSelectedObjectBinding],
|
||||
selectedObject = nil;
|
||||
|
||||
if (selectedBindingInfo)
|
||||
{
|
||||
var destination = [selectedBindingInfo objectForKey:CPObservedObjectKey],
|
||||
keyPath = [selectedBindingInfo objectForKey:CPObservedKeyPathKey];
|
||||
|
||||
selectedObject = [destination valueForKeyPath:keyPath];
|
||||
}
|
||||
|
||||
if (count + offset != [_source numberOfItems])
|
||||
{
|
||||
@@ -863,9 +868,19 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var item = [[CPMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:nil];
|
||||
[self _setValue:[aValue objectAtIndex:i] forItem:item withOptions:options];
|
||||
var item = [[CPMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:nil],
|
||||
itemValue = [aValue objectAtIndex:i];
|
||||
|
||||
[self _setValue:itemValue forItem:item withOptions:options];
|
||||
[_source addItem:item];
|
||||
|
||||
// Select this item if it is the one selected by the selected object binding
|
||||
// This is needed if the selected object binding is set before the items
|
||||
// from the content binding
|
||||
if (itemValue === selectedObject)
|
||||
{
|
||||
[_source setSelectedIndex:[_source numberOfItems] - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -86,6 +86,9 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
@"spinning-mini-gif": [CPNull null],
|
||||
@"spinning-small-gif": [CPNull null],
|
||||
@"spinning-regular-gif": [CPNull null],
|
||||
@"circular-border-color": [CPNull null],
|
||||
@"circular-border-size": 1,
|
||||
@"circular-color": [CPNull null]
|
||||
};
|
||||
}
|
||||
|
||||
@@ -368,6 +371,7 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
- (void)drawBar
|
||||
{
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||
@@ -413,6 +417,9 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
{
|
||||
if (_style == CPProgressIndicatorSpinningStyle)
|
||||
{
|
||||
if (!_indeterminate)
|
||||
return;
|
||||
|
||||
// This will cause the bar view to go away due to having a nil rect when _style == CPProgressIndicatorSpinningStyle.
|
||||
[self layoutEphemeralSubviewNamed:"bar-view"
|
||||
positioned:CPWindowBelow
|
||||
@@ -438,6 +445,54 @@ var CPProgressIndicatorSpinningStyleColors = [];
|
||||
[self setBackgroundColor:nil];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)aRect
|
||||
{
|
||||
if (_style == CPProgressIndicatorSpinningStyle && !_indeterminate)
|
||||
{
|
||||
var context = [[CPGraphicsContext currentContext] graphicsPort],
|
||||
rect = CGRectMakeCopy(aRect),
|
||||
borderSize = [self currentValueForThemeAttribute:@"circular-border-size"];
|
||||
|
||||
rect.origin.x += borderSize;
|
||||
rect.origin.y += borderSize;
|
||||
rect.size.width = rect.size.width - borderSize * 2;
|
||||
rect.size.height = rect.size.height - borderSize * 2;
|
||||
|
||||
if ([self doubleValue] > [self minValue] && [self doubleValue] < [self maxValue])
|
||||
{
|
||||
var midX = CGRectGetMidX(rect),
|
||||
midY = CGRectGetMidY(rect),
|
||||
endAngle = Math.PI * 2 * (([self doubleValue] - [self minValue]) / ([self maxValue] - [self minValue])) - Math.PI / 2,
|
||||
radius = MIN(rect.size.width / 2, rect.size.height / 2);
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetLineWidth(context, borderSize);
|
||||
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"]);
|
||||
CGContextMoveToPoint(context, midX, midY);
|
||||
CGContextAddArc(context, midX, midY, radius, 3 * Math.PI / 2, endAngle, YES);
|
||||
CGContextAddLineToPoint(context, midX, midY);
|
||||
CGContextClosePath(context);
|
||||
CGContextFillPath(context);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
else if ([self doubleValue] == [self maxValue])
|
||||
{
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetFillColor(context, [self currentValueForThemeAttribute:@"circular-color"]);
|
||||
CGContextAddEllipseInRect(context, rect);
|
||||
CGContextClosePath(context);
|
||||
CGContextFillPath(context);
|
||||
}
|
||||
|
||||
CGContextBeginPath(context);
|
||||
CGContextSetStrokeColor(context , [self currentValueForThemeAttribute:@"circular-border-color"]);
|
||||
CGContextSetLineWidth(context, borderSize);
|
||||
CGContextAddEllipseInRect(context, rect);
|
||||
CGContextClosePath(context);
|
||||
CGContextStrokePath(context);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
+4
-6
@@ -29,6 +29,7 @@
|
||||
|
||||
@global CPApp
|
||||
|
||||
CPRadioImageOffset = 4.0;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@@ -67,9 +68,6 @@
|
||||
option.
|
||||
|
||||
*/
|
||||
|
||||
CPRadioImageOffset = 4.0;
|
||||
|
||||
@implementation CPRadio : CPButton
|
||||
{
|
||||
CPRadioGroup _radioGroup;
|
||||
@@ -269,9 +267,9 @@ var CPRadioRadioGroupKey = @"CPRadioRadioGroupKey";
|
||||
- (BOOL)selectRadioWithTag:(int)tag
|
||||
{
|
||||
var index = [_radios indexOfObjectPassingTest:function(radio)
|
||||
{
|
||||
return [radio tag] === tag;
|
||||
}];
|
||||
{
|
||||
return [radio tag] === tag;
|
||||
}];
|
||||
|
||||
if (index !== CPNotFound)
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
@import <Foundation/CPObjJRuntime.j>
|
||||
|
||||
@import "CPEvent.j"
|
||||
@import "CPCursor.j"
|
||||
|
||||
@class CPKeyBinding
|
||||
@class CPMenu
|
||||
@@ -200,6 +201,18 @@ CPDeleteForwardKeyCode = 46;
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
}
|
||||
|
||||
/*!
|
||||
Notifies the receiver that the mouse entered the receiver's area and that it can adapt the cursor.
|
||||
@param anEvent contains information about the exit
|
||||
*/
|
||||
- (void)cursorUpdate:(CPEvent)anEvent
|
||||
{
|
||||
if (_nextResponder)
|
||||
[_nextResponder performSelector:_cmd withObject:anEvent];
|
||||
else
|
||||
[[CPCursor arrowCursor] set];
|
||||
}
|
||||
|
||||
/*!
|
||||
Notifies the receiver that the mouse scroll wheel has moved.
|
||||
@param anEvent information about the scroll
|
||||
|
||||
@@ -471,9 +471,11 @@ CPTransformableAttributeType = 1800;
|
||||
|
||||
var value;
|
||||
if (attributeType >= CPInteger16AttributeType && attributeType <= CPFloatAttributeType)
|
||||
value = [aView intValue];
|
||||
value = [aView floatValue];
|
||||
else if (attributeType == CPBooleanAttributeType)
|
||||
value = [aView state];
|
||||
else if (attributeType == CPDateAttributeType)
|
||||
value = [aView dateValue];
|
||||
else
|
||||
value = [aView stringValue];
|
||||
|
||||
@@ -487,9 +489,9 @@ CPTransformableAttributeType = 1800;
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
var views = [CPArray array];
|
||||
var views = [CPArray array],
|
||||
copy = [[[self class] alloc] init];
|
||||
|
||||
var copy = [[[self class] alloc] init];
|
||||
[copy _setTemplateType:_templateType];
|
||||
[copy _setOptions:_predicateOptions];
|
||||
[copy _setModifier:_predicateModifier];
|
||||
|
||||
@@ -146,7 +146,7 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType",
|
||||
@"slice-top-border-color": [CPNull null],
|
||||
@"slice-bottom-border-color": [CPNull null],
|
||||
@"slice-last-bottom-border-color": [CPNull null],
|
||||
@"font": [CPNull null],
|
||||
@"font": [CPFont systemFontOfSize:12],
|
||||
@"font-color": [CPNull null],
|
||||
@"add-image": [CPNull null],
|
||||
@"remove-image": [CPNull null],
|
||||
@@ -2121,7 +2121,7 @@ TODO: implement
|
||||
return;
|
||||
|
||||
var point = [self convertPoint:[event locationInWindow] fromView:nil],
|
||||
view = [_slices objectAtIndex:FLOOR(point.y / _sliceHeight)];
|
||||
view = [_slices objectAtIndex:FLOOR(MAX(0, point.y) / _sliceHeight)];
|
||||
|
||||
if ([self _dragShouldBeginFromMouseDown:view])
|
||||
[self _performDragForSlice:view withEvent:event];
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
CGRect _animationTargetRect @accessors(property=_animationTargetRect);
|
||||
BOOL _selected @accessors(getter=_isSelected, setter=_setSelected:);
|
||||
BOOL _lastSelected @accessors(getter=_isLastSelected, setter=_setLastSelected:);
|
||||
CPColor _backgroundColor @accessors(property=backgroundColor);
|
||||
BOOL _editable @accessors(getter=isEditable, setter=setEditable:);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,8 +209,7 @@
|
||||
|
||||
- (void)_reconfigureSubviews
|
||||
{
|
||||
var ruleItems,
|
||||
criteria,
|
||||
var criteria,
|
||||
repObject,
|
||||
menuItem,
|
||||
ruleView,
|
||||
@@ -366,11 +365,12 @@
|
||||
count = [_ruleOptionViews count],
|
||||
sliceFrame = [self frame],
|
||||
image = [_ruleEditor _imageAdd],
|
||||
imageSize = image ? [image size] : CGSizeMake(0,0),
|
||||
|
||||
buttonFrame = CGRectMake(CGRectGetWidth(sliceFrame) - [image size].width - [self _rowButtonsRightHorizontalPadding], ([_ruleEditor rowHeight] - [image size].height) / 2 - 1, [image size].width, [image size].height);
|
||||
buttonFrame = CGRectMake(CGRectGetWidth(sliceFrame) - imageSize.width - [self _rowButtonsRightHorizontalPadding], ([_ruleEditor rowHeight] - imageSize.height) / 2 - 1, imageSize.width, imageSize.height);
|
||||
|
||||
[_addButton setFrame:buttonFrame];
|
||||
buttonFrame.origin.x -= [image size].width + [self _rowButtonsInterviewHorizontalPadding];
|
||||
buttonFrame.origin.x -= imageSize.width + [self _rowButtonsInterviewHorizontalPadding];
|
||||
[_subtractButton setFrame:buttonFrame];
|
||||
|
||||
if (widthChanged)
|
||||
|
||||
@@ -35,6 +35,11 @@
|
||||
CPArray _allowedFileTypes @accessors(property=allowedFileTypes);
|
||||
}
|
||||
|
||||
+ (CPURL)proposedFileURLWithDocumentName:(CPString)aDocumentName
|
||||
{
|
||||
return [CPURL URLWithString:aDocumentName];
|
||||
}
|
||||
|
||||
+ (id)savePanel
|
||||
{
|
||||
return [[CPSavePanel alloc] init];
|
||||
|
||||
+41
-32
@@ -33,6 +33,15 @@
|
||||
#define SHOULD_SHOW_CORNER_VIEW() (_scrollerStyle === CPScrollerStyleLegacy && _verticalScroller && ![_verticalScroller isHidden])
|
||||
|
||||
|
||||
@protocol CPScrollViewDelegate <CPObject>
|
||||
|
||||
@optional
|
||||
- (void)scrollViewWillScroll:(CPScrollView)aScrollView;
|
||||
- (void)scrollViewDidScroll:(CPScrollView)aScrollView;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/*! @ignore */
|
||||
var _isBrowserUsingOverlayScrollers = function()
|
||||
{
|
||||
@@ -77,15 +86,6 @@ var _isBrowserUsingOverlayScrollers = function()
|
||||
#endif
|
||||
};
|
||||
|
||||
/*!
|
||||
@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.
|
||||
*/
|
||||
|
||||
var TIMER_INTERVAL = 0.2,
|
||||
CPScrollViewDelegate_scrollViewWillScroll_ = 1 << 0,
|
||||
CPScrollViewDelegate_scrollViewDidScroll_ = 1 << 1,
|
||||
@@ -95,38 +95,45 @@ var TIMER_INTERVAL = 0.2,
|
||||
var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
|
||||
CPScrollerStyleGlobalChangeNotification = @"CPScrollerStyleGlobalChangeNotification";
|
||||
|
||||
/*!
|
||||
@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;
|
||||
CPView _bottomCornerView;
|
||||
CPClipView _contentView;
|
||||
CPClipView _headerClipView;
|
||||
CPView _cornerView;
|
||||
CPView _bottomCornerView;
|
||||
|
||||
id _delegate;
|
||||
CPTimer _scrollTimer;
|
||||
id <CPScrollViewDelegate> _delegate;
|
||||
CPTimer _scrollTimer;
|
||||
|
||||
BOOL _hasVerticalScroller;
|
||||
BOOL _hasHorizontalScroller;
|
||||
BOOL _autohidesScrollers;
|
||||
BOOL _hasVerticalScroller;
|
||||
BOOL _hasHorizontalScroller;
|
||||
BOOL _autohidesScrollers;
|
||||
|
||||
CPScroller _verticalScroller;
|
||||
CPScroller _horizontalScroller;
|
||||
CPScroller _verticalScroller;
|
||||
CPScroller _horizontalScroller;
|
||||
|
||||
CPInteger _recursionCount;
|
||||
CPInteger _implementedDelegateMethods;
|
||||
CPInteger _recursionCount;
|
||||
CPInteger _implementedDelegateMethods;
|
||||
|
||||
float _verticalLineScroll;
|
||||
float _verticalPageScroll;
|
||||
float _horizontalLineScroll;
|
||||
float _horizontalPageScroll;
|
||||
float _verticalLineScroll;
|
||||
float _verticalPageScroll;
|
||||
float _horizontalLineScroll;
|
||||
float _horizontalPageScroll;
|
||||
|
||||
CPBorderType _borderType;
|
||||
CPBorderType _borderType;
|
||||
|
||||
CPTimer _timerScrollersHide;
|
||||
CPTimer _timerScrollersHide;
|
||||
|
||||
int _scrollerStyle;
|
||||
int _scrollerKnobStyle;
|
||||
int _scrollerStyle;
|
||||
int _scrollerKnobStyle;
|
||||
}
|
||||
|
||||
|
||||
@@ -296,7 +303,7 @@ Notifies the delegate when the scroll view has finished scrolling.
|
||||
@endcode
|
||||
|
||||
*/
|
||||
- (void)setDelegate:(id)aDelegate
|
||||
- (void)setDelegate:(id <CPScrollViewDelegate>)aDelegate
|
||||
{
|
||||
if (aDelegate === _delegate)
|
||||
return;
|
||||
@@ -1246,7 +1253,7 @@ Notifies the delegate when the scroll view has finished scrolling.
|
||||
}
|
||||
|
||||
if (_timerScrollersHide)
|
||||
[_timerScrollersHide invalidate]
|
||||
[_timerScrollersHide invalidate];
|
||||
|
||||
_timerScrollersHide = [CPTimer scheduledTimerWithTimeInterval:CPScrollViewFadeOutTime target:self selector:@selector(_hideScrollers:) userInfo:nil repeats:NO];
|
||||
}
|
||||
@@ -1575,6 +1582,8 @@ var CPScrollViewContentViewKey = @"CPScrollViewContentView",
|
||||
*/
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
[super awakeFromCib];
|
||||
|
||||
[self _updateScrollerStyle];
|
||||
[self _updateCornerAndHeaderView];
|
||||
}
|
||||
|
||||
+16
-14
@@ -50,11 +50,6 @@ CPNoScrollerParts = 0;
|
||||
CPOnlyScrollerArrows = 1;
|
||||
CPAllScrollerParts = 2;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPScroller
|
||||
*/
|
||||
|
||||
var PARTS_ARRANGEMENT = [CPScrollerKnobSlot, CPScrollerDecrementLine, CPScrollerIncrementLine, CPScrollerKnob],
|
||||
NAMES_FOR_PARTS = {},
|
||||
PARTS_FOR_NAMES = {};
|
||||
@@ -78,6 +73,11 @@ CPThemeStateScrollViewLegacy = CPThemeState("scroller-style-legacy");
|
||||
CPThemeStateScrollerKnobLight = CPThemeState("scroller-knob-light");
|
||||
CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPScroller
|
||||
*/
|
||||
|
||||
@implementation CPScroller : CPControl
|
||||
{
|
||||
CPUsableScrollerParts _usableParts;
|
||||
@@ -316,7 +316,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
// The ordering of these tests is important. We check the knob and
|
||||
// page rects first since they may overlap with the arrows.
|
||||
|
||||
if (![self hasThemeState:CPThemeStateSelected])
|
||||
if (![self hasThemeState:CPThemeStateSelected] && ![self hasThemeState:CPThemeStateScrollViewLegacy])
|
||||
return CPScrollerNoPart;
|
||||
|
||||
if (CGRectContainsPoint([self rectForPart:CPScrollerKnob], aPoint))
|
||||
@@ -380,9 +380,10 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
effectiveIncrementLineHeight = incrementLineSize.height + trackInset.bottom,
|
||||
slotSize = height - effectiveDecrementLineHeight - effectiveIncrementLineHeight,
|
||||
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
||||
knobVerticalInset = knobInset.top + knobInset.bottom,
|
||||
knobWidth = width - knobInset.left - knobInset.right,
|
||||
knobHeight = MAX(minimumKnobLength, (slotSize * _knobProportion)),
|
||||
knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight) * [self floatValue];
|
||||
knobHeight = MAX(minimumKnobLength, ((slotSize - knobVerticalInset) * _knobProportion)),
|
||||
knobLocation = effectiveDecrementLineHeight + (slotSize - knobHeight - knobVerticalInset) * [self floatValue] + knobInset.top;
|
||||
|
||||
_partRects[CPScrollerDecrementPage] = CGRectMake(0.0, effectiveDecrementLineHeight, width, knobLocation - effectiveDecrementLineHeight);
|
||||
_partRects[CPScrollerKnob] = CGRectMake(knobInset.left, knobLocation, knobWidth, knobHeight);
|
||||
@@ -409,9 +410,10 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
effectiveIncrementLineWidth = incrementLineSize.width + trackInset.right,
|
||||
slotSize = width - effectiveDecrementLineWidth - effectiveIncrementLineWidth,
|
||||
minimumKnobLength = [self currentValueForThemeAttribute:"minimum-knob-length"],
|
||||
knobWidth = MAX(minimumKnobLength, (slotSize * _knobProportion)),
|
||||
knobHorizontalInset = knobInset.left + knobInset.right,
|
||||
knobWidth = MAX(minimumKnobLength, ((slotSize - knobHorizontalInset) * _knobProportion)),
|
||||
knobHeight = height - knobInset.top - knobInset.bottom,
|
||||
knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth) * [self floatValue];
|
||||
knobLocation = effectiveDecrementLineWidth + (slotSize - knobWidth - knobHorizontalInset) * [self floatValue] + knobInset.left;
|
||||
|
||||
_partRects[CPScrollerDecrementPage] = CGRectMake(effectiveDecrementLineWidth, 0.0, knobLocation - effectiveDecrementLineWidth, height);
|
||||
_partRects[CPScrollerKnob] = CGRectMake(knobLocation, knobInset.top, knobWidth, knobHeight);
|
||||
@@ -460,9 +462,6 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
*/
|
||||
- (void)fadeOut
|
||||
{
|
||||
if ([self hasThemeState:CPThemeStateScrollViewLegacy])
|
||||
return;
|
||||
|
||||
[_animationScroller startAnimation];
|
||||
}
|
||||
|
||||
@@ -765,7 +764,10 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
if (_timerFadeOut)
|
||||
[_timerFadeOut invalidate];
|
||||
|
||||
_timerFadeOut = [CPTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(_performFadeOut:) userInfo:nil repeats:NO];
|
||||
if ([self hasThemeState:CPThemeStateScrollViewLegacy])
|
||||
[self unsetThemeState:CPThemeStateSelected];
|
||||
else
|
||||
_timerFadeOut = [CPTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(_performFadeOut:) userInfo:nil repeats:NO];
|
||||
}
|
||||
|
||||
|
||||
|
||||
+150
-43
@@ -36,8 +36,6 @@ CPSearchFieldNoRecentsMenuItemTag = 1003;
|
||||
|
||||
var CPAutosavedRecentsChangedNotification = @"CPAutosavedRecentsChangedNotification";
|
||||
|
||||
var RECENT_SEARCH_PREFIX = @" ";
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPSearchField
|
||||
@@ -73,10 +71,21 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
@"image-search": [CPNull null],
|
||||
@"image-find": [CPNull null],
|
||||
@"image-cancel": [CPNull null],
|
||||
@"image-cancel-pressed": [CPNull null]
|
||||
@"image-cancel-pressed": [CPNull null],
|
||||
@"image-search-inset" : CGInsetMake(0, 0, 0, 5),
|
||||
@"image-cancel-inset" : CGInsetMake(0, 5, 0, 0),
|
||||
@"search-right-margin": 2
|
||||
};
|
||||
}
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding === CPPredicateBinding)
|
||||
return [_CPSearchFieldPredicateBinder class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame])
|
||||
@@ -87,10 +96,6 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
_recentsAutosaveName = nil;
|
||||
|
||||
[self _init];
|
||||
#if PLATFORM(DOM)
|
||||
_cancelButton._DOMElement.style.cursor = "default";
|
||||
_searchButton._DOMElement.style.cursor = "default";
|
||||
#endif
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -177,7 +182,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
- (void)resetSearchButton
|
||||
{
|
||||
var button = [self searchButton],
|
||||
searchButtonImage = (_searchMenuTemplate === nil) ? [self valueForThemeAttribute:@"image-search"] : [self valueForThemeAttribute:@"image-find"];
|
||||
searchButtonImage = (_searchMenuTemplate === nil) ? [self currentValueForThemeAttribute:@"image-search"] : [self currentValueForThemeAttribute:@"image-find"];
|
||||
|
||||
[button setBordered:NO];
|
||||
[button setImageScaling:CPImageScaleAxesIndependently];
|
||||
@@ -246,8 +251,9 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
|
||||
if (_searchButton)
|
||||
{
|
||||
var searchBounds = [self searchButtonRectForBounds:bounds];
|
||||
leftOffset = CGRectGetMaxX(searchBounds) + 2;
|
||||
var searchBounds = [self searchButtonRectForBounds:bounds],
|
||||
rightMargin = [self currentValueForThemeAttribute:@"search-right-margin"];
|
||||
leftOffset = CGRectGetMaxX(searchBounds) + rightMargin;
|
||||
}
|
||||
|
||||
if (_cancelButton)
|
||||
@@ -266,9 +272,10 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
*/
|
||||
- (CGRect)searchButtonRectForBounds:(CGRect)rect
|
||||
{
|
||||
var size = [[self valueForThemeAttribute:@"image-search"] size] || CGSizeMakeZero();
|
||||
var size = [[self currentValueForThemeAttribute:@"image-search"] size] || CGSizeMakeZero(),
|
||||
inset = [self currentValueForThemeAttribute:@"image-search-inset"];
|
||||
|
||||
return CGRectMake(5, (CGRectGetHeight(rect) - size.height) / 2, size.width, size.height);
|
||||
return CGRectMake(inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.height) / 2, size.width, size.height);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -278,9 +285,10 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
*/
|
||||
- (CGRect)cancelButtonRectForBounds:(CGRect)rect
|
||||
{
|
||||
var size = [[self valueForThemeAttribute:@"image-cancel"] size] || CGSizeMakeZero();
|
||||
var size = [[self currentValueForThemeAttribute:@"image-cancel"] size] || CGSizeMakeZero(),
|
||||
inset = [self currentValueForThemeAttribute:@"image-cancel-inset"];
|
||||
|
||||
return CGRectMake(CGRectGetWidth(rect) - size.width - 5, (CGRectGetHeight(rect) - size.width) / 2, size.height, size.height);
|
||||
return CGRectMake(CGRectGetWidth(rect) - size.width + inset.left - inset.right, inset.top - inset.bottom + (CGRectGetHeight(rect) - size.width) / 2, size.height, size.height);
|
||||
}
|
||||
|
||||
// Managing Menu Templates
|
||||
@@ -384,9 +392,9 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
*/
|
||||
- (void)setRecentSearches:(CPArray)searches
|
||||
{
|
||||
var max = MIN([self maximumRecents], [searches count]),
|
||||
searches = [searches subarrayWithRange:CPMakeRange(0, max)];
|
||||
var max = MIN([self maximumRecents], [searches count]);
|
||||
|
||||
searches = [searches subarrayWithRange:CPMakeRange(0, max)];
|
||||
_recentSearches = searches;
|
||||
[self _autosaveRecentSearchList];
|
||||
}
|
||||
@@ -529,19 +537,16 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
}
|
||||
|
||||
/*!
|
||||
Provides the common case items for a recent searches menu. If there are not recent searches,
|
||||
displays a single disabled item:
|
||||
|
||||
No Recent Searches
|
||||
Provides the common case items for a recent searches menu.
|
||||
|
||||
If there are 1 more recent searches, it displays:
|
||||
|
||||
Recent Searches
|
||||
recent search 1
|
||||
recent search 2
|
||||
etc.
|
||||
Clear
|
||||
---------------------
|
||||
Clear Recent Searches
|
||||
Recent Searches
|
||||
recent search 1
|
||||
recent search 2
|
||||
etc.
|
||||
|
||||
If you wish to add items before or after the template, you can. If you put items
|
||||
before, a separator will automatically be placed before the default template item.
|
||||
@@ -566,6 +571,13 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
var template = [[CPMenu alloc] init],
|
||||
item;
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Clear"
|
||||
action:@selector(_searchFieldClearRecents:)
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldClearRecentsMenuItemTag];
|
||||
[item setTarget:self];
|
||||
[template addItem:item];
|
||||
|
||||
item = [[CPMenuItem alloc] initWithTitle:@"Recent Searches"
|
||||
action:nil
|
||||
keyEquivalent:@""];
|
||||
@@ -580,20 +592,6 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
[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:nil
|
||||
keyEquivalent:@""];
|
||||
[item setTag:CPSearchFieldNoRecentsMenuItemTag];
|
||||
[item setEnabled:NO];
|
||||
[template addItem:item];
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
@@ -606,6 +604,8 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
countOfRecents = [_recentSearches count],
|
||||
numberOfItems = [_searchMenuTemplate numberOfItems];
|
||||
|
||||
[menu setAutoenablesItems:NO];
|
||||
|
||||
for (var i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
var item = [[_searchMenuTemplate itemAtIndex:i] copy];
|
||||
@@ -626,8 +626,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
|
||||
for (var recentIndex = 0; recentIndex < countOfRecents; ++recentIndex)
|
||||
{
|
||||
// RECENT_SEARCH_PREFIX is a hack until CPMenuItem -setIndentationLevel works
|
||||
var recentItem = [[CPMenuItem alloc] initWithTitle:RECENT_SEARCH_PREFIX + [_recentSearches objectAtIndex:recentIndex]
|
||||
var recentItem = [[CPMenuItem alloc] initWithTitle:[_recentSearches objectAtIndex:recentIndex]
|
||||
action:itemAction
|
||||
keyEquivalent:[item keyEquivalent]];
|
||||
[item setTarget:self];
|
||||
@@ -687,7 +686,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
|
||||
- (void)_showMenu
|
||||
{
|
||||
if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![self isEnabled])
|
||||
if (_searchMenu === nil || [_searchMenu numberOfItems] === 0 || ![self isEnabled] || ([_recentSearches count] === 0))
|
||||
return;
|
||||
|
||||
var aFrame = [[self superview] convertRect:[self frame] toView:nil],
|
||||
@@ -715,7 +714,7 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
|
||||
- (void)_searchFieldSearch:(id)sender
|
||||
{
|
||||
var searchString = [[sender title] substringFromIndex:[RECENT_SEARCH_PREFIX length]];
|
||||
var searchString = [sender title];
|
||||
|
||||
if ([sender tag] != CPSearchFieldRecentsMenuItemTag)
|
||||
[self _addStringToRecentSearches:searchString];
|
||||
@@ -769,8 +768,75 @@ var RECENT_SEARCH_PREFIX = @" ";
|
||||
_recentSearches = list;
|
||||
}
|
||||
|
||||
- (void)unbind:(CPString)aBinding
|
||||
{
|
||||
[super unbind:aBinding];
|
||||
|
||||
// our CPPredicateBinding binder adds a binding to the value.
|
||||
// this private binding has to be also removed
|
||||
if (aBinding === CPPredicateBinding)
|
||||
[[[self class] _binderClassForBinding:aBinding] unbind:CPValueBinding forObject:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation CPSearchField (CPTrackingArea)
|
||||
{
|
||||
CPTrackingArea _searchButtonTrackingArea;
|
||||
CPTrackingArea _cancelButtonTrackingArea;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (_searchButtonTrackingArea)
|
||||
{
|
||||
[self removeTrackingArea:_searchButtonTrackingArea];
|
||||
_searchButtonTrackingArea = nil;
|
||||
}
|
||||
|
||||
if (_cancelButtonTrackingArea)
|
||||
{
|
||||
[self removeTrackingArea:_cancelButtonTrackingArea];
|
||||
_cancelButtonTrackingArea = nil;
|
||||
}
|
||||
|
||||
if (_searchButton)
|
||||
{
|
||||
_searchButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:[_searchButton frame]
|
||||
options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
|
||||
owner:self
|
||||
userInfo:@{ @"isButton": YES }];
|
||||
|
||||
[self addTrackingArea:_searchButtonTrackingArea];
|
||||
}
|
||||
|
||||
if (_cancelButton)
|
||||
{
|
||||
_cancelButtonTrackingArea = [[CPTrackingArea alloc] initWithRect:[_cancelButton frame]
|
||||
options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
|
||||
owner:self
|
||||
userInfo:@{ @"isButton": YES }];
|
||||
|
||||
[self addTrackingArea:_cancelButtonTrackingArea];
|
||||
}
|
||||
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
- (void)cursorUpdate:(CPEvent)anEvent
|
||||
{
|
||||
if ([[[anEvent trackingArea] userInfo] objectForKey:@"isButton"])
|
||||
[[CPCursor arrowCursor] set];
|
||||
else
|
||||
[super cursorUpdate:anEvent];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
|
||||
CPSendsWholeSearchStringKey = @"CPSendsWholeSearchStringKey",
|
||||
CPSendsSearchStringImmediatelyKey = @"CPSendsSearchStringImmediatelyKey",
|
||||
@@ -823,3 +889,44 @@ var CPRecentsAutosaveNameKey = @"CPRecentsAutosaveNameKey",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPSearchFieldPredicateBinder : CPBinder
|
||||
{
|
||||
CPArrayController _controller;
|
||||
CPString _predicateFormat;
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding === CPPredicateBinding)
|
||||
{
|
||||
var options = [_info objectForKey:CPOptionsKey];
|
||||
|
||||
_controller = [_info objectForKey:CPObservedObjectKey];
|
||||
_predicateFormat = [options objectForKey:"CPPredicateFormat"];
|
||||
[_source bind:CPValueBinding toObject:self withKeyPath:"searchFieldValue" options:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSearchFieldValue:(CPString)aValue
|
||||
{
|
||||
var destination = [_info objectForKey:CPObservedObjectKey],
|
||||
keyPath = [_info objectForKey:CPObservedKeyPathKey];
|
||||
|
||||
var formatString = _predicateFormat.replace(/\$value/g, "%@");
|
||||
[self suppressSpecificNotificationFromObject:destination keyPath:keyPath];
|
||||
|
||||
if (aValue)
|
||||
[_controller setFilterPredicate:[CPPredicate predicateWithFormat:formatString, aValue]];
|
||||
else
|
||||
[_controller setFilterPredicate:nil];
|
||||
|
||||
[self unsuppressSpecificNotificationFromObject:destination keyPath:keyPath];
|
||||
}
|
||||
- (CPString)searchFieldValue
|
||||
{
|
||||
return [_source stringValue];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
+251
-188
@@ -42,7 +42,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
@implementation CPSegmentedControl : CPControl
|
||||
{
|
||||
CPArray _segments;
|
||||
CPArray _segments @accessors(getter=segments);
|
||||
CPArray _themeStates;
|
||||
|
||||
int _selectedSegment;
|
||||
@@ -87,6 +87,8 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
_selectedSegment = -1;
|
||||
|
||||
_trackingMode = CPSegmentSwitchTrackingSelectOne;
|
||||
_trackingHighlighted = NO;
|
||||
_trackingSegment = -1;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -107,7 +109,54 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (int)selectedTag
|
||||
{
|
||||
return [_segments[_selectedSegment] tag];
|
||||
return [[_segments objectAtIndex:_selectedSegment] tag];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (void)setSegments:(CPArray)segments
|
||||
{
|
||||
[self removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self segmentCount])]];
|
||||
|
||||
[self insertSegments:segments atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [segments count])]];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (void)insertSegments:(CPArray)segments atIndexes:(CPIndexSet)indices
|
||||
{
|
||||
if ([segments count] == 0)
|
||||
return;
|
||||
|
||||
var newStates = @[],
|
||||
count = [indices count];
|
||||
|
||||
while (count--)
|
||||
[newStates addObject:CPThemeStateNormal];
|
||||
|
||||
[_segments insertObjects:segments atIndexes:indices];
|
||||
[_themeStates insertObjects:newStates atIndexes:indices];
|
||||
|
||||
if (_selectedSegment >= [indices firstIndex])
|
||||
_selectedSegment += [indices count];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (void)removeSegmentsAtIndexes:(CPIndexSet)indices
|
||||
{
|
||||
if ([indices count] == 0)
|
||||
return;
|
||||
|
||||
[indices enumerateIndexesUsingBlock:function(idx, stop)
|
||||
{
|
||||
[[_segments objectAtIndex:idx] setSelected:NO];
|
||||
}];
|
||||
|
||||
if ([indices containsIndex:_selectedSegment])
|
||||
_selectedSegment = -1;
|
||||
else if ([indices lastIndex] < _selectedSegment)
|
||||
_selectedSegment -= [indices count];
|
||||
|
||||
[_segments removeObjectsAtIndexes:indices];
|
||||
[_themeStates removeObjectsAtIndexes:indices];
|
||||
}
|
||||
|
||||
// Specifying the number of segments
|
||||
@@ -117,41 +166,32 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setSegmentCount:(unsigned)aCount
|
||||
{
|
||||
if (_segments.length == aCount)
|
||||
var prevCount = [_segments count];
|
||||
|
||||
if (aCount == prevCount)
|
||||
return;
|
||||
|
||||
var height = CGRectGetHeight([self bounds]),
|
||||
dividersBefore = MAX(0, _segments.length - 1),
|
||||
dividersAfter = MAX(0, aCount - 1);
|
||||
|
||||
if (_segments.length < aCount)
|
||||
if (aCount > prevCount)
|
||||
{
|
||||
for (var index = _segments.length; index < aCount; ++index)
|
||||
{
|
||||
_segments[index] = [[_CPSegmentItem alloc] init];
|
||||
_themeStates[index] = CPThemeStateNormal;
|
||||
}
|
||||
}
|
||||
else if (aCount < _segments.length)
|
||||
{
|
||||
_segments.length = aCount;
|
||||
_themeStates.length = aCount;
|
||||
}
|
||||
var count = aCount - prevCount,
|
||||
segments = @[];
|
||||
|
||||
if (_selectedSegment >= _segments.length)
|
||||
while (count--)
|
||||
[segments addObject:[[_CPSegmentItem alloc] init]];
|
||||
|
||||
[self insertSegments:segments atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(prevCount, aCount - prevCount)]];
|
||||
}
|
||||
else
|
||||
[self removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(aCount, prevCount - aCount)]];
|
||||
|
||||
[self _updateSelectionIfNeeded];
|
||||
[self tileWithChangedSegment:MAX(MIN(prevCount, aCount) - 1, 0)];
|
||||
}
|
||||
|
||||
- (void)_updateSelectionIfNeeded
|
||||
{
|
||||
if (_selectedSegment >= [self segmentCount])
|
||||
_selectedSegment = -1;
|
||||
|
||||
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
|
||||
frame = [self frame],
|
||||
widthOfAllSegments = 0,
|
||||
dividerExtraSpace = ([_segments count] - 1) * thickness;
|
||||
|
||||
for (var i = 0; i < [_segments count]; i++)
|
||||
widthOfAllSegments += [_segments[i] width];
|
||||
|
||||
[self setFrameSize:CGSizeMake(widthOfAllSegments + dividerExtraSpace, frame.size.height)];
|
||||
|
||||
[self tileWithChangedSegment:0];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -159,7 +199,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (unsigned)segmentCount
|
||||
{
|
||||
return _segments.length;
|
||||
return [_segments count];
|
||||
}
|
||||
|
||||
// Specifying Selected Segment
|
||||
@@ -171,7 +211,20 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
- (void)setSelectedSegment:(unsigned)aSegment
|
||||
{
|
||||
// setSelected:forSegment throws the exception for us (if necessary)
|
||||
[self setSelected:YES forSegment:aSegment];
|
||||
if (_selectedSegment == aSegment)
|
||||
return;
|
||||
|
||||
if (aSegment == -1)
|
||||
{
|
||||
var count = [self segmentCount];
|
||||
|
||||
while (count--)
|
||||
[self setSelected:NO forSegment:count];
|
||||
|
||||
_selectedSegment = -1;
|
||||
}
|
||||
else
|
||||
[self setSelected:YES forSegment:aSegment];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -189,8 +242,8 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
for (; index < _segments.length; ++index)
|
||||
if (_segments[index].tag == aTag)
|
||||
for (; index < [_segments count]; ++index)
|
||||
if ([[_segments objectAtIndex:index] tag] == aTag)
|
||||
{
|
||||
[self setSelectedSegment:index];
|
||||
|
||||
@@ -204,8 +257,8 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
for (; index < _segments.length; ++index)
|
||||
if (_segments[index].label == aLabel)
|
||||
for (; index < [_segments count]; ++index)
|
||||
if ([[_segments objectAtIndex:index] label] == aLabel)
|
||||
{
|
||||
[self setSelectedSegment:index];
|
||||
|
||||
@@ -216,7 +269,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
}
|
||||
|
||||
// Specifying Tracking Mode
|
||||
|
||||
/*! @ignore */
|
||||
- (BOOL)isTracking
|
||||
{
|
||||
|
||||
@@ -234,7 +287,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
var index = 0,
|
||||
selected = NO;
|
||||
|
||||
for (; index < _segments.length; ++index)
|
||||
for (; index < [self segmentCount]; ++index)
|
||||
if ([_segments[index] selected])
|
||||
if (selected)
|
||||
[self setSelected:NO forSegment:index];
|
||||
@@ -246,7 +299,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
for (; index < _segments.length; ++index)
|
||||
for (; index < [self segmentCount]; ++index)
|
||||
if ([_segments[index] selected])
|
||||
[self setSelected:NO forSegment:index];
|
||||
}
|
||||
@@ -269,7 +322,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setWidth:(float)aWidth forSegment:(unsigned)aSegment
|
||||
{
|
||||
[_segments[aSegment] setWidth:aWidth];
|
||||
[[_segments objectAtIndex:aSegment] setWidth:aWidth];
|
||||
[self tileWithChangedSegment:aSegment];
|
||||
}
|
||||
|
||||
@@ -280,7 +333,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (float)widthForSegment:(unsigned)aSegment
|
||||
{
|
||||
return [_segments[aSegment] width];
|
||||
return [[_segments objectAtIndex:aSegment] width];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -291,7 +344,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setImage:(CPImage)anImage forSegment:(unsigned)aSegment
|
||||
{
|
||||
[_segments[aSegment] setImage:anImage];
|
||||
[[_segments objectAtIndex:aSegment] setImage:anImage];
|
||||
|
||||
[self tileWithChangedSegment:aSegment];
|
||||
}
|
||||
@@ -303,7 +356,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (CPImage)imageForSegment:(unsigned)aSegment
|
||||
{
|
||||
return [_segments[aSegment] image];
|
||||
return [[_segments objectAtIndex:aSegment] image];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -314,7 +367,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setLabel:(CPString)aLabel forSegment:(unsigned)aSegment
|
||||
{
|
||||
[_segments[aSegment] setLabel:aLabel];
|
||||
[[_segments objectAtIndex:aSegment] setLabel:aLabel];
|
||||
|
||||
[self tileWithChangedSegment:aSegment];
|
||||
}
|
||||
@@ -326,7 +379,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (CPString)labelForSegment:(unsigned)aSegment
|
||||
{
|
||||
return [_segments[aSegment] label];
|
||||
return [[_segments objectAtIndex:aSegment] label];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -337,7 +390,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setMenu:(CPMenu)aMenu forSegment:(unsigned)aSegment
|
||||
{
|
||||
[_segments[aSegment] setMenu:aMenu];
|
||||
[[_segments objectAtIndex:aSegment] setMenu:aMenu];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -347,7 +400,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (CPMenu)menuForSegment:(unsigned)aSegment
|
||||
{
|
||||
return [_segments[aSegment] menu];
|
||||
return [[_segments objectAtIndex:aSegment] menu];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -359,7 +412,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setSelected:(BOOL)isSelected forSegment:(unsigned)aSegment
|
||||
{
|
||||
var segment = _segments[aSegment];
|
||||
var segment = [_segments objectAtIndex:aSegment];
|
||||
|
||||
// If we're already in this state, bail.
|
||||
if ([segment selected] == isSelected)
|
||||
@@ -376,7 +429,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
_selectedSegment = aSegment;
|
||||
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1)
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectOne && oldSelectedSegment != aSegment && oldSelectedSegment != -1 && oldSelectedSegment < _segments.length)
|
||||
{
|
||||
[_segments[oldSelectedSegment] setSelected:NO];
|
||||
_themeStates[oldSelectedSegment] = CPThemeStateNormal;
|
||||
@@ -399,7 +452,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (BOOL)isSelectedForSegment:(unsigned)aSegment
|
||||
{
|
||||
return [_segments[aSegment] selected];
|
||||
return [[_segments objectAtIndex:aSegment] selected];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -410,10 +463,12 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setEnabled:(BOOL)shouldBeEnabled forSegment:(unsigned)aSegment
|
||||
{
|
||||
if ([_segments[aSegment] enabled] === shouldBeEnabled)
|
||||
var segment = [_segments objectAtIndex:aSegment];
|
||||
|
||||
if ([segment enabled] === shouldBeEnabled)
|
||||
return;
|
||||
|
||||
[_segments[aSegment] setEnabled:shouldBeEnabled];
|
||||
[segment setEnabled:shouldBeEnabled];
|
||||
|
||||
if (shouldBeEnabled)
|
||||
_themeStates[aSegment] = _themeStates[aSegment].without(CPThemeStateDisabled);
|
||||
@@ -431,7 +486,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (BOOL)isEnabledForSegment:(unsigned)aSegment
|
||||
{
|
||||
return [_segments[aSegment] enabled];
|
||||
return [[_segments objectAtIndex:aSegment] enabled];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -441,7 +496,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)setTag:(int)aTag forSegment:(unsigned)aSegment
|
||||
{
|
||||
[_segments[aSegment] setTag:aTag];
|
||||
[[_segments objectAtIndex:aSegment] setTag:aTag];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -450,7 +505,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (int)tagForSegment:(unsigned)aSegment
|
||||
{
|
||||
return [_segments[aSegment] tag];
|
||||
return [[_segments objectAtIndex:aSegment] tag];
|
||||
}
|
||||
|
||||
// Drawings
|
||||
@@ -461,7 +516,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (void)drawSegmentBezel:(int)aSegment highlight:(BOOL)shouldHighlight
|
||||
{
|
||||
if(aSegment < _themeStates.length)
|
||||
if (aSegment < _themeStates.length)
|
||||
{
|
||||
if (shouldHighlight)
|
||||
_themeStates[aSegment] = _themeStates[aSegment].and(CPThemeStateHighlighted);
|
||||
@@ -475,14 +530,12 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
|
||||
- (float)_leftOffsetForSegment:(unsigned)segment
|
||||
{
|
||||
var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
|
||||
|
||||
if (segment == 0)
|
||||
return bezelInset.left;
|
||||
return [self currentValueForThemeAttribute:@"bezel-inset"].left;
|
||||
|
||||
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
|
||||
|
||||
return [self _leftOffsetForSegment:segment - 1] + [self widthForSegment:segment - 1] + thickness;
|
||||
return [self _leftOffsetForSegment:segment - 1] + CGRectGetWidth([self frameForSegment:segment - 1]) + thickness;
|
||||
}
|
||||
|
||||
- (unsigned)_indexOfLastSegment
|
||||
@@ -516,7 +569,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
else if (aName.indexOf("segment-bezel") === 0)
|
||||
{
|
||||
var segment = parseInt(aName.substring("segment-bezel-".length), 10),
|
||||
frame = CGRectCreateCopy([_segments[segment] frame]);
|
||||
frame = CGRectCreateCopy([self frameForSegment:segment]);
|
||||
|
||||
if (segment === 0)
|
||||
{
|
||||
@@ -524,7 +577,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
frame.size.width -= contentInset.left;
|
||||
}
|
||||
|
||||
if (segment === _segments.length - 1)
|
||||
if (segment === [self segmentCount] - 1)
|
||||
frame.size.width = CGRectGetWidth([self bounds]) - contentInset.right - frame.origin.x;
|
||||
|
||||
return frame;
|
||||
@@ -532,7 +585,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
else if (aName.indexOf("divider-bezel") === 0)
|
||||
{
|
||||
var segment = parseInt(aName.substring("divider-bezel-".length), 10),
|
||||
width = [self widthForSegment:segment],
|
||||
width = CGRectGetWidth([self frameForSegment:segment]),
|
||||
left = [self _leftOffsetForSegment:segment],
|
||||
thickness = [self currentValueForThemeAttribute:@"divider-thickness"];
|
||||
|
||||
@@ -551,14 +604,19 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
- (CPView)createEphemeralSubviewNamed:(CPString)aName
|
||||
{
|
||||
if ([aName hasPrefix:@"segment-content"])
|
||||
return [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
|
||||
{
|
||||
var view = [[_CPImageAndTextView alloc] initWithFrame:CGRectMakeZero()];
|
||||
[view _setUsesSingleLineMode:YES];
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
return [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
if (_segments.length <= 0)
|
||||
if ([self segmentCount] <= 0)
|
||||
return;
|
||||
|
||||
var themeState = _themeStates[0],
|
||||
@@ -646,9 +704,8 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
if (i == count - 1)
|
||||
continue;
|
||||
|
||||
var borderState = _themeStates[i].and(_themeStates[i + 1]);
|
||||
|
||||
borderState = (borderState.hasThemeState(CPThemeStateSelected) && !borderState.hasThemeState(CPThemeStateHighlighted)) ? CPThemeStateSelected : CPThemeStateNormal;
|
||||
var borderState = _themeStates[i].and(_themeStates[i + 1]);
|
||||
|
||||
borderState = isDisabled ? borderState.and(CPThemeStateDisabled) : borderState;
|
||||
|
||||
@@ -678,60 +735,33 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
}
|
||||
|
||||
- (void)tileWithChangedSegment:(unsigned)aSegment
|
||||
/*! @ignore */
|
||||
- (void)tile
|
||||
{
|
||||
if (aSegment >= _segments.length)
|
||||
[self tileWithChangedSegment:0];
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (void)tileWithChangedSegment:(CPInteger)aSegment
|
||||
{
|
||||
var segmentCount = [self segmentCount];
|
||||
|
||||
// Corner case: when segmentCount == 0 and aSegment == 0, we do not return here because we still need to set the new frameSize bellow.
|
||||
if (aSegment < 0 || (segmentCount > 0 && aSegment >= segmentCount))
|
||||
return;
|
||||
|
||||
var segment = _segments[aSegment],
|
||||
segmentWidth = [segment width],
|
||||
themeState = _themeState.hasThemeState(CPThemeStateDisabled) ? _themeStates[aSegment].and(CPThemeStateDisabled) : _themeStates[aSegment],
|
||||
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
|
||||
font = [self font];
|
||||
var width = 0;
|
||||
|
||||
if (!segmentWidth)
|
||||
if (segmentCount > 0)
|
||||
{
|
||||
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;
|
||||
// Invalidate frames for segments on the right. They will be lazily computed by -frameForSegment:.
|
||||
for (var i = aSegment; i < segmentCount; i++)
|
||||
[_segments[i] setFrame:CGRectMakeZero()];
|
||||
|
||||
width = CGRectGetMaxX([self frameForSegment:(segmentCount - 1)]);
|
||||
}
|
||||
|
||||
var delta = segmentWidth - CGRectGetWidth([segment frame]);
|
||||
|
||||
if (!delta)
|
||||
{
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Update control size
|
||||
var frame = [self frame];
|
||||
[self setFrameSize:CGSizeMake(CGRectGetWidth(frame) + delta, CGRectGetHeight(frame))];
|
||||
|
||||
// Update segment width
|
||||
[segment setWidth:segmentWidth];
|
||||
[segment setFrame:[self frameForSegment:aSegment]];
|
||||
|
||||
// Update following segments widths
|
||||
var index = aSegment + 1;
|
||||
|
||||
for (; index < _segments.length; ++index)
|
||||
{
|
||||
[_segments[index] frame].origin.x += delta;
|
||||
|
||||
[self drawSegmentBezel:index highlight:NO];
|
||||
[self drawSegment:index highlight:NO];
|
||||
}
|
||||
|
||||
[self drawSegmentBezel:aSegment highlight:NO];
|
||||
[self drawSegment:aSegment highlight:NO];
|
||||
[self setFrameSize:CGSizeMake(width, CGRectGetHeight([self frame]))];
|
||||
|
||||
[self setNeedsLayout];
|
||||
[self setNeedsDisplay:YES];
|
||||
@@ -743,29 +773,57 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
*/
|
||||
- (CGRect)frameForSegment:(unsigned)aSegment
|
||||
{
|
||||
return [self bezelFrameForSegment:aSegment];
|
||||
var segment = [_segments objectAtIndex:aSegment],
|
||||
frame = [segment frame];
|
||||
|
||||
if (CGRectEqualToRect(frame, CGRectMakeZero()))
|
||||
{
|
||||
frame = [self bezelFrameForSegment:aSegment];
|
||||
[segment setFrame:frame];
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (CGRect)bezelFrameForSegment:(unsigned)aSegment
|
||||
{
|
||||
var height = [self currentValueForThemeAttribute:@"min-size"].height,
|
||||
bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"],
|
||||
var left = [self _leftOffsetForSegment:aSegment],
|
||||
top = [self currentValueForThemeAttribute:@"bezel-inset"].top,
|
||||
width = [self widthForSegment:aSegment],
|
||||
left = [self _leftOffsetForSegment:aSegment];
|
||||
height = [self currentValueForThemeAttribute:@"min-size"].height;
|
||||
|
||||
return CGRectMake(left, bezelInset.top, width, height);
|
||||
if (width == 0)
|
||||
{
|
||||
var themeState = _themeState.hasThemeState(CPThemeStateDisabled) ? _themeStates[aSegment].and(CPThemeStateDisabled) : _themeStates[aSegment],
|
||||
contentInset = [self valueForThemeAttribute:@"content-inset" inState:themeState],
|
||||
contentInsetWidth = contentInset.left + contentInset.right,
|
||||
|
||||
segment = _segments[aSegment],
|
||||
label = [segment label],
|
||||
image = [segment image];
|
||||
|
||||
width = (label ? [label sizeWithFont:[self font]].width : 4.0) + (image ? [image size].width : 0) + contentInsetWidth;
|
||||
}
|
||||
|
||||
return CGRectMake(left, top, width, height);
|
||||
}
|
||||
|
||||
- (CGRect)contentFrameForSegment:(unsigned)aSegment
|
||||
{
|
||||
var height = [self currentValueForThemeAttribute:@"min-size"].height,
|
||||
contentInset = [self currentValueForThemeAttribute:@"content-inset"],
|
||||
width = [self widthForSegment:aSegment],
|
||||
width = CGRectGetWidth([self frameForSegment:aSegment]),
|
||||
left = [self _leftOffsetForSegment:aSegment];
|
||||
|
||||
return CGRectMake(left + contentInset.left, contentInset.top, width - contentInset.left - contentInset.right, height - contentInset.top - contentInset.bottom);
|
||||
}
|
||||
|
||||
- (CGSize)_minimumFrameSize
|
||||
{
|
||||
// The current width is always the minimum width.
|
||||
return CGSizeMake(CGRectGetWidth([self frame]), [self currentValueForThemeAttribute:@"min-size"].height);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the segment that is hit by the specified point.
|
||||
@param aPoint the point to test for a segment hit
|
||||
@@ -774,19 +832,19 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
- (unsigned)testSegment:(CGPoint)aPoint
|
||||
{
|
||||
var location = [self convertPoint:aPoint fromView:nil],
|
||||
count = _segments.length;
|
||||
count = [self segmentCount];
|
||||
|
||||
while (count--)
|
||||
if (CGRectContainsPoint([_segments[count] frame], aPoint))
|
||||
if (CGRectContainsPoint([self frameForSegment:count], aPoint))
|
||||
return count;
|
||||
|
||||
if (_segments.length)
|
||||
if ([self segmentCount])
|
||||
{
|
||||
var adjustedLastFrame = CGRectCreateCopy([_segments[_segments.length - 1] frame]);
|
||||
var adjustedLastFrame = CGRectCreateCopy([self frameForSegment:(_segments.length - 1)]);
|
||||
adjustedLastFrame.size.width = CGRectGetWidth([self bounds]) - adjustedLastFrame.origin.x;
|
||||
|
||||
if (CGRectContainsPoint(adjustedLastFrame, aPoint))
|
||||
return _segments.length - 1;
|
||||
return [self segmentCount] - 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@@ -814,64 +872,68 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
var type = [anEvent type],
|
||||
location = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
if (type == CPLeftMouseUp)
|
||||
switch (type)
|
||||
{
|
||||
if (_trackingSegment == -1)
|
||||
case CPLeftMouseUp:
|
||||
|
||||
if (_trackingSegment === CPNotFound)
|
||||
return;
|
||||
|
||||
if (_trackingSegment === [self testSegment:location])
|
||||
{
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
|
||||
{
|
||||
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
|
||||
|
||||
// With ANY, _selectedSegment means last pressed.
|
||||
_selectedSegment = _trackingSegment;
|
||||
}
|
||||
else
|
||||
[self setSelected:YES forSegment:_trackingSegment];
|
||||
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
|
||||
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
||||
{
|
||||
[self setSelected:NO forSegment:_trackingSegment];
|
||||
|
||||
_selectedSegment = CPNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||
|
||||
_trackingSegment = CPNotFound;
|
||||
|
||||
return;
|
||||
|
||||
if (_trackingSegment === [self testSegment:location])
|
||||
{
|
||||
if (_trackingMode == CPSegmentSwitchTrackingSelectAny)
|
||||
case CPLeftMouseDown:
|
||||
|
||||
var trackingSegment = [self testSegment:location];
|
||||
if (trackingSegment > CPNotFound && [self isEnabledForSegment:trackingSegment])
|
||||
{
|
||||
[self setSelected:![self isSelectedForSegment:_trackingSegment] forSegment:_trackingSegment];
|
||||
|
||||
// With ANY, _selectedSegment means last pressed.
|
||||
_selectedSegment = _trackingSegment;
|
||||
_trackingHighlighted = YES;
|
||||
_trackingSegment = trackingSegment;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
}
|
||||
else
|
||||
[self setSelected:YES forSegment:_trackingSegment];
|
||||
|
||||
[self sendAction:[self action] to:[self target]];
|
||||
break;
|
||||
|
||||
if (_trackingMode == CPSegmentSwitchTrackingMomentary)
|
||||
case CPLeftMouseDragged:
|
||||
|
||||
if (_trackingSegment === CPNotFound)
|
||||
return;
|
||||
|
||||
var highlighted = [self testSegment:location] === _trackingSegment;
|
||||
|
||||
if (highlighted != _trackingHighlighted)
|
||||
{
|
||||
[self setSelected:NO forSegment:_trackingSegment];
|
||||
_trackingHighlighted = highlighted;
|
||||
|
||||
_selectedSegment = -1;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
|
||||
}
|
||||
}
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||
|
||||
_trackingSegment = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == CPLeftMouseDown)
|
||||
{
|
||||
var trackingSegment = [self testSegment:location];
|
||||
if (trackingSegment > -1 && [self isEnabledForSegment:trackingSegment])
|
||||
{
|
||||
_trackingHighlighted = YES;
|
||||
_trackingSegment = trackingSegment;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == CPLeftMouseDragged)
|
||||
{
|
||||
if (_trackingSegment == -1)
|
||||
return;
|
||||
|
||||
var highlighted = [self testSegment:location] === _trackingSegment;
|
||||
|
||||
if (highlighted != _trackingHighlighted)
|
||||
{
|
||||
_trackingHighlighted = highlighted;
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:_trackingHighlighted];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
@@ -881,7 +943,7 @@ CPSegmentSwitchTrackingMomentary = 2;
|
||||
{
|
||||
[super setFont:aFont];
|
||||
|
||||
[self tileWithChangedSegment:0];
|
||||
[self tile];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -913,7 +975,7 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
||||
if ([aCoder containsValueForKey:CPSegmentedControlSelectedKey])
|
||||
_selectedSegment = [aCoder decodeIntForKey:CPSegmentedControlSelectedKey];
|
||||
else
|
||||
_selectedSegment = -1;
|
||||
_selectedSegment = CPNotFound;
|
||||
|
||||
if ([aCoder containsValueForKey:CPSegmentedControlTrackingModeKey])
|
||||
_trackingMode = [aCoder decodeIntForKey:CPSegmentedControlTrackingModeKey];
|
||||
@@ -921,23 +983,24 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
||||
_trackingMode = CPSegmentSwitchTrackingSelectOne;
|
||||
|
||||
// Here we update the themeStates array for each segments to know if there are selected or not
|
||||
for (var i = 0; i < _segments.length; i++)
|
||||
for (var i = 0; i < [self segmentCount]; i++)
|
||||
_themeStates[i] = [_segments[i] selected] ? CPThemeStateSelected : CPThemeStateNormal;
|
||||
|
||||
// We do this in a second loop because it relies on all the themeStates being set first
|
||||
for (var i = 0; i < _segments.length; i++)
|
||||
[self tileWithChangedSegment:i];
|
||||
[self tile];
|
||||
|
||||
var thickness = [self currentValueForThemeAttribute:@"divider-thickness"],
|
||||
dividerExtraSpace = ([_segments count] - 1) * thickness,
|
||||
difference = MAX(originalWidth - [self frame].size.width - dividerExtraSpace, 0.0),
|
||||
remainingWidth = FLOOR(difference / _segments.length),
|
||||
remainingWidth = FLOOR(difference / [self segmentCount]),
|
||||
widthOfAllSegments = 0;
|
||||
|
||||
for (var i = 0; i < _segments.length; i++)
|
||||
// We do this in a second loop because it relies on all the themeStates being set first
|
||||
for (var i = 0; i < [self segmentCount]; i++)
|
||||
{
|
||||
[self setWidth:[_segments[i] width] + remainingWidth forSegment:i];
|
||||
widthOfAllSegments += [_segments[i] width];
|
||||
var frame = [_segments[i] frame];
|
||||
frame.size.width += remainingWidth;
|
||||
|
||||
widthOfAllSegments += CGRectGetWidth(frame);
|
||||
}
|
||||
|
||||
// Here we handle the leftovers pixel, and we will add one pixel to each segment cell till we have the same size as the originalSize.
|
||||
@@ -945,16 +1008,16 @@ var CPSegmentedControlSegmentsKey = "CPSegmentedControlSegmentsKey",
|
||||
var leftOversPixel = originalWidth - (widthOfAllSegments + dividerExtraSpace);
|
||||
|
||||
// Make sure we don't make an out of range
|
||||
if (leftOversPixel < _segments.length - 1)
|
||||
if (leftOversPixel < [self segmentCount] - 1)
|
||||
{
|
||||
for (var i = 0; i < leftOversPixel; i++)
|
||||
{
|
||||
[self setWidth:[_segments[i] width] + 1 forSegment:i];
|
||||
[_segments[i] frame].size.width += 1;
|
||||
}
|
||||
}
|
||||
|
||||
[self setFrameSize:CGSizeMake(originalWidth, [self frame].size.height)];
|
||||
[self tileWithChangedSegment:0];
|
||||
[self setFrameSize:CGSizeMake(originalWidth, CGRectGetHeight([self frame]))];
|
||||
[self tile];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
+156
-109
@@ -26,6 +26,7 @@
|
||||
@import "CPImage.j"
|
||||
@import "CPView.j"
|
||||
@import "CPCursor.j"
|
||||
@import "CPTrackingArea.j"
|
||||
|
||||
@class CPUserDefaults
|
||||
@global CPApp
|
||||
@@ -55,7 +56,9 @@ var CPSplitViewDelegate_splitView_canCollapseSubview_
|
||||
CPSplitViewDelegate_splitView_constrainMaxCoordinate_ofSubviewAt_ = 1 << 5,
|
||||
CPSplitViewDelegate_splitView_constrainMinCoordinate_ofSubviewAt_ = 1 << 6,
|
||||
CPSplitViewDelegate_splitView_constrainSplitPosition_ofSubviewAt_ = 1 << 7,
|
||||
CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_ = 1 << 8;
|
||||
CPSplitViewDelegate_splitView_resizeSubviewsWithOldSize_ = 1 << 8,
|
||||
CPSplitViewDelegate_splitViewDidResizeSubviews_ = 1 << 9,
|
||||
CPSplitViewDelegate_splitViewWillResizeSubviews_ = 1 << 10;
|
||||
|
||||
#define SPLIT_VIEW_MAYBE_POST_WILL_RESIZE() \
|
||||
if ((_suppressResizeNotificationsMask & DidPostWillResizeNotification) === 0) \
|
||||
@@ -114,6 +117,7 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
CPArray _DOMDividerElements;
|
||||
CPString _dividerImagePath;
|
||||
int _drawingDivider;
|
||||
BOOL _isTracking;
|
||||
|
||||
CPString _autosaveName;
|
||||
BOOL _shouldAutosave;
|
||||
@@ -155,6 +159,7 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
_buttonBars = [];
|
||||
|
||||
_shouldAutosave = YES;
|
||||
_isTracking = NO;
|
||||
|
||||
[self _setVertical:YES];
|
||||
}
|
||||
@@ -210,6 +215,9 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
[_subviews[index] setFrame:CGRectMake(0, ROUND((eachSize + dividerThickness) * index), frame.size.width, eachSize)];
|
||||
}
|
||||
|
||||
if (_DOMDividerElements[_drawingDivider])
|
||||
[self _setupDOMDivider];
|
||||
|
||||
[self setNeedsDisplay:YES];
|
||||
[self _postNotificationDidResize];
|
||||
|
||||
@@ -360,9 +368,9 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
_DOMDividerElements[_drawingDivider].style.backgroundRepeat = "repeat";
|
||||
|
||||
CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]);
|
||||
[self _setupDOMDivider];
|
||||
}
|
||||
|
||||
[self _setupDOMDivider];
|
||||
CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, CGRectGetMinX(aRect), CGRectGetMinY(aRect));
|
||||
CPDOMDisplayServerSetStyleSize(_DOMDividerElements[_drawingDivider], CGRectGetWidth(aRect), CGRectGetHeight(aRect));
|
||||
#endif
|
||||
@@ -449,17 +457,26 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
return nil;
|
||||
|
||||
var point = [self convertPoint:aPoint fromView:[self superview]],
|
||||
count = [_subviews count] - 1;
|
||||
dividerIndex = [self _dividerAtPoint:point];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if ([self cursorAtPoint:point hitDividerAtIndex:i])
|
||||
return self;
|
||||
}
|
||||
if (dividerIndex !== CPNotFound)
|
||||
return self;
|
||||
|
||||
return [super hitTest:aPoint];
|
||||
}
|
||||
|
||||
- (CPInteger)_dividerAtPoint:(CGPoint)aPoint
|
||||
{
|
||||
var count = [_subviews count] - 1;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if ([self cursorAtPoint:aPoint hitDividerAtIndex:i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return CPNotFound;
|
||||
}
|
||||
/*
|
||||
Tracks the divider.
|
||||
@param anEvent the input event
|
||||
@@ -470,12 +487,13 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
|
||||
if (type == CPLeftMouseUp)
|
||||
{
|
||||
// We disabled autosaving during tracking.
|
||||
_shouldAutosave = YES;
|
||||
|
||||
if (_currentDivider != CPNotFound)
|
||||
if ([anEvent clickCount] == 2 || (_isTracking && _currentDivider != CPNotFound))
|
||||
{
|
||||
// We disabled autosaving during tracking.
|
||||
_shouldAutosave = YES;
|
||||
_currentDivider = CPNotFound;
|
||||
_isTracking = NO;
|
||||
|
||||
[self _autosave];
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
@@ -485,59 +503,55 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
|
||||
if (type == CPLeftMouseDown)
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
count = [_subviews count] - 1;
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
_currentDivider = [self _dividerAtPoint:point];
|
||||
|
||||
_currentDivider = CPNotFound;
|
||||
var frame = [_subviews[_currentDivider] frame],
|
||||
startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent];
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
if ([anEvent clickCount] == 2 &&
|
||||
[self _delegateRespondsToSplitViewCanCollapseSubview] &&
|
||||
[self _delegateRespondsToSplitViewshouldCollapseSubviewForDoubleClickOnDividerAtIndex])
|
||||
{
|
||||
var frame = [_subviews[i] frame],
|
||||
startPosition = frame.origin[_originComponent] + frame.size[_sizeComponent];
|
||||
var minPosition = [self minPossiblePositionOfDividerAtIndex:_currentDivider],
|
||||
maxPosition = [self maxPossiblePositionOfDividerAtIndex:_currentDivider],
|
||||
preCollapsePosition = [_preCollapsePositions objectForKey:"" + _currentDivider] || 0;
|
||||
|
||||
if ([self cursorAtPoint:point hitDividerAtIndex:i])
|
||||
if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[_currentDivider]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[_currentDivider] forDoubleClickOnDividerAtIndex:_currentDivider])
|
||||
{
|
||||
if ([anEvent clickCount] == 2 &&
|
||||
[self _delegateRespondsToSplitViewCanCollapseSubview] &&
|
||||
[self _delegateRespondsToSplitViewshouldCollapseSubviewForDoubleClickOnDividerAtIndex])
|
||||
{
|
||||
var minPosition = [self minPossiblePositionOfDividerAtIndex:i],
|
||||
maxPosition = [self maxPossiblePositionOfDividerAtIndex:i],
|
||||
preCollapsePosition = [_preCollapsePositions objectForKey:"" + i] || 0;
|
||||
|
||||
if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[i]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[i] forDoubleClickOnDividerAtIndex:i])
|
||||
{
|
||||
if ([self isSubviewCollapsed:_subviews[i]])
|
||||
[self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||
else
|
||||
[self setPosition:minPosition ofDividerAtIndex:i];
|
||||
}
|
||||
else if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[i + 1]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[i + 1] forDoubleClickOnDividerAtIndex:i])
|
||||
{
|
||||
if ([self isSubviewCollapsed:_subviews[i + 1]])
|
||||
[self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:i];
|
||||
else
|
||||
[self setPosition:maxPosition ofDividerAtIndex:i];
|
||||
}
|
||||
}
|
||||
if ([self isSubviewCollapsed:_subviews[_currentDivider]])
|
||||
[self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:_currentDivider];
|
||||
else
|
||||
{
|
||||
_currentDivider = i;
|
||||
_initialOffset = startPosition - point[_originComponent];
|
||||
|
||||
// Don't autosave during a resize. We'll wait until it's done.
|
||||
_shouldAutosave = NO;
|
||||
[self _postNotificationWillResize];
|
||||
}
|
||||
[self setPosition:minPosition ofDividerAtIndex:_currentDivider];
|
||||
}
|
||||
else if ([self _sendDelegateSplitViewCanCollapseSubview:_subviews[_currentDivider + 1]] && [self _sendDelegateSplitViewShouldCollapseSubview:_subviews[_currentDivider + 1] forDoubleClickOnDividerAtIndex:_currentDivider])
|
||||
{
|
||||
if ([self isSubviewCollapsed:_subviews[_currentDivider + 1]])
|
||||
[self setPosition:preCollapsePosition ? preCollapsePosition : (minPosition + (maxPosition - minPosition) / 2) ofDividerAtIndex:_currentDivider];
|
||||
else
|
||||
[self setPosition:maxPosition ofDividerAtIndex:_currentDivider];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_initialOffset = startPosition - point[_originComponent];
|
||||
// Don't autosave during a resize. We'll wait until it's done.
|
||||
_shouldAutosave = NO;
|
||||
[self _postNotificationWillResize];
|
||||
}
|
||||
|
||||
if (_currentDivider === CPNotFound)
|
||||
return;
|
||||
}
|
||||
|
||||
else if (type == CPLeftMouseDragged && _currentDivider != CPNotFound)
|
||||
{
|
||||
if (!_isTracking)
|
||||
{
|
||||
// Don't autosave during a resize. We'll wait until it's done.
|
||||
_shouldAutosave = NO;
|
||||
[self _postNotificationWillResize];
|
||||
|
||||
_isTracking = YES;
|
||||
}
|
||||
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
|
||||
[self setPosition:(point[_originComponent] + _initialOffset) ofDividerAtIndex:_currentDivider];
|
||||
@@ -550,8 +564,11 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
// FIXME: This should not trap events if not on a divider!
|
||||
[self trackDivider:anEvent];
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
dividerIndex = [self _dividerAtPoint:point];
|
||||
|
||||
if (dividerIndex !== CPNotFound)
|
||||
[self trackDivider:anEvent];
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
@@ -560,26 +577,6 @@ var ShouldSuppressResizeNotifications = 1,
|
||||
//[[self window] setAcceptsMouseMovedEvents:YES];
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(CPEvent)anEvent
|
||||
{
|
||||
// Tracking code handles cursor by itself.
|
||||
if (_currentDivider == CPNotFound)
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(CPEvent)anEvent
|
||||
{
|
||||
if (_currentDivider == CPNotFound)
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(CPEvent)anEvent
|
||||
{
|
||||
if (_currentDivider == CPNotFound)
|
||||
// FIXME: we should use CPCursor push/pop (if previous currentCursor != arrow).
|
||||
[[CPCursor arrowCursor] set];
|
||||
}
|
||||
|
||||
- (void)_updateResizeCursor:(CPEvent)anEvent
|
||||
{
|
||||
var point = [self convertPoint:[anEvent locationInWindow] fromView:nil];
|
||||
@@ -967,26 +964,14 @@ The sum of the views and the sum of the dividers should be equal to the size of
|
||||
if (_delegate === aDelegate)
|
||||
return;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewDidResizeSubviewsNotification object:self];
|
||||
_delegate = aDelegate;
|
||||
_implementedDelegateMethods = 0;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:_delegate name:CPSplitViewWillResizeSubviewsNotification object:self];
|
||||
_implementedDelegateMethods |= CPSplitViewDelegate_splitViewWillResizeSubviews_;
|
||||
|
||||
_delegate = aDelegate;
|
||||
_implementedDelegateMethods = 0;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
|
||||
[[CPNotificationCenter defaultCenter] addObserver:_delegate
|
||||
selector:@selector(splitViewDidResizeSubviews:)
|
||||
name:CPSplitViewDidResizeSubviewsNotification
|
||||
object:self];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitViewWillResizeSubviews:)])
|
||||
[[CPNotificationCenter defaultCenter] addObserver:_delegate
|
||||
selector:@selector(splitViewWillResizeSubviews:)
|
||||
name:CPSplitViewWillResizeSubviewsNotification
|
||||
object:self];
|
||||
if ([_delegate respondsToSelector:@selector(splitViewDidResizeSubviews:)])
|
||||
_implementedDelegateMethods |= CPSplitViewDelegate_splitViewDidResizeSubviews_;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(splitView:canCollapseSubview:)])
|
||||
_implementedDelegateMethods |= CPSplitViewDelegate_splitView_canCollapseSubview_;
|
||||
@@ -1064,27 +1049,12 @@ The sum of the views and the sum of the dividers should be equal to the size of
|
||||
|
||||
- (void)_postNotificationWillResize
|
||||
{
|
||||
var userInfo = nil;
|
||||
|
||||
if (_currentDivider !== CPNotFound)
|
||||
userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewWillResizeSubviewsNotification
|
||||
object:self
|
||||
userInfo:userInfo];
|
||||
[self _sendDelegateSplitViewWillResizeSubviews];
|
||||
}
|
||||
|
||||
- (void)_postNotificationDidResize
|
||||
{
|
||||
var userInfo = nil;
|
||||
|
||||
if (_currentDivider !== CPNotFound)
|
||||
userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewDidResizeSubviewsNotification
|
||||
object:self
|
||||
userInfo:userInfo];
|
||||
|
||||
[self _sendDelegateSplitViewDidResizeSubviews];
|
||||
|
||||
// TODO Cocoa always autosaves on "viewDidEndLiveResize". If Cappuccino adds support for this we
|
||||
// should do the same.
|
||||
@@ -1230,6 +1200,46 @@ The sum of the views and the sum of the dividers should be equal to the size of
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPSplitView (CPTrackingArea)
|
||||
{
|
||||
CPMutableArray _splitViewTrackingAreas;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (_splitViewTrackingAreas)
|
||||
{
|
||||
for (var i = 0, count = [_splitViewTrackingAreas count]; i < count; i++)
|
||||
[self removeTrackingArea:_splitViewTrackingAreas[i]];
|
||||
|
||||
_splitViewTrackingAreas = nil;
|
||||
}
|
||||
|
||||
var options = CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow;
|
||||
|
||||
_splitViewTrackingAreas = @[];
|
||||
|
||||
for (var i = 0; i < _subviews.length - 1; i++)
|
||||
{
|
||||
[_splitViewTrackingAreas addObject:[[CPTrackingArea alloc] initWithRect:[self effectiveRectOfDividerAtIndex:i]
|
||||
options:options
|
||||
owner:self
|
||||
userInfo:nil]];
|
||||
|
||||
[self addTrackingArea:_splitViewTrackingAreas[i]];
|
||||
}
|
||||
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
- (void)cursorUpdate:(CPEvent)anEvent
|
||||
{
|
||||
if (_currentDivider === CPNotFound)
|
||||
[self _updateResizeCursor:anEvent];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPSplitView (CPSplitViewDelegate)
|
||||
|
||||
@@ -1369,6 +1379,42 @@ The sum of the views and the sum of the dividers should be equal to the size of
|
||||
[_delegate splitView:self resizeSubviewsWithOldSize:oldSize];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
Call the delegate splitViewWillResizeSubviews:
|
||||
*/
|
||||
- (void)_sendDelegateSplitViewWillResizeSubviews
|
||||
{
|
||||
var userInfo = nil;
|
||||
|
||||
if (_currentDivider !== CPNotFound)
|
||||
userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
|
||||
|
||||
if (_implementedDelegateMethods & CPSplitViewDelegate_splitViewWillResizeSubviews_)
|
||||
[_delegate splitViewWillResizeSubviews:[[CPNotification alloc] initWithName:CPSplitViewWillResizeSubviewsNotification object:self userInfo:userInfo]];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewWillResizeSubviewsNotification object:self userInfo:userInfo];
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
Call the delegate splitViewDidResizeSubviews:
|
||||
*/
|
||||
- (void)_sendDelegateSplitViewDidResizeSubviews
|
||||
{
|
||||
var userInfo = nil;
|
||||
|
||||
if (_currentDivider !== CPNotFound)
|
||||
userInfo = @{ @"CPSplitViewDividerIndex": _currentDivider };
|
||||
|
||||
if (_implementedDelegateMethods & CPSplitViewDelegate_splitViewDidResizeSubviews_)
|
||||
[_delegate splitViewDidResizeSubviews:[[CPNotification alloc] initWithName:CPSplitViewDidResizeSubviewsNotification object:self userInfo:userInfo]];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPSplitViewDidResizeSubviewsNotification object:self userInfo:userInfo];
|
||||
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1423,6 +1469,7 @@ var CPSplitViewDelegateKey = "CPSplitViewDelegateKey",
|
||||
|
||||
_currentDivider = CPNotFound;
|
||||
_shouldAutosave = YES;
|
||||
_isTracking = NO;
|
||||
|
||||
_DOMDividerElements = [];
|
||||
|
||||
|
||||
+6
-6
@@ -188,12 +188,12 @@
|
||||
[_buttonUp setFrame:upFrame];
|
||||
[_buttonDown setFrame:downFrame];
|
||||
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateDisabled]];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateHighlighted]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateDisabled]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inState:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inState:[CPThemeStateBordered, CPThemeStateHighlighted]];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]];
|
||||
[_buttonUp setValue:[self valueForThemeAttribute:@"bezel-color-up-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered]] forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateDisabled]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateDisabled]];
|
||||
[_buttonDown setValue:[self valueForThemeAttribute:@"bezel-color-down-button" inStates:[controlSizeThemeState, CPThemeStateBordered, CPThemeStateHighlighted]] forThemeAttribute:@"bezel-color" inStates:[CPThemeStateBordered, CPThemeStateHighlighted]];
|
||||
}
|
||||
|
||||
- (void)_sizeToFit
|
||||
|
||||
@@ -24,9 +24,13 @@
|
||||
|
||||
@import "CGGeometry.j"
|
||||
@import "CPPlatformString.j"
|
||||
@import "CPFont.j"
|
||||
@import "CPCompatibility.j"
|
||||
|
||||
|
||||
var CPStringSizeWithFontInWidthCache = {};
|
||||
var CPStringSizeWithFontInWidthCache = [],
|
||||
CPStringSizeWithFontHeightCache = [],
|
||||
CPStringSizeMeasuringContext;
|
||||
|
||||
CPStringSizeCachingEnabled = YES;
|
||||
|
||||
@@ -53,21 +57,67 @@ CPStringSizeCachingEnabled = YES;
|
||||
return [self sizeWithFont:aFont inWidth:NULL];
|
||||
}
|
||||
|
||||
- (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
|
||||
+ (void) initialize
|
||||
{
|
||||
if ([self class] != [CPString class])
|
||||
return;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (CPFeatureIsCompatible(CPHTMLCanvasFeature) && !CPStringSizeMeasuringContext)
|
||||
CPStringSizeMeasuringContext = CGBitmapGraphicsContextCreate();
|
||||
#endif
|
||||
}
|
||||
|
||||
- (CGSize)_sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
|
||||
{
|
||||
var size;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (!CPStringSizeCachingEnabled)
|
||||
return [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
|
||||
|
||||
var cacheKey = self + [aFont cssString] + aWidth,
|
||||
size = CPStringSizeWithFontInWidthCache[cacheKey];
|
||||
var sizeCacheForFont = CPStringSizeWithFontInWidthCache[self];
|
||||
|
||||
if (size === undefined)
|
||||
{
|
||||
if (sizeCacheForFont === undefined)
|
||||
sizeCacheForFont = CPStringSizeWithFontInWidthCache[self] = [];
|
||||
|
||||
if (!aWidth)
|
||||
aWidth = '0';
|
||||
|
||||
var cssString = [aFont cssString],
|
||||
cacheKey = cssString + '_' + aWidth;
|
||||
|
||||
size = sizeCacheForFont[cacheKey];
|
||||
|
||||
if (size !== undefined && sizeCacheForFont.hasOwnProperty(cacheKey))
|
||||
return CGSizeMakeCopy(size);
|
||||
|
||||
if (!CPFeatureIsCompatible(CPHTMLCanvasFeature) || aWidth > 0)
|
||||
size = [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth];
|
||||
CPStringSizeWithFontInWidthCache[cacheKey] = size;
|
||||
else
|
||||
{
|
||||
if (CPPlatformHasBug(CPTextSizingAlwaysNeedsSetFontBug) || CPStringSizeMeasuringContext.font !== cssString)
|
||||
CPStringSizeMeasuringContext.font = cssString;
|
||||
|
||||
var fontHeight = CPStringSizeWithFontHeightCache[cssString];
|
||||
|
||||
if (fontHeight === undefined)
|
||||
fontHeight = CPStringSizeWithFontHeightCache[cssString] = [aFont defaultLineHeightForFont];
|
||||
|
||||
size = CGSizeMake(CPStringSizeMeasuringContext.measureText(self).width, fontHeight);
|
||||
}
|
||||
|
||||
sizeCacheForFont[cacheKey] = size;
|
||||
#else
|
||||
size = CGSizeMake(0, 0);
|
||||
#endif
|
||||
return CGSizeMakeCopy(size);
|
||||
}
|
||||
|
||||
- (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth
|
||||
{
|
||||
var size = [self _sizeWithFont:aFont inWidth:aWidth];
|
||||
return CGSizeMake(CEIL(size.width), size.height);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+482
-97
@@ -34,6 +34,9 @@ CPNoTabsBezelBorder = 4; //Displays no tabs and has a bezeled border.
|
||||
CPNoTabsLineBorder = 5; //Has no tabs and displays a line border.
|
||||
CPNoTabsNoBorder = 6; //Displays no tabs and no border.
|
||||
|
||||
@class _CPTabViewBox
|
||||
@class _CPSegmentedControl
|
||||
|
||||
var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
CPTabViewShouldSelectTabViewItemSelector = 1 << 2,
|
||||
CPTabViewWillSelectTabViewItemSelector = 1 << 3,
|
||||
@@ -63,10 +66,11 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
{
|
||||
CPArray _items;
|
||||
|
||||
CPSegmentedControl _tabs;
|
||||
CPBox _box;
|
||||
_CPSegmentedControl _tabs;
|
||||
_CPTabViewBox _box;
|
||||
CPView _placeholderView @accessors;
|
||||
|
||||
CPNumber _selectedIndex;
|
||||
CPTabViewItem _selectedTabViewItem;
|
||||
|
||||
CPTabViewType _type;
|
||||
CPFont _font;
|
||||
@@ -79,9 +83,8 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_items = [CPArray array];
|
||||
|
||||
[self _init];
|
||||
_selectedTabViewItem = nil;
|
||||
[self setTabViewType:CPTopTabsBezelBorder];
|
||||
}
|
||||
|
||||
@@ -90,19 +93,26 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_selectedIndex = CPNotFound;
|
||||
|
||||
_tabs = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
|
||||
[_tabs setHitTests:NO];
|
||||
_tabs = [[_CPSegmentedControl alloc] initWithFrame:CGRectMakeZero()];
|
||||
[_tabs setTabView:self];
|
||||
[_tabs setSegments:[CPArray array]];
|
||||
|
||||
var height = [_tabs valueForThemeAttribute:@"min-size"].height;
|
||||
[_tabs setFrameSize:CGSizeMake(0, height)];
|
||||
|
||||
_box = [[CPBox alloc] initWithFrame:[self bounds]];
|
||||
_box = [[_CPTabViewBox alloc] initWithFrame:[self bounds]];
|
||||
[_box setTabView:self];
|
||||
[self setBackgroundColor:[CPColor colorWithCalibratedWhite:0.95 alpha:1.0]];
|
||||
|
||||
[self addSubview:_box];
|
||||
[self addSubview:_tabs];
|
||||
|
||||
_placeholderView = nil;
|
||||
}
|
||||
|
||||
- (CPArray)items
|
||||
{
|
||||
return [_tabs segments];
|
||||
}
|
||||
|
||||
// Adding and Removing Tabs
|
||||
@@ -112,7 +122,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)addTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
[self insertTabViewItem:aTabViewItem atIndex:[_items count]];
|
||||
[self insertTabViewItem:aTabViewItem atIndex:[self numberOfTabViewItems]];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -122,15 +132,24 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)insertTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(CPUInteger)anIndex
|
||||
{
|
||||
[_items insertObject:aTabViewItem atIndex:anIndex];
|
||||
[self _insertTabViewItems:[aTabViewItem] atIndexes:[CPIndexSet indexSetWithIndex:anIndex]];
|
||||
}
|
||||
|
||||
[self _updateItems];
|
||||
[self _repositionTabs];
|
||||
- (void)_insertTabViewItems:(CPArray)tabViewItems atIndexes:(CPIndexSet)indexes
|
||||
{
|
||||
var prevItemsCount = [self numberOfTabViewItems];
|
||||
|
||||
[aTabViewItem _setTabView:self];
|
||||
[_tabs insertSegments:tabViewItems atIndexes:indexes];
|
||||
[tabViewItems makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||
[self tileWithChangedItem:[tabViewItems firstObject]];
|
||||
[self _reverseSetContent];
|
||||
|
||||
[self _sendDelegateTabViewDidChangeNumberOfTabViewItems];
|
||||
|
||||
// Do not allow empty selection if selection bindings are not enabled.
|
||||
if (prevItemsCount == 0 && [self numberOfTabViewItems] > 0 && ![self _isSelectionBinded])
|
||||
[self _selectTabViewItemAtIndex:0];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -139,23 +158,39 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)removeTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
var count = [_items count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if ([_items objectAtIndex:i] === aTabViewItem)
|
||||
{
|
||||
[_items removeObjectAtIndex:i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
var idx = [[self items] indexOfObjectIdenticalTo:aTabViewItem];
|
||||
|
||||
[self _updateItems];
|
||||
[self _repositionTabs];
|
||||
if (idx == CPNotFound)
|
||||
return;
|
||||
|
||||
[_tabs removeSegmentsAtIndexes:[CPIndexSet indexSetWithIndex:idx]];
|
||||
[aTabViewItem _setTabView:nil];
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||
[self tileWithChangedItem:nil];
|
||||
[self _didRemoveTabViewItem:aTabViewItem atIndex:idx];
|
||||
[self _reverseSetContent];
|
||||
|
||||
[self _sendDelegateTabViewDidChangeNumberOfTabViewItems];
|
||||
}
|
||||
|
||||
- (void)_didRemoveTabViewItem:(CPTabViewItem)aTabViewItem atIndex:(CPInteger)idx
|
||||
{
|
||||
// If the selection is managed by bindings, let the binder do that.
|
||||
if ([self _isSelectionBinded])
|
||||
return;
|
||||
|
||||
if (_selectedTabViewItem == aTabViewItem)
|
||||
{
|
||||
var didSelect = NO;
|
||||
|
||||
if (idx > 0)
|
||||
didSelect = [self selectTabViewItemAtIndex:idx - 1];
|
||||
else if ([self numberOfTabViewItems] > 0)
|
||||
didSelect = [self selectTabViewItemAtIndex:0];
|
||||
|
||||
if (didSelect == NO)
|
||||
_selectedTabViewItem == nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Accessing Tabs
|
||||
@@ -166,7 +201,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (int)indexOfTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
return [_items indexOfObjectIdenticalTo:aTabViewItem];
|
||||
return [[self items] indexOfObjectIdenticalTo:aTabViewItem];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -176,11 +211,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (int)indexOfTabViewItemWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
for (var index = [_items count]; index >= 0; index--)
|
||||
if ([[_items[index] identifier] isEqual:anIdentifier])
|
||||
return index;
|
||||
|
||||
return CPNotFound;
|
||||
return [[self items] indexOfObjectPassingTest:function(item, idx, stop)
|
||||
{
|
||||
return [[item identifier] isEqual:anIdentifier];
|
||||
}];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -189,7 +223,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (unsigned)numberOfTabViewItems
|
||||
{
|
||||
return [_items count];
|
||||
return [[self items] count];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -198,7 +232,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (CPTabViewItem)tabViewItemAtIndex:(CPUInteger)anIndex
|
||||
{
|
||||
return [_items objectAtIndex:anIndex];
|
||||
return [[self items] objectAtIndex:anIndex];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -207,7 +241,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (CPArray)tabViewItems
|
||||
{
|
||||
return [_items copy]; // Copy?
|
||||
return [[self items] copy]; // Copy?
|
||||
}
|
||||
|
||||
// Selecting a Tab
|
||||
@@ -217,7 +251,7 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)selectFirstTabViewItem:(id)aSender
|
||||
{
|
||||
if ([_items count] === 0)
|
||||
if ([self numberOfTabViewItems] === 0)
|
||||
return; // throw?
|
||||
|
||||
[self selectTabViewItemAtIndex:0];
|
||||
@@ -229,10 +263,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)selectLastTabViewItem:(id)aSender
|
||||
{
|
||||
if ([_items count] === 0)
|
||||
if ([self numberOfTabViewItems] === 0)
|
||||
return; // throw?
|
||||
|
||||
[self selectTabViewItemAtIndex:[_items count] - 1];
|
||||
[self selectTabViewItemAtIndex:[self numberOfTabViewItems] - 1];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -241,12 +275,12 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)selectNextTabViewItem:(id)aSender
|
||||
{
|
||||
if (_selectedIndex === CPNotFound)
|
||||
if (_selectedTabViewItem === nil)
|
||||
return;
|
||||
|
||||
var nextIndex = _selectedIndex + 1;
|
||||
var nextIndex = [self indexOfTabViewItem:_selectedTabViewItem] + 1;
|
||||
|
||||
if (nextIndex === [_items count])
|
||||
if (nextIndex === [self numberOfTabViewItems])
|
||||
// does nothing. According to spec at (http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/ApplicationKit/Classes/NSTabView_Class/Reference/Reference.html#//apple_ref/occ/instm/NSTabView/selectNextTabViewItem:)
|
||||
return;
|
||||
|
||||
@@ -259,10 +293,10 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (void)selectPreviousTabViewItem:(id)aSender
|
||||
{
|
||||
if (_selectedIndex === CPNotFound)
|
||||
if (_selectedTabViewItem === nil)
|
||||
return;
|
||||
|
||||
var previousIndex = _selectedIndex - 1;
|
||||
var previousIndex = [self indexOfTabViewItem:_selectedTabViewItem] - 1;
|
||||
|
||||
if (previousIndex < 0)
|
||||
return; // does nothing. See above.
|
||||
@@ -285,36 +319,70 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
*/
|
||||
- (BOOL)selectTabViewItemAtIndex:(CPUInteger)anIndex
|
||||
{
|
||||
if (anIndex === _selectedIndex)
|
||||
return;
|
||||
|
||||
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
|
||||
|
||||
if ((_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector) && ![_delegate tabView:self shouldSelectTabViewItem:aTabViewItem])
|
||||
if (![self _selectTabViewItemAtIndex:anIndex])
|
||||
return NO;
|
||||
|
||||
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
|
||||
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
|
||||
|
||||
[_tabs selectSegmentWithTag:anIndex];
|
||||
[self _setSelectedIndex:anIndex];
|
||||
|
||||
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
|
||||
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
|
||||
[self _reverseSetSelectedIndex];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Like selectTabViewItemAtIndex: but without bindings interaction
|
||||
- (BOOL)_selectTabViewItemAtIndex:(CPUInteger)anIndex
|
||||
{
|
||||
var aTabViewItem = [self tabViewItemAtIndex:anIndex];
|
||||
|
||||
if (aTabViewItem == _selectedTabViewItem)
|
||||
return NO;
|
||||
|
||||
if (![self _sendDelegateShouldSelectTabViewItem:aTabViewItem])
|
||||
return NO;
|
||||
|
||||
[self _sendDelegateWillSelectTabViewItem:aTabViewItem];
|
||||
|
||||
[_tabs setSelectedSegment:anIndex];
|
||||
_selectedTabViewItem = aTabViewItem;
|
||||
[self _loadTabViewItem:aTabViewItem];
|
||||
|
||||
[self _sendDelegateDidSelectTabViewItem:aTabViewItem];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)_loadTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
var controller = [aTabViewItem viewController];
|
||||
|
||||
if (controller !== nil && ![controller isViewLoaded])
|
||||
{
|
||||
[controller loadViewWithCompletionHandler:function(view, error)
|
||||
{
|
||||
if (error !== nil)
|
||||
{
|
||||
CPLog.warn("Could not load the view for item " + aTabViewItem + ". " + error);
|
||||
}
|
||||
else if (view !== nil)
|
||||
{
|
||||
[aTabViewItem setView:view];
|
||||
|
||||
if ([self selectedTabViewItem] == aTabViewItem)
|
||||
[self _displayItemView:view];
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self _displayItemView:[aTabViewItem view]];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the current item being displayed.
|
||||
@return the tab view item currenly being displayed by the receiver
|
||||
*/
|
||||
- (CPTabViewItem)selectedTabViewItem
|
||||
{
|
||||
if (_selectedIndex != CPNotFound)
|
||||
return [_items objectAtIndex:_selectedIndex];
|
||||
|
||||
return nil;
|
||||
return _selectedTabViewItem;
|
||||
}
|
||||
|
||||
// Modifying the font
|
||||
@@ -375,6 +443,14 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)tileWithChangedItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
var segment = aTabViewItem ? [self indexOfTabViewItem:aTabViewItem] : 0;
|
||||
[_tabs tileWithChangedSegment:segment];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
// Even if CPTabView's autoresizesSubviews is NO, _tabs and _box has to be laid out.
|
||||
@@ -392,7 +468,6 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
[_box setFrame:CGRectMake(0, origin, CGRectGetWidth(aFrame),
|
||||
CGRectGetHeight(aFrame) - segmentedHeight / 2)];
|
||||
|
||||
[self _updateItems];
|
||||
[self _repositionTabs];
|
||||
}
|
||||
}
|
||||
@@ -451,14 +526,6 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
return [_box backgroundColor];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
var segmentIndex = [_tabs testSegment:[_tabs convertPoint:[anEvent locationInWindow] fromView:nil]];
|
||||
|
||||
if (segmentIndex != CPNotFound && [self selectTabViewItemAtIndex:segmentIndex])
|
||||
[_tabs trackSegment:anEvent];
|
||||
}
|
||||
|
||||
- (void)_repositionTabs
|
||||
{
|
||||
var horizontalCenterOfSelf = CGRectGetWidth([self bounds]) / 2,
|
||||
@@ -470,30 +537,243 @@ var CPTabViewDidSelectTabViewItemSelector = 1 << 1,
|
||||
[_tabs setCenter:CGPointMake(horizontalCenterOfSelf, verticalCenterOfTabs)];
|
||||
}
|
||||
|
||||
- (void)_setSelectedIndex:(CPNumber)index
|
||||
- (void)_displayItemView:(CPView)aView
|
||||
{
|
||||
_selectedIndex = index;
|
||||
[self _setContentViewFromItem:[_items objectAtIndex:_selectedIndex]];
|
||||
[_box setContentView:aView];
|
||||
}
|
||||
|
||||
- (void)_setContentViewFromItem:(CPTabViewItem)anItem
|
||||
// DELEGATE METHODS
|
||||
|
||||
- (BOOL)_sendDelegateShouldSelectTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
[_box setContentView:[anItem view]];
|
||||
if (_delegateSelectors & CPTabViewShouldSelectTabViewItemSelector)
|
||||
return [_delegate tabView:self shouldSelectTabViewItem:aTabViewItem];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)_updateItems
|
||||
- (void)_sendDelegateWillSelectTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
var count = [_items count];
|
||||
[_tabs setSegmentCount:count];
|
||||
if (_delegateSelectors & CPTabViewWillSelectTabViewItemSelector)
|
||||
[_delegate tabView:self willSelectTabViewItem:aTabViewItem];
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
- (void)_sendDelegateDidSelectTabViewItem:(CPTabViewItem)aTabViewItem
|
||||
{
|
||||
if (_delegateSelectors & CPTabViewDidSelectTabViewItemSelector)
|
||||
[_delegate tabView:self didSelectTabViewItem:aTabViewItem];
|
||||
}
|
||||
|
||||
- (void)_sendDelegateTabViewDidChangeNumberOfTabViewItems
|
||||
{
|
||||
if (_delegateSelectors & CPTabViewDidChangeNumberOfTabViewItemsSelector)
|
||||
[_delegate tabViewDidChangeNumberOfTabViewItems:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTabView (BindingSupport)
|
||||
|
||||
+ (Class)_binderClassForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding == CPContentBinding)
|
||||
return [_CPTabViewContentBinder class];
|
||||
else if (aBinding == CPSelectionIndexesBinding || aBinding == CPSelectedIndexBinding)
|
||||
return [_CPTabViewSelectionBinder class];
|
||||
|
||||
return [super _binderClassForBinding:aBinding];
|
||||
}
|
||||
|
||||
+ (BOOL)isBindingExclusive:(CPString)aBinding
|
||||
{
|
||||
return (aBinding == CPSelectionIndexesBinding || aBinding == CPSelectedIndexBinding);
|
||||
}
|
||||
|
||||
- (void)_reverseSetContent
|
||||
{
|
||||
var theBinder = [self binderForBinding:CPContentBinding];
|
||||
[theBinder reverseSetValueFor:@"items"];
|
||||
}
|
||||
|
||||
- (void)_reverseSetSelectedIndex
|
||||
{
|
||||
var theBinder = [self binderForBinding:CPSelectionIndexesBinding];
|
||||
|
||||
if (theBinder !== nil)
|
||||
[theBinder reverseSetValueFor:@"selectionIndexes"];
|
||||
else
|
||||
{
|
||||
[_tabs setLabel:[[_items objectAtIndex:i] label] forSegment:i];
|
||||
[_tabs setTag:i forSegment:i];
|
||||
theBinder = [self binderForBinding:CPSelectedIndexBinding];
|
||||
[theBinder reverseSetValueFor:@"selectedIndex"];
|
||||
}
|
||||
}
|
||||
|
||||
- (CPBinder)binderForBinding:(CPString)aBinding
|
||||
{
|
||||
var cls = [[self class] _binderClassForBinding:aBinding];
|
||||
return [cls getBinding:aBinding forObject:self];
|
||||
}
|
||||
|
||||
- (BOOL)_isSelectionBinded
|
||||
{
|
||||
return [self binderForBinding:CPSelectionIndexesBinding] || [self binderForBinding:CPSelectedIndexBinding];
|
||||
}
|
||||
|
||||
- (void)setItems:(CPArray)tabViewItems
|
||||
{
|
||||
if ([tabViewItems isEqualToArray:[_tabs segments]])
|
||||
return;
|
||||
|
||||
[[self items] makeObjectsPerformSelector:@selector(_setTabView:) withObject:nil];
|
||||
[_tabs setSegments:tabViewItems];
|
||||
[tabViewItems makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
|
||||
|
||||
[self tileWithChangedItem:nil];
|
||||
|
||||
// Update the selection because setSegments: did remove all previous segments AND the selection.
|
||||
[_tabs setSelectedSegment:[self indexOfTabViewItem:_selectedTabViewItem]];
|
||||
|
||||
// should we send delegate methods in bindings mode ?
|
||||
//[self _delegateTabViewDidChangeNumberOfTabViewItems:self];
|
||||
}
|
||||
|
||||
- (void)_deselectAll
|
||||
{
|
||||
[_tabs setSelectedSegment:-1];
|
||||
_selectedTabViewItem = nil;
|
||||
}
|
||||
|
||||
- (void)_displayPlaceholder:(CPString)aPlaceholder
|
||||
{
|
||||
if (_placeholderView == nil)
|
||||
{
|
||||
_placeholderView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
|
||||
var textField = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
|
||||
[textField setTag:1000];
|
||||
[textField setTextColor:[CPColor whiteColor]];
|
||||
[textField setFont:[CPFont boldFontWithName:@"Geneva" size:18 italic:YES]];
|
||||
[_placeholderView addSubview:textField];
|
||||
}
|
||||
|
||||
if (_selectedIndex === CPNotFound)
|
||||
[self selectFirstTabViewItem:self];
|
||||
var textField = [_placeholderView viewWithTag:1000];
|
||||
[textField setStringValue:aPlaceholder];
|
||||
[textField sizeToFit];
|
||||
|
||||
var boxBounds = [_box bounds],
|
||||
textFieldBounds = [textField bounds],
|
||||
origin = CGPointMake(CGRectGetWidth(boxBounds)/2 - CGRectGetWidth(textFieldBounds)/2, CGRectGetHeight(boxBounds)/2 - CGRectGetHeight(textFieldBounds));
|
||||
|
||||
[textField setFrameOrigin:origin];
|
||||
|
||||
[self _displayItemView:_placeholderView];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Override
|
||||
|
||||
/*!
|
||||
Enabled controls accept first mouse by default.
|
||||
*/
|
||||
- (BOOL)acceptsFirstMouse:(CPEvent)anEvent
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPTabViewContentBinderNull = @"NO CONTENT";
|
||||
|
||||
@implementation _CPTabViewContentBinder : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
|
||||
{
|
||||
[super _updatePlaceholdersWithOptions:options];
|
||||
[self _setPlaceholder:_CPTabViewContentBinderNull forMarker:CPNullMarker isDefault:YES];
|
||||
}
|
||||
|
||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
||||
{
|
||||
[_source setItems:@[]];
|
||||
[_source _setPlaceholderView:aValue];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
||||
{
|
||||
[_source setItems:aValue];
|
||||
}
|
||||
|
||||
- (id)valueForBinding:(CPString)aBinding
|
||||
{
|
||||
return [_source items];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _CPTabViewSelectionBinderMultipleValues = @"Multiple Selection",
|
||||
_CPTabViewSelectionBinderNoSelection = @"No Selection";
|
||||
|
||||
@implementation _CPTabViewSelectionBinder : CPBinder
|
||||
{
|
||||
}
|
||||
|
||||
- (void)_updatePlaceholdersWithOptions:(CPDictionary)options
|
||||
{
|
||||
[super _updatePlaceholdersWithOptions:options];
|
||||
|
||||
[self _setPlaceholder:_CPTabViewSelectionBinderMultipleValues forMarker:CPMultipleValuesMarker isDefault:YES];
|
||||
[self _setPlaceholder:_CPTabViewSelectionBinderNoSelection forMarker:CPNoSelectionMarker isDefault:YES];
|
||||
}
|
||||
|
||||
- (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
|
||||
{
|
||||
if (aMarker == CPNoSelectionMarker || aMarker == CPNullMarker)
|
||||
[_source _deselectAll];
|
||||
|
||||
[_source _displayPlaceholder:aValue];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding == CPSelectionIndexesBinding)
|
||||
{
|
||||
if (aValue == nil || [aValue count] == 0)
|
||||
{
|
||||
[_source _deselectAll];
|
||||
[_source _displayPlaceholder:_CPTabViewSelectionBinderNoSelection];
|
||||
}
|
||||
else if ([aValue count] > 1)
|
||||
[_source _displayPlaceholder:_CPTabViewSelectionBinderMultipleValues];
|
||||
else if ([aValue firstIndex] < [_source numberOfTabViewItems])
|
||||
[_source _selectTabViewItemAtIndex:[aValue firstIndex]];
|
||||
}
|
||||
else if (aBinding == CPSelectedIndexBinding)
|
||||
{
|
||||
if (aValue == CPNotFound)
|
||||
{
|
||||
[_source _deselectAll];
|
||||
[_source _displayPlaceholder:_CPTabViewSelectionBinderNoSelection];
|
||||
}
|
||||
else if (aValue < [_source numberOfTabViewItems])
|
||||
[_source _selectTabViewItemAtIndex:aValue];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)valueForBinding:(CPString)aBinding
|
||||
{
|
||||
if (aBinding == CPSelectionIndexesBinding)
|
||||
{
|
||||
var result = [CPIndexSet indexSet],
|
||||
idx = [_source indexOfTabViewItem:[_source selectedTabViewItem]];
|
||||
|
||||
if (idx !== CPNotFound)
|
||||
[result addIndex:idx];
|
||||
|
||||
return result;
|
||||
}
|
||||
else if (aBinding == CPSelectedIndexBinding)
|
||||
return [_source indexOfTabViewItem:[_source selectedTabViewItem]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -515,12 +795,13 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
_font = [aCoder decodeObjectForKey:CPTabViewFontKey];
|
||||
[_tabs setFont:_font];
|
||||
|
||||
_items = [aCoder decodeObjectForKey:CPTabViewItemsKey];
|
||||
[_items makeObjectsPerformSelector:@selector(_setTabView:) withObject:self];
|
||||
var items = [aCoder decodeObjectForKey:CPTabViewItemsKey] || [CPArray array];
|
||||
[self _insertTabViewItems:items atIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [items count])]];
|
||||
|
||||
[self setDelegate:[aCoder decodeObjectForKey:CPTabViewDelegateKey]];
|
||||
|
||||
self.selectOnAwake = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
||||
_selectedTabViewItem = [aCoder decodeObjectForKey:CPTabViewSelectedItemKey];
|
||||
|
||||
_type = [aCoder decodeIntForKey:CPTabViewTypeKey];
|
||||
}
|
||||
|
||||
@@ -529,14 +810,23 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
[super awakeFromCib];
|
||||
|
||||
// This cannot be run in initWithCoder because it might call selectTabViewItem:, which is
|
||||
// not safe to call before the views of the tab views items are fully decoded.
|
||||
[self _updateItems];
|
||||
|
||||
if (self.selectOnAwake)
|
||||
if (_selectedTabViewItem)
|
||||
{
|
||||
[self selectTabViewItem:self.selectOnAwake];
|
||||
delete self.selectOnAwake;
|
||||
var idx = [self indexOfTabViewItem:_selectedTabViewItem];
|
||||
|
||||
if (idx !== CPNotFound)
|
||||
{
|
||||
// Temporarily set the selected item to not selected.
|
||||
// It allows the initial selection to be made correctly.
|
||||
_selectedTabViewItem = nil;
|
||||
|
||||
[self selectTabViewItemAtIndex:idx];
|
||||
}
|
||||
}
|
||||
|
||||
var type = _type;
|
||||
@@ -558,9 +848,7 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
|
||||
[aCoder encodeObject:_items forKey:CPTabViewItemsKey];
|
||||
|
||||
var selected = [self selectedTabViewItem];
|
||||
if (selected)
|
||||
[aCoder encodeObject:selected forKey:CPTabViewSelectedItemKey];
|
||||
[aCoder encodeConditionalObject:_selectedTabViewItem forKey:CPTabViewSelectedItemKey];
|
||||
|
||||
[aCoder encodeInt:_type forKey:CPTabViewTypeKey];
|
||||
[aCoder encodeObject:_font forKey:CPTabViewFontKey];
|
||||
@@ -569,3 +857,100 @@ var CPTabViewItemsKey = "CPTabViewItemsKey",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPTabViewBox : CPBox
|
||||
{
|
||||
CPTabView _tabView @accessors(property=tabView);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Override
|
||||
|
||||
- (CPView)hitTest:(CGPoint)aPoint
|
||||
{
|
||||
// Here we check if we have clicked on the segmentedControl of the tabView or not
|
||||
// If YES, the CPBox should not handle the click
|
||||
var segmentIndex = [_tabView._tabs testSegment:[_tabView._tabs convertPoint:aPoint fromView:[self superview]]];
|
||||
|
||||
if (segmentIndex != CPNotFound)
|
||||
return nil;
|
||||
|
||||
return [super hitTest:aPoint];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
# pragma mark -
|
||||
|
||||
// This subclass of CPSegmentedControl implements specific behaviour needed by CPTabView, which
|
||||
// differs in some ways from normal CPSegmentedControl habits :
|
||||
//
|
||||
// - all items are enabled
|
||||
// - when you select a tab (mouse down, holding) and drag upon another tab, the second one reacts
|
||||
// (showing a pushed state) and can then be selected by releasing the mouse over it.
|
||||
// - a delegate has the opportunity to accept or not the selection of an item
|
||||
|
||||
@implementation _CPSegmentedControl : CPSegmentedControl
|
||||
{
|
||||
CPTabView _tabView @accessors(property=tabView);
|
||||
}
|
||||
|
||||
- (void)trackSegment:(CPEvent)anEvent
|
||||
{
|
||||
var type = [anEvent type],
|
||||
location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
|
||||
currentSegment = [self testSegment:location];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CPLeftMouseUp:
|
||||
|
||||
if (_trackingSegment === CPNotFound)
|
||||
return;
|
||||
|
||||
if ((_trackingSegment !== _selectedSegment) &&
|
||||
[_tabView _sendDelegateShouldSelectTabViewItem:[_tabView tabViewItemAtIndex:_trackingSegment]])
|
||||
{
|
||||
[self setSelected:YES forSegment:_trackingSegment];
|
||||
_selectedSegment = _trackingSegment;
|
||||
[_tabView selectTabViewItemAtIndex:_selectedSegment];
|
||||
}
|
||||
|
||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||
|
||||
_trackingSegment = CPNotFound;
|
||||
|
||||
return;
|
||||
|
||||
case CPLeftMouseDown:
|
||||
|
||||
if (currentSegment > CPNotFound)
|
||||
{
|
||||
_trackingSegment = currentSegment;
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CPLeftMouseDragged:
|
||||
|
||||
if (_trackingSegment !== currentSegment)
|
||||
{
|
||||
if (_trackingSegment > CPNotFound)
|
||||
[self drawSegmentBezel:_trackingSegment highlight:NO];
|
||||
|
||||
_trackingSegment = currentSegment;
|
||||
|
||||
if (_trackingSegment > CPNotFound)
|
||||
[self drawSegmentBezel:_trackingSegment highlight:YES];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
[CPApp setTarget:self selector:@selector(trackSegment:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
+102
-10
@@ -24,6 +24,7 @@
|
||||
|
||||
@import "CPView.j"
|
||||
@class CPTabView
|
||||
@class CPViewController
|
||||
|
||||
/*
|
||||
The tab is currently selected.
|
||||
@@ -53,14 +54,34 @@ CPPressedTab = 2;
|
||||
*/
|
||||
@implementation CPTabViewItem : CPObject
|
||||
{
|
||||
id _identifier;
|
||||
CPString _label;
|
||||
id _identifier;
|
||||
CPString _label;
|
||||
CPInteger _tag @accessors(property=tag);
|
||||
|
||||
CPView _view;
|
||||
CPView _auxiliaryView;
|
||||
CPView _view;
|
||||
CPView _auxiliaryView;
|
||||
|
||||
CPTabView _tabView;
|
||||
unsigned _tabState; // Looks like it is not yet implemented
|
||||
CPTabView _tabView;
|
||||
unsigned _tabState; // Looks like it is not yet implemented
|
||||
|
||||
CPImage _image @accessors(property=image);
|
||||
CPViewController _viewController @accessors(getter=viewController);
|
||||
|
||||
BOOL _enabled @accessors(property=enabled);
|
||||
BOOL _selected @accessors(property=selected);
|
||||
CGRect _tabRect @accessors(property=frame);
|
||||
float _width @accessors(property=width);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
+ (CPTabViewItem)tabViewItemWithViewController:(CPViewController)aViewController
|
||||
{
|
||||
var item = [[CPTabViewItem alloc] init];
|
||||
[item setViewController:aViewController];
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
@@ -68,6 +89,19 @@ CPPressedTab = 2;
|
||||
return [self initWithIdentifier:@""];
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_tag = 0;
|
||||
_viewController = nil;
|
||||
_image = nil;
|
||||
_tabState = 0;
|
||||
_tabView = nil;
|
||||
_enabled = YES;
|
||||
_selected = NO;
|
||||
_tabRect = CGRectMakeZero();
|
||||
_width = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes the tab view item with the specified identifier.
|
||||
@return the initialized CPTabViewItem
|
||||
@@ -76,8 +110,12 @@ CPPressedTab = 2;
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_identifier = anIdentifier;
|
||||
[self _init];
|
||||
|
||||
_identifier = anIdentifier;
|
||||
_label = nil;
|
||||
_view = nil;
|
||||
//_auxiliaryView = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -89,8 +127,11 @@ CPPressedTab = 2;
|
||||
*/
|
||||
- (void)setLabel:(CPString)aLabel
|
||||
{
|
||||
if ([aLabel isEqualToString:_label])
|
||||
return;
|
||||
|
||||
_label = aLabel;
|
||||
[_tabView setNeedsLayout];
|
||||
[_tabView tileWithChangedItem:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -101,6 +142,28 @@ CPPressedTab = 2;
|
||||
return _label;
|
||||
}
|
||||
|
||||
// Working With Images
|
||||
/*!
|
||||
Sets the CPTabViewItem's image.
|
||||
@param anImage the image for the item
|
||||
*/
|
||||
- (void)setImage:(CPImage)anImage
|
||||
{
|
||||
if ([anImage isEqual:_image])
|
||||
return;
|
||||
|
||||
_image = anImage;
|
||||
[_tabView tileWithChangedItem:self];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the CPTabViewItem's image
|
||||
*/
|
||||
- (CPImage)image
|
||||
{
|
||||
return _image;
|
||||
}
|
||||
|
||||
// Checking the Tab Display State
|
||||
/*!
|
||||
Returns the tab's current state.
|
||||
@@ -140,7 +203,7 @@ CPPressedTab = 2;
|
||||
_view = aView;
|
||||
|
||||
if ([_tabView selectedTabViewItem] == self)
|
||||
[_tabView _setContentViewFromItem:self];
|
||||
[_tabView _displayItemView:_view];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -148,6 +211,9 @@ CPPressedTab = 2;
|
||||
*/
|
||||
- (CPView)view
|
||||
{
|
||||
if (!_view && _viewController)
|
||||
return [_viewController view]; // The view controller loads here.
|
||||
|
||||
return _view;
|
||||
}
|
||||
|
||||
@@ -186,10 +252,32 @@ CPPressedTab = 2;
|
||||
_tabView = aView;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the specified view controller for the tab view item.
|
||||
@param aViewController an instance of CPViewController.
|
||||
*/
|
||||
- (void)setViewController:(CPViewController)aViewController
|
||||
{
|
||||
_viewController = aViewController;
|
||||
|
||||
var identifier = [aViewController cibName],
|
||||
title = [_viewController title];
|
||||
|
||||
if (identifier)
|
||||
_identifier = identifier;
|
||||
|
||||
if (title)
|
||||
[self setLabel:title];
|
||||
|
||||
if ([_tabView selectedTabViewItem] == self)
|
||||
[_tabView _loadTabViewItem:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||
CPTabViewItemLabelKey = "CPTabViewItemLabelKey",
|
||||
CPTabViewItemImageKey = "CPTabViewItemImageKey",
|
||||
CPTabViewItemViewKey = "CPTabViewItemViewKey",
|
||||
CPTabViewItemAuxViewKey = "CPTabViewItemAuxViewKey";
|
||||
|
||||
@@ -202,8 +290,11 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_identifier = [aCoder decodeObjectForKey:CPTabViewItemIdentifierKey];
|
||||
_label = [aCoder decodeObjectForKey:CPTabViewItemLabelKey];
|
||||
_image = [aCoder decodeObjectForKey:CPTabViewItemImageKey];
|
||||
|
||||
_view = [aCoder decodeObjectForKey:CPTabViewItemViewKey];
|
||||
_auxiliaryView = [aCoder decodeObjectForKey:CPTabViewItemAuxViewKey];
|
||||
@@ -216,6 +307,7 @@ var CPTabViewItemIdentifierKey = "CPTabViewItemIdentifierKey",
|
||||
{
|
||||
[aCoder encodeObject:_identifier forKey:CPTabViewItemIdentifierKey];
|
||||
[aCoder encodeObject:_label forKey:CPTabViewItemLabelKey];
|
||||
[aCoder encodeObject:_image forKey:CPTabViewItemImageKey];
|
||||
|
||||
[aCoder encodeObject:_view forKey:CPTabViewItemViewKey];
|
||||
[aCoder encodeObject:_auxiliaryView forKey:CPTabViewItemAuxViewKey];
|
||||
|
||||
+29
-10
@@ -180,7 +180,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
||||
columns = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(index, [tableView._exposedColumns lastIndex] - index + 1)];
|
||||
|
||||
// FIXME: Would be faster with some sort of -setNeedsDisplayInColumns: that updates a dirtyTableColumnForDisplay cache; then marked columns would relayout their data views at display time.
|
||||
[tableView _layoutDataViewsInRows:rows columns:columns];
|
||||
[tableView _layoutViewsForRowIndexes:rows columnIndexes:columns];
|
||||
[tableView tile];
|
||||
|
||||
if (!_disableResizingPosting)
|
||||
@@ -287,7 +287,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
||||
if (width < [self minWidth])
|
||||
[self setMinWidth:width];
|
||||
else if (width > [self maxWidth])
|
||||
[self setMaxWidth:width]
|
||||
[self setMaxWidth:width];
|
||||
|
||||
if (_width !== width)
|
||||
[self setWidth:width];
|
||||
@@ -393,8 +393,8 @@ CPTableColumnUserResizingMask = 1 << 1;
|
||||
*/
|
||||
- (void)setDataView:(CPView)aView
|
||||
{
|
||||
if (_dataView)
|
||||
_dataViewData = nil;
|
||||
if (_dataView === aView)
|
||||
return;
|
||||
|
||||
[aView setThemeState:CPThemeStateTableDataView];
|
||||
|
||||
@@ -499,7 +499,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
||||
*/
|
||||
- (void)setHidden:(BOOL)shouldBeHidden
|
||||
{
|
||||
shouldBeHidden = !!shouldBeHidden
|
||||
shouldBeHidden = !!shouldBeHidden;
|
||||
|
||||
if (_isHidden === shouldBeHidden)
|
||||
return;
|
||||
@@ -537,6 +537,14 @@ CPTableColumnUserResizingMask = 1 << 1;
|
||||
return _headerToolTip;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (CPString)description
|
||||
{
|
||||
return [CPString stringWithFormat:"<%@ 0x%@ identifier=%@>", [self className], [CPString stringWithHash:[self UID]], [self identifier]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTableColumnValueBinder : CPBinder
|
||||
@@ -546,11 +554,22 @@ CPTableColumnUserResizingMask = 1 << 1;
|
||||
- (void)setValueFor:(CPString)aBinding
|
||||
{
|
||||
var tableView = [_source tableView],
|
||||
column = [[tableView tableColumns] indexOfObjectIdenticalTo:_source],
|
||||
rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [tableView numberOfRows])],
|
||||
columnIndexes = [CPIndexSet indexSetWithIndex:column];
|
||||
newNumberOfRows = [tableView _numberOfRows];
|
||||
|
||||
[tableView reloadDataForRowIndexes:rowIndexes columnIndexes:columnIndexes];
|
||||
if ([tableView numberOfRows] == newNumberOfRows)
|
||||
{
|
||||
var rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, newNumberOfRows)],
|
||||
column = [[tableView tableColumns] indexOfObjectIdenticalTo:_source],
|
||||
columnIndexes = [CPIndexSet indexSetWithIndex:column];
|
||||
|
||||
// Reloads objectValues only, not the views.
|
||||
// FIXME: reload data for all rows or just rows intersecting exposed rows ?
|
||||
[tableView _reloadDataForRowIndexes:rowIndexes columnIndexes:columnIndexes];
|
||||
}
|
||||
else
|
||||
{
|
||||
[tableView reloadData];
|
||||
}
|
||||
}
|
||||
|
||||
- (CPSortDescriptor)_defaultSortDescriptorPrototype
|
||||
@@ -590,7 +609,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
Binds the receiver to an object.
|
||||
Binds the receiver to an object. Note that unlike Cocoa, this works only *after* the receiver has been added to a \c CPTableView.
|
||||
|
||||
@param CPString aBinding - The binding you wish to make. Typically CPValueBinding.
|
||||
@param id anObject - The object to bind the receiver to.
|
||||
|
||||
+413
-364
File diff suppressed because it is too large
Load Diff
+798
-658
File diff suppressed because it is too large
Load Diff
+321
-1
@@ -5,6 +5,13 @@
|
||||
* Created by Alexander Ljungberg.
|
||||
* Copyright 2010, WireLoad, LLC.
|
||||
*
|
||||
* additions from
|
||||
*
|
||||
* Daniel Boehringer on 8/02/2014.
|
||||
* Copyright Daniel Boehringer on 8/02/2014.
|
||||
*
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@@ -20,6 +27,30 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
|
||||
@import "CPPasteboard.j"
|
||||
@import "CPView.j"
|
||||
|
||||
@global CPStringPboardType
|
||||
@global _CPSmartPboardType
|
||||
|
||||
@class CPAttributedString
|
||||
@class _CPRTFParser
|
||||
|
||||
@protocol CPTextDelegate <CPObject>
|
||||
|
||||
@optional
|
||||
- (BOOL)textShouldBeginEditing:(CPText)aTextObject;
|
||||
- (BOOL)textShouldEndEditing:(CPText)aTextObject;
|
||||
- (void)textDidBeginEditing:(CPNotification)aNotification;
|
||||
- (void)textDidChange:(CPNotification)aNotification;
|
||||
- (void)textDidEndEditing:(CPNotification)aNotification;
|
||||
|
||||
@end
|
||||
|
||||
CPParagraphSeparatorCharacter = 0x2029;
|
||||
CPLineSeparatorCharacter = 0x2028;
|
||||
CPEnterCharacter = "\u0003";
|
||||
CPBackspaceCharacter = "\u0008";
|
||||
CPTabCharacter = "\u0009";
|
||||
@@ -29,6 +60,7 @@ CPCarriageReturnCharacter = "\u000d";
|
||||
CPBackTabCharacter = "\u0019";
|
||||
CPDeleteCharacter = "\u007f";
|
||||
|
||||
@typedef CPTextMovement
|
||||
CPIllegalTextMovement = 0;
|
||||
CPOtherTextMovement = 0;
|
||||
CPReturnTextMovement = 16;
|
||||
@@ -38,4 +70,292 @@ CPLeftTextMovement = 19;
|
||||
CPRightTextMovement = 20;
|
||||
CPUpTextMovement = 21;
|
||||
CPDownTextMovement = 22;
|
||||
CPCancelTextMovement = 23;
|
||||
CPCancelTextMovement = 23;
|
||||
|
||||
@typedef CPWritingDirection
|
||||
CPWritingDirectionNatural = -1;
|
||||
CPWritingDirectionLeftToRight = 0;
|
||||
CPWritingDirectionRightToLeft = 1;
|
||||
|
||||
@typedef CPTextAlignment
|
||||
CPLeftTextAlignment = 0;
|
||||
CPRightTextAlignment = 1;
|
||||
CPCenterTextAlignment = 2;
|
||||
CPJustifiedTextAlignment = 3;
|
||||
CPNaturalTextAlignment = 4;
|
||||
|
||||
/*
|
||||
CPText notifications
|
||||
*/
|
||||
CPTextDidBeginEditingNotification = @"CPTextDidBeginEditingNotification";
|
||||
CPTextDidChangeNotification = @"CPTextDidChangeNotification";
|
||||
CPTextDidEndEditingNotification = @"CPTextDidEndEditingNotification";
|
||||
|
||||
/*
|
||||
CPTextView Notifications
|
||||
*/
|
||||
CPTextViewDidChangeSelectionNotification = @"CPTextViewDidChangeSelectionNotification";
|
||||
CPTextViewDidChangeTypingAttributesNotification = @"CPTextViewDidChangeTypingAttributesNotification";
|
||||
|
||||
/*
|
||||
FIXME: move these to CPAttributed string
|
||||
Make use of attributed keys in AppKit
|
||||
*/
|
||||
CPFontAttributeName = @"CPFontAttributeName";
|
||||
CPForegroundColorAttributeName = @"CPForegroundColorAttributeName";
|
||||
CPBackgroundColorAttributeName = @"CPBackgroundColorAttributeName";
|
||||
CPShadowAttributeName = @"CPShadowAttributeName";
|
||||
CPUnderlineStyleAttributeName = @"CPUnderlineStyleAttributeName";
|
||||
CPSuperscriptAttributeName = @"CPSuperscriptAttributeName";
|
||||
CPBaselineOffsetAttributeName = @"CPBaselineOffsetAttributeName";
|
||||
CPAttachmentAttributeName = @"CPAttachmentAttributeName";
|
||||
CPLigatureAttributeName = @"CPLigatureAttributeName";
|
||||
CPKernAttributeName = @"CPKernAttributeName";
|
||||
|
||||
@implementation CPText : CPView
|
||||
{
|
||||
BOOL _isEditable @accessors(getter=isEditable, setter=setEditable:);
|
||||
BOOL _isSelectable @accessors(getter=isSelectable, setter=setSelectable:);
|
||||
BOOL _isRichText @accessors(getter=isRichText, setter=setRichText:);
|
||||
}
|
||||
|
||||
- (void)setSelectable:(BOOL)flag
|
||||
{
|
||||
[self willChangeValueForKey:@"selectable"];
|
||||
_isSelectable = flag;
|
||||
[self didChangeValueForKey:@"selectable"];
|
||||
|
||||
if (!flag)
|
||||
[self setEditable:flag];
|
||||
}
|
||||
|
||||
- (void)setEditable:(BOOL)flag
|
||||
{
|
||||
[self willChangeValueForKey:@"editable"];
|
||||
_isEditable = flag;
|
||||
[self didChangeValueForKey:@"editable"];
|
||||
|
||||
if (flag)
|
||||
[self setSelectable:flag];
|
||||
}
|
||||
|
||||
- (void)changeFont:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)copy:(id)sender
|
||||
{
|
||||
var selectedRange = [self selectedRange];
|
||||
|
||||
if (selectedRange.length < 1)
|
||||
return;
|
||||
|
||||
var pasteboard = [CPPasteboard generalPasteboard];
|
||||
|
||||
// put plain representation on the pasteboad unconditionally
|
||||
[pasteboard declareTypes:[CPStringPboardType] owner:nil];
|
||||
[pasteboard setString:[[self stringValue] substringWithRange:selectedRange] forType:CPStringPboardType];
|
||||
}
|
||||
|
||||
- (id)_plainStringForPasting
|
||||
{
|
||||
return [[CPPasteboard generalPasteboard] stringForType:CPStringPboardType];
|
||||
}
|
||||
|
||||
- (id)_stringForPasting
|
||||
{
|
||||
var pasteboard = [CPPasteboard generalPasteboard],
|
||||
dataForPasting = [pasteboard stringForType:CPRTFPboardType],
|
||||
stringForPasting = [pasteboard stringForType:CPStringPboardType];
|
||||
|
||||
if (dataForPasting || [stringForPasting hasPrefix:"{\\rtf1\\ansi"])
|
||||
stringForPasting = [[_CPRTFParser new] parseRTF:dataForPasting ? dataForPasting : stringForPasting];
|
||||
|
||||
if (![self isRichText] && [stringForPasting isKindOfClass:[CPAttributedString class]])
|
||||
stringForPasting = stringForPasting._string;
|
||||
|
||||
return stringForPasting;
|
||||
}
|
||||
|
||||
- (BOOL)_shouldUseSmartPasting
|
||||
{
|
||||
return parseInt([[CPPasteboard generalPasteboard] stringForType:_CPSmartPboardType], 10) > 0 || NO;
|
||||
}
|
||||
|
||||
- (void)paste:(id)sender
|
||||
{
|
||||
var stringForPasting = [self _stringForPasting];
|
||||
|
||||
if (stringForPasting)
|
||||
[self insertText:stringForPasting];
|
||||
}
|
||||
|
||||
- (void)copyFont:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)delete:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (CPFont)font:(CPFont)aFont
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)isHorizontallyResizable
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isRulerVisible
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isVerticallyResizable
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (CGSize)maxSize
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return CGSizeMake(0,0);
|
||||
}
|
||||
|
||||
- (CGSize)minSize
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
return CGSizeMake(0,0);
|
||||
}
|
||||
|
||||
- (void)pasteFont:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)replaceCharactersInRange:(CPRange)aRange withString:(CPString)aString
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)scrollRangeToVisible:(CPRange)aRange
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)selectedAll:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (CPRange)selectedRange
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return CPMakeRange(CPNotFound, 0);
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setFont:(CPFont)aFont range:(CPRange)aRange
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setHorizontallyResizable:(BOOL)flag
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setMaxSize:(CGSize)aSize
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setMinSize:(CGSize)aSize
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setString:(CPString)aString
|
||||
{
|
||||
[self replaceCharactersInRange:CPMakeRange(0, [[self string] length]) withString:aString];
|
||||
}
|
||||
|
||||
- (void)setUsesFontPanel:(BOOL)flag
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (void)setVerticallyResizable:(BOOL)flag
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (CPString)string
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)underline:(id)sender
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
- (BOOL)usesFontPanel
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTextViewIsEditableKey = @"CPTextViewIsEditableKey",
|
||||
CPTextViewIsSelectableKey = @"CPTextViewIsSelectableKey",
|
||||
CPTextViewIsRichTextKey = @"CPTextViewIsRichTextKey";
|
||||
|
||||
@implementation CPText (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self setSelectable:[aCoder decodeBoolForKey:CPTextViewIsSelectableKey]];
|
||||
[self setEditable:[aCoder decodeBoolForKey:CPTextViewIsEditableKey]];
|
||||
[self setRichText:[aCoder decodeBoolForKey:CPTextViewIsRichTextKey]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
[aCoder encodeBool:_isEditable forKey:CPTextViewIsEditableKey];
|
||||
[aCoder encodeBool:_isSelectable forKey:CPTextViewIsSelectableKey];
|
||||
[aCoder encodeBool:_isRichText forKey:CPTextViewIsRichTextKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+166
-59
@@ -28,12 +28,12 @@
|
||||
@import "_CPImageAndTextView.j"
|
||||
|
||||
@class CPPasteboard
|
||||
@class CPScrollView
|
||||
|
||||
@global CPApp
|
||||
@global CPStringPboardType
|
||||
@global CPCursor
|
||||
|
||||
|
||||
@protocol CPTextFieldDelegate <CPControlTextEditingDelegate>
|
||||
|
||||
@end
|
||||
@@ -65,11 +65,11 @@ var CPTextFieldDOMCurrentElement = nil,
|
||||
CPTextFieldCachedSelectStartFunction = nil,
|
||||
CPTextFieldCachedDragFunction = nil,
|
||||
CPTextFieldBlurHandler = nil,
|
||||
CPTextFieldInputFunction = nil;
|
||||
CPTextFieldInputFunction = nil,
|
||||
CPTexFieldCurrentCSSSelectableField = nil;
|
||||
|
||||
var CPSecureTextFieldCharacter = "\u2022";
|
||||
|
||||
|
||||
function CPTextFieldBlurFunction(anEvent, owner, domElement, inputElement, resigning, didBlurRef)
|
||||
{
|
||||
if (owner && domElement != inputElement.parentNode)
|
||||
@@ -77,20 +77,16 @@ function CPTextFieldBlurFunction(anEvent, owner, domElement, inputElement, resig
|
||||
|
||||
var ownerWindow = [owner window];
|
||||
|
||||
if (!resigning && [ownerWindow isKeyWindow])
|
||||
if (!resigning && [ownerWindow isKeyWindow] && [ownerWindow firstResponder] === owner)
|
||||
{
|
||||
/*
|
||||
Browsers blur text fields when a click occurs anywhere outside the text field. That is normal for browsers, but in Cocoa the key view retains focus unless the click target accepts first responder. So if we lost focus but were not told to resign and our window is still key, restore focus,
|
||||
but only if the text field is completely within the browser window. If we restore focus when it
|
||||
is off screen, the entire body scrolls out of our control.
|
||||
Previously we had code here which would force the input to regain focus if the input lost focus without the CPTextField having actually lost first responder status. This typically happened because the user clicked away from the input in the browser, without clicking on something that could become the first responder. In the browser, clicking outside of an input blurs it, but in Cocoa and Cappuccino it does not (unless you actually click something that will become the first responder).
|
||||
|
||||
That refocusing code has now been removed because we simply prevent the default action on clicks in the browser instead, which combined with the fix in 58d5d7d7, successfully prevents unintentional focus loss in (at least) Safari 9.1.1, Chrome 51 and Safari for iOS 9.3 when you click outside of a text field.
|
||||
|
||||
Now we can still lose focus unexpectedly: this is when the 'done' button is tapped on the virtual keyboard of a mobile device. In this case we actually do want to resign first responder status, because that is what the done button should do (and if we did not the keyboard would go away and then immediately come back which looks dumb and isn't what the user wanted).
|
||||
*/
|
||||
if ([owner _isWithinUsablePlatformRect])
|
||||
{
|
||||
window.setTimeout(function()
|
||||
{
|
||||
inputElement.focus();
|
||||
}, 0.0);
|
||||
}
|
||||
[ownerWindow makeFirstResponder:nil];
|
||||
}
|
||||
|
||||
CPTextFieldHandleBlur(anEvent, @ref(owner));
|
||||
@@ -335,7 +331,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[CPTextFieldInputOwner keyUp:cappEvent];
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
}
|
||||
};
|
||||
|
||||
CPTextFieldDOMPasswordInputElement.oninput = CPTextFieldInputFunction;
|
||||
CPTextFieldDOMPasswordInputElement.onblur = CPTextFieldBlurHandler;
|
||||
@@ -370,7 +366,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
_sendActionOn = CPKeyUpMask | CPKeyDownMask;
|
||||
|
||||
[self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
|
||||
[self setValue:CPNaturalTextAlignment forThemeAttribute:@"alignment"];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -397,7 +393,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateEditable];
|
||||
|
||||
// We only allow first responder status if the field is enable, and editable or selectable.
|
||||
// We only allow first responder status if the field is enabled, and editable or selectable.
|
||||
if (!(shouldBeEditable && ![self isSelectable]) && [[self window] firstResponder] === self)
|
||||
[[self window] makeFirstResponder:nil];
|
||||
|
||||
@@ -405,6 +401,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[self setThemeState:CPThemeStateEditable];
|
||||
else
|
||||
[self unsetThemeState:CPThemeStateEditable];
|
||||
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -426,6 +424,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
// We only allow first responder status if the field is enabled.
|
||||
if (!shouldBeEnabled && [[self window] firstResponder] === self)
|
||||
[[self window] makeFirstResponder:nil];
|
||||
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -435,6 +435,8 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
- (void)setSelectable:(BOOL)aFlag
|
||||
{
|
||||
_isSelectable = aFlag;
|
||||
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -643,6 +645,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
_stringValue = [self stringValue];
|
||||
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
[self _setCSSStyleForInputElement];
|
||||
@@ -664,30 +667,56 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
|
||||
CPTextFieldInputOwner = self;
|
||||
|
||||
window.setTimeout(function()
|
||||
[[CPRunLoop mainRunLoop] performBlock:function()
|
||||
{
|
||||
/*
|
||||
setTimeout handlers are not guaranteed to fire in the order they were initiated. This can cause a race condition when several windows with text fields are opened quickly, resulting in several instances of this timeout function being fired, perhaps out of order. So we have to check that by the time this function is fired, CPTextFieldInputOwner has not been changed to another text field in the meantime.
|
||||
*/
|
||||
if (CPTextFieldInputOwner !== self)
|
||||
return;
|
||||
|
||||
// This will prevent to jump to the focused element
|
||||
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
|
||||
|
||||
element.focus();
|
||||
|
||||
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
|
||||
|
||||
// Select the text if the textfield became first responder through keyboard interaction
|
||||
if (!_willBecomeFirstResponderByClick)
|
||||
{
|
||||
[self _selectText:self immediately:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
var point = CGPointMake([self convertPointFromBase:[[CPApp currentEvent] locationInWindow]].x - [self currentValueForThemeAttribute:@"content-inset"].left, 0),
|
||||
position = [CPPlatformString charPositionOfString:[self stringValue] withFont:[self font] forPoint:point];
|
||||
|
||||
[self setSelectedRange:CPMakeRange(position, 0)];
|
||||
}
|
||||
|
||||
_willBecomeFirstResponderByClick = NO;
|
||||
|
||||
[self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
|
||||
}, 0.0);
|
||||
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
|
||||
#endif
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the selection css style for the DOM element of the textField
|
||||
@ignore
|
||||
*/
|
||||
- (void)_setEnableCSSSelection:(BOOL)shouldEnable
|
||||
{
|
||||
#if PLATFORM (DOM)
|
||||
if (CPTexFieldCurrentCSSSelectableField)
|
||||
CPTexFieldCurrentCSSSelectableField._DOMElement.style[CPBrowserStyleProperty(@"user-select")] = @"none";
|
||||
|
||||
CPTexFieldCurrentCSSSelectableField = self;
|
||||
_DOMElement.style[CPBrowserStyleProperty(@"user-select")] = shouldEnable ? @"text" : @"none";
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the css style for the input element of the textField
|
||||
@ignore
|
||||
@@ -741,6 +770,10 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
element.style.textAlign = "right";
|
||||
break;
|
||||
|
||||
case CPNaturalTextAlignment:
|
||||
element.style.textAlign = "";
|
||||
break;
|
||||
|
||||
default:
|
||||
element.style.textAlign = "left";
|
||||
}
|
||||
@@ -783,7 +816,13 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
// even if the value has not changed.
|
||||
if ([self _valueIsValid:newValue] === NO)
|
||||
{
|
||||
// This will prevent to jump to the focused element
|
||||
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
|
||||
|
||||
element.focus();
|
||||
|
||||
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
|
||||
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
@@ -792,9 +831,11 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
// When we are no longer the first responder we don't worry about the key status of our window anymore.
|
||||
[self _setObserveWindowKeyNotifications:NO];
|
||||
|
||||
[self _resignFirstKeyResponder];
|
||||
if ([[self window] isKeyWindow])
|
||||
[self _resignFirstKeyResponder];
|
||||
|
||||
_isEditing = NO;
|
||||
|
||||
if ([self isEditable])
|
||||
{
|
||||
[self textDidEndEditing:[CPNotification notificationWithName:CPControlTextDidEndEditingNotification object:self userInfo:@{"CPTextMovement": [self _currentTextMovement]}]];
|
||||
@@ -832,12 +873,18 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
if (!CPTextFieldInputDidBlur)
|
||||
CPTextFieldBlurHandler();
|
||||
|
||||
CPTextFieldInputDidBlur = NO;
|
||||
CPTextFieldInputResigning = NO;
|
||||
|
||||
if (element.parentNode == _DOMElement)
|
||||
element.parentNode.removeChild(element);
|
||||
|
||||
// Previosly, we unflagged CPTextFieldInputDidBlur and CPTextFieldInputResigning before
|
||||
// the call to removeChild. This resulted in DOM exceptions in Chrome under certain conditions.
|
||||
// See https://stackoverflow.com/questions/21926083/failed-to-execute-removechild-on-node
|
||||
// for why we need to unflag CPTextFieldInputDidBlur and CPTextFieldInputResigning
|
||||
// only after removing the element.
|
||||
|
||||
CPTextFieldInputDidBlur = NO;
|
||||
CPTextFieldInputResigning = NO;
|
||||
|
||||
CPTextFieldInputIsActive = NO;
|
||||
|
||||
if (document.attachEvent)
|
||||
@@ -954,14 +1001,11 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[self textDidChange:[CPNotification notificationWithName:CPControlTextDidChangeNotification object:self userInfo:nil]];
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(CPEvent)anEvent
|
||||
{
|
||||
[super mouseMoved:anEvent];
|
||||
[self _updateCursorForEvent:anEvent];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled])
|
||||
return [[self nextResponder] mouseDown:anEvent];
|
||||
|
||||
// Don't track! (ever?)
|
||||
if ([self isEditable] && [self isEnabled])
|
||||
{
|
||||
@@ -970,6 +1014,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
else if ([self isSelectable])
|
||||
{
|
||||
[self _setEnableCSSSelection:YES];
|
||||
if (document.attachEvent)
|
||||
{
|
||||
CPTextFieldCachedSelectStartFunction = [[self window] platformWindow]._DOMBodyElement.onselectstart;
|
||||
@@ -995,7 +1040,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[[self window] platformWindow]._DOMBodyElement.ondrag = CPTextFieldCachedDragFunction;
|
||||
[[self window] platformWindow]._DOMBodyElement.onselectstart = CPTextFieldCachedSelectStartFunction;
|
||||
|
||||
CPTextFieldCachedSelectStartFunction = nil
|
||||
CPTextFieldCachedSelectStartFunction = nil;
|
||||
CPTextFieldCachedDragFunction = nil;
|
||||
}
|
||||
|
||||
@@ -1011,6 +1056,14 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)rightMouseDown:(CPEvent)anEvent
|
||||
{
|
||||
if ([self menuForEvent:anEvent] || [[self nextResponder] isKindOfClass:CPView])
|
||||
[super rightMouseDown:anEvent];
|
||||
else
|
||||
[[[anEvent window] platformWindow] _propagateContextMenuDOMEvent:YES];
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(CPEvent)anEvent
|
||||
{
|
||||
if (![self isEnabled] || !([self isSelectable] || [self isEditable]))
|
||||
@@ -1161,7 +1214,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
- (void)textDidFocus:(CPNotification)note
|
||||
{
|
||||
// this looks to prevent false propagation of notifications for other objects
|
||||
if ([note object] != self)
|
||||
if ([note object] !== self)
|
||||
return;
|
||||
|
||||
if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidFocus_)
|
||||
@@ -1190,7 +1243,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
return;
|
||||
|
||||
if (_implementedDelegateMethods & CPTextFieldDelegate_controlTextDidBeginEditing_)
|
||||
[_delegate controlTextDidBeginEditing:[[CPNotification alloc] initWithName:CPControlTextDidBeginEditingNotification object:self userInfo:@{"CPFieldEditor": [note object]}]]
|
||||
[_delegate controlTextDidBeginEditing:[[CPNotification alloc] initWithName:CPControlTextDidBeginEditingNotification object:self userInfo:@{"CPFieldEditor": [note object]}]];
|
||||
|
||||
[super textDidBeginEditing:note];
|
||||
}
|
||||
@@ -1207,27 +1260,6 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
[_delegate controlTextDidEndEditing:note];
|
||||
}
|
||||
|
||||
- (void)_updateCursorForEvent:(CPEvent)anEvent
|
||||
{
|
||||
var frame = CGRectMakeCopy([self frame]),
|
||||
contentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
||||
|
||||
frame = [[self superview] convertRectToBase:CGRectInsetByInset(frame, contentInset)];
|
||||
|
||||
if ([self isEnabled] && ([self isSelectable] || [self isEditable]) && CGRectContainsPoint(frame, [anEvent locationInWindow]))
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
self._DOMElement.style.cursor = "text";
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PLATFORM(DOM)
|
||||
self._DOMElement.style.cursor = "default";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the string in the text field.
|
||||
*/
|
||||
@@ -1463,7 +1495,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
if (immediately)
|
||||
element.select();
|
||||
else
|
||||
window.setTimeout(function() { element.select(); }, 0);
|
||||
[[CPRunLoop mainRunLoop] performBlock:function(){ element.select(); } argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
}
|
||||
else if (wind !== nil && [wind makeFirstResponder:self])
|
||||
[self _selectText:sender immediately:immediately];
|
||||
@@ -1758,7 +1790,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
_implementedDelegateMethods = 0;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(control:didFailToFormatString:errorDescription:)])
|
||||
_implementedDelegateMethods |= CPTextFieldDelegate_control_didFailToFormatString_errorDescription_
|
||||
_implementedDelegateMethods |= CPTextFieldDelegate_control_didFailToFormatString_errorDescription_;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(controlTextDidBeginEditing:)])
|
||||
_implementedDelegateMethods |= CPTextFieldDelegate_controlTextDidBeginEditing_;
|
||||
@@ -1950,17 +1982,55 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
|
||||
if (!wind)
|
||||
return NO;
|
||||
|
||||
var scrollView = [self enclosingScrollView],
|
||||
previousContentViewBoundsOrigin;
|
||||
|
||||
// Here we scroll to the textField, otherwise the textField could not be in the usable platformRect
|
||||
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
|
||||
|
||||
var frame = [self convertRectToBase:[self contentRectForBounds:[self bounds]]],
|
||||
usableRect = [[wind platformWindow] usableContentFrame];
|
||||
|
||||
frame.origin = [wind convertBaseToGlobal:frame.origin];
|
||||
|
||||
// Here we restore the previous scrolling posiition
|
||||
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
|
||||
|
||||
return (CGRectGetMinX(frame) >= CGRectGetMinX(usableRect) &&
|
||||
CGRectGetMaxX(frame) <= CGRectGetMaxX(usableRect) &&
|
||||
CGRectGetMinY(frame) >= CGRectGetMinY(usableRect) &&
|
||||
CGRectGetMaxY(frame) <= CGRectGetMaxY(usableRect));
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (CGPoint)_scrollToVisibleRectAndReturnPreviousOrigin
|
||||
{
|
||||
var scrollView = [self enclosingScrollView],
|
||||
previousContentViewBoundsOrigin;
|
||||
|
||||
// Here we scroll to the textField, otherwise the textField could not be in the usable platformRect
|
||||
if ([scrollView isKindOfClass:[CPScrollView class]])
|
||||
{
|
||||
previousContentViewBoundsOrigin = CGPointMakeCopy([[scrollView contentView] boundsOrigin]);
|
||||
|
||||
if (![[self superview] scrollRectToVisible:[self frame]])
|
||||
previousContentViewBoundsOrigin = nil;
|
||||
}
|
||||
|
||||
return previousContentViewBoundsOrigin;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ignore
|
||||
*/
|
||||
- (void)_restorePreviousScrollingOrigin:(CGPoint)scrollingOrigin
|
||||
{
|
||||
if (scrollingOrigin)
|
||||
[[[self enclosingScrollView] contentView] setBoundsOrigin:scrollingOrigin];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var secureStringForString = function(aString)
|
||||
@@ -2082,7 +2152,8 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
newValue = [self valueForBinding:aBinding],
|
||||
value = [destination valueForKeyPath:keyPath];
|
||||
|
||||
if (CPIsControllerMarker(value) && newValue === nil) return;
|
||||
if (CPIsControllerMarker(value) && newValue === nil)
|
||||
return;
|
||||
|
||||
newValue = [self reverseTransformValue:newValue withOptions:options];
|
||||
|
||||
@@ -2110,3 +2181,39 @@ var CPTextFieldIsEditableKey = "CPTextFieldIsEditableKey",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTextField (CPTrackingArea)
|
||||
{
|
||||
CPTrackingArea _textFieldTrackingArea;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (_textFieldTrackingArea)
|
||||
{
|
||||
[self removeTrackingArea:_textFieldTrackingArea];
|
||||
_textFieldTrackingArea = nil;
|
||||
}
|
||||
|
||||
if ([self isEnabled] && (_isEditable || _isSelectable))
|
||||
{
|
||||
var myBounds = CGRectMakeCopy([self bounds]),
|
||||
contentInset = [self currentValueForThemeAttribute:@"content-inset"];
|
||||
|
||||
_textFieldTrackingArea = [[CPTrackingArea alloc] initWithRect:CGRectInsetByInset(myBounds, contentInset)
|
||||
options:CPTrackingCursorUpdate | CPTrackingActiveInKeyWindow
|
||||
owner:self
|
||||
userInfo:nil];
|
||||
|
||||
[self addTrackingArea:_textFieldTrackingArea];
|
||||
}
|
||||
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
- (void)cursorUpdate:(CPEvent)anEvent
|
||||
{
|
||||
[[CPCursor IBeamCursor] set];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* CPFontDescriptor.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Emmanuel Maillard on 07/03/10.
|
||||
* Copyright Emmanuel Maillard 2010.
|
||||
*
|
||||
* 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>
|
||||
/*
|
||||
Font descriptor dictionary keys
|
||||
*/
|
||||
|
||||
/*
|
||||
CPFontNameAttribute contains a CPString that specified the font name
|
||||
(may be an name list like: 'Marker Felt, Lucida Grande, Helvetica')
|
||||
*/
|
||||
CPFontNameAttribute = @"CPFontNameAttribute";
|
||||
/*
|
||||
CPFontSizeAttribute contains a CPString that specified the font size
|
||||
(as a float value)
|
||||
*/
|
||||
CPFontSizeAttribute = @"CPFontSizeAttribute";
|
||||
/*
|
||||
CPFontTraitsAttribute a CPDictionary that contains font traits keys
|
||||
(CPFontSymbolicTrait or CPFontWeightTrait)
|
||||
*/
|
||||
CPFontTraitsAttribute = @"CPFontTraitsAttribute";
|
||||
|
||||
// Font traits dictionary keys
|
||||
/*
|
||||
CPFontSymbolicTrait a CPNumber that contains CPFontFamilyClass and
|
||||
typeface information flags.
|
||||
*/
|
||||
CPFontSymbolicTrait = @"CPFontSymbolicTrait";
|
||||
|
||||
/*
|
||||
CPFontWeightTrait
|
||||
We use CPString with CSS string values for font weight
|
||||
(normal | bold | bolder | lighter | 100 | 200 | 300 | 400
|
||||
| 500 | 600 | 700 | 800 | 900)
|
||||
NOTE: Cocoa compatibility issue: NSFontWeightTrait are NSNumber for
|
||||
font weight (from -1.0 to 1.0, 0.0 for normal weight).
|
||||
*/
|
||||
CPFontWeightTrait = @"CPFontWeightTrait";
|
||||
|
||||
/*
|
||||
CPFontFamilyClass
|
||||
*/
|
||||
CPFontUnknownClass = 0 << 28;
|
||||
CPFontOldStyleSerifsClass = 1 << 28;
|
||||
CPFontTransitionalSerifsClass = 2 << 28;
|
||||
CPFontModernSerifsClass = 3 << 28;
|
||||
CPFontClarendonSerifsClass = 4 << 28;
|
||||
CPFontSlabSerifsClass = 5 << 28;
|
||||
CPFontFreeformSerifsClass = 7 << 28;
|
||||
CPFontSansSerifClass = 8 << 28;
|
||||
|
||||
CPFontSerifClass = (CPFontOldStyleSerifsClass | CPFontTransitionalSerifsClass |
|
||||
CPFontModernSerifsClass | CPFontClarendonSerifsClass |
|
||||
CPFontSlabSerifsClass | CPFontFreeformSerifsClass);
|
||||
|
||||
CPFontFamilyClassMask = 0xF0000000;
|
||||
|
||||
/*
|
||||
Typeface information
|
||||
*/
|
||||
CPFontItalicTrait = 1 << 0;
|
||||
CPFontBoldTrait = 1 << 1;
|
||||
CPFontExpandedTrait = 1 << 5; /* TODO: CCS 3 font-stretch */
|
||||
CPFontCondensedTrait = 1 << 6;
|
||||
CPFontSmallCapsTrait = 1 << 7;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPFontDescriptor
|
||||
*/
|
||||
@implementation CPFontDescriptor : CPObject
|
||||
{
|
||||
CPDictionary _attributes;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a font descriptor with the specified attributes.
|
||||
|
||||
@param attributes a dictionary that describe the desired font descriptor
|
||||
@return the requested font descriptor
|
||||
*/
|
||||
+ (CPFontDescriptor)fontDescriptorWithFontAttributes:(CPDictionary)attributes
|
||||
{
|
||||
return [[CPFontDescriptor alloc] initWithFontAttributes:attributes];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a font descriptor with the specified name and size.
|
||||
|
||||
@param fontName the name of the font
|
||||
@param aSize the size of the font (in points)
|
||||
@return the requested font descriptor
|
||||
*/
|
||||
+ (CPFontDescriptor)fontDescriptorWithName:(CPString)fontName size:(float)size
|
||||
{
|
||||
return [[CPFontDescriptor alloc] initWithFontAttributes:[CPDictionary dictionaryWithObjects:[fontName, [CPString stringWithString:size + '']] forKeys:[CPFontNameAttribute,CPFontSizeAttribute]]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initialize a font descriptor with the specified attributes.
|
||||
|
||||
@param attributes a dictionary that describe the desired font descriptor
|
||||
@return the requested font descriptor
|
||||
*/
|
||||
- (id)initWithFontAttributes:(CPDictionary)attributes
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_attributes = [[CPMutableDictionary alloc] init];
|
||||
|
||||
if (attributes)
|
||||
[_attributes addEntriesFromDictionary:attributes];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new font descriptor that is the same as the receiver but with the
|
||||
specified attributes taking precedence over the existing ones.
|
||||
|
||||
@param attributes a dictionary that describe the desired font descriptor
|
||||
@return the new font descriptor
|
||||
*/
|
||||
- (CPFontDescriptor)fontDescriptorByAddingAttributes:(CPDictionary)attributes
|
||||
{
|
||||
var attrib = [_attributes copy];
|
||||
|
||||
[attrib addEntriesFromDictionary:attributes];
|
||||
|
||||
return [[CPFontDescriptor alloc] initWithFontAttributes:attrib];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new font descriptor that is the same as the receiver but with the specified size taking precedence over the existing ones.
|
||||
|
||||
@param aSize the new size
|
||||
@return the new font descriptor
|
||||
*/
|
||||
- (CPFontDescriptor)fontDescriptorWithSize:(float)aSize
|
||||
{
|
||||
var attrib = [_attributes copy];
|
||||
|
||||
[attrib setObject:[CPString stringWithString:aSize + ''] forKey:CPFontSizeAttribute];
|
||||
|
||||
return [[CPFontDescriptor alloc] initWithFontAttributes:attrib];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new font descriptor that is the same as the receiver but with
|
||||
the specified symbolic traits taking precedence over the existing ones.
|
||||
|
||||
@param symbolicTraits the desired new symbolic traits
|
||||
@return the new font descriptor
|
||||
*/
|
||||
- (CPFontDescriptor)fontDescriptorWithSymbolicTraits:(CPFontSymbolicTraits)symbolicTraits
|
||||
{
|
||||
var attrib = [_attributes copy];
|
||||
|
||||
if ([attrib objectForKey:CPFontTraitsAttribute])
|
||||
[[attrib objectForKey:CPFontTraitsAttribute] setObject:[CPNumber numberWithUnsignedInt:symbolicTraits]
|
||||
forKey:CPFontSymbolicTrait];
|
||||
else
|
||||
[attrib setObject:[CPDictionary dictionaryWithObject:[CPNumber numberWithUnsignedInt:symbolicTraits]
|
||||
forKey:CPFontSymbolicTrait] forKey:CPFontTraitsAttribute];
|
||||
|
||||
return [[CPFontDescriptor alloc] initWithFontAttributes:attrib];
|
||||
}
|
||||
|
||||
- (id)objectForKey:(id)aKey
|
||||
{
|
||||
return [_attributes objectForKey:aKey];
|
||||
}
|
||||
|
||||
- (CPDictionary)fontAttributes
|
||||
{
|
||||
return _attributes;
|
||||
}
|
||||
|
||||
- (float)pointSize
|
||||
{
|
||||
var value = [_attributes objectForKey:CPFontSizeAttribute];
|
||||
|
||||
return value ? [value floatValue] : 0.0;
|
||||
}
|
||||
|
||||
- (CPFontSymbolicTraits)symbolicTraits
|
||||
{
|
||||
var traits = [_attributes objectForKey:CPFontTraitsAttribute];
|
||||
|
||||
return (traits && [traits objectForKey:CPFontSymbolicTrait]) ? [[traits objectForKey:CPFontSymbolicTrait] unsignedIntValue] : 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPFontDescriptorAttributesKey = @"CPFontDescriptorAttributesKey";
|
||||
|
||||
@implementation CPFontDescriptor (CPCoding)
|
||||
|
||||
/*!
|
||||
Initializes the font descriptor from a coder.
|
||||
|
||||
@param aCoder the coder from which to read the font descriptor data
|
||||
@return the initialized font
|
||||
*/
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
return [self initWithFontAttributes:[aCoder decodeObjectForKey:CPFontDescriptorAttributesKey]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Writes the font descriptor to a coder.
|
||||
|
||||
@param aCoder the coder to which the data will be written
|
||||
*/
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_attributes forKey:CPFontDescriptorAttributesKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var _wrapNameRegEx = new RegExp(/(\w+\s+\w+)(,*)/g);
|
||||
|
||||
/*
|
||||
Helper methods to CPFont for generating CSS font style
|
||||
*/
|
||||
@implementation CPFontDescriptor (CPFontCSSHelper)
|
||||
|
||||
- (CPString)fontStyleCSSString
|
||||
{
|
||||
return [self symbolicTraits] & CPFontItalicTrait ? @"italic" : @"normal";
|
||||
}
|
||||
|
||||
- (CPString)fontWeightCSSString
|
||||
{
|
||||
var traitsAttributes = [_attributes objectForKey:CPFontTraitsAttribute];
|
||||
|
||||
if (traitsAttributes)
|
||||
{
|
||||
/* give preference to CPFontWeightTrait */
|
||||
if ([traitsAttributes objectForKey:CPFontWeightTrait])
|
||||
return [traitsAttributes objectForKey:CPFontWeightTrait];
|
||||
/* else fallback to facetype symbolic traits */
|
||||
if ([self symbolicTraits] & CPFontBoldTrait)
|
||||
return @"bold";
|
||||
}
|
||||
|
||||
return @"normal";
|
||||
}
|
||||
|
||||
- (CPString)fontSizeCSSString
|
||||
{
|
||||
return [_attributes objectForKey:CPFontSizeAttribute] ? [[_attributes objectForKey:CPFontSizeAttribute] intValue] + "px" : @"";
|
||||
}
|
||||
|
||||
- (CPString)fontFamilyCSSString
|
||||
{
|
||||
var aName = @"";
|
||||
|
||||
if ([_attributes objectForKey:CPFontNameAttribute])
|
||||
aName += [_attributes objectForKey:CPFontNameAttribute].replace(_wrapNameRegEx, '"$1"$2');
|
||||
|
||||
var symbolicTraits = [self symbolicTraits];
|
||||
|
||||
if (symbolicTraits)
|
||||
{
|
||||
if ((symbolicTraits & CPFontFamilyClassMask) & CPFontSansSerifClass)
|
||||
aName += @", sans-serif";
|
||||
else if ((symbolicTraits & CPFontFamilyClassMask) & CPFontSerifClass)
|
||||
aName += @", serif";
|
||||
}
|
||||
|
||||
return aName;
|
||||
}
|
||||
|
||||
- (CPString)fontVariantCSSString
|
||||
{
|
||||
if ([self symbolicTraits] & CPFontSmallCapsTrait)
|
||||
return @"small-caps";
|
||||
|
||||
return @"normal";
|
||||
}
|
||||
|
||||
- (CPString)cssString
|
||||
{
|
||||
return [CPString stringWithString:[self fontStyleCSSString] + " "
|
||||
+ [self fontVariantCSSString] + " "
|
||||
+ [self fontWeightCSSString] + " "
|
||||
+ [self fontSizeCSSString] + " "
|
||||
+ [self fontFamilyCSSString]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* CPFontPanel.j
|
||||
* AppKit
|
||||
*
|
||||
* TODOs:
|
||||
* 1. make browser-width for size smaller and fix columns
|
||||
* 2. add all the missing features from the MacOS X counterpart (sampleview)
|
||||
*
|
||||
*
|
||||
* Created by Daniel Boehringer on 2/JAN/2014.
|
||||
* All modifications copyright Daniel Boehringer 2013.
|
||||
* Extensive code formatting and review by Andrew Hankinson
|
||||
* Based on original work by
|
||||
* Created by Emmanuel Maillard on 06/03/2010.
|
||||
* Copyright Emmanuel Maillard 2010.
|
||||
*
|
||||
* 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 "CPPanel.j"
|
||||
@import "CPColorWell.j"
|
||||
@import "CPColorPanel.j"
|
||||
@import "CPBrowser.j"
|
||||
@import "CPText.j"
|
||||
@import "CPFontManager.j"
|
||||
|
||||
|
||||
@class CPTextStorage
|
||||
@class CPLayoutManager
|
||||
@class CPTextContainer
|
||||
@class CPFontManager
|
||||
|
||||
/*
|
||||
Collection indexes
|
||||
*/
|
||||
var kTypefaceIndex_Normal = 0,
|
||||
kTypefaceIndex_Italic = 1,
|
||||
kTypefaceIndex_Bold = 2,
|
||||
kTypefaceIndex_BoldItalic = 3,
|
||||
kToolbarHeight = 32,
|
||||
kBorderSpacing = 6,
|
||||
kInnerSpacing = 2,
|
||||
kNothingChanged = 0,
|
||||
kFontNameChanged = 1,
|
||||
kTypefaceChanged = 2,
|
||||
kSizeChanged = 3,
|
||||
kTextColorChanged = 4,
|
||||
kBackgroundColorChanged = 5,
|
||||
kUnderlineChanged = 6,
|
||||
kWeightChanged = 7,
|
||||
_sharedFontPanel;
|
||||
|
||||
// FIXME<!> Locale support
|
||||
var _availableTraits= [@"Normal", @"Italic", @"Bold", @"Bold Italic"],
|
||||
_availableSizes = [@"9", @"10", @"11", @"12", @"13", @"14", @"18", @"24", @"36", @"48", @"72", @"96"];
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPFontPanel
|
||||
*/
|
||||
@implementation CPFontPanel : CPPanel
|
||||
{
|
||||
id _fontBrowser;
|
||||
id _traitBrowser;
|
||||
id _sizeBrowser;
|
||||
CPArray _availableFonts;
|
||||
id _textColorWell;
|
||||
CPColor _textColor;
|
||||
int _currentColorButtonTag;
|
||||
BOOL _setupDone;
|
||||
int _fontChanges;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class methods
|
||||
|
||||
/*!
|
||||
Check if the shared Font panel exists.
|
||||
*/
|
||||
+ (BOOL)sharedFontPanelExists
|
||||
{
|
||||
return _sharedFontPanel !== nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Return the shared Font panel.
|
||||
*/
|
||||
+ (CPFontPanel)sharedFontPanel
|
||||
{
|
||||
if (!_sharedFontPanel)
|
||||
_sharedFontPanel = [[CPFontPanel alloc] init];
|
||||
|
||||
return _sharedFontPanel;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Init methods
|
||||
|
||||
/*! @ignore */
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super initWithContentRect:CGRectMake(100, 90, 450, 394) styleMask:(CPTitledWindowMask | CPClosableWindowMask /*| CPResizableWindowMask*/ )])
|
||||
{
|
||||
[[self contentView] setBackgroundColor:[CPColor colorWithWhite:0.95 alpha:1.0]];
|
||||
[self setTitle:@"Font Panel"];
|
||||
[self setLevel:CPFloatingWindowLevel];
|
||||
[self setFloatingPanel:YES];
|
||||
[self setBecomesKeyOnlyIfNeeded:YES];
|
||||
[self setMinSize:CGSizeMake(378, 394)];
|
||||
|
||||
_availableFonts = [[CPFontManager sharedFontManager] availableFonts];
|
||||
_textColor = [CPColor blackColor];
|
||||
_setupDone = NO;
|
||||
_fontChanges = kNothingChanged;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*! @ignore */
|
||||
- (void)_setupToolbarView
|
||||
{
|
||||
_toolbarView = [[CPView alloc] initWithFrame:CGRectMake(0, kBorderSpacing, CGRectGetWidth([self frame]), kToolbarHeight)];
|
||||
[_toolbarView setAutoresizingMask:CPViewWidthSizable];
|
||||
|
||||
// Text color
|
||||
_textColorWell = [[CPColorWell alloc] initWithFrame:CGRectMake(10, 0, 25, 25)];
|
||||
[_textColorWell setColor:_textColor];
|
||||
[_toolbarView addSubview:_textColorWell];
|
||||
}
|
||||
|
||||
- (void)_setupBrowser:(CPBrowser)aBrowser
|
||||
{
|
||||
[aBrowser setTarget:self];
|
||||
[aBrowser setAction:@selector(browserClicked:)];
|
||||
[aBrowser setDoubleAction:@selector(dblClicked:)];
|
||||
[aBrowser setAllowsEmptySelection:NO];
|
||||
[aBrowser setAllowsMultipleSelection:NO];
|
||||
[aBrowser setDelegate:self];
|
||||
[[self contentView] addSubview:aBrowser];
|
||||
}
|
||||
|
||||
- (void)_setupContents
|
||||
{
|
||||
if (_setupDone)
|
||||
return;
|
||||
|
||||
_setupDone = YES;
|
||||
|
||||
[self _setupToolbarView];
|
||||
|
||||
var contentView = [self contentView],
|
||||
label = [CPTextField labelWithTitle:@"Font name"],
|
||||
contentBounds = [contentView bounds],
|
||||
upperView = [[CPView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(contentBounds), CGRectGetHeight(contentBounds) - (kBorderSpacing + kToolbarHeight + kInnerSpacing))];
|
||||
|
||||
[contentView addSubview:_toolbarView];
|
||||
|
||||
_fontBrowser = [[CPBrowser alloc] initWithFrame:CGRectMake(10, 35, 150, 350)];
|
||||
_traitBrowser = [[CPBrowser alloc] initWithFrame:CGRectMake(155, 35, 150, 350)];
|
||||
_sizeBrowser = [[CPBrowser alloc] initWithFrame:CGRectMake(300, 35, 140, 350)];
|
||||
|
||||
[self _setupBrowser:_fontBrowser];
|
||||
[self _setupBrowser:_traitBrowser];
|
||||
[self _setupBrowser:_sizeBrowser];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(textViewDidChangeSelection:)
|
||||
name:CPTextViewDidChangeSelectionNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)textViewDidChangeSelection:(CPNotification)notification
|
||||
{
|
||||
[self _refreshWithTextView:[notification object]];
|
||||
|
||||
}
|
||||
|
||||
- (void)_refreshWithTextView:(CPTextView)textView
|
||||
{
|
||||
if (![self isVisible])
|
||||
return;
|
||||
|
||||
var attribs = [textView _attributesForFontPanel],
|
||||
font = [attribs objectForKey:CPFontAttributeName] || [[textView textStorage] font] || [CPFont systemFontOfSize:12.0],
|
||||
color = [attribs objectForKey:CPForegroundColorAttributeName];
|
||||
|
||||
if (!font)
|
||||
return;
|
||||
|
||||
var trait = kTypefaceIndex_Normal;
|
||||
|
||||
if ([font isItalic] && [font isBold])
|
||||
trait = kTypefaceIndex_BoldItalic;
|
||||
else if ([font isItalic])
|
||||
trait = kTypefaceIndex_Italic;
|
||||
else if ([font isBold])
|
||||
trait = kTypefaceIndex_Bold;
|
||||
|
||||
[self setCurrentFont:font];
|
||||
[self setCurrentTrait:trait];
|
||||
[self setCurrentSize:[font size] + ""]; //cast to string
|
||||
|
||||
if (!color)
|
||||
return;
|
||||
|
||||
[_textColorWell setColor:color];
|
||||
}
|
||||
|
||||
- (void)orderFront:(id)sender
|
||||
{
|
||||
[self _setupContents];
|
||||
[super orderFront:sender];
|
||||
[self _refreshWithTextView:[[CPApp keyWindow] firstResponder]];
|
||||
}
|
||||
|
||||
- (void)reloadDefaultFontFamilies
|
||||
{
|
||||
_availableFonts = [[CPFontManager sharedFontManager] availableFonts];
|
||||
}
|
||||
|
||||
- (BOOL)worksWhenModal
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
@param aFont the font to convert.
|
||||
@return The converted font or \c aFont if failed to convert.
|
||||
*/
|
||||
- (CPFont)panelConvertFont:(CPFont)aFont
|
||||
{
|
||||
var newFont = aFont,
|
||||
index = 0;
|
||||
|
||||
switch (_fontChanges)
|
||||
{
|
||||
case kFontNameChanged:
|
||||
newFont = [CPFont fontWithDescriptor:[[aFont fontDescriptor] fontDescriptorByAddingAttributes:
|
||||
[CPDictionary dictionaryWithObject:[self currentFont] forKey:CPFontNameAttribute]] size:0.0];
|
||||
break;
|
||||
|
||||
case kTypefaceChanged:
|
||||
index = [self currentTrait];
|
||||
if (index == kTypefaceIndex_BoldItalic)
|
||||
newFont = [[CPFontManager sharedFontManager] convertFont:aFont toHaveTrait:CPBoldFontMask | CPItalicFontMask];
|
||||
else if (index == kTypefaceIndex_Bold)
|
||||
newFont = [[CPFontManager sharedFontManager] convertFont:aFont toHaveTrait:CPBoldFontMask];
|
||||
else if (index == kTypefaceIndex_Italic)
|
||||
newFont = [[CPFontManager sharedFontManager] convertFont:aFont toHaveTrait:CPItalicFontMask];
|
||||
else
|
||||
newFont = [[CPFontManager sharedFontManager] convertFont:aFont toNotHaveTrait:CPBoldFontMask | CPItalicFontMask];
|
||||
break;
|
||||
|
||||
case kSizeChanged:
|
||||
newFont = [[CPFontManager sharedFontManager] convertFont:aFont toSize:[self currentSize]];
|
||||
break;
|
||||
|
||||
case kNothingChanged:
|
||||
break;
|
||||
|
||||
default:
|
||||
CPLog.trace(@"FIXME: -[" + [self className] + " " + _cmd + "] unhandled _fontChanges: " + _fontChanges);
|
||||
break;
|
||||
}
|
||||
|
||||
return newFont;
|
||||
}
|
||||
|
||||
- (void)setCurrentSize:(CGSize)aSize
|
||||
{
|
||||
[_sizeBrowser selectRow:[_availableSizes indexOfObject:aSize] inColumn:0];
|
||||
}
|
||||
|
||||
- (CPString)currentSize
|
||||
{
|
||||
return [_sizeBrowser selectedItem];
|
||||
}
|
||||
|
||||
- (void)setCurrentFont:(CPFont)aFont
|
||||
{
|
||||
[_fontBrowser selectRow:[_availableFonts indexOfObject:[aFont familyName]] inColumn:0];
|
||||
}
|
||||
|
||||
- (CPString)currentFont
|
||||
{
|
||||
return [_fontBrowser selectedItem];
|
||||
}
|
||||
|
||||
- (void)setCurrentTrait:(unsigned)aTrait
|
||||
{
|
||||
var row = 0;
|
||||
|
||||
switch (aTrait)
|
||||
{
|
||||
case kTypefaceIndex_Italic:
|
||||
row = 1;
|
||||
break;
|
||||
|
||||
case kTypefaceIndex_Bold:
|
||||
row = 2;
|
||||
break;
|
||||
|
||||
case kTypefaceIndex_BoldItalic:
|
||||
row = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
[_traitBrowser selectRow:row inColumn:0];
|
||||
}
|
||||
|
||||
// FIXME<!> Locale support
|
||||
- (unsigned)currentTrait
|
||||
{
|
||||
var sel = [_traitBrowser selectedItem];
|
||||
|
||||
if (sel === "Italic")
|
||||
return kTypefaceIndex_Italic;
|
||||
|
||||
if (sel === "Bold")
|
||||
return kTypefaceIndex_Bold;
|
||||
|
||||
if (sel === "Bold Italic")
|
||||
return kTypefaceIndex_BoldItalic;
|
||||
|
||||
return kTypefaceIndex_Normal;
|
||||
}
|
||||
|
||||
/*!
|
||||
Set the selected font in Font panel.
|
||||
@param font the selected font
|
||||
@param flag if \c the current selection have multiple fonts.
|
||||
*/
|
||||
- (void)setPanelFont:(CPFont)font isMultiple:(BOOL)flag
|
||||
{
|
||||
[self _setupContents];
|
||||
|
||||
if ([self currentFont] !== [font familyName])
|
||||
[self setCurrentFont:[font familyName]];
|
||||
|
||||
if ([self currentSize] != [font size])
|
||||
[self setCurrentSize:[font size]];
|
||||
|
||||
var typefaceIndex = kTypefaceIndex_Normal,
|
||||
symbolicTraits = [[font fontDescriptor] symbolicTraits];
|
||||
|
||||
if ((symbolicTraits & CPFontItalicTrait) && (symbolicTraits & CPFontBoldTrait))
|
||||
typefaceIndex = kTypefaceIndex_BoldItalic;
|
||||
else if (symbolicTraits & CPFontItalicTrait)
|
||||
typefaceIndex = kTypefaceIndex_Italic;
|
||||
else if (symbolicTraits & CPFontBoldTrait)
|
||||
typefaceIndex = kTypefaceIndex_Bold;
|
||||
|
||||
if ([self currentTrait] != typefaceIndex)
|
||||
[self setCurrentTrait:typefaceIndex ];
|
||||
|
||||
_fontChanges = kNothingChanged;
|
||||
}
|
||||
|
||||
- (void)changeColor:(id)sender
|
||||
{
|
||||
_textColor = [sender color];
|
||||
_fontChanges = kTextColorChanged;
|
||||
[[CPFontManager sharedFontManager] modifyFontViaPanel:self];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// TODO: ask CPFontManager for traits //
|
||||
- (void)browserClicked:(id)aBrowser
|
||||
{
|
||||
if (aBrowser === _fontBrowser)
|
||||
{
|
||||
_fontChanges = kFontNameChanged;
|
||||
[[CPFontManager sharedFontManager] modifyFontViaPanel:self];
|
||||
}
|
||||
else if (aBrowser === _traitBrowser)
|
||||
{
|
||||
_fontChanges = kTypefaceChanged;
|
||||
[[CPFontManager sharedFontManager] modifyFontViaPanel:self];
|
||||
}
|
||||
else if (aBrowser === _sizeBrowser)
|
||||
{
|
||||
_fontChanges = kSizeChanged;
|
||||
[[CPFontManager sharedFontManager] modifyFontViaPanel:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dblClicked:(id)sender
|
||||
{
|
||||
// alert("DOUBLE");
|
||||
}
|
||||
|
||||
- (id)browser:(id)aBrowser numberOfChildrenOfItem:(id)anItem
|
||||
{
|
||||
if (aBrowser === _fontBrowser)
|
||||
return [_availableFonts count];
|
||||
|
||||
if (aBrowser === _traitBrowser)
|
||||
return [_availableTraits count];
|
||||
|
||||
return [_availableSizes count]
|
||||
}
|
||||
|
||||
- (id)browser:(id)aBrowser child:(int)index ofItem:(id)anItem
|
||||
{
|
||||
if (aBrowser === _fontBrowser)
|
||||
return [_availableFonts objectAtIndex:index];
|
||||
|
||||
if (aBrowser === _traitBrowser)
|
||||
return [_availableTraits objectAtIndex:index];
|
||||
|
||||
return [_availableSizes objectAtIndex:index];
|
||||
}
|
||||
|
||||
- (id)browser:(id)aBrowser objectValueForItem:(id)anItem
|
||||
{
|
||||
return anItem;
|
||||
}
|
||||
|
||||
- (BOOL)browser:(id)aBrowser isLeafItem:(id)anItem
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
[CPFontManager setFontPanelFactory:[CPFontPanel class]];
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* CPParagraphStyle.j
|
||||
* AppKit
|
||||
*
|
||||
* FIXME
|
||||
* This is basically a stub.
|
||||
* We need to store all the spacing informations as well as writing direction (among others)
|
||||
*
|
||||
* Created by Daniel Boehringer on 11/01/2014
|
||||
* Copyright Daniel Boehringer 2014.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
@import <Foundation/CPObject.j>
|
||||
@import <Foundation/CPArray.j>
|
||||
|
||||
@import "CPText.j"
|
||||
|
||||
CPLeftTabStopType = 0;
|
||||
|
||||
CPParagraphStyleAttributeName = @"CPParagraphStyleAttributeName";
|
||||
|
||||
var _sharedDefaultParagraphStyle,
|
||||
_defaultTabStopArray;
|
||||
|
||||
@implementation CPParagraphStyle : CPObject
|
||||
{
|
||||
CPArray _tabStops @accessors(property=tabStops);
|
||||
CPTextAlignment _alignment @accessors(property=alignment);
|
||||
unsigned _firstLineHeadIndent @accessors(property=firstLineHeadIndent);
|
||||
unsigned _headIndent @accessors(property=headIndent);
|
||||
unsigned _tailIndent @accessors(property=tailIndent);
|
||||
unsigned _paragraphSpacing @accessors(property=paragraphSpacing);
|
||||
unsigned _minimumLineHeight @accessors(property=minimumLineHeight);
|
||||
unsigned _maximumLineHeight @accessors(property=maximumLineHeight);
|
||||
unsigned _lineSpacing @accessors(property=lineSpacing);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class methods
|
||||
|
||||
+ (CPParagraphStyle)defaultParagraphStyle
|
||||
{
|
||||
if (!_sharedDefaultParagraphStyle)
|
||||
_sharedDefaultParagraphStyle = [self new];
|
||||
|
||||
return _sharedDefaultParagraphStyle;
|
||||
}
|
||||
|
||||
+ (CPArray)_defaultTabStops
|
||||
{
|
||||
if (!_defaultTabStopArray)
|
||||
{
|
||||
var i;
|
||||
_defaultTabStopArray = [];
|
||||
|
||||
// <!> FIXME: Define constants for these magic numbers: 13, 28
|
||||
for (i = 1; i < 16 ; i++)
|
||||
{
|
||||
_defaultTabStopArray.push([[CPTextTab alloc] initWithType:CPLeftTabStopType location:i * 28]);
|
||||
}
|
||||
}
|
||||
|
||||
return _defaultTabStopArray;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Init methods
|
||||
|
||||
- (id)init
|
||||
{
|
||||
[self _initWithDefaults];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPParagraphStyle)initWithParagraphStyle:(CPParagraphStyle)other
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_tabStops = [other._tabStops copy];
|
||||
_alignment = other._alignment;
|
||||
_firstLineHeadIndent = other._firstLineHeadIndent;
|
||||
_headIndent = other._headIndent;
|
||||
_tailIndent = other._tailIndent;
|
||||
_paragraphSpacing = other._paragraphSpacing;
|
||||
_minimumLineHeight = other._minimumLineHeight;
|
||||
_maximumLineHeight = other._maximumLineHeight;
|
||||
_lineSpacing = other._lineSpacing;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_initWithDefaults
|
||||
{
|
||||
_alignment = CPLeftTextAlignment;
|
||||
_tabStops = [[[self class] _defaultTabStops] copy];
|
||||
}
|
||||
|
||||
- (void)addTabStop:(CPTextTab)aStop
|
||||
{
|
||||
_tabStops.push(aStop);
|
||||
}
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
var other = [[self class] alloc];
|
||||
|
||||
return [other initWithParagraphStyle:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPParagraphStyleTabStopsKey = @"CPParagraphStyleTabStopsKey",
|
||||
CPParagraphStyleAlignmentKey = @"CPParagraphStyleAlignmentKey",
|
||||
CPParagraphStyleFirstLineHeadIndentKey = @"CPParagraphStyleFirstLineHeadIndentKey",
|
||||
CPParagraphStyleHeadIndentKey = @"CPParagraphStyleHeadIndentKey",
|
||||
CPParagraphStyleTailIndentKey = @"CPParagraphStyleTailIndentKey",
|
||||
CPParagraphStyleParagraphSpacingKey = @"CPParagraphStyleParagraphSpacingKey",
|
||||
CPParagraphStyleMinimumLineHeightKey = @"CPParagraphStyleMinimumLineHeightKey",
|
||||
CPParagraphStyleMaximumLineHeightKey = @"CPParagraphStyleMaximumLineHeightKey",
|
||||
CPParagraphStyleLineSpacingKey = @"CPParagraphStyleLineSpacingKey";
|
||||
|
||||
@implementation CPParagraphStyle (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(id)aCoder
|
||||
{
|
||||
self = [self init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_tabStops = [aCoder decodeObjectForKey:"CPParagraphStyleTabStopsKey"];
|
||||
_alignment = [aCoder decodeIntForKey:"CPParagraphStyleAlignmentKey"];
|
||||
_firstLineHeadIndent = [aCoder decodeIntForKey:"CPParagraphStyleFirstLineHeadIndentKey"];
|
||||
_headIndent = [aCoder decodeIntForKey:"CPParagraphStyleHeadIndentKey"];
|
||||
_tailIndent = [aCoder decodeIntForKey:"CPParagraphStyleTailIndentKey"];
|
||||
_paragraphSpacing = [aCoder decodeIntForKey:"CPParagraphStyleParagraphSpacingKey"];
|
||||
_minimumLineHeight = [aCoder decodeIntForKey:"CPParagraphStyleMinimumLineHeightKey"];
|
||||
_maximumLineHeight = [aCoder decodeIntForKey:"CPParagraphStyleMaximumLineHeightKey"];
|
||||
_lineSpacing = [aCoder decodeIntForKey:"CPParagraphStyleLineSpacingKey"];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(id)aCoder
|
||||
{
|
||||
[aCoder encodeInt:_alignment forKey:"CPParagraphStyleAlignmentKey"];
|
||||
[aCoder encodeObject:_tabStops forKey:"CPParagraphStyleTabStopsKey"];
|
||||
[aCoder encodeInt:_firstLineHeadIndent forKey:"CPParagraphStyleFirstLineHeadIndentKey"];
|
||||
[aCoder encodeInt:_headIndent forKey:"CPParagraphStyleHeadIndentKey"];
|
||||
[aCoder encodeInt:_tailIndent forKey:"CPParagraphStyleTailIndentKey"];
|
||||
[aCoder encodeInt:_paragraphSpacing forKey:"CPParagraphStyleParagraphSpacingKey"];
|
||||
[aCoder encodeInt:_minimumLineHeight forKey:"CPParagraphStyleMinimumLineHeightKey"];
|
||||
[aCoder encodeInt:_maximumLineHeight forKey:"CPParagraphStyleMaximumLineHeightKey"];
|
||||
[aCoder encodeInt:_lineSpacing forKey:"CPParagraphStyleLineSpacingKey"];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPTextTab : CPObject
|
||||
{
|
||||
int _type @accessors(property = tabStopType);
|
||||
double _location @accessors(property = location);
|
||||
}
|
||||
|
||||
- (id)initWithType:(CPTabStopType) aType location:(double) aLocation
|
||||
{
|
||||
if ([self = [super init]])
|
||||
{
|
||||
_type = aType;
|
||||
_location = aLocation;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTextTabTypeKey = @"CPTextTabTypeKey",
|
||||
CPTextTabLocationKey = @"CPTextTabLocationKey";
|
||||
|
||||
@implementation CPTextTab (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(id)aCoder
|
||||
{
|
||||
self = [self init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_type = [aCoder decodeIntForKey:"CPTextTabTypeKey"];
|
||||
_location = [aCoder decodeDoubleForKey:"CPTextTabLocationKey"];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(id)aCoder
|
||||
{
|
||||
[aCoder encodeInt:_type forKey:"CPTextTabTypeKey"];
|
||||
[aCoder encodeDouble:_location forKey:"CPTextTabLocationKey"];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* CPTextContainer.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Emmanuel Maillard on 27/02/2010.
|
||||
* Copyright Emmanuel Maillard 2010.
|
||||
*
|
||||
* 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/CPGeometry.j>
|
||||
@import "CPLayoutManager.j"
|
||||
|
||||
@class CPTextView
|
||||
@class CPLayoutManager
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPLineSweepDirection
|
||||
*/
|
||||
CPLineSweepLeft = 0;
|
||||
/*
|
||||
@global
|
||||
@group CPLineSweepDirection
|
||||
*/
|
||||
CPLineSweepRight = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPLineSweepDirection
|
||||
*/
|
||||
CPLineSweepDown = 2;
|
||||
/*
|
||||
@global
|
||||
@group CPLineSweepDirection
|
||||
*/
|
||||
CPLineSweepUp = 3;
|
||||
|
||||
/*
|
||||
@global
|
||||
@group CPLineMovementDirection
|
||||
*/
|
||||
CPLineDoesntMoves = 0;
|
||||
/*
|
||||
@global
|
||||
@group CPLineMovementDirection
|
||||
*/
|
||||
CPLineMovesLeft = 1;
|
||||
/*
|
||||
@global
|
||||
@group CPLineMovementDirection
|
||||
*/
|
||||
CPLineMovesRight = 2;
|
||||
/*
|
||||
@global
|
||||
@group CPLineMovementDirection
|
||||
*/
|
||||
CPLineMovesDown = 3;
|
||||
/*
|
||||
@global
|
||||
@group CPLineMovementDirection
|
||||
*/
|
||||
CPLineMovesUp = 4;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPTextContainer
|
||||
*/
|
||||
@implementation CPTextContainer : CPObject
|
||||
{
|
||||
float _lineFragmentPadding @accessors(property=lineFragmentPadding);
|
||||
CGSize _size @accessors(property=containerSize);
|
||||
CPLayoutManager _layoutManager @accessors(property=layoutManager);
|
||||
CPTextView _textView @accessors(property=textView);
|
||||
BOOL _inResizing;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Init methods
|
||||
|
||||
- (id)initWithContainerSize:(CGSize)aSize
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_size = aSize;
|
||||
[self _init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithContainerSize:CPMakeSize(1e7, 1e7)];
|
||||
}
|
||||
|
||||
- (void)_init
|
||||
{
|
||||
_lineFragmentPadding = 0.0;
|
||||
|
||||
_layoutManager = [[CPLayoutManager alloc] init];
|
||||
[_layoutManager addTextContainer:self];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Setter methods
|
||||
|
||||
- (void)setContainerSize:(CGSize)someSize
|
||||
{
|
||||
var oldSize = _size;
|
||||
|
||||
_size = CGSizeMakeCopy(someSize);
|
||||
|
||||
if (oldSize.width != _size.width)
|
||||
{
|
||||
_inResizing = YES;
|
||||
[_layoutManager invalidateLayoutForCharacterRange:CPMakeRange(0, [[_layoutManager textStorage] length])
|
||||
isSoft:NO
|
||||
actualCharacterRange:NULL];
|
||||
|
||||
[_layoutManager _validateLayoutAndGlyphs];
|
||||
[_textView sizeToFit]; // this is necessary to adopt the height of CPTextView in case of rewrapping
|
||||
_inResizing = NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Controls whether the receiver adjusts the width of its bounding rectangle when its text view is resized.
|
||||
- (void)setWidthTracksTextView:(BOOL)flag
|
||||
{
|
||||
[_textView setPostsFrameChangedNotifications:flag];
|
||||
|
||||
if (flag)
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(textViewFrameChanged:)
|
||||
name:CPViewFrameDidChangeNotification
|
||||
object:_textView];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:self
|
||||
name:CPViewFrameDidChangeNotification
|
||||
object:_textView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)textViewFrameChanged:(CPNotification)aNotification
|
||||
{
|
||||
var newSize = CGSizeMake([_textView frame].size.width, _size.height);
|
||||
|
||||
[self setContainerSize:newSize];
|
||||
}
|
||||
|
||||
- (void)setTextView:(CPTextView)aTextView
|
||||
{
|
||||
if (_textView)
|
||||
[_textView setTextContainer:nil];
|
||||
|
||||
_textView = aTextView;
|
||||
|
||||
if (_textView)
|
||||
[_textView setTextContainer:self];
|
||||
|
||||
[_layoutManager textContainerChangedTextView:self];
|
||||
}
|
||||
|
||||
- (BOOL)containsPoint:(CGPoint)aPoint
|
||||
{
|
||||
return CGRectContainsPoint(CGRectMake(0, 0, _size.width, _size.height), aPoint);
|
||||
}
|
||||
|
||||
- (BOOL)isSimpleRectangularTextContainer
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CGRect)lineFragmentRectForProposedRect:(CGRect)proposedRect
|
||||
sweepDirection:(CPLineSweepDirection)sweep
|
||||
movementDirection:(CPLineMovementDirection)movement
|
||||
remainingRect:(CGRectPointer)remainingRect
|
||||
{
|
||||
var resultRect = CGRectCreateCopy(proposedRect);
|
||||
|
||||
if (sweep != CPLineSweepRight || movement != CPLineMovesDown)
|
||||
{
|
||||
CPLog.trace(@"FIXME: unsupported sweep (" + sweep + ") or movement (" + movement + ")");
|
||||
return CGRectMakeZero();
|
||||
}
|
||||
|
||||
if (resultRect.origin.x + resultRect.size.width > _size.width)
|
||||
resultRect.size.width = _size.width - resultRect.origin.x;
|
||||
|
||||
if (resultRect.size.width < 0)
|
||||
resultRect = CGRectMakeZero();
|
||||
|
||||
if (remainingRect)
|
||||
{
|
||||
remainingRect.origin.x = resultRect.origin.x + resultRect.size.width;
|
||||
remainingRect.origin.y = resultRect.origin.y;
|
||||
remainingRect.size.height = resultRect.size.height;
|
||||
remainingRect.size.width = _size.width - (resultRect.origin.x + resultRect.size.width);
|
||||
}
|
||||
|
||||
return resultRect;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTextContainerSizeKey = @"CPTextContainerSizeKey",
|
||||
CPTextContainerLayoutManagerKey = @"CPTextContainerLayoutManagerKey";
|
||||
|
||||
@implementation CPTextContainer (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _init];
|
||||
|
||||
_size = [aCoder decodeSizeForKey:CPTextContainerSizeKey];
|
||||
|
||||
_layoutManager = [aCoder decodeObjectForKey:CPTextContainerLayoutManagerKey];
|
||||
[_layoutManager addTextContainer:self];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeSize:_size forKey:CPTextContainerSizeKey];
|
||||
[aCoder encodeObject:_layoutManager forKey:CPTextContainerLayoutManagerKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* CPTextStorage.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Emmanuel Maillard on 27/02/2010.
|
||||
* Copyright Emmanuel Maillard 2010.
|
||||
*
|
||||
* 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/CPNotificationCenter.j>
|
||||
@import <Foundation/CPAttributedString.j>
|
||||
|
||||
@import "CPText.j"
|
||||
@import "CPFont.j"
|
||||
|
||||
@class CPLayoutManager;
|
||||
|
||||
CPTextStorageEditedAttributes = 1;
|
||||
CPTextStorageEditedCharacters = 2;
|
||||
|
||||
CPTextStorageWillProcessEditingNotification = @"CPTextStorageWillProcessEditingNotification";
|
||||
CPTextStorageDidProcessEditingNotification = @"CPTextStorageDidProcessEditingNotification";
|
||||
|
||||
@protocol CPTextStorageDelegate <CPObject>
|
||||
|
||||
- (void)textStorageWillProcessEditing:(CPNotification)aNotification;
|
||||
- (void)textStorageDidProcessEditing:(CPNotification)aNotification;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPTextStorageDelegate_textStorageWillProcessEditing_ = 1 << 1,
|
||||
CPTextStorageDelegate_textStorageDidProcessEditing_ = 1 << 2;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
@class CPTextStorage
|
||||
*/
|
||||
@implementation CPTextStorage : CPMutableAttributedString
|
||||
{
|
||||
CPColor _foregroundColor @accessors(property=foregroundColor);
|
||||
CPFont _font @accessors(property=font);
|
||||
CPMutableArray _layoutManagers @accessors(getter=layoutManagers);
|
||||
CPRange _editedRange @accessors(getter=editedRange);
|
||||
id <CPTextStorageDelegate> _delegate @accessors(property=delegate);
|
||||
int _changeInLength @accessors(property=changeInLength);
|
||||
unsigned _editedMask @accessors(property=editedMask);
|
||||
|
||||
int _editCount; // {begin,end}Editing counter
|
||||
unsigned _implementedDelegateMethods;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Init methods
|
||||
|
||||
- (id)initWithString:(CPString)aString attributes:(CPDictionary)attributes
|
||||
{
|
||||
self = [super initWithString:aString attributes:attributes];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_layoutManagers = [[CPMutableArray alloc] init];
|
||||
_editedRange = CPMakeRange(CPNotFound, 0);
|
||||
_changeInLength = 0;
|
||||
_editedMask = 0;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithString:(CPString)aString
|
||||
{
|
||||
return [self initWithString:aString attributes:nil];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithString:@"" attributes:nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Delegate methods
|
||||
|
||||
- (void)setDelegate:(id <CPTextStorageDelegate>)aDelegate
|
||||
{
|
||||
if (_delegate === aDelegate)
|
||||
return;
|
||||
|
||||
_implementedDelegateMethods = 0;
|
||||
_delegate = aDelegate;
|
||||
|
||||
if (_delegate)
|
||||
{
|
||||
if ([_delegate respondsToSelector:@selector(textStorageWillProcessEditing:)])
|
||||
_implementedDelegateMethods |= CPTextStorageDelegate_textStorageWillProcessEditing_;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(textStorageDidProcessEditing:)])
|
||||
_implementedDelegateMethods |= CPTextStorageDelegate_textStorageDidProcessEditing_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Layout manager methods
|
||||
|
||||
- (void)addLayoutManager:(CPLayoutManager)aManager
|
||||
{
|
||||
if ([_layoutManagers containsObject:aManager])
|
||||
return
|
||||
|
||||
[aManager setTextStorage:self];
|
||||
[_layoutManagers addObject:aManager];
|
||||
}
|
||||
|
||||
- (void)removeLayoutManager:(CPLayoutManager)aManager
|
||||
{
|
||||
if (![_layoutManagers containsObject:aManager])
|
||||
return
|
||||
|
||||
[aManager setTextStorage:nil];
|
||||
[_layoutManagers removeObject:aManager];
|
||||
}
|
||||
|
||||
- (void)invalidateAttributesInRange:(CPRange)aRange
|
||||
{
|
||||
/* FIXME: stub */
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Editing methods
|
||||
|
||||
- (void)processEditing
|
||||
{
|
||||
[self _sendDelegateWillProcessEditingNotification];
|
||||
[self invalidateAttributesInRange:[self editedRange]];
|
||||
[self _sendDelegateDidProcessEditingNotification];
|
||||
|
||||
var c = [_layoutManagers count];
|
||||
|
||||
for (var i = 0; i < c; i++)
|
||||
{
|
||||
[[_layoutManagers objectAtIndex:i] textStorage:self
|
||||
edited:_editedMask
|
||||
range:_editedRange
|
||||
changeInLength:_changeInLength
|
||||
invalidatedRange:_editedRange];
|
||||
}
|
||||
|
||||
_editedRange.location = CPNotFound;
|
||||
_editedMask = 0;
|
||||
_changeInLength = 0;
|
||||
}
|
||||
|
||||
- (void)beginEditing
|
||||
{
|
||||
if (_editCount == 0)
|
||||
_editedRange = CPMakeRange(CPNotFound, 0);
|
||||
|
||||
_editCount++;
|
||||
}
|
||||
|
||||
- (void)endEditing
|
||||
{
|
||||
_editCount--;
|
||||
|
||||
if (_editCount == 0)
|
||||
[self processEditing];
|
||||
}
|
||||
|
||||
- (void)edited:(unsigned)editedMask range:(CPRange)aRange changeInLength:(int)lengthChange
|
||||
{
|
||||
var copyRange = CPMakeRangeCopy(aRange);
|
||||
|
||||
if (_editCount == 0) // used outside a beginEditing/endEditing
|
||||
{
|
||||
_editedMask = editedMask;
|
||||
_changeInLength = lengthChange;
|
||||
copyRange.length += lengthChange;
|
||||
_editedRange = copyRange;
|
||||
[self processEditing];
|
||||
}
|
||||
else
|
||||
{
|
||||
_editedMask |= editedMask;
|
||||
_changeInLength += lengthChange;
|
||||
copyRange.length += lengthChange;
|
||||
|
||||
if (_editedRange.location == CPNotFound)
|
||||
_editedRange = copyRange;
|
||||
else
|
||||
_editedRange = CPUnionRange(_editedRange,copyRange);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeAttribute:(CPString)anAttribute range:(CPRange)aRange
|
||||
{
|
||||
[self beginEditing];
|
||||
[super removeAttribute:anAttribute range:aRange];
|
||||
[self edited:CPTextStorageEditedAttributes range:aRange changeInLength:0];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
- (void)addAttributes:(CPDictionary)aDictionary range:(CPRange)aRange
|
||||
{
|
||||
[self beginEditing];
|
||||
[super addAttributes:aDictionary range:aRange];
|
||||
[self edited:CPTextStorageEditedAttributes range:aRange changeInLength:0];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
- (void)deleteCharactersInRange:(CPRange)aRange
|
||||
{
|
||||
[self beginEditing];
|
||||
[super deleteCharactersInRange:aRange];
|
||||
[self edited:CPTextStorageEditedCharacters range:aRange changeInLength:-aRange.length];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
- (void)replaceCharactersInRange:(CPRange)aRange withString:(CPString)aString
|
||||
{
|
||||
[self beginEditing];
|
||||
[super replaceCharactersInRange:aRange withString:aString];
|
||||
[self edited:CPTextStorageEditedCharacters range:aRange changeInLength:([aString length] - aRange.length)];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
- (void)replaceCharactersInRange:(CPRange)aRange withAttributedString:(CPAttributedString)aString
|
||||
{
|
||||
[self beginEditing];
|
||||
[super replaceCharactersInRange:aRange withAttributedString:aString];
|
||||
[self edited:(CPTextStorageEditedAttributes | CPTextStorageEditedCharacters) range:aRange changeInLength:([aString length] - aRange.length)];
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
- (CPAttributedString)attributedSubstringFromRange:(CPRange)aRange
|
||||
{
|
||||
if (!aRange.length)
|
||||
return [CPAttributedString new];
|
||||
|
||||
return [super attributedSubstringFromRange:aRange];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPTextStorage (CPTextStorageDelegate)
|
||||
|
||||
- (void)_sendDelegateWillProcessEditingNotification
|
||||
{
|
||||
if (_implementedDelegateMethods & CPTextStorageDelegate_textStorageWillProcessEditing_)
|
||||
[_delegate textStorageWillProcessEditing:[[CPNotification alloc] initWithName:CPTextStorageWillProcessEditingNotification object:self userInfo:nil]];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextStorageWillProcessEditingNotification object:self];
|
||||
}
|
||||
|
||||
- (void)_sendDelegateDidProcessEditingNotification
|
||||
{
|
||||
if (_implementedDelegateMethods & CPTextStorageDelegate_textStorageDidProcessEditing_)
|
||||
[_delegate textStorageWillProcessEditing:[[CPNotification alloc] initWithName:CPTextStorageDidProcessEditingNotification object:self userInfo:nil]];
|
||||
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextStorageDidProcessEditingNotification object:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPTextStorage (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
self = [super initWithCoder:aCoder];
|
||||
|
||||
if (self)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
}
|
||||
|
||||
@end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* CPTypesetter.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Daniel Boehringer on 27/12/2013.
|
||||
* All modifications copyright Daniel Boehringer 2013.
|
||||
* Extensive code formatting and review by Andrew Hankinson
|
||||
* Based on original work by
|
||||
* Emmanuel Maillard on 27/02/2010.
|
||||
* Copyright Emmanuel Maillard 2010.
|
||||
*
|
||||
* 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 "CPParagraphStyle.j"
|
||||
@import "CPTextStorage.j"
|
||||
@import "CPFont.j"
|
||||
|
||||
// forward declare these classes for type matching
|
||||
@class CPLayoutManager
|
||||
@class CPTextContainer
|
||||
@class CPTextView
|
||||
|
||||
/*
|
||||
CPTypesetterControlCharacterAction
|
||||
*/
|
||||
CPTypesetterZeroAdvancementAction = 1 << 0;
|
||||
CPTypesetterWhitespaceAction = 1 << 1;
|
||||
CPSTypesetterHorizontalTabAction = 1 << 2;
|
||||
CPTypesetterLineBreakAction = 1 << 3;
|
||||
CPTypesetterParagraphBreakAction = 1 << 4;
|
||||
CPTypesetterContainerBreakAction = 1 << 5;
|
||||
|
||||
var CPSystemTypesetterFactory,
|
||||
_sharedSimpleTypesetter;
|
||||
|
||||
@implementation CPTypesetter : CPObject
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class methods
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
[CPTypesetter _setSystemTypesetterFactory:[CPSimpleTypesetter class]];
|
||||
}
|
||||
|
||||
+ (id)sharedSystemTypesetter
|
||||
{
|
||||
return [CPSystemTypesetterFactory sharedInstance];
|
||||
}
|
||||
|
||||
+ (void)_setSystemTypesetterFactory:(Class)aClass
|
||||
{
|
||||
CPSystemTypesetterFactory = aClass;
|
||||
}
|
||||
|
||||
- (CPTypesetterControlCharacterAction)actionForControlCharacterAtIndex:(unsigned)charIndex
|
||||
{
|
||||
return CPTypesetterZeroAdvancementAction;
|
||||
}
|
||||
|
||||
- (CPLayoutManager)layoutManager
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPTextContainer)currentTextContainer
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPArray)textContainers
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)layoutGlyphsInLayoutManager:(CPLayoutManager)layoutManager
|
||||
startingAtGlyphIndex:(unsigned)startGlyphIndex
|
||||
maxNumberOfLineFragments:(unsigned)maxNumLines
|
||||
nextGlyphIndex:(UIntegerReference)nextGlyph
|
||||
{
|
||||
_CPRaiseInvalidAbstractInvocation(self, _cmd);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPSimpleTypesetter : CPTypesetter
|
||||
{
|
||||
CPLayoutManager _layoutManager @accessors(property=layoutManager);
|
||||
CPTextContainer _currentTextContainer @accessors(property=currentTextContainer);
|
||||
CPTextStorage _textStorage;
|
||||
|
||||
CPRange _attributesRange;
|
||||
CPDictionary _currentAttributes;
|
||||
CPParagraphStyle _currentParagraph;
|
||||
|
||||
float _lineHeight;
|
||||
float _lineBase;
|
||||
float _lineWidth;
|
||||
|
||||
unsigned _indexOfCurrentContainer;
|
||||
|
||||
CPArray _lineFragments;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class methods
|
||||
|
||||
+ (id)sharedInstance
|
||||
{
|
||||
if (!_sharedSimpleTypesetter)
|
||||
_sharedSimpleTypesetter = [[CPSimpleTypesetter alloc] init];
|
||||
|
||||
return _sharedSimpleTypesetter;
|
||||
}
|
||||
|
||||
- (CPArray)textContainers
|
||||
{
|
||||
return [_layoutManager textContainers];
|
||||
}
|
||||
|
||||
- (CPTextTab)textTabForWidth:(double)aWidth writingDirection:(CPWritingDirection)direction
|
||||
{
|
||||
var tabStops = [_currentParagraph tabStops];
|
||||
|
||||
if (!tabStops)
|
||||
tabStops = [CPParagraphStyle _defaultTabStops];
|
||||
|
||||
var l = tabStops.length;
|
||||
|
||||
if (aWidth > tabStops[l - 1]._location)
|
||||
return nil;
|
||||
|
||||
for (var i = l - 1; i >= 0; i--)
|
||||
{
|
||||
if (aWidth > tabStops[i]._location)
|
||||
{
|
||||
if (i + 1 < l)
|
||||
return tabStops[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (i === -1)
|
||||
return tabStops[0];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)_flushRange:(CPRange)lineRange
|
||||
lineOrigin:(CGPoint)lineOrigin
|
||||
currentContainer:(CPTextContainer)aContainer
|
||||
advancements:(CPArray)advancements
|
||||
lineCount:(unsigned)lineCount
|
||||
sameLine:(BOOL)sameLine
|
||||
{
|
||||
var myX = 0,
|
||||
rect = CGRectMake(lineOrigin.x, lineOrigin.y, _lineWidth, _lineHeight),
|
||||
containerSize = aContainer._size;
|
||||
|
||||
[_layoutManager _appendNewLineFragmentInTextContainer:_currentTextContainer forGlyphRange:lineRange];
|
||||
|
||||
var fragment = [_layoutManager._lineFragments lastObject];
|
||||
fragment._isLast = !sameLine;
|
||||
_lineFragments.push(fragment);
|
||||
|
||||
[_layoutManager setLineFragmentRect:rect forGlyphRange:lineRange usedRect:rect];
|
||||
|
||||
switch ([_currentParagraph alignment])
|
||||
{
|
||||
case CPLeftTextAlignment:
|
||||
myX = 0;
|
||||
break;
|
||||
|
||||
case CPCenterTextAlignment:
|
||||
myX = (containerSize.width - _lineWidth) / 2;
|
||||
break;
|
||||
|
||||
case CPRightTextAlignment:
|
||||
myX = containerSize.width - _lineWidth;
|
||||
break;
|
||||
}
|
||||
|
||||
[_layoutManager setLocation:CGPointMake(myX, _lineBase) forStartOfGlyphRange:lineRange];
|
||||
[_layoutManager _setAdvancements:advancements forGlyphRange:lineRange];
|
||||
|
||||
if (!sameLine) //fix the _lineFragments when fontsizes differ
|
||||
{
|
||||
var l = _lineFragments.length;
|
||||
|
||||
for (var i = 0 ; i < l ; i++)
|
||||
[_lineFragments[i] _adjustForHeight:_lineHeight];
|
||||
}
|
||||
|
||||
if (!lineCount) // do not rescue on first line
|
||||
return NO;
|
||||
|
||||
if (aContainer._inResizing)
|
||||
return NO;
|
||||
|
||||
return ([_layoutManager _rescuingInvalidFragmentsWasPossibleForGlyphRange:lineRange]);
|
||||
}
|
||||
|
||||
- (void)layoutGlyphsInLayoutManager:(CPLayoutManager)layoutManager
|
||||
startingAtGlyphIndex:(unsigned)glyphIndex
|
||||
maxNumberOfLineFragments:(unsigned)maxNumLines
|
||||
nextGlyphIndex:(UIntegerReference)nextGlyph
|
||||
{
|
||||
var textContainers = [layoutManager textContainers],
|
||||
textContainersCount = [textContainers count];
|
||||
|
||||
_layoutManager = layoutManager;
|
||||
_textStorage = [_layoutManager textStorage];
|
||||
_indexOfCurrentContainer = MAX(0, [textContainers
|
||||
indexOfObject:[_layoutManager textContainerForGlyphAtIndex:glyphIndex effectiveRange:nil withoutAdditionalLayout:YES]
|
||||
inRange:CPMakeRange(0, textContainersCount)]);
|
||||
|
||||
_currentTextContainer = textContainers[_indexOfCurrentContainer];
|
||||
|
||||
_attributesRange = CPMakeRange(0, 0);
|
||||
_lineHeight = 0;
|
||||
_lineBase = 0;
|
||||
_lineWidth = 0;
|
||||
|
||||
var containerSize = [_currentTextContainer containerSize],
|
||||
containerSizeWidth = containerSize.width,
|
||||
containerSizeHeight = containerSize.height,
|
||||
lineRange = CPMakeRange(glyphIndex, 0),
|
||||
wrapRange = CPMakeRange(0, 0),
|
||||
wrapWidth = 0,
|
||||
isNewline = NO,
|
||||
isTabStop = NO,
|
||||
isWordWrapped = NO,
|
||||
numberOfGlyphs= [_textStorage length],
|
||||
leading,
|
||||
numLines = 0,
|
||||
theString = [_textStorage string],
|
||||
lineOrigin,
|
||||
ascent,
|
||||
descent,
|
||||
advancements = [],
|
||||
prevRangeWidth = 0,
|
||||
measuringRange = CPMakeRange(glyphIndex, 0),
|
||||
currentAnchor = 0,
|
||||
currentFont,
|
||||
currentFontLineHeight,
|
||||
previousFont,
|
||||
currentParagraphMinimumLineHeight,
|
||||
currentParagraphMaximumLineHeight,
|
||||
currentParagraphLineSpacing;
|
||||
|
||||
if (glyphIndex > 0)
|
||||
lineOrigin = CGPointCreateCopy([_layoutManager lineFragmentRectForGlyphAtIndex:glyphIndex effectiveRange:nil].origin);
|
||||
else if ([_layoutManager extraLineFragmentTextContainer])
|
||||
lineOrigin = CGPointMake(0, [_layoutManager extraLineFragmentUsedRect].origin.y);
|
||||
else
|
||||
lineOrigin = CGPointMake(0, 0);
|
||||
|
||||
[_layoutManager _removeInvalidLineFragments];
|
||||
|
||||
if (![_textStorage length])
|
||||
return;
|
||||
|
||||
_lineFragments = [];
|
||||
|
||||
for (; numLines != maxNumLines && glyphIndex < numberOfGlyphs; glyphIndex++)
|
||||
{
|
||||
// check whether there any change in the attributes from here on
|
||||
if (!CPLocationInRange(glyphIndex, _attributesRange))
|
||||
{
|
||||
_currentAttributes = [_textStorage attributesAtIndex:glyphIndex effectiveRange:_attributesRange];
|
||||
currentFont = [_currentAttributes objectForKey:CPFontAttributeName];
|
||||
_currentParagraph = [_currentAttributes objectForKey:CPParagraphStyleAttributeName] || [CPParagraphStyle defaultParagraphStyle];
|
||||
currentParagraphMinimumLineHeight = [_currentParagraph minimumLineHeight];
|
||||
currentParagraphMaximumLineHeight = [_currentParagraph maximumLineHeight];
|
||||
currentParagraphLineSpacing = [_currentParagraph lineSpacing];
|
||||
|
||||
if (!currentFont)
|
||||
currentFont = [_textStorage font] || [CPFont systemFontOfSize:12.0];
|
||||
|
||||
ascent = [currentFont ascender];
|
||||
descent = [currentFont descender];
|
||||
leading = (ascent - descent) * 0.2; // FAKE leading
|
||||
|
||||
currentFontLineHeight = ascent - descent + leading;
|
||||
|
||||
if (previousFont !== currentFont)
|
||||
{
|
||||
measuringRange = CPMakeRange(glyphIndex, 0);
|
||||
currentAnchor = prevRangeWidth;
|
||||
previousFont = currentFont;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (currentFontLineHeight > _lineHeight)
|
||||
_lineHeight = currentFontLineHeight;
|
||||
|
||||
if (ascent > _lineBase)
|
||||
_lineBase = ascent;
|
||||
|
||||
lineRange.length++;
|
||||
measuringRange.length++;
|
||||
|
||||
var currentCharCode = theString.charCodeAt(glyphIndex), // use pure javascript methods for performance reasons
|
||||
rangeWidth = [theString.substr(measuringRange.location, measuringRange.length) _sizeWithFont:currentFont inWidth:NULL].width + currentAnchor;
|
||||
|
||||
switch (currentCharCode) // faster than sending actionForControlCharacterAtIndex: called for each char.
|
||||
{
|
||||
case 9: // '\t'
|
||||
{
|
||||
var nextTab = [self textTabForWidth:rangeWidth + lineOrigin.x writingDirection:0];
|
||||
|
||||
isTabStop = YES;
|
||||
|
||||
if (nextTab)
|
||||
rangeWidth = nextTab._location - lineOrigin.x;
|
||||
else
|
||||
rangeWidth += 28; //FIXME
|
||||
} // fallthrough intentional
|
||||
case 32: // ' '
|
||||
wrapRange = CPMakeRangeCopy(lineRange);
|
||||
wrapWidth = rangeWidth;
|
||||
wrapRange._height = _lineHeight;
|
||||
wrapRange._base = _lineBase;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
case 13:
|
||||
isNewline = YES;
|
||||
}
|
||||
|
||||
advancements.push({width: rangeWidth - prevRangeWidth, height: ascent, descent: descent});
|
||||
|
||||
prevRangeWidth = _lineWidth = rangeWidth;
|
||||
|
||||
if (lineOrigin.x + rangeWidth > containerSizeWidth)
|
||||
{
|
||||
if (wrapWidth)
|
||||
{
|
||||
lineRange = wrapRange;
|
||||
_lineWidth = wrapWidth;
|
||||
_lineHeight = wrapRange._height;
|
||||
_lineBase = wrapRange._base;
|
||||
}
|
||||
|
||||
isNewline = YES;
|
||||
isWordWrapped = YES;
|
||||
glyphIndex = CPMaxRange(lineRange) - 1; // start the line starts directly at current character
|
||||
}
|
||||
|
||||
if (isNewline || isTabStop)
|
||||
{
|
||||
if ([self _flushRange:lineRange lineOrigin:lineOrigin currentContainer:_currentTextContainer advancements:advancements lineCount:numLines sameLine:!isNewline])
|
||||
return;
|
||||
|
||||
if (isTabStop)
|
||||
{
|
||||
lineOrigin.x += rangeWidth;
|
||||
isTabStop = NO;
|
||||
}
|
||||
|
||||
if (isNewline)
|
||||
{
|
||||
if (currentParagraphMinimumLineHeight && currentParagraphMinimumLineHeight > _lineHeight)
|
||||
_lineHeight = currentParagraphMinimumLineHeight;
|
||||
|
||||
if (currentParagraphMaximumLineHeight && currentParagraphMaximumLineHeight < _lineHeight)
|
||||
_lineHeight = currentParagraphMaximumLineHeight;
|
||||
|
||||
lineOrigin.y += _lineHeight;
|
||||
|
||||
if (currentParagraphLineSpacing)
|
||||
lineOrigin.y += currentParagraphLineSpacing;
|
||||
|
||||
if (lineOrigin.y > containerSizeHeight && _indexOfCurrentContainer < textContainersCount - 1)
|
||||
{
|
||||
_currentTextContainer = textContainers[++_indexOfCurrentContainer];
|
||||
containerSize = [_currentTextContainer containerSize];
|
||||
containerSizeWidth = containerSize.width;
|
||||
containerSizeHeight = containerSize.height;
|
||||
}
|
||||
|
||||
lineOrigin.x = 0;
|
||||
numLines++;
|
||||
isNewline = NO;
|
||||
_lineFragments = [];
|
||||
_lineHeight = 0;
|
||||
_lineBase = ascent;
|
||||
}
|
||||
|
||||
_lineWidth = 0;
|
||||
advancements = [];
|
||||
currentAnchor = 0;
|
||||
prevRangeWidth = 0;
|
||||
lineRange = CPMakeRange(glyphIndex + 1, 0);
|
||||
measuringRange = CPMakeRange(glyphIndex + 1, 0);
|
||||
wrapRange = CPMakeRange(0, 0);
|
||||
wrapWidth = 0;
|
||||
isWordWrapped = NO;
|
||||
}
|
||||
}
|
||||
|
||||
// this is to "flush" the remaining characters
|
||||
if (lineRange.length)
|
||||
{
|
||||
[self _flushRange:lineRange lineOrigin:lineOrigin currentContainer:_currentTextContainer advancements:advancements lineCount:numLines sameLine:NO];
|
||||
}
|
||||
|
||||
var rect = CGRectMake(0, lineOrigin.y, containerSizeWidth, [_layoutManager._lineFragments lastObject]._usedRect.size.height - descent);
|
||||
[_layoutManager setExtraLineFragmentRect:rect usedRect:rect textContainer:_currentTextContainer];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,793 @@
|
||||
/* RTFParser.j
|
||||
|
||||
Parse a RTF string into a CPAttributedString
|
||||
|
||||
Copyright (C) 2014 Daniel Boehringer
|
||||
|
||||
FIXME: this class should be redone using a 'real' parser
|
||||
|
||||
* all paragraph spacing information is currently not parsed
|
||||
|
||||
|
||||
* 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/CPAttributedString.j>
|
||||
@import <Foundation/CPGeometry.j>
|
||||
@import "CPFontManager.j"
|
||||
@import "CPParagraphStyle.j"
|
||||
|
||||
@global CPLeftTextAlignment
|
||||
@global CPRightTextAlignment
|
||||
@global CPCenterTextAlignment
|
||||
@global CPJustifiedTextAlignment
|
||||
@global CPNaturalTextAlignment
|
||||
|
||||
@global CPFontAttributeName
|
||||
@global CPForegroundColorAttributeName
|
||||
|
||||
var hexTable = [];
|
||||
|
||||
// Hold the attributes of the current run
|
||||
@implementation _RTFAttribute : CPObject
|
||||
{
|
||||
CPRange _range;
|
||||
CPParagraphStyle paragraph;
|
||||
CPColor fgColour;
|
||||
CPColor bgColour;
|
||||
CPColor ulColour;
|
||||
CPString fontName;
|
||||
unsigned fontSize;
|
||||
BOOL bold;
|
||||
BOOL italic;
|
||||
BOOL underline;
|
||||
BOOL strikethrough;
|
||||
BOOL script;
|
||||
BOOL _tabChanged;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
[self resetFont];
|
||||
[self resetParagraphStyle];
|
||||
_range = CPMakeRange(0, 0);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
var mynew = [_RTFAttribute new];
|
||||
|
||||
mynew.paragraph = [paragraph copy];
|
||||
mynew.fontName = fontName;
|
||||
mynew.fgColour = fgColour;
|
||||
mynew.bgColour = bgColour;
|
||||
mynew.ulColour = ulColour;
|
||||
|
||||
return mynew;
|
||||
}
|
||||
|
||||
- (CPFont)currentFont
|
||||
{
|
||||
var font = [CPFont _fontWithName:fontName size:fontSize bold:bold italic:italic];
|
||||
|
||||
if (font)
|
||||
return font;
|
||||
|
||||
//Before giving up and using a default font, we try if this is
|
||||
//not the case of a font with a composite name, such as
|
||||
//'Helvetica-Light'. In that case, even if we don't have
|
||||
//exactly an 'Helvetica-Light' font family, we might have an
|
||||
//'Helvetica' one.
|
||||
var range = [fontName rangeOfString:@"-"];
|
||||
|
||||
if (range.location != CPNotFound)
|
||||
{
|
||||
var fontFamily = [fontName substringToIndex: range.location];
|
||||
|
||||
font = [CPFont fontWithName:fontFamily size:fontSize];
|
||||
}
|
||||
|
||||
/* Last resort, default font. :-( */
|
||||
if (font == nil)
|
||||
font = [CPFont systemFontOfSize:fontSize];
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
- (CPNumber)script
|
||||
{
|
||||
return [CPNumber numberWithInt:script];
|
||||
}
|
||||
|
||||
- (CPNumber)underline
|
||||
{
|
||||
if (underline != 0)
|
||||
return [CPNumber numberWithInt:underline];
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (CPNumber)strikethrough
|
||||
{
|
||||
if (strikethrough != 0)
|
||||
return [CPNumber numberWithInt:strikethrough];
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)resetParagraphStyle
|
||||
{
|
||||
paragraph = [[CPParagraphStyle defaultParagraphStyle] copy];
|
||||
}
|
||||
|
||||
- (void)resetFont
|
||||
{
|
||||
var font = [CPFont systemFontOfSize:12];
|
||||
|
||||
fontName = [font familyName];
|
||||
fontSize = 12.0;
|
||||
italic = NO;
|
||||
bold = NO;
|
||||
underline = 0;
|
||||
strikethrough = 0;
|
||||
script = 0;
|
||||
}
|
||||
|
||||
- (void)addTab:(float)location type:(CPTextTabType)type
|
||||
{
|
||||
var tab = [[CPTextTab alloc] initWithType:CPLeftTabStopType
|
||||
location:location];
|
||||
|
||||
if (!_tabChanged)
|
||||
{
|
||||
[paragraph setTabStops:[tab]];
|
||||
_tabChanged = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
[paragraph addTabStop: tab];
|
||||
}
|
||||
}
|
||||
|
||||
- (CPDictionary)dictionary
|
||||
{
|
||||
var ret = @{};
|
||||
[ret setObject:[self currentFont] forKey:CPFontAttributeName];
|
||||
[ret setObject:paragraph forKey:CPParagraphStyleAttributeName];
|
||||
|
||||
if (fgColour)
|
||||
[ret setObject:fgColour forKey:CPForegroundColorAttributeName];
|
||||
|
||||
return ret;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
// based on https://github.com/lazygyu/RTF-parser
|
||||
|
||||
var kRTFParserType_char = 0,
|
||||
kRTFParserType_dest = 1,
|
||||
kRTFParserType_prop = 2,
|
||||
kRTFParserType_spec = 3;
|
||||
|
||||
// Keyword descriptions
|
||||
var kRgsymRtf = {
|
||||
// keyword dflt fPassDflt kwd idx
|
||||
"b" : [ "b", 1, false, kRTFParserType_prop, "propBold"],
|
||||
"ul" : [ "ul", 1, false, kRTFParserType_prop, "propUnderline"],
|
||||
"i" : [ "i", 1, false, kRTFParserType_prop, "propItalic"],
|
||||
"li" : [ "li", 0, false, kRTFParserType_prop, "propPgnFormat"],
|
||||
"pgnucltr" : [ "pgnucltr", "pgULtr", true, kRTFParserType_prop, "propPgnFormat"],
|
||||
"pgnlcltr" : [ "pgnlcltr", "pgLLtr", true, kRTFParserType_prop, "propPgnFormat"],
|
||||
"qc" : [ "qc", "justC", true, kRTFParserType_prop, "propJust"],
|
||||
"ql" : [ "ql", "justL", true, kRTFParserType_prop, "propJust"],
|
||||
"qr" : [ "qr", "justR", true, kRTFParserType_prop, "propJust"],
|
||||
"qj" : [ "qj", "justF", true, kRTFParserType_prop, "propJust"],
|
||||
"paperw" : [ "paperw", 12240, false, kRTFParserType_prop, "propXaPage"],
|
||||
"paperh" : [ "paperh", 15480, false, kRTFParserType_prop, "propYaPage"],
|
||||
"margl" : [ "margl", 1800, false, kRTFParserType_prop, "propXaLeft"],
|
||||
"margr" : [ "margr", 1800, false, kRTFParserType_prop, "propXaRight"],
|
||||
"margt" : [ "margt", 1440, false, kRTFParserType_prop, "propYaTop"],
|
||||
"margb" : [ "margb", 1440, false, kRTFParserType_prop, "propYaBottom"],
|
||||
"pgnstart" : [ "pgnstart", 1, true, kRTFParserType_prop, "propPgnStart"],
|
||||
"facingp" : [ "facingp", 1, true, kRTFParserType_prop, "propFacingp"],
|
||||
"landscape" : [ "landscape",1, true, kRTFParserType_prop, "propLandscape"],
|
||||
"par" : [ "par", 0, false, kRTFParserType_char, "\n"],
|
||||
"pard" : [ "pard", 0, false, kRTFParserType_prop, "propDefaultPara"],
|
||||
"\0x0a" : [ "\0x0a", 0, false, kRTFParserType_char, "\n"],
|
||||
"\0x0d" : [ "\0x0d", 0, false, kRTFParserType_char, ""],
|
||||
"tab" : [ "tab", 0, false, kRTFParserType_char, "\t"],
|
||||
"ldblquote" : [ "ldblquote",0, false, kRTFParserType_char, '"'],
|
||||
"rdblquote" : [ "rdblquote",0, false, kRTFParserType_char, '"'],
|
||||
"bin" : [ "bin", 0, false, kRTFParserType_spec, "ipfnBin"],
|
||||
"*" : [ "*", 0, false, kRTFParserType_spec, "ipfnDestSkip"],
|
||||
"'" : [ "'", 0, false, kRTFParserType_spec, "ipfnHex"],
|
||||
"author" : [ "author", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"buptim" : [ "buptim", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"colortbl" : [ "colortbl", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"comment" : [ "comment", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"creatim" : [ "creatim", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"doccomm" : [ "doccomm", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"fonttbl" : [ "fonttbl", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"footer" : [ "footer", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"footerf" : [ "footerf", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"footerl" : [ "footerl", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"footerr" : [ "footerr", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"footnote" : [ "footnote", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"ftncn" : [ "ftncn", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"ftnsep" : [ "ftnsep", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"ftnsepc" : [ "ftnsepc", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"fprq" : [ "fprq", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
// "fcharset" : [ "fcharset", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"rquote" : [ "rquote", 0, false, kRTFParserType_char, "'"],
|
||||
// "s" : [ "s", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"header" : [ "header", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"headerf" : [ "headerf", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"headerl" : [ "headerl", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"headerr" : [ "headerr", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"info" : [ "info", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"keywords" : [ "keywords", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"operator" : [ "operator", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"pict" : [ "pict", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"printim" : [ "printim", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"private1" : [ "private1", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"revtim" : [ "revtim", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"rxe" : [ "rxe", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"stylesheet" : [ "stylesheet",0, false, kRTFParserType_dest, "destSkip"],
|
||||
"subject" : [ "subject", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"tc" : [ "tc", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"title" : [ "title", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"txe" : [ "txe", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"xe" : [ "xe", 0, false, kRTFParserType_dest, "destSkip"],
|
||||
"[" : [ "[", 0, false, kRTFParserType_char, '['],
|
||||
" " : [ " ", 0, false, kRTFParserType_char, ' '],
|
||||
"]" : [ "]", 0, false, kRTFParserType_char, ']'],
|
||||
"{" : [ "{", 0, false, kRTFParserType_char, '{'],
|
||||
"}" : [ "}", 0, false, kRTFParserType_char, '}'],
|
||||
"\\" : [ "\\", 0, false, kRTFParserType_char, '\\']
|
||||
};
|
||||
|
||||
@implementation _CPRTFParser : CPObject
|
||||
{
|
||||
CPString _codePage;
|
||||
CGSize _paper;
|
||||
CPString _rtf;
|
||||
unsigned _curState;
|
||||
CPArray _states;
|
||||
unsigned _currentParseIndex;
|
||||
BOOL _hexreturn;
|
||||
_RTFAttribute _currentRun;
|
||||
CPAttributedString _result;
|
||||
CPArray _colorArray;
|
||||
CPArray _fontArray;
|
||||
CPString _freename;
|
||||
BOOL _parsingFontTable;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_paper = CPMakeSize(0, 0);
|
||||
_rtf = "";
|
||||
_curState = 0; // 0 = normal, 1 = skip
|
||||
_states = [];
|
||||
_currentParseIndex = 0;
|
||||
_hexreturn = NO;
|
||||
_result = [CPAttributedString new];
|
||||
_colorArray = [];
|
||||
_fontArray = ['Arial']; // FIXME: should be name of system font
|
||||
_freename = "";
|
||||
_parsingFontTable = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPString)_checkChar:(CPArray)sym parameter:(CPString)ch
|
||||
{
|
||||
switch (_curState)
|
||||
{
|
||||
case 0:
|
||||
if (sym && sym[4])
|
||||
return sym[4];
|
||||
|
||||
case 1:
|
||||
// CPLogConsole("skipped : " + sym[4]);
|
||||
return '';
|
||||
|
||||
default:
|
||||
if (sym && sym[4])
|
||||
return sym[4];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)pushState
|
||||
{
|
||||
_states.push["group"];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)popState
|
||||
{
|
||||
_states.pop();
|
||||
|
||||
if (_curState > 0)
|
||||
_curState--;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CPString)_parseSpec:(CPArray)sym parameter:(CPString)v
|
||||
{
|
||||
var ch = '';
|
||||
|
||||
switch (sym[4])
|
||||
{
|
||||
case "ipfnDestSkip":
|
||||
_curState++;
|
||||
return '';
|
||||
|
||||
case "ipfnHex":
|
||||
ch = _rtf.charAt(++_currentParseIndex);
|
||||
|
||||
var hex = '';
|
||||
|
||||
while (new RegExp("[a-fA-F0-9\\']").test(ch))
|
||||
{
|
||||
if (ch == "'")
|
||||
{
|
||||
_currentParseIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
hex += (ch + '');
|
||||
ch = _rtf.charAt(++_currentParseIndex);
|
||||
}
|
||||
//ch = parseInt(ch, 16);
|
||||
//console.log("hex : " + hex);
|
||||
_hexreturn = YES;
|
||||
_currentParseIndex--;
|
||||
|
||||
if (_curState !== 0)
|
||||
return '';
|
||||
else
|
||||
return hex;
|
||||
break;
|
||||
|
||||
case "codePage":
|
||||
ch = _rtf.charAt(++_currentParseIndex);
|
||||
|
||||
var code = '';
|
||||
|
||||
while (new RegExp("[0-9]").test(ch))
|
||||
{
|
||||
code += (ch + '');
|
||||
ch = _rtf.charAt(++_currentParseIndex);
|
||||
}
|
||||
|
||||
_codePage = code;
|
||||
_currentParseIndex--;
|
||||
break;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
- (void)_flushCurrentRun
|
||||
{
|
||||
var newOffset = 0;
|
||||
|
||||
if (_currentRun)
|
||||
{
|
||||
if ([_result length] == _currentRun._range.location)
|
||||
return;
|
||||
|
||||
_currentRun._range.length = [_result length] - _currentRun._range.location;
|
||||
newOffset = CPMaxRange(_currentRun._range);
|
||||
|
||||
var dict = [_currentRun dictionary];
|
||||
|
||||
[_result setAttributes:dict range:_currentRun._range]; // flush previous run
|
||||
_currentRun.fgColour = [CPColor blackColor];
|
||||
}
|
||||
else
|
||||
_currentRun = [_RTFAttribute new];
|
||||
|
||||
_currentRun._range = CPMakeRange(newOffset, 0); // open a new one
|
||||
}
|
||||
|
||||
- (CPString)_applyPropChange:sym parameter:param
|
||||
{
|
||||
//console.log("prop : " + sym[0] + " / param : " + param+ ' ');
|
||||
|
||||
switch (sym[0])
|
||||
{
|
||||
case "pard":
|
||||
[self _flushCurrentRun];
|
||||
break;
|
||||
|
||||
case "b": // bold
|
||||
if (param === 0)
|
||||
{
|
||||
if (_currentRun && _currentRun.bold)
|
||||
[self _flushCurrentRun];
|
||||
|
||||
_currentRun.bold = NO
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentRun && !_currentRun.bold)
|
||||
[self _flushCurrentRun];
|
||||
|
||||
_currentRun.bold = YES;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "i": // italic
|
||||
if (param === 0)
|
||||
{
|
||||
if (_currentRun && _currentRun.italic)
|
||||
[self _flushCurrentRun];
|
||||
|
||||
_currentRun.italic = NO
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentRun && !_currentRun.italic)
|
||||
[self _flushCurrentRun];
|
||||
|
||||
_currentRun.italic = YES;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "qc": // paragraph center
|
||||
[_currentRun.paragraph setAlignment:CPCenterTextAlignment];
|
||||
break;
|
||||
|
||||
case "paperw":
|
||||
_paper.width = param;
|
||||
break;
|
||||
|
||||
case "paperh":
|
||||
_paper.height = param;
|
||||
break;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
- (CPString)_changeDest:(CPArray)sym
|
||||
{
|
||||
switch (sym[0])
|
||||
{
|
||||
case "colortbl":
|
||||
_colorArray.push([CPColor blackColor]);
|
||||
break;
|
||||
|
||||
case "fonttbl":
|
||||
_parsingFontTable = YES;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sym[4] == "destSkip")
|
||||
{
|
||||
CPLogConsole("Dest skip start : [" + sym[0] + "]");
|
||||
_curState++;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
- (CPString)_translateKeyword:(CPString)keyword parameter:(CPString)param fParameter:(BOOL)fParam
|
||||
{
|
||||
if (kRgsymRtf[keyword] !== undefined)
|
||||
{
|
||||
var sym = kRgsymRtf[keyword];
|
||||
|
||||
switch (sym[3])
|
||||
{
|
||||
case kRTFParserType_prop:
|
||||
if (sym[2] || !fParam)
|
||||
param = sym[1];
|
||||
|
||||
return [self _applyPropChange:sym parameter:param];
|
||||
|
||||
case kRTFParserType_char:
|
||||
if((param + '') !== 'NaN' && (param + '') !== 'null' && (param + '').length)
|
||||
_currentParseIndex -= (param + '').length;
|
||||
|
||||
return [self _checkChar:sym parameter:param];
|
||||
|
||||
case kRTFParserType_dest:
|
||||
return [self _changeDest:sym];
|
||||
|
||||
case kRTFParserType_spec:
|
||||
return [self _parseSpec:sym parameter:param];
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (keyword)
|
||||
{
|
||||
case "red":
|
||||
var oldColor = [_colorArray lastObject],
|
||||
green = [oldColor greenComponent],
|
||||
blue = [oldColor blueComponent];
|
||||
|
||||
_colorArray.pop();
|
||||
_colorArray.push([CPColor colorWithRed:parseInt(param) / 255 green:green blue:blue alpha:1.0]);
|
||||
break;
|
||||
|
||||
case "green":
|
||||
var oldColor = [_colorArray lastObject],
|
||||
red = [oldColor redComponent],
|
||||
blue = [oldColor blueComponent];
|
||||
|
||||
_colorArray.pop();
|
||||
_colorArray.push([CPColor colorWithRed:red green: parseInt(param) / 255 blue:blue alpha:1.0]);
|
||||
break;
|
||||
|
||||
case "blue":
|
||||
var oldColor = [_colorArray lastObject],
|
||||
green = [oldColor greenComponent],
|
||||
red = [oldColor redComponent];
|
||||
|
||||
_colorArray.pop();
|
||||
_colorArray.push([CPColor colorWithRed:red green:green blue:parseInt(param) / 255 alpha:1.0]);
|
||||
_colorArray.push([CPColor blackColor]); // placeholder for next color
|
||||
break;
|
||||
|
||||
case "cf": // change foreground color
|
||||
[self _flushCurrentRun];
|
||||
var fontIndex = parseInt(param) - 1;
|
||||
|
||||
if (_currentRun && fontIndex >= 0)
|
||||
_currentRun.fgColour = _colorArray[fontIndex];
|
||||
|
||||
break;
|
||||
|
||||
case "f": // change font
|
||||
[self _flushCurrentRun];
|
||||
var fontIndex = parseInt(param);
|
||||
|
||||
if (_currentRun && fontIndex >= 0 && fontIndex < _fontArray.length)
|
||||
_currentRun.fontName = _fontArray[fontIndex];
|
||||
break;
|
||||
|
||||
case "fs": // change font size
|
||||
[self _flushCurrentRun];
|
||||
_currentRun.fontSize = parseInt(param) / 2;
|
||||
break;
|
||||
|
||||
case "tx": // tabstop
|
||||
var location = parseInt(param) / 20;
|
||||
|
||||
if (_currentRun)
|
||||
[_currentRun addTab:location type:CPLeftTabStopType];
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
CPLogConsole("skip : " + keyword + " param: " + param);
|
||||
|
||||
}
|
||||
|
||||
if (_states.length > 0)
|
||||
_curState = 1;
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
- (CPString)_parseKeyword:(CPString)rtf length:(unsigned)len
|
||||
{
|
||||
var ch = '',
|
||||
fParam = false,
|
||||
fNeg = false,
|
||||
keyword = '',
|
||||
param = '';
|
||||
|
||||
_rtf = rtf;
|
||||
|
||||
if (++_currentParseIndex >= len)
|
||||
return len;
|
||||
|
||||
ch = rtf.charAt(_currentParseIndex);
|
||||
|
||||
if (!/[a-zA-Z]/.test(ch))
|
||||
return [self _translateKeyword:ch parameter:nil fParameter:fParam];
|
||||
|
||||
while (new RegExp("[a-zA-Z]").test(ch))
|
||||
{
|
||||
keyword += ch;
|
||||
ch = rtf.charAt(++_currentParseIndex);
|
||||
}
|
||||
|
||||
if (ch == '-')
|
||||
{
|
||||
fNeg = true;
|
||||
ch = rtf.charAt(++_currentParseIndex);
|
||||
}
|
||||
|
||||
fParam = true;
|
||||
|
||||
while (new RegExp("[0-9]").test(ch))
|
||||
{
|
||||
param += (ch + '');
|
||||
ch = rtf.charAt(++_currentParseIndex);
|
||||
}
|
||||
|
||||
_currentParseIndex--;
|
||||
param = parseInt(param);
|
||||
|
||||
if (fNeg)
|
||||
param *= -1;
|
||||
|
||||
return [self _translateKeyword:keyword parameter:param fParameter:fParam];
|
||||
}
|
||||
|
||||
- (void)_appendPlainString:(CPString) aString
|
||||
{
|
||||
[_result replaceCharactersInRange:CPMakeRange([_result length], 0) withString:aString];
|
||||
|
||||
}
|
||||
- (CPAttributedString)parseRTF:(CPString)rtf
|
||||
{
|
||||
rtf = rtf.replace(/\\\n/g, "\\par\n");
|
||||
|
||||
if (rtf.length == 0)
|
||||
return '';
|
||||
|
||||
_currentParseIndex = -1;
|
||||
|
||||
var len = rtf.length,
|
||||
tmp = '',
|
||||
ch = '',
|
||||
hex = '',
|
||||
lastchar = 0;
|
||||
|
||||
while (_currentParseIndex < len)
|
||||
{
|
||||
tmp = rtf.charAt(++_currentParseIndex);
|
||||
|
||||
if (tmp !== "\\" && hex.length > 0)
|
||||
{
|
||||
[self _appendPlainString: String.fromCharCode(parseInt((hex), 16))];
|
||||
hex = '';
|
||||
}
|
||||
|
||||
switch (tmp)
|
||||
{
|
||||
case " ":
|
||||
if (lastchar == 1)
|
||||
{
|
||||
lastchar = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_freename += tmp;
|
||||
[self _appendPlainString:tmp];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "{":
|
||||
if ([self pushState])
|
||||
CPLogConsole("push");
|
||||
|
||||
break;
|
||||
|
||||
case "}":
|
||||
if ([self popState])
|
||||
CPLogConsole("pop");
|
||||
|
||||
if (_freename)
|
||||
{
|
||||
CPLogConsole(_freename);
|
||||
|
||||
if (_parsingFontTable)
|
||||
{
|
||||
_fontArray.push(_freename);
|
||||
_parsingFontTable = NO;
|
||||
}
|
||||
|
||||
_freename = "";
|
||||
}
|
||||
|
||||
[self _flushCurrentRun];
|
||||
break;
|
||||
|
||||
case "\\":
|
||||
_freename = '';
|
||||
ch = [self _parseKeyword:rtf length:len];
|
||||
|
||||
if (!_hexreturn && ch.length == 0)
|
||||
lastchar = 1;
|
||||
else
|
||||
lastchar = 0;
|
||||
|
||||
if (_hexreturn)
|
||||
{
|
||||
if (ch.length > 0)
|
||||
{
|
||||
if (parseInt(ch, 16) & 0x80)
|
||||
{
|
||||
hex += ch.toUpperCase();
|
||||
}
|
||||
else
|
||||
{
|
||||
[self _appendPlainString: String.fromCharCode(parseInt((hex + ch), 16))];
|
||||
hex = '';
|
||||
}
|
||||
|
||||
if (hex.length == 4)
|
||||
{
|
||||
var temp = parseInt(hex, 16);
|
||||
|
||||
if (hexTable && hexTable[hex.toUpperCase()] !== undefined)
|
||||
temp = parseInt(hexTable[hex.toUpperCase()], 16);
|
||||
|
||||
[self _appendPlainString: String.fromCharCode(temp)];
|
||||
hex = '';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLogConsole("hex skipped");
|
||||
}
|
||||
|
||||
_hexreturn = NO;
|
||||
}
|
||||
else if (ch !== undefined && _curState === 0)
|
||||
{
|
||||
[self _appendPlainString:ch];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0x0d:
|
||||
case 0x0a:
|
||||
case '\n':
|
||||
case '\r':
|
||||
break;
|
||||
|
||||
default:
|
||||
lastchar = 0;
|
||||
|
||||
if (_curState == 0)
|
||||
[self _appendPlainString:tmp];
|
||||
else if (tmp !== ';')
|
||||
_freename += tmp;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _result;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,613 @@
|
||||
/*
|
||||
_CPRTFProducer.j
|
||||
|
||||
Serialize CPAttributedString to a RTF String
|
||||
|
||||
Copyright (C) 2014 Daniel Boehringer
|
||||
This file is based on the RTFProducer from GNUStep
|
||||
(which i co-authored with Fred Kiefer in 1999)
|
||||
|
||||
* 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/CPAttributedString.j>
|
||||
@import "CPParagraphStyle.j"
|
||||
@import "CPColor.j"
|
||||
@import "CPGraphics.j"
|
||||
@import "CPFontManager.j"
|
||||
|
||||
@global CPForegroundColorAttributeName
|
||||
@global CPBackgroundColorAttributeName
|
||||
@global CPUnderlineStyleAttributeName
|
||||
@global CPSuperscriptAttributeName
|
||||
@global CPBaselineOffsetAttributeName
|
||||
@global CPAttachmentAttributeName
|
||||
@global CPLigatureAttributeName
|
||||
@global CPKernAttributeName
|
||||
|
||||
@global CPLeftTextAlignment
|
||||
@global CPRightTextAlignment
|
||||
@global CPCenterTextAlignment
|
||||
@global CPJustifiedTextAlignment
|
||||
@global CPNaturalTextAlignment
|
||||
|
||||
var PAPERSIZE = @"PaperSize",
|
||||
LEFTMARGIN = @"LeftMargin",
|
||||
RIGHTMARGIN = @"RightMargin",
|
||||
TOPMARGIN = @"TopMargin",
|
||||
BUTTOMMARGIN = @"ButtomMargin";
|
||||
|
||||
function _points2twips(a) { return (a) * 20.0; }
|
||||
|
||||
@implementation _CPRTFProducer : CPObject
|
||||
{
|
||||
CPAttributedString text;
|
||||
CPMutableDictionary fontDict;
|
||||
CPMutableDictionary colorDict;
|
||||
CPDictionary docDict;
|
||||
CPMutableArray attachments;
|
||||
CPFont currentFont;
|
||||
CPColor fgColor;
|
||||
CPColor bgColor;
|
||||
CPColor ulColor;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Class methods
|
||||
|
||||
|
||||
+ (CPString)produceRTF:(CPAttributedString)aText documentAttributes:(CPDictionary)dict
|
||||
{
|
||||
var mynew = [self new];
|
||||
|
||||
return [mynew RTFDStringFromAttributedString:aText documentAttributes:dict];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark init methods
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
// maintain a dictionary for the used colours
|
||||
// (for rtf-header generation)
|
||||
colorDict = [CPMutableDictionary new];
|
||||
|
||||
//maintain a dictionary for the used fonts
|
||||
//(for rtf-header generation)
|
||||
fontDict = [CPMutableDictionary new];
|
||||
|
||||
fgColor = [CPColor blackColor];
|
||||
bgColor= [CPColor whiteColor];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// private stuff follows
|
||||
- (CPString)fontTable
|
||||
{
|
||||
if (![fontDict count])
|
||||
return @"";
|
||||
|
||||
var fontlistString = "",
|
||||
fontEnum,
|
||||
currFont,
|
||||
keyArray;
|
||||
|
||||
keyArray = [fontDict allKeys];
|
||||
keyArray = [keyArray sortedArrayUsingSelector:@selector(compare:)];
|
||||
fontEnum = [keyArray objectEnumerator];
|
||||
|
||||
while ((currFont = [fontEnum nextObject]) !== nil)
|
||||
{
|
||||
var fontFamily,
|
||||
detail;
|
||||
|
||||
if ([currFont isEqualToString:@"Symbol"])
|
||||
fontFamily = @"tech";
|
||||
else if ([currFont isEqualToString:@"Helvetica"])
|
||||
fontFamily = @"swiss";
|
||||
else if ([currFont isEqualToString:@"Arial"])
|
||||
fontFamily = @"swiss";
|
||||
else if ([currFont isEqualToString:@"Courier"])
|
||||
fontFamily = @"modern";
|
||||
else if ([currFont isEqualToString:@"Times"])
|
||||
fontFamily = @"roman";
|
||||
else fontFamily = @"nil";
|
||||
|
||||
detail = [CPString stringWithFormat:@"%@\\f%@ %@;", [fontDict objectForKey:currFont], fontFamily, currFont];
|
||||
fontlistString += detail;
|
||||
}
|
||||
|
||||
return [CPString stringWithFormat:@"{\\fonttbl%@}\n", fontlistString];
|
||||
}
|
||||
|
||||
- (CPString)colorTable
|
||||
{
|
||||
if (![colorDict count])
|
||||
return @"";
|
||||
|
||||
var result,
|
||||
count = [colorDict count],
|
||||
list = [CPMutableArray arrayWithCapacity:count],
|
||||
keyEnum = [colorDict keyEnumerator],
|
||||
next,
|
||||
i;
|
||||
|
||||
while ((next = [keyEnum nextObject]) !== nil)
|
||||
{
|
||||
var cn = [colorDict objectForKey:next];
|
||||
[list insertObject:[CPColor colorWithCSSString:next] atIndex:[cn intValue]-1];
|
||||
}
|
||||
|
||||
result = [CPString stringWithString:@"{\\colortbl;"];
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
var color = [[list objectAtIndex:i]
|
||||
colorUsingColorSpaceName:CPCalibratedRGBColorSpace];
|
||||
|
||||
result += [CPString stringWithFormat:@"\\red%d\\green%d\\blue%d;",
|
||||
([color redComponent] * 255),
|
||||
([color greenComponent] * 255),
|
||||
([color blueComponent] * 255)];
|
||||
}
|
||||
|
||||
result += @"}\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (CPString)documentAttributes
|
||||
{
|
||||
if (!docDict)
|
||||
return @"";
|
||||
|
||||
var result,
|
||||
detail,
|
||||
val,
|
||||
num;
|
||||
|
||||
result = [CPString string];
|
||||
|
||||
val = [docDict objectForKey:PAPERSIZE];
|
||||
|
||||
if (val)
|
||||
{
|
||||
detail = [CPString stringWithFormat:@"\\paperw%d \\paperh%d",
|
||||
_points2twips(val.width),
|
||||
_points2twips(val.height)];
|
||||
|
||||
result += detail;
|
||||
}
|
||||
|
||||
num = [docDict objectForKey:LEFTMARGIN];
|
||||
|
||||
if (num)
|
||||
{
|
||||
var f = [num floatValue];
|
||||
|
||||
detail = [CPString stringWithFormat:@"\\margl%d", _points2twips(f)];
|
||||
result += detail;
|
||||
}
|
||||
|
||||
num = [docDict objectForKey:RIGHTMARGIN];
|
||||
|
||||
if (num)
|
||||
{
|
||||
var f = [num floatValue];
|
||||
|
||||
detail = [CPString stringWithFormat:@"\\margr%d", _points2twips(f)];
|
||||
result += detail;
|
||||
}
|
||||
|
||||
num = [docDict objectForKey:TOPMARGIN];
|
||||
|
||||
if (num)
|
||||
{
|
||||
var f = [num floatValue];
|
||||
|
||||
detail = [CPString stringWithFormat:@"\\margt%d", _points2twips(f)];
|
||||
result += detail;
|
||||
}
|
||||
|
||||
num = [docDict objectForKey:BUTTOMMARGIN];
|
||||
|
||||
if (num)
|
||||
{
|
||||
var f = [num floatValue];
|
||||
|
||||
detail = [CPString stringWithFormat:@"\\margb%d", _points2twips(f)];
|
||||
result += detail;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (CPString)headerString
|
||||
{
|
||||
var result;
|
||||
|
||||
result = [CPString stringWithString:@"{\\rtf1\\ansi"];
|
||||
result += [self fontTable];
|
||||
result += [self colorTable];
|
||||
result += [self documentAttributes];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (CPString)trailerString
|
||||
{
|
||||
return @"}";
|
||||
}
|
||||
|
||||
- (CPString)fontToken:(CPString) fontName
|
||||
{
|
||||
var fCount = [fontDict objectForKey:fontName];
|
||||
|
||||
if (fCount == nil)
|
||||
{
|
||||
var count = [fontDict count];
|
||||
|
||||
fCount = [CPString stringWithFormat:@"\\f%d", count];
|
||||
[fontDict setObject:fCount forKey:fontName];
|
||||
}
|
||||
|
||||
return fCount;
|
||||
}
|
||||
|
||||
- (int)numberForColor:(CPColor)color
|
||||
{
|
||||
var num = [colorDict objectForKey:[color cssString]];
|
||||
|
||||
if (!num)
|
||||
[colorDict setObject:num = [CPNumber numberWithInt:[colorDict count] + 1]
|
||||
forKey:[color cssString]];
|
||||
|
||||
return [num intValue];
|
||||
}
|
||||
|
||||
- (CPString)paragraphStyle:(CPParagraphStyle)paraStyle
|
||||
{
|
||||
var headerString = [CPString stringWithString:@"\\pard"],
|
||||
twips;
|
||||
|
||||
if (paraStyle == nil)
|
||||
return headerString;
|
||||
|
||||
switch ([paraStyle alignment])
|
||||
{
|
||||
case CPRightTextAlignment:
|
||||
headerString += @"\\qr";
|
||||
break;
|
||||
|
||||
case CPCenterTextAlignment:
|
||||
headerString += @"\\qc";
|
||||
break;
|
||||
|
||||
case CPLeftTextAlignment:
|
||||
headerString += @"\\ql";
|
||||
break;
|
||||
|
||||
case CPJustifiedTextAlignment:
|
||||
headerString += @"\\qj";
|
||||
break;
|
||||
|
||||
default:
|
||||
headerString += @"\\ql";
|
||||
break;
|
||||
}
|
||||
|
||||
// write first line indent and left indent
|
||||
var twips = _points2twips([paraStyle firstLineHeadIndent]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\fi%d", twips];
|
||||
|
||||
twips = _points2twips([paraStyle headIndent]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\li%d", twips];
|
||||
|
||||
twips = _points2twips([paraStyle tailIndent]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\ri%d", twips];
|
||||
|
||||
twips = _points2twips([paraStyle paragraphSpacing]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\sa%d", twips];
|
||||
|
||||
twips = _points2twips([paraStyle minimumLineHeight]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\sl%d", twips];
|
||||
|
||||
twips = _points2twips([paraStyle maximumLineHeight]);
|
||||
|
||||
if (twips != 0.0)
|
||||
headerString += [CPString stringWithFormat:@"\\sl-%d", twips];
|
||||
|
||||
var enumerator,
|
||||
tab;
|
||||
|
||||
enumerator = [[paraStyle tabStops] objectEnumerator];
|
||||
|
||||
while ((tab = [enumerator nextObject]))
|
||||
{
|
||||
switch ([tab tabStopType])
|
||||
{
|
||||
case CPLeftTabStopType:
|
||||
// no tabkind emission needed
|
||||
break;
|
||||
/* case NSRightTabStopType:
|
||||
headerString += @"\\tqr";
|
||||
break;
|
||||
case NSCenterTabStopType:
|
||||
headerString += @"\\tqc";
|
||||
break;
|
||||
case NSDecimalTabStopType:
|
||||
headerString += @"\\tqdec";
|
||||
break;
|
||||
default:
|
||||
NSLog(@"Unknown tab stop type.");
|
||||
*/
|
||||
}
|
||||
|
||||
headerString += [CPString stringWithFormat:@"\\tx%d",_points2twips([tab location])];
|
||||
}
|
||||
|
||||
return headerString;
|
||||
}
|
||||
|
||||
- (CPString)runStringForString:(CPString) substring
|
||||
attributes:(CPDictionary) attributes
|
||||
paragraphStart:(BOOL) first
|
||||
{
|
||||
var result = "",
|
||||
headerString = "",
|
||||
trailerString = "",
|
||||
attribEnum,
|
||||
currAttrib;
|
||||
|
||||
if (first)
|
||||
{
|
||||
var paraStyle = [attributes objectForKey:CPParagraphStyleAttributeName];
|
||||
headerString += [self paragraphStyle:paraStyle];
|
||||
}
|
||||
|
||||
/*
|
||||
* analyze attributes of current run
|
||||
*
|
||||
* FIXME: All the character attributes should be output relative to the font
|
||||
* attributes of the paragraph. So if the paragraph has underline on it should
|
||||
* still be possible to switch it off for some characters, which currently is
|
||||
* not possible.
|
||||
*/
|
||||
attribEnum = [attributes keyEnumerator];
|
||||
|
||||
while ((currAttrib = [attribEnum nextObject]) != nil)
|
||||
{
|
||||
if ([currAttrib isEqualToString:CPFontAttributeName])
|
||||
{
|
||||
/*
|
||||
* handle fonts
|
||||
*/
|
||||
var font,
|
||||
fontName,
|
||||
traits;
|
||||
|
||||
font = [attributes objectForKey:CPFontAttributeName];
|
||||
fontName = [font familyName];
|
||||
traits = [[CPFontManager sharedFontManager] traitsOfFont:font];
|
||||
|
||||
/*
|
||||
* font name
|
||||
*/
|
||||
if (currentFont == nil || ![fontName isEqualToString:[currentFont familyName]])
|
||||
headerString += [self fontToken:fontName];
|
||||
|
||||
/*
|
||||
* font size
|
||||
*/
|
||||
if (currentFont == nil || [font size] != [currentFont size])
|
||||
{
|
||||
var points = [font size] * 2,
|
||||
pString;
|
||||
|
||||
pString = [CPString stringWithFormat:@"\\fs%d", points];
|
||||
headerString += pString;
|
||||
}
|
||||
/*
|
||||
* font attributes
|
||||
*/
|
||||
if (traits & CPItalicFontMask)
|
||||
{
|
||||
headerString += @"\\i";
|
||||
trailerString += @"\\i0";
|
||||
}
|
||||
|
||||
if (traits & CPBoldFontMask)
|
||||
{
|
||||
headerString += @"\\b";
|
||||
trailerString += @"\\b0";
|
||||
}
|
||||
|
||||
if (first)
|
||||
currentFont = font;
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPForegroundColorAttributeName])
|
||||
{
|
||||
var color = [attributes objectForKey:CPForegroundColorAttributeName];
|
||||
|
||||
if (![color isEqual:fgColor])
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\cf%d", [self numberForColor:color]];
|
||||
trailerString += @"\\cf0";
|
||||
}
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPBackgroundColorAttributeName])
|
||||
{
|
||||
var color = [attributes objectForKey:CPBackgroundColorAttributeName];
|
||||
|
||||
if (![color isEqual:bgColor])
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\cb%d", [self numberForColor:color]];
|
||||
trailerString += @"\\cb0";
|
||||
}
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPUnderlineStyleAttributeName])
|
||||
{
|
||||
headerString += @"\\ul";
|
||||
trailerString += @"\\ulnone";
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPSuperscriptAttributeName])
|
||||
{
|
||||
var value = [attributes objectForKey:CPSuperscriptAttributeName],
|
||||
svalue = [value intValue] * 6;
|
||||
|
||||
if (svalue > 0)
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\up%d", svalue];
|
||||
trailerString += @"\\up0";
|
||||
}
|
||||
else if (svalue < 0)
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\dn-%d", svalue];
|
||||
trailerString += @"\\dn0";
|
||||
}
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPBaselineOffsetAttributeName])
|
||||
{
|
||||
var value = [attributes objectForKey:CPBaselineOffsetAttributeName],
|
||||
svalue = [value floatValue] * 2;
|
||||
|
||||
if (svalue > 0)
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\up%d", svalue];
|
||||
trailerString += @"\\up0";
|
||||
}
|
||||
else if (svalue < 0)
|
||||
{
|
||||
headerString += [CPString stringWithFormat:@"\\dn-%d", svalue];
|
||||
trailerString += @"\\dn0";
|
||||
}
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPAttachmentAttributeName])
|
||||
{
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPLigatureAttributeName])
|
||||
{
|
||||
}
|
||||
else if ([currAttrib isEqualToString:CPKernAttributeName])
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
substring = substring.replace(/\\/g, '\\\\');
|
||||
substring = substring.replace(/\n/g, '\\par\n');
|
||||
substring = substring.replace(/\t/g, '\\tab');
|
||||
substring = substring.replace(/{/g, '\\{');
|
||||
substring = substring.replace(/}/g, '\\}');
|
||||
// FIXME: All characters not in the standard encoding must be
|
||||
// replaced by \'xx
|
||||
|
||||
if (!first)
|
||||
{
|
||||
var braces;
|
||||
|
||||
if ([headerString length])
|
||||
braces = [CPString stringWithFormat:@"{%@ %@}", headerString, substring];
|
||||
else
|
||||
braces = substring;
|
||||
|
||||
result += braces;
|
||||
}
|
||||
else
|
||||
{
|
||||
var nobraces;
|
||||
|
||||
if ([headerString length])
|
||||
nobraces = [CPString stringWithFormat:@"%@ %@", headerString, substring];
|
||||
else
|
||||
nobraces = substring;
|
||||
|
||||
result += nobraces;
|
||||
}
|
||||
|
||||
return result + trailerString;
|
||||
}
|
||||
|
||||
- (CPString)bodyString
|
||||
{
|
||||
var string = [text string],
|
||||
result = "",
|
||||
loc = 0,
|
||||
length = [string length],
|
||||
currRange = CPMakeRange(loc, 0),
|
||||
completeRange = CPMakeRange(0, length),
|
||||
first = YES;
|
||||
|
||||
// FIXME <!> split along newline characters and run as outer loop
|
||||
while (CPMaxRange(currRange) < CPMaxRange(completeRange)) // save all "runs"
|
||||
{
|
||||
var attributes,
|
||||
substring,
|
||||
runString;
|
||||
|
||||
attributes = [text attributesAtIndex:CPMaxRange(currRange)
|
||||
longestEffectiveRange:currRange
|
||||
inRange:completeRange];
|
||||
substring = [string substringWithRange:currRange];
|
||||
runString = [self runStringForString:substring
|
||||
attributes:attributes
|
||||
paragraphStart:YES];
|
||||
|
||||
result += runString;
|
||||
first = NO;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
- (CPString)RTFDStringFromAttributedString:(CPAttributedString)aText
|
||||
documentAttributes:(CPDictionary)dict
|
||||
{
|
||||
var output = [CPString string],
|
||||
headerString,
|
||||
trailerString,
|
||||
bodyString;
|
||||
|
||||
text = aText;
|
||||
docDict = dict;
|
||||
|
||||
/*
|
||||
* do not change order! (esp. body has to be generated first; builds context)
|
||||
*/
|
||||
bodyString = [self bodyString];
|
||||
trailerString = [self trailerString];
|
||||
headerString = [self headerString];
|
||||
|
||||
output += headerString;
|
||||
output += bodyString;
|
||||
output += trailerString;
|
||||
return output;
|
||||
}
|
||||
@end
|
||||
+204
-56
@@ -24,9 +24,12 @@
|
||||
@import <Foundation/CPMutableArray.j>
|
||||
@import <Foundation/CPString.j>
|
||||
@import <Foundation/CPKeyedUnarchiver.j>
|
||||
@import <Foundation/CPBundle.j>
|
||||
|
||||
@class CPView
|
||||
@class _CPThemeAttribute
|
||||
@class CPImage
|
||||
@class CPColor
|
||||
|
||||
var CPThemesByName = { },
|
||||
CPThemeDefaultTheme = nil,
|
||||
@@ -36,7 +39,6 @@ var CPThemesByName = { },
|
||||
/*!
|
||||
@ingroup appkit
|
||||
*/
|
||||
|
||||
@implementation CPTheme : CPObject
|
||||
{
|
||||
CPString _name;
|
||||
@@ -142,20 +144,19 @@ var CPThemesByName = { },
|
||||
|
||||
if (!className)
|
||||
{
|
||||
if ([aClass isKindOfClass:[CPView class]])
|
||||
if ([aClass respondsToSelector:@selector(defaultThemeClass)])
|
||||
{
|
||||
if ([aClass respondsToSelector:@selector(defaultThemeClass)])
|
||||
className = [aClass defaultThemeClass];
|
||||
else if ([aClass respondsToSelector:@selector(themeClass)])
|
||||
{
|
||||
CPLog.warn(@"%@ themeClass is deprecated in favor of defaultThemeClass", CPStringFromClass(aClass));
|
||||
className = [aClass themeClass];
|
||||
}
|
||||
else
|
||||
return nil;
|
||||
className = [aClass defaultThemeClass];
|
||||
}
|
||||
else if ([aClass respondsToSelector:@selector(themeClass)])
|
||||
{
|
||||
CPLog.warn(@"%@ themeClass is deprecated in favor of defaultThemeClass", CPStringFromClass(aClass));
|
||||
className = [aClass themeClass];
|
||||
}
|
||||
else
|
||||
[CPException raise:CPInvalidArgumentException reason:@"aClass must be a class object or a string."];
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return [_attributes objectForKey:className];
|
||||
@@ -315,6 +316,71 @@ var CPThemeNameKey = @"CPThemeNameKey",
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CSS Theming
|
||||
|
||||
// The code below adds support for CSS theming with 100% compatibility with current theming system.
|
||||
// The idea is to extend CPColor and CPImage with CSS components and adapt low level UI components to
|
||||
// support this new kind of CPColor/CPImage. See CPImageView, CPView and _CPImageAndTextView.
|
||||
//
|
||||
// To be considered and treated as CSS based, a theme must set to YES the attribute "css-based" for CPView
|
||||
// in the theDescriptor (default value is NO), like in the example below :
|
||||
//
|
||||
// + (CPView)themedView
|
||||
// {
|
||||
// var view = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
|
||||
//
|
||||
// [self registerThemeValues:[[@"css-based", YES]] forView:view];
|
||||
//
|
||||
// return view;
|
||||
// }
|
||||
//
|
||||
// You can use the method -(BOOL)isCSSBased on a theme to determine how to cope with it in your own code.
|
||||
//
|
||||
// The method -(void)setCSSResourcesPath is meant to be used only by CPThemeBlend during theme loading in order to
|
||||
// replace the special path "%%" in CSS components (like in url(%%packed.png) ) with the path to the theme blend resources folder.
|
||||
|
||||
@implementation CPTheme (CSSTheming)
|
||||
|
||||
- (void)setCSSResourcesPath:(CPString)pathToResources
|
||||
{
|
||||
[_attributes enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
[anObject enumerateKeysAndObjectsUsingBlock:function(aKey2, anObject2, stop2)
|
||||
{
|
||||
// anObject2 is now a _CPThemeAttribute
|
||||
|
||||
[[anObject2 values] enumerateKeysAndObjectsUsingBlock:function(aKey3, anObject3, stop3)
|
||||
{
|
||||
if (anObject3.isa && ([anObject3 isKindOfClass:CPImage] || [anObject3 isKindOfClass:CPColor]) && [anObject3 cssDictionary])
|
||||
{
|
||||
// We have a CSS defined image or color
|
||||
[self _fixPathInCSSDictionary:[anObject3 cssDictionary] withPathToResources:pathToResources];
|
||||
[self _fixPathInCSSDictionary:[anObject3 cssBeforeDictionary] withPathToResources:pathToResources];
|
||||
[self _fixPathInCSSDictionary:[anObject3 cssAfterDictionary] withPathToResources:pathToResources];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_fixPathInCSSDictionary:(CPDictionary)aDictionary withPathToResources:(CPString)pathToResources
|
||||
{
|
||||
[aDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
|
||||
{
|
||||
[aDictionary setObject:[anObject stringByReplacingOccurrencesOfString:@"%%" withString:pathToResources] forKey:aKey];
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)isCSSBased
|
||||
{
|
||||
return !![self valueForAttributeWithName:@"css-based" forClass:[CPView class]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
/*!
|
||||
* ThemeStates are immutable objects representing a particular ThemeState. Applications should never be creating
|
||||
* ThemeStates directly but should instead use the CPThemeState function.
|
||||
@@ -324,10 +390,11 @@ function ThemeState(stateNames)
|
||||
var stateNameKeys = [];
|
||||
this._stateNames = {};
|
||||
|
||||
for (key in stateNames)
|
||||
for (var key in stateNames)
|
||||
{
|
||||
if (!stateNames.hasOwnProperty(key))
|
||||
continue;
|
||||
|
||||
if (key !== 'normal')
|
||||
{
|
||||
this._stateNames[key] = true;
|
||||
@@ -345,8 +412,10 @@ function ThemeState(stateNames)
|
||||
this._stateNameString = stateNameKeys[0];
|
||||
|
||||
var stateNameLength = stateNameKeys.length;
|
||||
|
||||
for (var stateIndex = 1; stateIndex < stateNameLength; stateIndex++)
|
||||
this._stateNameString = this._stateNameString + "+" + stateNameKeys[stateIndex];
|
||||
|
||||
this._stateNameCount = stateNameLength;
|
||||
}
|
||||
|
||||
@@ -393,7 +462,19 @@ ThemeState.prototype.without = function(aState)
|
||||
if (!aState || aState === [CPNull null])
|
||||
return this;
|
||||
|
||||
var firstTransform = CPThemeWithoutTransform[this._stateNameString],
|
||||
result;
|
||||
|
||||
if (firstTransform)
|
||||
{
|
||||
result = firstTransform[aState._stateNameString];
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
var newStates = {};
|
||||
|
||||
for (var stateName in this._stateNames)
|
||||
{
|
||||
if (!this._stateNames.hasOwnProperty(stateName))
|
||||
@@ -403,25 +484,54 @@ ThemeState.prototype.without = function(aState)
|
||||
newStates[stateName] = true;
|
||||
}
|
||||
|
||||
return ThemeState._cacheThemeState(new ThemeState(newStates));
|
||||
result = ThemeState._cacheThemeState(new ThemeState(newStates));
|
||||
|
||||
if (!firstTransform)
|
||||
firstTransform = CPThemeWithoutTransform[this._stateNameString] = {};
|
||||
|
||||
firstTransform[aState._stateNameString] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ThemeState.prototype.and = function(aState)
|
||||
{
|
||||
return CPThemeState(this, aState);
|
||||
var firstTransform = CPThemeAndTransform[this._stateNameString],
|
||||
result;
|
||||
|
||||
if (firstTransform)
|
||||
{
|
||||
result = firstTransform[aState._stateNameString];
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = CPThemeState(this, aState);
|
||||
|
||||
if (!firstTransform)
|
||||
firstTransform = CPThemeAndTransform[this._stateNameString] = {};
|
||||
|
||||
firstTransform[aState._stateNameString] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var CPThemeStates = {};
|
||||
var CPThemeStates = {},
|
||||
CPThemeWithoutTransform = {},
|
||||
CPThemeAndTransform = {};
|
||||
|
||||
ThemeState._cacheThemeState = function(aState)
|
||||
{
|
||||
// We do this caching so themeState equality works. Basically, doing CPThemeState('foo+bar') === CPThemeState('bar', 'foo') will return true.
|
||||
var themeState = CPThemeStates[String(aState)];
|
||||
|
||||
if (themeState === undefined)
|
||||
{
|
||||
themeState = aState;
|
||||
CPThemeStates[String(themeState)] = themeState;
|
||||
}
|
||||
|
||||
return themeState;
|
||||
}
|
||||
|
||||
@@ -439,14 +549,17 @@ function CPThemeState()
|
||||
throw "CPThemeState() must be called with at least one string argument";
|
||||
|
||||
var themeState;
|
||||
|
||||
if (arguments.length === 1 && typeof arguments[0] === 'string')
|
||||
{
|
||||
themeState = CPThemeStates[arguments[0]];
|
||||
|
||||
if (themeState !== undefined)
|
||||
return themeState;
|
||||
}
|
||||
|
||||
var stateNames = {};
|
||||
|
||||
for (var argIndex = 0; argIndex < arguments.length; argIndex++)
|
||||
{
|
||||
if (arguments[argIndex] === [CPNull null] || !arguments[argIndex])
|
||||
@@ -458,12 +571,14 @@ function CPThemeState()
|
||||
{
|
||||
if (!arguments[argIndex]._stateNames.hasOwnProperty(stateName))
|
||||
continue;
|
||||
|
||||
stateNames[stateName] = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var allNames = arguments[argIndex].split('+');
|
||||
|
||||
for (var nameIndex = 0; nameIndex < allNames.length; nameIndex++)
|
||||
stateNames[allNames[nameIndex]] = true;
|
||||
}
|
||||
@@ -523,6 +638,9 @@ CPThemeStateControlSizeRegular = CPThemeState("controlSizeRegular");
|
||||
CPThemeStateControlSizeSmall = CPThemeState("controlSizeSmall");
|
||||
CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
|
||||
CPThemeStateNormalString = String(CPThemeStateNormal);
|
||||
|
||||
|
||||
@implementation _CPThemeAttribute : CPObject
|
||||
{
|
||||
CPString _name;
|
||||
@@ -533,7 +651,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
_CPThemeAttribute _themeDefaultAttribute;
|
||||
}
|
||||
|
||||
- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue
|
||||
- (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue defaultAttribute:(_CPThemeAttribute)aDefaultAttribute
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
@@ -542,7 +660,9 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
_cache = { };
|
||||
_name = aName;
|
||||
_defaultValue = aDefaultValue;
|
||||
_values = @{};
|
||||
|
||||
if (aDefaultAttribute)
|
||||
_themeDefaultAttribute = aDefaultAttribute;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -563,24 +683,41 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
return [_values count] > 0;
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue
|
||||
- (_CPThemeAttribute)attributeBySettingValue:(id)aValue
|
||||
{
|
||||
_cache = {};
|
||||
var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute];
|
||||
|
||||
if (aValue === undefined || aValue === nil)
|
||||
_values = @{};
|
||||
else
|
||||
_values = @{ String(CPThemeStateNormal): aValue };
|
||||
if (aValue !== undefined && aValue !== nil)
|
||||
attribute._values = @{ CPThemeStateNormalString: aValue };
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
- (void)setValue:(id)aValue forState:(ThemeState)aState
|
||||
- (_CPThemeAttribute)attributeBySettingValue:(id)aValue forState:(ThemeState)aState
|
||||
{
|
||||
_cache = { };
|
||||
var shouldRemoveValue = aValue === undefined || aValue === nil,
|
||||
attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute],
|
||||
values = _values;
|
||||
|
||||
if ((aValue === undefined) || (aValue === nil))
|
||||
[_values removeObjectForKey:String(aState)];
|
||||
else
|
||||
[_values setObject:aValue forKey:String(aState)];
|
||||
if (values != null)
|
||||
{
|
||||
values = [values copy];
|
||||
|
||||
if (shouldRemoveValue)
|
||||
[values removeObjectForKey:String(aState)];
|
||||
else
|
||||
[values setObject:aValue forKey:String(aState)];
|
||||
|
||||
attribute._values = values;
|
||||
}
|
||||
else if (!shouldRemoveValue)
|
||||
{
|
||||
values = [[CPDictionary alloc] init];
|
||||
[values setObject:aValue forKey:String(aState)];
|
||||
attribute._values = values;
|
||||
}
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
- (id)value
|
||||
@@ -605,7 +742,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
if (aState._stateNameCount > 1)
|
||||
{
|
||||
var states = [_values allKeys],
|
||||
count = states.length,
|
||||
count = states ? states.length : 0,
|
||||
largestThemeState = 0;
|
||||
|
||||
while (count--)
|
||||
@@ -643,27 +780,41 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void)setParentAttribute:(_CPThemeAttribute)anAttribute
|
||||
- (_CPThemeAttribute)attributeBySettingParentAttribute:(_CPThemeAttribute)anAttribute
|
||||
{
|
||||
if (_themeDefaultAttribute === anAttribute)
|
||||
return;
|
||||
return self;
|
||||
|
||||
_cache = { };
|
||||
_themeDefaultAttribute = anAttribute;
|
||||
var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:anAttribute];
|
||||
|
||||
attribute._values = [_values copy];
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
- (_CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
|
||||
{
|
||||
var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue];
|
||||
var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute];
|
||||
|
||||
mergedAttribute._values = [_values copy];
|
||||
[mergedAttribute._values addEntriesFromDictionary:anAttribute._values];
|
||||
|
||||
if (anAttribute._values)
|
||||
mergedAttribute._values ? [mergedAttribute._values addEntriesFromDictionary:anAttribute._values] : [anAttribute._values copy];
|
||||
|
||||
return mergedAttribute;
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
{
|
||||
return [super description] + @" Name: " + _name + @", defaultAttribute: " + _themeDefaultAttribute + @", defaultValue: " + _defaultValue + @", values: " + _values;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// This is used to pass 'parrentAttribute' to the coder
|
||||
var ParentAttributeForCoder = nil;
|
||||
|
||||
@implementation _CPThemeAttribute (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
@@ -677,13 +828,16 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
_name = [aCoder decodeObjectForKey:@"name"];
|
||||
_defaultValue = [aCoder decodeObjectForKey:@"defaultValue"];
|
||||
_values = @{};
|
||||
_themeDefaultAttribute = ParentAttributeForCoder;
|
||||
|
||||
if ([aCoder containsValueForKey:@"value"])
|
||||
{
|
||||
var state = String(CPThemeStateNormal);
|
||||
var state;
|
||||
|
||||
if ([aCoder containsValueForKey:@"state"])
|
||||
state = [aCoder decodeObjectForKey:@"state"];
|
||||
else
|
||||
state = CPThemeStateNormalString;
|
||||
|
||||
[_values setObject:[aCoder decodeObjectForKey:"value"] forKey:state];
|
||||
}
|
||||
@@ -711,7 +865,7 @@ CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
|
||||
[aCoder encodeObject:_defaultValue forKey:@"defaultValue"];
|
||||
|
||||
var keys = [_values allKeys],
|
||||
count = keys.length;
|
||||
count = keys ? keys.length : 0;
|
||||
|
||||
if (count === 1)
|
||||
{
|
||||
@@ -749,7 +903,7 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute)
|
||||
{
|
||||
var state = [values allKeys][0];
|
||||
|
||||
if (state === String(CPThemeStateNormal))
|
||||
if (state === CPThemeStateNormalString)
|
||||
{
|
||||
[aCoder encodeObject:[values objectForKey:state] forKey:key];
|
||||
|
||||
@@ -767,30 +921,24 @@ function CPThemeAttributeEncode(aCoder, aThemeAttribute)
|
||||
return NO;
|
||||
}
|
||||
|
||||
function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme, aClass)
|
||||
function CPThemeAttributeDecode(aCoder, attribute)
|
||||
{
|
||||
var key = "$a" + anAttributeName;
|
||||
var key = "$a" + attribute._name;
|
||||
|
||||
if (![aCoder containsValueForKey:key])
|
||||
var attribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
|
||||
|
||||
else
|
||||
if ([aCoder containsValueForKey:key])
|
||||
{
|
||||
var attribute = [aCoder decodeObjectForKey:key];
|
||||
ParentAttributeForCoder = attribute._themeDefaultAttribute;
|
||||
|
||||
if (!attribute || !attribute.isa || ![attribute isKindOfClass:[_CPThemeAttribute class]])
|
||||
{
|
||||
var themeAttribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
|
||||
var decodedAttribute = [aCoder decodeObjectForKey:key];
|
||||
|
||||
[themeAttribute setValue:attribute];
|
||||
ParentAttributeForCoder = nil;
|
||||
|
||||
attribute = themeAttribute;
|
||||
}
|
||||
if (!decodedAttribute || !decodedAttribute.isa || ![decodedAttribute isKindOfClass:[_CPThemeAttribute class]])
|
||||
attribute = [attribute attributeBySettingValue:decodedAttribute];
|
||||
else
|
||||
attribute = decodedAttribute;
|
||||
}
|
||||
|
||||
if (aTheme && aClass)
|
||||
[attribute setParentAttribute:[aTheme attributeWithName:anAttributeName forClass:aClass]];
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
|
||||
+15
-6
@@ -64,12 +64,10 @@
|
||||
*/
|
||||
- (CPArray)themeNames
|
||||
{
|
||||
var names = [];
|
||||
|
||||
for (var i = 0; i < _themes.length; ++i)
|
||||
names.push(_themes[i].substring(0, _themes[i].indexOf(".keyedtheme")));
|
||||
|
||||
return names;
|
||||
return [_themes arrayByApplyingBlock:function(theme)
|
||||
{
|
||||
return theme.substring(0, theme.indexOf(".keyedtheme"));
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)loadWithDelegate:(id)aDelegate
|
||||
@@ -97,6 +95,17 @@
|
||||
[unarchiver finishDecoding];
|
||||
}
|
||||
|
||||
// CSS Theming
|
||||
|
||||
for (var i = 0, nb = [_themes count], allThemes = [self themeNames], aThemeName, aTheme; i < nb; i++)
|
||||
{
|
||||
aThemeName = allThemes[i];
|
||||
aTheme = [CPTheme themeNamed:aThemeName];
|
||||
|
||||
if ([aTheme isCSSBased])
|
||||
[aTheme setCSSResourcesPath:[aBundle resourcePath]];
|
||||
}
|
||||
|
||||
[_loadDelegate blendDidFinishLoading:self];
|
||||
}
|
||||
|
||||
|
||||
+39
-16
@@ -448,21 +448,27 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
element.style.width = CGRectGetWidth(contentRect) + "px";
|
||||
element.style.height = [font defaultLineHeightForFont] + "px";
|
||||
|
||||
window.setTimeout(function()
|
||||
[[CPRunLoop mainRunLoop] performBlock:function()
|
||||
{
|
||||
[_tokenScrollView documentView]._DOMElement.appendChild(element);
|
||||
|
||||
//post CPControlTextDidBeginEditingNotification
|
||||
[self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]];
|
||||
|
||||
window.setTimeout(function()
|
||||
[[CPRunLoop mainRunLoop] performBlock:function()
|
||||
{
|
||||
// This will prevent to jump to the focused element
|
||||
var previousScrollingOrigin = [self _scrollToVisibleRectAndReturnPreviousOrigin];
|
||||
|
||||
element.focus();
|
||||
|
||||
[self _restorePreviousScrollingOrigin:previousScrollingOrigin];
|
||||
|
||||
CPTokenFieldInputOwner = self;
|
||||
}, 0.0);
|
||||
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
|
||||
[self textDidFocus:[CPNotification notificationWithName:CPTextFieldDidFocusNotification object:self userInfo:nil]];
|
||||
}, 0.0);
|
||||
} argument:nil order:0 modes:[CPDefaultRunLoopMode]];
|
||||
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
|
||||
@@ -517,17 +523,25 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
var element = [self _inputElement];
|
||||
|
||||
CPTokenFieldInputResigning = YES;
|
||||
element.blur();
|
||||
|
||||
if (CPTokenFieldInputIsActive)
|
||||
element.blur();
|
||||
|
||||
if (!CPTokenFieldInputDidBlur)
|
||||
CPTokenFieldBlurHandler();
|
||||
|
||||
CPTokenFieldInputDidBlur = NO;
|
||||
CPTokenFieldInputResigning = NO;
|
||||
|
||||
if (element.parentNode == [_tokenScrollView documentView]._DOMElement)
|
||||
element.parentNode.removeChild(element);
|
||||
|
||||
// Previosly, we unflagged CPTokenFieldInputDidBlur and CPTokenFieldInputResigning before
|
||||
// the call to removeChild. This may result in DOM exceptions in Chrome under certain conditions.
|
||||
// See https://stackoverflow.com/questions/21926083/failed-to-execute-removechild-on-node
|
||||
// for why we need to unflag CPTokenFieldInputDidBlur and CPTokenFieldInputResigning
|
||||
// only after removing the element.
|
||||
|
||||
CPTokenFieldInputDidBlur = NO;
|
||||
CPTokenFieldInputResigning = NO;
|
||||
|
||||
CPTokenFieldInputIsActive = NO;
|
||||
|
||||
if (document.attachEvent)
|
||||
@@ -535,7 +549,7 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
CPTokenFieldCachedSelectStartFunction = nil;
|
||||
CPTokenFieldCachedDragFunction = nil;
|
||||
|
||||
document.body.ondrag = CPTokenFieldCachedDragFunction
|
||||
document.body.ondrag = CPTokenFieldCachedDragFunction;
|
||||
document.body.onselectstart = CPTokenFieldCachedSelectStartFunction
|
||||
}
|
||||
|
||||
@@ -1033,6 +1047,21 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
[[[self window] platformWindow] _propagateCurrentDOMEvent:YES];
|
||||
}
|
||||
|
||||
- (BOOL)performKeyEquivalent:(CPEvent)anEvent
|
||||
{
|
||||
var characters = [anEvent characters];
|
||||
|
||||
// Here we handle the event when getting a CPNewlineCharacter or CPCarriageReturnCharacter when the menu is open
|
||||
// We don't want that the application dispatches the event to the other controls
|
||||
if ([self hasThemeState:CPThemeStateAutocompleting] && (characters === CPNewlineCharacter || characters === CPCarriageReturnCharacter))
|
||||
{
|
||||
[self keyDown:anEvent];
|
||||
return YES;
|
||||
}
|
||||
|
||||
return [super performKeyEquivalent:anEvent];
|
||||
}
|
||||
|
||||
- (void)textDidChange:(CPNotification)aNotification
|
||||
{
|
||||
if ([aNotification object] !== self)
|
||||
@@ -1218,7 +1247,7 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
var scrollToToken = _shouldScrollTo;
|
||||
|
||||
if (scrollToToken === CPScrollDestinationLeft)
|
||||
scrollToToken = tokens[_selectedRange.location]
|
||||
scrollToToken = tokens[_selectedRange.location];
|
||||
else if (scrollToToken === CPScrollDestinationRight)
|
||||
scrollToToken = tokens[MAX(0, CPMaxRange(_selectedRange) - 1)];
|
||||
[self _scrollTokenViewToVisible:scrollToToken];
|
||||
@@ -1454,9 +1483,6 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
|
||||
- (BOOL)setThemeState:(ThemeState)aState
|
||||
{
|
||||
if (aState.isa && [aState isKindOfClass:CPArray])
|
||||
aState = CPThemeState.apply(null, aState);
|
||||
|
||||
var r = [super setThemeState:aState];
|
||||
|
||||
// Share hover state with the disclosure and delete buttons.
|
||||
@@ -1471,9 +1497,6 @@ CPTokenFieldDeleteButtonType = 1;
|
||||
|
||||
- (BOOL)unsetThemeState:(ThemeState)aState
|
||||
{
|
||||
if (aState.isa && [aState isKindOfClass:CPArray])
|
||||
aState = CPThemeState.apply(null, aState);
|
||||
|
||||
var r = [super unsetThemeState:aState];
|
||||
|
||||
// Share hover state with the disclosure and delete button.
|
||||
|
||||
+6
-7
@@ -153,7 +153,7 @@ var CPToolbarsByIdentifier = nil,
|
||||
|
||||
if (!toolbarsSharingIdentifier)
|
||||
{
|
||||
toolbarsSharingIdentifier = []
|
||||
toolbarsSharingIdentifier = [];
|
||||
[CPToolbarsByIdentifier setObject:toolbarsSharingIdentifier forKey:identifier];
|
||||
}
|
||||
|
||||
@@ -459,11 +459,10 @@ var CPToolbarsByIdentifier = nil,
|
||||
/* @ignore */
|
||||
- (id)_itemsWithIdentifiers:(CPArray)identifiers
|
||||
{
|
||||
var items = [];
|
||||
for (var i = 0; i < identifiers.length; i++)
|
||||
[items addObject:[self _itemForItemIdentifier:identifiers[i] willBeInsertedIntoToolbar:NO]];
|
||||
|
||||
return items;
|
||||
return [identifiers arrayByApplyingBlock:function(identifier)
|
||||
{
|
||||
return [self _itemForItemIdentifier:identifier willBeInsertedIntoToolbar:NO];
|
||||
}];
|
||||
}
|
||||
|
||||
/* @ignore */
|
||||
@@ -589,7 +588,7 @@ var CPToolbarIdentifierKey = @"CPToolbarIdentifierKey",
|
||||
[aCoder encodeBool:_showsBaselineSeparator forKey:CPToolbarShowsBaselineSeparatorKey];
|
||||
[aCoder encodeBool:_allowsUserCustomization forKey:CPToolbarAllowsUserCustomizationKey];
|
||||
[aCoder encodeBool:_isVisible forKey:CPToolbarIsVisibleKey];
|
||||
[aCoder encodeInt:_sizeMode forKey:CPToolbarSizeModeKey]
|
||||
[aCoder encodeInt:_sizeMode forKey:CPToolbarSizeModeKey];
|
||||
|
||||
[aCoder encodeObject:_identifiedItems forKey:CPToolbarIdentifiedItemsKey];
|
||||
[aCoder encodeObject:_defaultItems forKey:CPToolbarDefaultItemsKey];
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* CPTrackingArea.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Didier Korthoudt.
|
||||
* Copyright 2015, Cappuccino Project.
|
||||
*
|
||||
* 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>
|
||||
|
||||
@class CPView
|
||||
|
||||
/* @group CPTrackingAreaOptions */
|
||||
@typedef CPTrackingAreaOptions
|
||||
CPTrackingMouseEnteredAndExited = 1 << 1;
|
||||
CPTrackingMouseMoved = 1 << 2;
|
||||
CPTrackingCursorUpdate = 1 << 3;
|
||||
CPTrackingActiveWhenFirstResponder = 1 << 4;
|
||||
CPTrackingActiveInKeyWindow = 1 << 5;
|
||||
CPTrackingActiveInActiveApp = 1 << 6;
|
||||
CPTrackingActiveAlways = 1 << 7;
|
||||
CPTrackingAssumeInside = 1 << 8;
|
||||
CPTrackingInVisibleRect = 1 << 9;
|
||||
CPTrackingEnabledDuringMouseDrag = 1 << 10;
|
||||
|
||||
var CPTrackingAreaViewRectKey = @"CPTrackinkAreaViewRectKey",
|
||||
CPTrackingAreaOptionsKey = @"CPTrackingAreaOptionsKey",
|
||||
CPTrackingAreaOwnerKey = @"CPTrackingAreaOwnerKey",
|
||||
CPTrackingAreaUserInfoKey = @"CPTrackingAreaUserInfoKey",
|
||||
CPTrackingAreaReferencingViewKey = @"CPTrackingAreaReferencingViewKey",
|
||||
CPTrackingAreaWindowRect = @"CPTrackingAreaWindowRect";
|
||||
|
||||
CPTrackingOwnerImplementsMouseEntered = 1 << 1;
|
||||
CPTrackingOwnerImplementsMouseExited = 1 << 2;
|
||||
CPTrackingOwnerImplementsMouseMoved = 1 << 3;
|
||||
CPTrackingOwnerImplementsCursorUpdate = 1 << 4;
|
||||
|
||||
/*!
|
||||
@ingroup appkit
|
||||
|
||||
A CPTrackingArea defines a region of view that generates mouse-tracking and
|
||||
cursor-update events when the mouse is over that region.
|
||||
*/
|
||||
@implementation CPTrackingArea : CPObject
|
||||
{
|
||||
CGRect _viewRect @accessors(getter=rect);
|
||||
CPTrackingAreaOptions _options @accessors(getter=options);
|
||||
id _owner @accessors(getter=owner);
|
||||
CPDictionary _userInfo @accessors(getter=userInfo);
|
||||
|
||||
CPView _referencingView @accessors(property=view);
|
||||
CGRect _windowRect @accessors(getter=windowRect);
|
||||
|
||||
unsigned _implementedOwnerMethods @accessors(getter=implementedOwnerMethods);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
/*!
|
||||
Initializes and returns an object defining a region of a view to receive mouse-tracking events, mouse-moved events, cursor-update events, or possibly
|
||||
all these events.
|
||||
*/
|
||||
- (CPTrackingArea)initWithRect:(CGRect)aRect options:(CPTrackingAreaOptions)options owner:(id)owner userInfo:(CPDictionary)userInfo
|
||||
{
|
||||
if (owner === nil)
|
||||
[CPException raise:CPInternalInconsistencyException reason:"No owner specified"];
|
||||
|
||||
if (options === 0)
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Invalid CPTrackingArea options"];
|
||||
|
||||
// Check options:
|
||||
// - at least one of CPTrackingMouseEnteredAndExited, CPTrackingMouseMoved, CPTrackingCursorUpdate
|
||||
// - exactly one of CPTrackingActiveWhenFirstResponder, CPTrackingActiveInKeyWindow, CPTrackingActiveInActiveApp, CPTrackingActiveAlways
|
||||
// - no check on CPTrackingAssumeInside, CPTrackingInVisibleRect, CPTrackingEnableDuringMouseDrag
|
||||
|
||||
if (!((options & CPTrackingMouseEnteredAndExited) || (options & CPTrackingMouseMoved) || (options & CPTrackingCursorUpdate)))
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Invalid CPTrackingAreaOptions: must use at least one of [CPTrackingMouseEnteredAndExited | CPTrackingMouseMoved | CPTrackingCursorUpdate]"];
|
||||
|
||||
if ((((options & CPTrackingActiveWhenFirstResponder) > 0) + ((options & CPTrackingActiveInKeyWindow) > 0) + ((options & CPTrackingActiveInActiveApp) > 0) + ((options & CPTrackingActiveAlways) > 0)) !== 1)
|
||||
[CPException raise:CPInternalInconsistencyException reason:"Tracking area options may only specify one of [CPTrackingActiveWhenFirstResponder | CPTrackingActiveInKeyWindow | CPTrackingActiveInActiveApp | CPTrackingActiveAlways]."];
|
||||
|
||||
if (self = [super init])
|
||||
{
|
||||
_viewRect = aRect;
|
||||
_options = options;
|
||||
_owner = owner;
|
||||
_userInfo = userInfo;
|
||||
|
||||
// Cache owner implemented methods
|
||||
|
||||
if ([_owner respondsToSelector:@selector(mouseEntered:)])
|
||||
_implementedOwnerMethods |= CPTrackingOwnerImplementsMouseEntered;
|
||||
|
||||
if ([_owner respondsToSelector:@selector(mouseExited:)])
|
||||
_implementedOwnerMethods |= CPTrackingOwnerImplementsMouseExited;
|
||||
|
||||
if ([_owner respondsToSelector:@selector(mouseMoved:)])
|
||||
_implementedOwnerMethods |= CPTrackingOwnerImplementsMouseMoved;
|
||||
|
||||
if ([_owner respondsToSelector:@selector(cursorUpdate:)])
|
||||
_implementedOwnerMethods |= CPTrackingOwnerImplementsCursorUpdate;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Implementation
|
||||
|
||||
- (void)_updateWindowRect
|
||||
{
|
||||
_windowRect = [_referencingView convertRect:((_options & CPTrackingInVisibleRect) ? [_referencingView visibleRect] : _viewRect) toView:[[_referencingView window] _windowView]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CPCoding
|
||||
|
||||
@implementation CPTrackingArea (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_viewRect = [aCoder decodeObjectForKey:CPTrackingAreaViewRectKey];
|
||||
_options = [aCoder decodeObjectForKey:CPTrackingAreaOptionsKey];
|
||||
_owner = [aCoder decodeObjectForKey:CPTrackingAreaOwnerKey];
|
||||
_userInfo = [aCoder decodeObjectForKey:CPTrackingAreaUserInfoKey];
|
||||
_referencingView = [aCoder decodeObjectForKey:CPTrackingAreaReferencingViewKey];
|
||||
_windowRect = [aCoder decodeObjectForKey:CPTrackingAreaWindowRect];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_viewRect forKey:CPTrackingAreaViewRectKey];
|
||||
[aCoder encodeObject:_options forKey:CPTrackingAreaOptionsKey];
|
||||
[aCoder encodeObject:_owner forKey:CPTrackingAreaOwnerKey];
|
||||
[aCoder encodeObject:_userInfo forKey:CPTrackingAreaUserInfoKey];
|
||||
[aCoder encodeObject:_referencingView forKey:CPTrackingAreaReferencingViewKey];
|
||||
[aCoder encodeObject:_windowRect forKey:CPTrackingAreaWindowRect];
|
||||
}
|
||||
|
||||
@end
|
||||
+650
-397
File diff suppressed because it is too large
Load Diff
+174
-2
@@ -149,9 +149,10 @@ var CPViewControllerCachedCibs;
|
||||
|
||||
If you use Interface Builder to create your views, and you initialize the
|
||||
controller using the initWithCibName:bundle: methods, then you MUST NOT override
|
||||
this method. The consequences risk shattering the space-time continuum.
|
||||
this method.
|
||||
|
||||
Note: The cib loading system is currently synchronous.
|
||||
@note When using this method, the cib loading system is synchronous.
|
||||
See the loadViewWithCompletionHandler: method for an asynchronous loading.
|
||||
*/
|
||||
- (void)loadView
|
||||
{
|
||||
@@ -176,6 +177,67 @@ var CPViewControllerCachedCibs;
|
||||
_view = [CPView new];
|
||||
}
|
||||
|
||||
/*!
|
||||
Asynchronously load the cib and create the view that the controller manages.
|
||||
|
||||
@param aHandler a function which will be passed the loaded view as the first
|
||||
argument and a network error or nil as the second argument: function(view, error).
|
||||
|
||||
@note If the view has already been loaded, the completion handler is run immediatly
|
||||
and the process is synchronous.
|
||||
*/
|
||||
- (void)loadViewWithCompletionHandler:(Function/*(view, error)*/)aHandler
|
||||
{
|
||||
if (_view)
|
||||
return;
|
||||
|
||||
if (_cibName)
|
||||
{
|
||||
// check if a cib is already cached for the current _cibName
|
||||
var cib = [CPViewControllerCachedCibs objectForKey:_cibName];
|
||||
|
||||
if (!cib)
|
||||
{
|
||||
var cibName = _cibName;
|
||||
|
||||
if (![cibName hasSuffix:@".cib"])
|
||||
cibName = [cibName stringByAppendingString:@".cib"];
|
||||
|
||||
// If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
|
||||
var bundle = _cibBundle || [CPBundle mainBundle],
|
||||
url = [bundle _cibPathForResource:cibName];
|
||||
|
||||
// if the cib isn't cached yet : fetch it and cache it
|
||||
[CPURLConnection sendAsynchronousRequest:[CPURLRequest requestWithURL:url] queue:[CPOperationQueue mainQueue] completionHandler:function(aResponse, aData, anError)
|
||||
{
|
||||
if (anError == nil)
|
||||
{
|
||||
var data = [CPData dataWithRawString:aData],
|
||||
aCib = [[CPCib alloc] _initWithData:data bundle:_cibBundle cibName:_cibName];
|
||||
|
||||
[CPViewControllerCachedCibs setObject:aCib forKey:_cibName];
|
||||
[aCib instantiateCibWithExternalNameTable:_cibExternalNameTable];
|
||||
[self _viewDidLoadWithCompletionHandler:aHandler];
|
||||
}
|
||||
else
|
||||
{
|
||||
aHandler(nil, anError);
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[cib instantiateCibWithExternalNameTable:_cibExternalNameTable];
|
||||
[self _viewDidLoadWithCompletionHandler:aHandler];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_view = [CPView new];
|
||||
[self _viewDidLoadWithCompletionHandler:aHandler];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the view that the controller manages.
|
||||
|
||||
@@ -230,6 +292,16 @@ var CPViewControllerCachedCibs;
|
||||
[self didChangeValueForKey:"isViewLoaded"];
|
||||
}
|
||||
|
||||
- (void)_viewDidLoadWithCompletionHandler:(Function)aHandler
|
||||
{
|
||||
[self _registerOrUnregister:YES notificationsForView:_view];
|
||||
|
||||
[self willChangeValueForKey:"isViewLoaded"];
|
||||
aHandler(_view, nil);
|
||||
_isViewLoaded = YES;
|
||||
[self didChangeValueForKey:"isViewLoaded"];
|
||||
}
|
||||
|
||||
/*!
|
||||
This method is called after the view controller has loaded its associated views into memory.
|
||||
|
||||
@@ -243,6 +315,79 @@ var CPViewControllerCachedCibs;
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
Called after the view controller’s view has been loaded into memory is about to be added to the
|
||||
view hierarchy in the window.
|
||||
|
||||
@discussion You can override this method to perform tasks prior to a view controller’s view
|
||||
getting added to view hierarchy, such as setting the view’s highlight color. This method is called when:
|
||||
|
||||
• The view is about to be added to the view hierarchy of the view controller
|
||||
|
||||
If you override this method, call this method on super at some point in your implementation in case
|
||||
a superclass also overrides this method.
|
||||
|
||||
The default implementation of this method does nothing.
|
||||
*/
|
||||
- (void)viewWillAppear
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
Called when the view controller’s view is fully transitioned onto the screen.
|
||||
|
||||
@discussion This method is called after the completion of any drawing and animations
|
||||
involved in the initial appearance of the view. You can override this method to
|
||||
perform tasks appropriate for that time, such as work that should not interfere
|
||||
with the presentation animation, or starting an animation that you want to begin
|
||||
after the view appears.
|
||||
|
||||
If you override this method, call this method on super at some point in your
|
||||
implementation in case a superclass also overrides this method.
|
||||
|
||||
The default implementation of this method does nothing.
|
||||
*/
|
||||
- (void)viewDidAppear
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
Called when the view controller’s view is about to be removed from the view hierarchy in the window.
|
||||
|
||||
@discussion You can override this method to perform tasks that are to precede the disappearance
|
||||
of the view controller’s view, such as stopping a continuous animation that you
|
||||
started in response to the viewDidAppear method call. This method is called when:
|
||||
|
||||
• The view is about to be removed from the view hierarchy of the window
|
||||
|
||||
If you override this method, call this method on super at some point in your
|
||||
implementation in case a superclass also overrides this method.
|
||||
|
||||
The default implementation of this method does nothing.
|
||||
*/
|
||||
- (void)viewWillDisappear
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
Called after the view controller’s view is removed from the view hierarchy in a window.
|
||||
|
||||
@discussion You can override this method to perform tasks associated with removing the view
|
||||
controller’s view from the window’s view hierarchy, such as releasing resources
|
||||
not needed when the view is not visible or no longer part of the window.
|
||||
|
||||
If you override this method, call this method on super at some point in your
|
||||
implementation in case a superclass also overrides this method.
|
||||
|
||||
The default implementation of this method does nothing.
|
||||
*/
|
||||
- (void)viewDidDisappear
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
Manually sets the view that the controller manages.
|
||||
@@ -256,6 +401,9 @@ var CPViewControllerCachedCibs;
|
||||
{
|
||||
var willChangeIsViewLoaded = (_isViewLoaded == NO && aView != nil) || (_isViewLoaded == YES && aView == nil);
|
||||
|
||||
[self _registerOrUnregister:NO notificationsForView:_view];
|
||||
[self _registerOrUnregister:YES notificationsForView:aView];
|
||||
|
||||
if (willChangeIsViewLoaded)
|
||||
[self willChangeValueForKey:"isViewLoaded"];
|
||||
|
||||
@@ -271,6 +419,30 @@ var CPViewControllerCachedCibs;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)_registerOrUnregister:(BOOL)shouldRegister notificationsForView:(CPView)aView
|
||||
{
|
||||
if (aView === nil)
|
||||
return;
|
||||
|
||||
var center = [CPNotificationCenter defaultCenter],
|
||||
notifs_to_sel = @{_CPViewWillAppearNotification : @"viewWillAppear",
|
||||
_CPViewDidAppearNotification : @"viewDidAppear",
|
||||
_CPViewWillDisappearNotification : @"viewWillDisappear",
|
||||
_CPViewDidDisappearNotification : @"viewDidDisappear"};
|
||||
|
||||
[notifs_to_sel enumerateKeysAndObjectsUsingBlock:function(notif, selString, stop)
|
||||
{
|
||||
var selector = CPSelectorFromString(selString);
|
||||
if ([self implementsSelector:selector])
|
||||
{
|
||||
if (shouldRegister)
|
||||
[center addObserver:self selector:selector name:notif object:aView];
|
||||
else
|
||||
[center removeObserver:self name:notif object:aView];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* CPVisualEffectView.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Antoine Mercadal.
|
||||
* Copyright 2015, 280 Cappuccino Project.
|
||||
*
|
||||
* 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 "CPAppearance.j"
|
||||
@import "CPView.j"
|
||||
|
||||
@typedef CPVisualEffectMaterial
|
||||
CPVisualEffectMaterialAppearanceBased = 0;
|
||||
CPVisualEffectMaterialLight = 1;
|
||||
CPVisualEffectMaterialDark = 2;
|
||||
CPVisualEffectMaterialTitlebar = 3;
|
||||
|
||||
@typedef CPVisualEffectBlendingMode
|
||||
CPVisualEffectBlendingModeBehindWindow = 0;
|
||||
CPVisualEffectBlendingModeWithinWindow = 1;
|
||||
|
||||
@typedef CPVisualEffectState
|
||||
CPVisualEffectStateFollowsWindowActiveState = 0;
|
||||
CPVisualEffectStateActive = 1;
|
||||
CPVisualEffectStateInactive = 2;
|
||||
|
||||
|
||||
/*! @ingroup appkit
|
||||
|
||||
Very naive implementation of CPVisualEffectView. This view allows
|
||||
to use vibrancy effect. This is only working with Safari 9+ and the
|
||||
support in Chrome/ium should come quite soon.
|
||||
Using this class with a browser that doesn't support backdrop-filter
|
||||
While still work, but you will not get the blurry effect.
|
||||
*/
|
||||
@implementation CPVisualEffectView : CPView
|
||||
{
|
||||
CPImage _maskImage @accessors(property=maskImage);
|
||||
CPVisualEffectBlendingMode _blendingMode @accessors(property=blendingMode);
|
||||
CPVisualEffectMaterial _material @accessors(property=material);
|
||||
CPVisualEffectState _state @accessors(property=state);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
- (id)initWithFrame:(CGRect)aFrame
|
||||
{
|
||||
if (self = [super initWithFrame:aFrame])
|
||||
{
|
||||
_material = CPVisualEffectMaterialAppearanceBased;
|
||||
_blendingMode = CPVisualEffectBlendingModeWithinWindow;
|
||||
_state = CPVisualEffectStateFollowsWindowActiveState;
|
||||
_appearance = [CPAppearance appearanceNamed:CPAppearanceNameVibrantDark];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CPVisualEffectView API
|
||||
|
||||
/*! Sets the appearance of the CPVisualEffectView.
|
||||
|
||||
Only CPAppearance named CPAppearanceNameVibrantDark or CPAppearanceNameVibrantLight are valid
|
||||
|
||||
@param anAppearance the CPAppearance.
|
||||
*/
|
||||
- (void)setAppearance:(CPAppearance)anAppearance
|
||||
{
|
||||
if (![self _validAppearance:anAppearance])
|
||||
[CPException raise:CPInvalidArgumentException reason:"Appearance can only be CPAppearanceNameVibrantDark or CPAppearanceNameVibrantLight in CPVisualEffectView, but is " + anAppearance];
|
||||
|
||||
[super setAppearance:anAppearance];
|
||||
|
||||
[self setNeedsLayout:YES];
|
||||
}
|
||||
|
||||
/*! Sets the received effect state.
|
||||
Possible values:
|
||||
<pre>
|
||||
CPVisualEffectStateFollowsWindowActiveState (default)
|
||||
CPVisualEffectStateActive
|
||||
CPVisualEffectStateInactive
|
||||
</pre>
|
||||
*/
|
||||
- (void)setState:(CPVisualEffectState)aState
|
||||
{
|
||||
if (_state == aState)
|
||||
return;
|
||||
|
||||
[self willChangeValueForKey:"state"];
|
||||
_state = aState;
|
||||
[self didChangeValueForKey:"state"];
|
||||
|
||||
[self setNeedsLayout:YES];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Utilities
|
||||
|
||||
- (void)_setEffectEnabled:(BOOL)shouldEnable
|
||||
{
|
||||
var dark = [[self appearance] isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantDark]],
|
||||
prop = CPBrowserStyleProperty("backdrop-filter"),
|
||||
color = (dark ? [CPColor colorWithHexString:@"1e1e1e"] : [CPColor whiteColor]),
|
||||
finalColor = shouldEnable ? [color colorWithAlphaComponent:0.6] : color;
|
||||
|
||||
[self setBackgroundColor:finalColor];
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
self._DOMElement.style[prop] = shouldEnable ? "blur(30px)" : nil;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
switch (_state)
|
||||
{
|
||||
case CPVisualEffectStateFollowsWindowActiveState:
|
||||
[self _setEffectEnabled:[self hasThemeState:CPThemeStateKeyWindow]];
|
||||
break;
|
||||
|
||||
case CPVisualEffectStateActive:
|
||||
[self _setEffectEnabled:YES];
|
||||
break;
|
||||
|
||||
case CPVisualEffectStateInactive:
|
||||
[self _setEffectEnabled:NO];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)_validAppearance:(CPAppearance)anAppearance
|
||||
{
|
||||
return [anAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantDark]] || [anAppearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantLight]];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark CPCoding
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
if (self = [super initWithCoder:aCoder])
|
||||
{
|
||||
_blendingMode = [aCoder decodeIntForKey:@"_blendingMode"] || CPVisualEffectBlendingModeWithinWindow;
|
||||
_maskImage = [aCoder decodeObjectForKey:@"_maskImage"];
|
||||
_material = [aCoder decodeIntForKey:@"_material"] || CPVisualEffectMaterialAppearanceBased;
|
||||
_state = [aCoder decodeIntForKey:@"_state"] || CPVisualEffectStateFollowsWindowActiveState;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[super encodeWithCoder:aCoder];
|
||||
|
||||
[aCoder encodeObject:_maskImage forKey:@"_maskImage"];
|
||||
[aCoder encodeInt:_blendingMode forKey:@"_blendingMode"];
|
||||
[aCoder encodeInt:_material forKey:@"_material"];
|
||||
[aCoder encodeInt:_state forKey:@"_state"];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
+658
-76
File diff suppressed because it is too large
Load Diff
@@ -176,6 +176,8 @@ CPWindowDidEndSheetNotification = @"CPWindowDidEndSheetNotificat
|
||||
CPWindowDidMiniaturizeNotification = @"CPWindowDidMiniaturizeNotification";
|
||||
CPWindowWillMiniaturizeNotification = @"CPWindowWillMiniaturizeNotification";
|
||||
CPWindowDidDeminiaturizeNotification = @"CPWindowDidDeminiaturizeNotification";
|
||||
CPWindowWillStartLiveResizeNotification = @"CPWindowWillStartLiveResizeNotification";
|
||||
CPWindowDidEndLiveResizeNotification = @"CPWindowDidEndLiveResizeNotification";
|
||||
|
||||
_CPWindowDidChangeFirstResponderNotification = @"_CPWindowDidChangeFirstResponderNotification";
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
+ (CPString)defaultThemeClass
|
||||
{
|
||||
return @"bordeless-bridge-window-view";
|
||||
return @"borderless-bridge-window-view";
|
||||
}
|
||||
|
||||
+ (CPDictionary)themeAttributes
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
|
||||
@implementation _CPHUDWindowView : _CPTitleableWindowView
|
||||
{
|
||||
CPView _toolbarView;
|
||||
CPButton _closeButton;
|
||||
}
|
||||
|
||||
@@ -184,7 +183,7 @@
|
||||
if (_styleMask & CPClosableWindowMask)
|
||||
{
|
||||
[_closeButton setFrameOrigin:[self valueForThemeAttribute:@"close-image-origin"]];
|
||||
[_closeButton setFrameSize:[self valueForThemeAttribute:@"close-image-size"]]
|
||||
[_closeButton setFrameSize:[self valueForThemeAttribute:@"close-image-size"]];
|
||||
[_closeButton setImage:[self valueForThemeAttribute:@"close-image"]];
|
||||
[_closeButton setAlternateImage:[self valueForThemeAttribute:@"close-active-image"]];
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
@import "CGGradient.j"
|
||||
@import "_CPWindowView.j"
|
||||
|
||||
@global CPPopoverAppearanceMinimal
|
||||
|
||||
var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
|
||||
@@ -39,7 +38,6 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
{
|
||||
float _arrowOffsetX @accessors(property=arrowOffsetX);
|
||||
float _arrowOffsetY @accessors(property=arrowOffsetY);
|
||||
int _appearance @accessors(property=appearance);
|
||||
unsigned _preferredEdge @accessors(property=preferredEdge);
|
||||
|
||||
CGSize _cursorSize;
|
||||
@@ -123,7 +121,7 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
{
|
||||
_arrowOffsetX = 0.0;
|
||||
_arrowOffsetY = 0.0;
|
||||
_appearance = CPPopoverAppearanceMinimal;
|
||||
_appearance = [CPAppearance appearanceNamed:CPAppearanceNameVibrantLight];
|
||||
_cursorSize = CGSizeMakeCopy(_CPPopoverWindowViewDefaultCursorSize);
|
||||
}
|
||||
|
||||
@@ -168,7 +166,7 @@ var _CPPopoverWindowViewDefaultCursorSize = CGSizeMake(16, 10);
|
||||
gradient,
|
||||
frame = [self bounds];
|
||||
|
||||
if (_appearance == CPPopoverAppearanceMinimal)
|
||||
if ([_appearance isEqual:[CPAppearance appearanceNamed:CPAppearanceNameVibrantLight]])
|
||||
{
|
||||
gradient = [self valueForThemeAttribute:@"background-gradient"];
|
||||
strokeColor = [self valueForThemeAttribute:@"stroke-color"];
|
||||
|
||||
@@ -94,7 +94,6 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
_CPTexturedWindowHeadView _headView;
|
||||
CPView _dividerView;
|
||||
CPView _bodyView;
|
||||
CPView _toolbarView;
|
||||
|
||||
CPButton _closeButton;
|
||||
CPButton _minimizeButton;
|
||||
|
||||
@@ -64,6 +64,8 @@ _CPWindowViewResizeSlop = 3;
|
||||
CGRect _cachedScreenFrame;
|
||||
|
||||
CPView _sheetShadowView;
|
||||
|
||||
BOOL _isTracking @accessors(getter=_isTracking);
|
||||
}
|
||||
|
||||
+ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect
|
||||
@@ -148,6 +150,7 @@ _CPWindowViewResizeSlop = 3;
|
||||
_styleMask = aStyleMask;
|
||||
_resizeIndicatorOffset = CGSizeMakeZero();
|
||||
_toolbarOffset = CGSizeMakeZero();
|
||||
_isTracking = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -353,7 +356,9 @@ _CPWindowViewResizeSlop = 3;
|
||||
if ([theWindow isFullPlatformWindow] ||
|
||||
!(_styleMask & CPResizableWindowMask) ||
|
||||
(CPWindowResizeStyle !== CPWindowResizeStyleModern))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var globalPoint = [theWindow convertBaseToGlobal:aPoint],
|
||||
resizeRegion = isResizing ? _resizeRegion : [self resizeRegionForPoint:globalPoint],
|
||||
@@ -456,6 +461,8 @@ _CPWindowViewResizeSlop = 3;
|
||||
if (type === CPLeftMouseUp)
|
||||
{
|
||||
_cachedScreenFrame = nil;
|
||||
_isTracking = NO;
|
||||
[_window _endLiveResize];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -472,6 +479,12 @@ _CPWindowViewResizeSlop = 3;
|
||||
}
|
||||
else if (type === CPLeftMouseDragged)
|
||||
{
|
||||
if (!_isTracking)
|
||||
{
|
||||
_isTracking = YES;
|
||||
[_window _startLiveResize];
|
||||
}
|
||||
|
||||
var deltaX = globalLocation.x - _mouseDraggedPoint.x,
|
||||
deltaY = globalLocation.y - _mouseDraggedPoint.y,
|
||||
startX = CGRectGetMinX(_cachedFrame),
|
||||
@@ -969,3 +982,24 @@ _CPWindowViewResizeSlop = 3;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPWindowView (TrackingAreaAdditions)
|
||||
{
|
||||
CPTrackingArea _windowViewTrackingArea;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (_windowViewTrackingArea)
|
||||
[self removeTrackingArea:_windowViewTrackingArea];
|
||||
|
||||
_windowViewTrackingArea = [[CPTrackingArea alloc] initWithRect:[self contentRectForFrameRect:[self frame]]
|
||||
options:CPTrackingCursorUpdate | CPTrackingActiveInActiveApp
|
||||
owner:self
|
||||
userInfo:nil];
|
||||
|
||||
[self addTrackingArea:_windowViewTrackingArea];
|
||||
[super updateTrackingAreas];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -364,7 +364,7 @@
|
||||
{
|
||||
[viewControllerView removeFromSuperview];
|
||||
[viewControllerView setFrame:[contentView frame]];
|
||||
[viewControllerView setAutoresizingMask:[contentView autoresizingMask]]
|
||||
[viewControllerView setAutoresizingMask:[contentView autoresizingMask]];
|
||||
[[self window] setContentView:viewControllerView];
|
||||
}
|
||||
else
|
||||
|
||||
+29
-17
@@ -32,6 +32,7 @@
|
||||
@import "_CPCibObjectData.j"
|
||||
@import "_CPCibProxyObject.j"
|
||||
@import "_CPCibWindowTemplate.j"
|
||||
@import "_CPLocalizableString.j"
|
||||
|
||||
CPCibOwner = @"CPCibOwner";
|
||||
CPCibTopLevelObjects = @"CPCibTopLevelObjects";
|
||||
@@ -46,19 +47,34 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
|
||||
@implementation CPCib : CPObject
|
||||
{
|
||||
CPData _data;
|
||||
CPBundle _bundle;
|
||||
BOOL _awakenCustomResources;
|
||||
BOOL _awakenCustomResources @accessors(property=_awakenCustomResources);
|
||||
|
||||
CPBundle _bundle;
|
||||
CPData _data;
|
||||
CPString _cibName;
|
||||
id _loadDelegate;
|
||||
}
|
||||
|
||||
- (id)_initWithData:(CPData)data bundle:(CPBundle)aBundle cibName:(CPString)aCibName
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_data = data;
|
||||
_cibName = aCibName;
|
||||
_bundle = aBundle;
|
||||
_awakenCustomResources = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithContentsOfURL:(CPURL)aURL
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_cibName = [aURL lastPathComponent];
|
||||
|
||||
_data = [CPURLConnection sendSynchronousRequest:[CPURLRequest requestWithURL:aURL] returningResponse:nil];
|
||||
|
||||
if (!_data)
|
||||
@@ -76,6 +92,8 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
|
||||
if (self)
|
||||
{
|
||||
_cibName = [aURL lastPathComponent];
|
||||
|
||||
[CPURLConnection connectionWithRequest:[CPURLRequest requestWithURL:aURL] delegate:self];
|
||||
|
||||
_awakenCustomResources = YES;
|
||||
@@ -92,7 +110,9 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
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]];
|
||||
var bundle = aBundle || [CPBundle mainBundle];
|
||||
|
||||
self = [self initWithContentsOfURL:[bundle _cibPathForResource:aName]];
|
||||
|
||||
if (self)
|
||||
_bundle = aBundle;
|
||||
@@ -106,7 +126,9 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
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];
|
||||
var bundle = aBundle || [CPBundle mainBundle];
|
||||
|
||||
self = [self initWithContentsOfURL:[bundle _cibPathForResource:aName] loadDelegate:aLoadDelegate];
|
||||
|
||||
if (self)
|
||||
_bundle = aBundle;
|
||||
@@ -114,16 +136,6 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_setAwakenCustomResources:(BOOL)shouldAwakenCustomResources
|
||||
{
|
||||
_awakenCustomResources = shouldAwakenCustomResources;
|
||||
}
|
||||
|
||||
- (BOOL)_awakenCustomResources
|
||||
{
|
||||
return _awakenCustomResources;
|
||||
}
|
||||
|
||||
- (BOOL)instantiateCibWithExternalNameTable:(CPDictionary)anExternalNameTable
|
||||
{
|
||||
var bundle = _bundle,
|
||||
@@ -132,7 +144,7 @@ var CPCibObjectDataKey = @"CPCibObjectDataKey";
|
||||
if (!bundle && owner)
|
||||
bundle = [CPBundle bundleForClass:[owner class]];
|
||||
|
||||
var unarchiver = [[_CPCibKeyedUnarchiver alloc] initForReadingWithData:_data bundle:bundle awakenCustomResources:_awakenCustomResources],
|
||||
var unarchiver = [[_CPCibKeyedUnarchiver alloc] initForReadingWithData:_data bundle:bundle awakenCustomResources:_awakenCustomResources cibName:_cibName],
|
||||
replacementClasses = [anExternalNameTable objectForKey:CPCibReplacementClasses];
|
||||
|
||||
if (replacementClasses)
|
||||
@@ -226,4 +238,4 @@ var CPCibDataFileKey = @"CPCibDataFileKey",
|
||||
[aCoder encodeObject:[_data base64] forKey:CPCibDataFileKey];
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
@implementation CPCibHelpConnector : CPCibConnector
|
||||
{
|
||||
id _destination;
|
||||
id _file;
|
||||
id _marker;
|
||||
}
|
||||
|
||||
+31
-17
@@ -26,7 +26,11 @@
|
||||
|
||||
@import "CPCib.j"
|
||||
|
||||
var CPCibOwner = @"CPCibOwner";
|
||||
var CPCibOwner = @"CPCibOwner",
|
||||
CPBundleDefaultLanguage = @"CPBundleDefaultLanguage",
|
||||
CPBundleTypeOfLocalization = @"CPBundleTypeOfLocalization",
|
||||
CPBundleBaseLocalizationType = @"CPBundleBaseLocalizationType",
|
||||
CPBundleInterfaceBuilderLocalizationType = @"CPBundleInterfaceBuilderLocalizationType";
|
||||
|
||||
@implementation CPObject (CPCibLoading)
|
||||
|
||||
@@ -45,14 +49,7 @@ var CPCibOwner = @"CPCibOwner";
|
||||
|
||||
+ (CPCib)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];
|
||||
|
||||
return [self loadCibFile:path externalNameTable:@{ CPCibOwner: anOwner }];
|
||||
return [self loadCibFile:[self _cibPathForName:aName withOwner:anOwner] externalNameTable:@{ CPCibOwner: anOwner }];
|
||||
}
|
||||
|
||||
- (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable
|
||||
@@ -71,14 +68,7 @@ var CPCibOwner = @"CPCibOwner";
|
||||
|
||||
+ (CPCib)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];
|
||||
|
||||
return [self loadCibFile:path externalNameTable:@{ CPCibOwner: anOwner } loadDelegate:aDelegate];
|
||||
return [self loadCibFile:[self _cibPathForName:aName withOwner:anOwner] externalNameTable:@{ CPCibOwner: anOwner } loadDelegate:aDelegate];
|
||||
}
|
||||
|
||||
- (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable loadDelegate:(id)aDelegate
|
||||
@@ -91,6 +81,30 @@ var CPCibOwner = @"CPCibOwner";
|
||||
externalNameTable:aNameTable]]);
|
||||
}
|
||||
|
||||
- (CPString)_cibPathForResource:(CPString)aName
|
||||
{
|
||||
var defaultBundleLanguage = [self objectForInfoDictionaryKey:CPBundleDefaultLanguage],
|
||||
typeOfLocalization = [self objectForInfoDictionaryKey:CPBundleTypeOfLocalization];
|
||||
|
||||
if (defaultBundleLanguage && (!typeOfLocalization || typeOfLocalization == CPBundleBaseLocalizationType))
|
||||
aName = @"Base.lproj/" + aName;
|
||||
else if (defaultBundleLanguage && typeOfLocalization == CPBundleInterfaceBuilderLocalizationType)
|
||||
aName = _bundle.loadedLanguage() + ".lproj/" + aName;
|
||||
|
||||
return [self pathForResource:aName];
|
||||
}
|
||||
|
||||
+ (CPString)_cibPathForName:(CPString)aName withOwner:(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];
|
||||
|
||||
return [bundle _cibPathForResource:aName];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibLoadDelegate : CPObject
|
||||
|
||||
@@ -222,3 +222,28 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation _CPCibCustomResource (CSSTheming)
|
||||
|
||||
- (BOOL)isCSSBased
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSDictionary
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSBeforeDictionary
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasCSSAfterDictionary
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -21,16 +21,22 @@
|
||||
*/
|
||||
|
||||
@import <Foundation/CPKeyedUnarchiver.j>
|
||||
|
||||
@import <Foundation/CPBundle.j>
|
||||
|
||||
@implementation _CPCibKeyedUnarchiver : CPKeyedUnarchiver
|
||||
{
|
||||
CPBundle _bundle;
|
||||
BOOL _awakenCustomResources;
|
||||
CPDictionary _externalObjectsForProxyIdentifiers;
|
||||
BOOL _awakenCustomResources @accessors(getter=awakenCustomResources);
|
||||
CPBundle _bundle @accessors(getter=bundle);
|
||||
CPDictionary _externalObjectsForProxyIdentifiers @accessors(setter=setExternalObjectsForProxyIdentifiers:);
|
||||
CPString _cibName @accessors(getter=cibName);
|
||||
}
|
||||
|
||||
- (id)initForReadingWithData:(CPData)data bundle:(CPBundle)aBundle awakenCustomResources:(BOOL)shouldAwakenCustomResources
|
||||
{
|
||||
return [self initForReadingWithData:data bundle:aBundle awakenCustomResources:shouldAwakenCustomResources cibName:@""];
|
||||
}
|
||||
|
||||
- (id)initForReadingWithData:(CPData)data bundle:(CPBundle)aBundle awakenCustomResources:(BOOL)shouldAwakenCustomResources cibName:(CPString)aCibName
|
||||
{
|
||||
self = [super initForReadingWithData:data];
|
||||
|
||||
@@ -38,6 +44,7 @@
|
||||
{
|
||||
_bundle = aBundle;
|
||||
_awakenCustomResources = shouldAwakenCustomResources;
|
||||
_cibName = aCibName;
|
||||
|
||||
[self setDelegate:self];
|
||||
}
|
||||
@@ -45,21 +52,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CPBundle)bundle
|
||||
{
|
||||
return _bundle;
|
||||
}
|
||||
|
||||
- (BOOL)awakenCustomResources
|
||||
{
|
||||
return _awakenCustomResources;
|
||||
}
|
||||
|
||||
- (void)setExternalObjectsForProxyIdentifiers:(CPDictionary)externalObjectsForProxyIdentifiers
|
||||
{
|
||||
_externalObjectsForProxyIdentifiers = externalObjectsForProxyIdentifiers;
|
||||
}
|
||||
|
||||
- (id)externalObjectForProxyIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
return [_externalObjectsForProxyIdentifiers objectForKey:anIdentifier];
|
||||
|
||||
@@ -230,7 +230,7 @@ var _CPCibObjectDataNamesKeysKey = @"_CPCibObjectDataNamesKeysKey
|
||||
|
||||
if ([object respondsToSelector:@selector(_cibInstantiate)])
|
||||
{
|
||||
var instantiatedObject = [object _cibInstantiate];
|
||||
instantiatedObject = [object _cibInstantiate];
|
||||
|
||||
if (instantiatedObject !== object)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* _CPLocalizableString.j
|
||||
* AppKit
|
||||
*
|
||||
* Created by Alexandre Wilhelm.
|
||||
* Copyright 2015, Cappuccino Project
|
||||
*
|
||||
* 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/CPString.j>
|
||||
@import <Foundation/CPCharacterSet.j>
|
||||
|
||||
@implementation _CPLocalizableString : CPObject
|
||||
{
|
||||
CPString _dev @accessors(property=dev);
|
||||
CPString _key @accessors(property=key);
|
||||
CPString _value @accessors(property=value);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
var CPLocalizableStringDev = @"CPLocalizableStringDev",
|
||||
CPLocalizableStringKey = @"CPLocalizableStringKey",
|
||||
CPLocalizableStringValue = @"CPLocalizableStringValue";
|
||||
|
||||
@implementation _CPLocalizableString (CPCoding)
|
||||
|
||||
- (id)initWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[self init];
|
||||
|
||||
_dev = [aCoder decodeObjectForKey:CPLocalizableStringDev];
|
||||
_key = [aCoder decodeObjectForKey:CPLocalizableStringKey];
|
||||
_value = [aCoder decodeObjectForKey:CPLocalizableStringValue];
|
||||
|
||||
var tableName = [[aCoder cibName] stringByTrimmingCharactersInSet:[CPCharacterSet characterSetWithCharactersInString:@".cib"]];
|
||||
|
||||
return [[aCoder bundle] localizedStringForKey:_key value:_value table:tableName];
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||||
{
|
||||
[aCoder encodeObject:_dev forKey:CPLocalizableStringDev];
|
||||
[aCoder encodeObject:_key forKey:CPLocalizableStringKey];
|
||||
[aCoder encodeObject:_value forKey:CPLocalizableStringValue];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -30,8 +30,10 @@
|
||||
*/
|
||||
@implementation CAAnimation : CPObject
|
||||
{
|
||||
BOOL _isRemovedOnCompletion;
|
||||
id _delegate;
|
||||
BOOL _isRemovedOnCompletion;
|
||||
id _delegate;
|
||||
CAMediaTimingFunction _timingFunction @accessors(property=timingFunction);
|
||||
double _duration @accessors(property=duration);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -47,8 +49,10 @@
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
_isRemovedOnCompletion = YES;
|
||||
_isRemovedOnCompletion = YES;
|
||||
_timingFunction = nil;
|
||||
_duration = 0.0;
|
||||
_delegate = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -102,7 +106,7 @@
|
||||
- (CAMediaTimingFunction)timingFunction
|
||||
{
|
||||
// Linear Pacing
|
||||
return nil;
|
||||
return _timingFunction;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -127,131 +131,4 @@
|
||||
[anObject addAnimation:self forKey:aKey];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
@implementation CAPropertyAnimation : CAAnimation
|
||||
{
|
||||
CPString _keyPath;
|
||||
|
||||
BOOL _isCumulative;
|
||||
BOOL _isAdditive;
|
||||
}
|
||||
|
||||
+ (id)animationWithKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
var animation = [self animation];
|
||||
|
||||
[animation setKeyPath:aKeyPath];
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
- (void)setKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
_keyPath = aKeyPath;
|
||||
}
|
||||
|
||||
- (CPString)keyPath
|
||||
{
|
||||
return _keyPath;
|
||||
}
|
||||
|
||||
- (void)setCumulative:(BOOL)isCumulative
|
||||
{
|
||||
_isCumulative = isCumulative;
|
||||
}
|
||||
|
||||
- (BOOL)cumulative
|
||||
{
|
||||
return _isCumulative;
|
||||
}
|
||||
|
||||
- (BOOL)isCumulative
|
||||
{
|
||||
return _isCumulative;
|
||||
}
|
||||
|
||||
- (void)setAdditive:(BOOL)isAdditive
|
||||
{
|
||||
_isAdditive = isAdditive;
|
||||
}
|
||||
|
||||
- (BOOL)additive
|
||||
{
|
||||
return _isAdditive;
|
||||
}
|
||||
|
||||
- (BOOL)isAdditive
|
||||
{
|
||||
return _isAdditive;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*!
|
||||
A CABasicAnimation is a simple animation that moves a
|
||||
CALayer from one point to another over a specified
|
||||
period of time.
|
||||
*/
|
||||
@implementation CABasicAnimation : CAPropertyAnimation
|
||||
{
|
||||
id _fromValue;
|
||||
id _toValue;
|
||||
id _byValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the starting position for the animation.
|
||||
@param aValue the animation starting position
|
||||
*/
|
||||
- (void)setFromValue:(id)aValue
|
||||
{
|
||||
_fromValue = aValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the animation's starting position.
|
||||
*/
|
||||
- (id)fromValue
|
||||
{
|
||||
return _fromValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the ending position for the animation.
|
||||
@param aValue the animation ending position
|
||||
*/
|
||||
- (void)setToValue:(id)aValue
|
||||
{
|
||||
_toValue = aValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the animation's ending position.
|
||||
*/
|
||||
- (id)toValue
|
||||
{
|
||||
return _toValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the optional byValue for animation interpolation.
|
||||
@param aValue the byValue
|
||||
*/
|
||||
- (void)setByValue:(id)aValue
|
||||
{
|
||||
_byValue = aValue;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the animation's byValue.
|
||||
*/
|
||||
- (id)byValue
|
||||
{
|
||||
return _byValue;
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user