Compare commits
2351 Commits
3.7.3
...
wip/overvi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1afb04d1e4 | ||
|
|
51e63233ce | ||
|
|
a7d7f94892 | ||
|
|
427f516d45 | ||
|
|
cbb88ffdbb | ||
|
|
71670bad3b | ||
|
|
a2e4153fa0 | ||
|
|
2fba8e29e0 | ||
|
|
de1bb4e203 | ||
|
|
b4680a5c25 | ||
|
|
d44f40d105 | ||
|
|
8d5771e302 | ||
|
|
fb31f99aed | ||
|
|
b97f3a9ecf | ||
|
|
ac22172a6e | ||
|
|
57367380f5 | ||
|
|
7101cc3170 | ||
|
|
7051411be7 | ||
|
|
bb8397b9b1 | ||
|
|
bb8fa61cb4 | ||
|
|
10147ee331 | ||
|
|
3779ac2c8a | ||
|
|
bdad4db9ec | ||
|
|
36c69124f7 | ||
|
|
7c8c811134 | ||
|
|
ccfc9f3ab0 | ||
|
|
d163b92e0b | ||
|
|
887a21afb9 | ||
|
|
634adc9f71 | ||
|
|
974f01cef7 | ||
|
|
ef9ade3548 | ||
|
|
24fdb73b44 | ||
|
|
ba06a87ba8 | ||
|
|
e6be483755 | ||
|
|
a36bfced47 | ||
|
|
4faf421d5a | ||
|
|
a739455414 | ||
|
|
73f6e75d8d | ||
|
|
17e7f8057a | ||
|
|
b62c157680 | ||
|
|
816f5162f9 | ||
|
|
9d8f8277aa | ||
|
|
cca14053a4 | ||
|
|
427790f005 | ||
|
|
17845bf71e | ||
|
|
452f5ab3ba | ||
|
|
335744e78a | ||
|
|
c9e24439b2 | ||
|
|
61c697b6db | ||
|
|
3227d4f3ed | ||
|
|
7e27afb645 | ||
|
|
9ba4790b4d | ||
|
|
3a26f7f4d5 | ||
|
|
8b99617513 | ||
|
|
587655f063 | ||
|
|
7e9ecf4eb2 | ||
|
|
5413010c60 | ||
|
|
7d13cf1587 | ||
|
|
24cd13935a | ||
|
|
1ae7dbec67 | ||
|
|
2b0a2ab3bc | ||
|
|
4ed0f3e5f0 | ||
|
|
9cacc703dd | ||
|
|
9ae70c6519 | ||
|
|
02b38fed49 | ||
|
|
b2a65f809f | ||
|
|
2931869522 | ||
|
|
11d8640ba6 | ||
|
|
65ff947b5e | ||
|
|
2dd7db4808 | ||
|
|
1d7354696e | ||
|
|
cbceac4c8a | ||
|
|
297877fbe2 | ||
|
|
0d92451c49 | ||
|
|
c8a58dcb69 | ||
|
|
a4dea25d76 | ||
|
|
bfb0235fc6 | ||
|
|
6dcc3d637f | ||
|
|
9bb4d17e31 | ||
|
|
9df09db5fe | ||
|
|
d8e28ec274 | ||
|
|
d3905734c1 | ||
|
|
8fe7f923ec | ||
|
|
933f38390b | ||
|
|
a4e019442f | ||
|
|
d1c4e60636 | ||
|
|
765d0228c0 | ||
|
|
2d2020a20d | ||
|
|
03ab282f67 | ||
|
|
7f94cb1cad | ||
|
|
a2a303bd72 | ||
|
|
d34bf9a14d | ||
|
|
68faba6bde | ||
|
|
5c5b9cfd96 | ||
|
|
9d683f4767 | ||
|
|
f2912bad95 | ||
|
|
c8adfe0131 | ||
|
|
8b7e637e74 | ||
|
|
43cffd7c4a | ||
|
|
f3dad3765e | ||
|
|
70c25141fc | ||
|
|
b1b81a2672 | ||
|
|
46197bf262 | ||
|
|
58ec409e7f | ||
|
|
c2d68599de | ||
|
|
65f00f3af2 | ||
|
|
6544326ffd | ||
|
|
a23c206ccb | ||
|
|
1b152e6bd0 | ||
|
|
d9624d9882 | ||
|
|
178b8471cc | ||
|
|
719d2092a7 | ||
|
|
2f3a4675da | ||
|
|
9513be664b | ||
|
|
64d8b7853a | ||
|
|
4174e57c13 | ||
|
|
88b395599a | ||
|
|
b6d682c92c | ||
|
|
3b02894341 | ||
|
|
f3feb13dfe | ||
|
|
114d8d0aba | ||
|
|
503a843bb3 | ||
|
|
c4d91aff40 | ||
|
|
2ffe5faf6e | ||
|
|
5d2a03aa82 | ||
|
|
f4c83d1221 | ||
|
|
100e91714b | ||
|
|
dafb7b5259 | ||
|
|
92906e217c | ||
|
|
5c7b721879 | ||
|
|
c27dcb0414 | ||
|
|
7fcae1e974 | ||
|
|
cc4659f5c6 | ||
|
|
9fce12d6b4 | ||
|
|
deb2f30b37 | ||
|
|
751a3f0e94 | ||
|
|
fee2a07e08 | ||
|
|
17726abb0a | ||
|
|
01f740ce69 | ||
|
|
b168ccb605 | ||
|
|
04a31a52ae | ||
|
|
6a236fb91e | ||
|
|
619fa1bff8 | ||
|
|
53b37e8d0c | ||
|
|
f8eb8adfbe | ||
|
|
3c2aecb81f | ||
|
|
efca9e11d6 | ||
|
|
49189e0e43 | ||
|
|
ea86c9bafb | ||
|
|
c361b6a85c | ||
|
|
c7ff45045c | ||
|
|
090af35ea1 | ||
|
|
8e668ca633 | ||
|
|
4d9a16f33b | ||
|
|
c9f2a0f694 | ||
|
|
46bac3069e | ||
|
|
d21aa0d85f | ||
|
|
3e87d699eb | ||
|
|
23aa216908 | ||
|
|
e34e681157 | ||
|
|
acd543fe4b | ||
|
|
c2df5939d6 | ||
|
|
b52e74b615 | ||
|
|
ec2bb039ae | ||
|
|
aeb9f5775f | ||
|
|
47a20756b9 | ||
|
|
a7aba1d585 | ||
|
|
c89af0cea4 | ||
|
|
9d3a109946 | ||
|
|
079f1e6fff | ||
|
|
7249b11899 | ||
|
|
4cfb000812 | ||
|
|
5262a41619 | ||
|
|
887590730d | ||
|
|
adb49bdf0b | ||
|
|
7f3aadc157 | ||
|
|
eb1c85f3f5 | ||
|
|
dbdc884c96 | ||
|
|
5166354e34 | ||
|
|
04ea95049a | ||
|
|
ec62e49001 | ||
|
|
6fb21850d8 | ||
|
|
98b50fd942 | ||
|
|
729c962b7c | ||
|
|
ebc15e60a8 | ||
|
|
85f2d94253 | ||
|
|
981a536cb5 | ||
|
|
abf7c333b1 | ||
|
|
b2f547e934 | ||
|
|
0870a25e2f | ||
|
|
1091e577a5 | ||
|
|
151ad16fe6 | ||
|
|
e325258091 | ||
|
|
1139a02b40 | ||
|
|
4b90953226 | ||
|
|
d77fc01580 | ||
|
|
216d84faeb | ||
|
|
0c9d95f183 | ||
|
|
913739d732 | ||
|
|
7ecb5af587 | ||
|
|
87f0e79749 | ||
|
|
c85145d73c | ||
|
|
eea689841b | ||
|
|
e50a59361d | ||
|
|
9862185bda | ||
|
|
fe05d35bbb | ||
|
|
ba602c17d4 | ||
|
|
831bd07b0d | ||
|
|
175c5d9fc3 | ||
|
|
2639e30d9c | ||
|
|
78a0218a91 | ||
|
|
e12bf8daed | ||
|
|
04e2072e2c | ||
|
|
7bafe20a34 | ||
|
|
554d5aeb7c | ||
|
|
3991d2729d | ||
|
|
5bc8a0860a | ||
|
|
ad03fb0815 | ||
|
|
e10d2a68f3 | ||
|
|
213ee8d381 | ||
|
|
52b1a1b835 | ||
|
|
fce2930b85 | ||
|
|
735f589b1c | ||
|
|
69f17da5ca | ||
|
|
5c0ee02251 | ||
|
|
0cb4c7e437 | ||
|
|
842c792868 | ||
|
|
4ba8518462 | ||
|
|
143dfb6246 | ||
|
|
da4238ec68 | ||
|
|
5f9e3edbe1 | ||
|
|
1c68aee577 | ||
|
|
e8d9a4bd49 | ||
|
|
9520e87a38 | ||
|
|
f9f5004909 | ||
|
|
4f7014b2d5 | ||
|
|
01dbfddb64 | ||
|
|
6266a22d86 | ||
|
|
09fe31179a | ||
|
|
78343f4837 | ||
|
|
a5619bc0a3 | ||
|
|
dcb28aad2a | ||
|
|
754cf172f5 | ||
|
|
7890af1659 | ||
|
|
d27d9fe694 | ||
|
|
634a599db6 | ||
|
|
5d0d859a1f | ||
|
|
40c966fcd6 | ||
|
|
d9245598a4 | ||
|
|
03b0f4b16b | ||
|
|
2daa0d057b | ||
|
|
76eca409a3 | ||
|
|
d84b018ba7 | ||
|
|
027c3d1661 | ||
|
|
4965b1ca7b | ||
|
|
9cd7ea9371 | ||
|
|
dc2468b27b | ||
|
|
ea2451d882 | ||
|
|
252617bd70 | ||
|
|
8bd7003ea7 | ||
|
|
280203158c | ||
|
|
d456c3f62e | ||
|
|
f64d17963b | ||
|
|
f12378cf7b | ||
|
|
15ff426be8 | ||
|
|
c4a6837d56 | ||
|
|
e6c28cf509 | ||
|
|
a347a75617 | ||
|
|
7fc2183826 | ||
|
|
b6c3c9891c | ||
|
|
d401b493a4 | ||
|
|
da1a8308b6 | ||
|
|
106d827a21 | ||
|
|
1ebb162a00 | ||
|
|
9d2791d9f8 | ||
|
|
04a00f6564 | ||
|
|
a5dd44c77f | ||
|
|
8f86fd6bae | ||
|
|
d5cd534320 | ||
|
|
a5a6fd3bc2 | ||
|
|
287ddda5df | ||
|
|
7747f1c31d | ||
|
|
8097cbbbe3 | ||
|
|
e4c07875a3 | ||
|
|
026fd4cf35 | ||
|
|
87016f9620 | ||
|
|
88393f0f65 | ||
|
|
f5a9dbb348 | ||
|
|
dbf3bb112c | ||
|
|
f3186bd501 | ||
|
|
3f1a252b91 | ||
|
|
1240d6be76 | ||
|
|
faf7b62f5c | ||
|
|
445011b1e5 | ||
|
|
e630fec63a | ||
|
|
17421e8a63 | ||
|
|
af06b78605 | ||
|
|
3749b09366 | ||
|
|
27cac10d0c | ||
|
|
0590962d36 | ||
|
|
c0c20d49a5 | ||
|
|
cf7cf45003 | ||
|
|
633dd0d9de | ||
|
|
b929320d4c | ||
|
|
7296bedd8e | ||
|
|
e9fbbf4000 | ||
|
|
dd44219aa5 | ||
|
|
8cc1fe007d | ||
|
|
c0b45c9fc4 | ||
|
|
41315f45a9 | ||
|
|
04d28a0eea | ||
|
|
1e9cd3f785 | ||
|
|
f462dd6a4c | ||
|
|
34e75fc595 | ||
|
|
dac513e046 | ||
|
|
2c538d247b | ||
|
|
f3b7f61e54 | ||
|
|
d47ecf19f5 | ||
|
|
da19b344b5 | ||
|
|
eb66407926 | ||
|
|
61c5b8e7d2 | ||
|
|
4b09d57ec2 | ||
|
|
a16f699dfc | ||
|
|
b908a3d70a | ||
|
|
d519c7263e | ||
|
|
5dedc5d8ba | ||
|
|
3ca1784ff4 | ||
|
|
b54d512f3f | ||
|
|
9d8fb19f55 | ||
|
|
1ac4ab7edc | ||
|
|
c9b73ac731 | ||
|
|
e0b87f1e14 | ||
|
|
394743efc8 | ||
|
|
27a86a4756 | ||
|
|
8ee0ef2cde | ||
|
|
43f4682ec4 | ||
|
|
5f081b8f8d | ||
|
|
5023542882 | ||
|
|
88d0731d80 | ||
|
|
06cb8c52d7 | ||
|
|
4d1358b7ed | ||
|
|
0cfa7c1c56 | ||
|
|
76928390a3 | ||
|
|
af1f9cd76d | ||
|
|
2f39f3d146 | ||
|
|
f72f39bc26 | ||
|
|
2659ba6bb4 | ||
|
|
0b8c0c202e | ||
|
|
37c8132632 | ||
|
|
02f2f694e4 | ||
|
|
d175a588f7 | ||
|
|
4bb41f2f66 | ||
|
|
088c46c7be | ||
|
|
4228c40b3d | ||
|
|
d581d29198 | ||
|
|
5a7e854f9e | ||
|
|
6e13823ccc | ||
|
|
5c5f2fdf8f | ||
|
|
96aa33f4ef | ||
|
|
25fd23e703 | ||
|
|
99cf4e5787 | ||
|
|
66a4cb5875 | ||
|
|
da14e2c349 | ||
|
|
4cda61a16a | ||
|
|
002afda503 | ||
|
|
cb7a2e8c6a | ||
|
|
d21ae1dad1 | ||
|
|
4548859509 | ||
|
|
09c6e6427a | ||
|
|
8c34398a15 | ||
|
|
a65b705080 | ||
|
|
7e28c71074 | ||
|
|
726f4a6715 | ||
|
|
a444b43548 | ||
|
|
473bb139d1 | ||
|
|
d084770cea | ||
|
|
619389ed20 | ||
|
|
ad043e009e | ||
|
|
89b9d079b1 | ||
|
|
58a8845047 | ||
|
|
3e6c8e68b4 | ||
|
|
40cd92f701 | ||
|
|
e216addf7c | ||
|
|
9291594330 | ||
|
|
f6010864ea | ||
|
|
13e6a6def5 | ||
|
|
575b373cd5 | ||
|
|
0892065649 | ||
|
|
766ef367fb | ||
|
|
0c7d9958f5 | ||
|
|
425b8f6073 | ||
|
|
63593e45a6 | ||
|
|
a6ee9806de | ||
|
|
a6579f4ceb | ||
|
|
639622a4fe | ||
|
|
e28ec2f5ab | ||
|
|
873753c735 | ||
|
|
ff14951be4 | ||
|
|
b47b445558 | ||
|
|
6e9a2fea89 | ||
|
|
64d2679b3c | ||
|
|
3c3d4dfccb | ||
|
|
0ccffed517 | ||
|
|
20c18c1fc0 | ||
|
|
a21c0097c2 | ||
|
|
404d9ef2af | ||
|
|
503fa1cbce | ||
|
|
63f7991d0f | ||
|
|
ae301c1f39 | ||
|
|
8e911fb719 | ||
|
|
a0d84e44c5 | ||
|
|
81fb7ebb31 | ||
|
|
3751211590 | ||
|
|
6b4cba09be | ||
|
|
6a7d184b7b | ||
|
|
bde5cfc8bb | ||
|
|
777c7a952b | ||
|
|
c79bdd9029 | ||
|
|
6a154efe65 | ||
|
|
627f3ef36b | ||
|
|
3d28836f2c | ||
|
|
c28bd04958 | ||
|
|
a6fb3acb42 | ||
|
|
8a8b3bf96e | ||
|
|
f7624e5f05 | ||
|
|
55edfd2e4a | ||
|
|
91878dd52c | ||
|
|
c132358ec9 | ||
|
|
e426f8ac47 | ||
|
|
740dca8afc | ||
|
|
6ae06d319c | ||
|
|
30bc2b2f9c | ||
|
|
7dc8e9d657 | ||
|
|
1ab2fa5bf0 | ||
|
|
779a1077bf | ||
|
|
815bf7a53a | ||
|
|
91a382dfb5 | ||
|
|
2d8d4cd57d | ||
|
|
0c5fe2b3bf | ||
|
|
56c487347f | ||
|
|
0a572fce1b | ||
|
|
e898e29910 | ||
|
|
843f076225 | ||
|
|
fdb732c8c2 | ||
|
|
33896a4e8f | ||
|
|
51e016a0d6 | ||
|
|
8737b06559 | ||
|
|
15ab285174 | ||
|
|
3a4782cc64 | ||
|
|
0256a6d47b | ||
|
|
8b0e846e0e | ||
|
|
41acb5d3cc | ||
|
|
a2f9b8ea9b | ||
|
|
6237a1c505 | ||
|
|
7c08db0b0f | ||
|
|
df1270ac49 | ||
|
|
46edc053d4 | ||
|
|
908046c31a | ||
|
|
8380c79875 | ||
|
|
8a4879a96a | ||
|
|
cdf1a77f08 | ||
|
|
3f9857ccbd | ||
|
|
1d65a31420 | ||
|
|
dafdf0838a | ||
|
|
f9cf135f68 | ||
|
|
a1878e54c9 | ||
|
|
95e5d899a9 | ||
|
|
ee8321df67 | ||
|
|
4918213e68 | ||
|
|
ed7f349fc6 | ||
|
|
2888f22a24 | ||
|
|
fcb217f681 | ||
|
|
9ffa9fe1a8 | ||
|
|
905020c507 | ||
|
|
02f5500641 | ||
|
|
465af55d6e | ||
|
|
80a3bb85aa | ||
|
|
ea26bd3003 | ||
|
|
508a511d2a | ||
|
|
2d80cb71db | ||
|
|
e31693bbee | ||
|
|
fb561f10a7 | ||
|
|
0c57d53e03 | ||
|
|
3b1b9f589b | ||
|
|
ac8d39acf4 | ||
|
|
664e795217 | ||
|
|
82bf323f63 | ||
|
|
547ac85113 | ||
|
|
46e0e4430d | ||
|
|
609a31ea46 | ||
|
|
3e99eb10d1 | ||
|
|
b9d935af0c | ||
|
|
31d3e82aa8 | ||
|
|
dfdc17197b | ||
|
|
62b965b4b7 | ||
|
|
ba221abea5 | ||
|
|
aa026c7134 | ||
|
|
496cfff97a | ||
|
|
ccaa7f5f3e | ||
|
|
f492d21c70 | ||
|
|
1983541f8c | ||
|
|
e4cb3672b9 | ||
|
|
a06a78a9c1 | ||
|
|
2ba91ad837 | ||
|
|
66eb3ea723 | ||
|
|
d30e992b20 | ||
|
|
c6a342563f | ||
|
|
fd584eda05 | ||
|
|
4301506590 | ||
|
|
88f7c3a970 | ||
|
|
f21a9f0cc2 | ||
|
|
2233c2e618 | ||
|
|
6264419bd4 | ||
|
|
b62effb8fa | ||
|
|
c8a07dd612 | ||
|
|
02c99e4b25 | ||
|
|
1242a16265 | ||
|
|
a89fd17b8e | ||
|
|
3d5e7bd6f1 | ||
|
|
cefcb89487 | ||
|
|
005272bde9 | ||
|
|
491e60e4f2 | ||
|
|
e5f72fd302 | ||
|
|
5f21b100b8 | ||
|
|
1a4c7629c5 | ||
|
|
d21734ee47 | ||
|
|
e140e2c367 | ||
|
|
7ced1f5b54 | ||
|
|
54b028ee3e | ||
|
|
703336e1ea | ||
|
|
9e936252ae | ||
|
|
fc71a0f081 | ||
|
|
86c72fa15d | ||
|
|
6ba5af1e9e | ||
|
|
33a4f59cfb | ||
|
|
39134f0d9b | ||
|
|
30e7440851 | ||
|
|
be1a7bac7c | ||
|
|
fb52a93a28 | ||
|
|
efdf1ff755 | ||
|
|
2c00dad211 | ||
|
|
c23786c73e | ||
|
|
7f1b07b76f | ||
|
|
2f35ad6e65 | ||
|
|
159c7d34c7 | ||
|
|
fe8e990ed7 | ||
|
|
1fb9b18cb6 | ||
|
|
5c2586127b | ||
|
|
661b266b45 | ||
|
|
98af044196 | ||
|
|
8006c336f5 | ||
|
|
bdf07d2ce8 | ||
|
|
efcf858e60 | ||
|
|
93d9c16672 | ||
|
|
7aaf261f5a | ||
|
|
5eb4450012 | ||
|
|
49c8cdd8f6 | ||
|
|
c860b96a86 | ||
|
|
69403bda80 | ||
|
|
f5456b66ff | ||
|
|
28b4c413cb | ||
|
|
5b97250bb1 | ||
|
|
5d26c29eaa | ||
|
|
613944eccd | ||
|
|
8d0e8fc021 | ||
|
|
41ee70d414 | ||
|
|
3691e8ddd7 | ||
|
|
be54e94045 | ||
|
|
366ca72342 | ||
|
|
fc4e392ac1 | ||
|
|
507be35d3a | ||
|
|
5c0d62cd0e | ||
|
|
7b7c4568b2 | ||
|
|
f38091d96b | ||
|
|
7c78e1fbf5 | ||
|
|
72f0a48fac | ||
|
|
193f872ebe | ||
|
|
c3f96cf0e8 | ||
|
|
df09109d81 | ||
|
|
662cb9e2a3 | ||
|
|
daa54a3798 | ||
|
|
f035a1a0e0 | ||
|
|
2688bf3333 | ||
|
|
4095a58eb9 | ||
|
|
c1b1ebe97e | ||
|
|
a47b97d443 | ||
|
|
df5d5583eb | ||
|
|
79e764d5ec | ||
|
|
2fcb04e5b2 | ||
|
|
da1e264687 | ||
|
|
03975287d2 | ||
|
|
50a61b38f7 | ||
|
|
1fe072471e | ||
|
|
93e840295e | ||
|
|
6ab7d640f0 | ||
|
|
255cb8edb1 | ||
|
|
367fb32493 | ||
|
|
ef6d1fd6ce | ||
|
|
3e8ab0645b | ||
|
|
135727c9f7 | ||
|
|
c58448817b | ||
|
|
8ae0f1a9dc | ||
|
|
ba9c1d98f6 | ||
|
|
4db6e70f97 | ||
|
|
9d1f789937 | ||
|
|
11c2933e23 | ||
|
|
fbd4951ea7 | ||
|
|
744749f2f3 | ||
|
|
db1c65970b | ||
|
|
2d8ed4c77f | ||
|
|
9ba970b83d | ||
|
|
954d262d67 | ||
|
|
1b6090fe13 | ||
|
|
f8234b07f8 | ||
|
|
25318f696d | ||
|
|
1ab3d12bc7 | ||
|
|
d66e0a0b45 | ||
|
|
d46ceead04 | ||
|
|
1cc9480e56 | ||
|
|
c022b541f1 | ||
|
|
96588466d4 | ||
|
|
dbde12f8bf | ||
|
|
660f0fec16 | ||
|
|
fd9401cc62 | ||
|
|
1edb9f7525 | ||
|
|
15cfb9d1d9 | ||
|
|
da6744da2d | ||
|
|
bd5aa66a5f | ||
|
|
7c30fe7738 | ||
|
|
8ce599df38 | ||
|
|
75fe13f1df | ||
|
|
8ad6ded3ec | ||
|
|
38d22c47f5 | ||
|
|
956b6b89b6 | ||
|
|
f27c2e6813 | ||
|
|
d35c9f880a | ||
|
|
d62aacf301 | ||
|
|
716ea64212 | ||
|
|
c9d6b13f6a | ||
|
|
b437e68026 | ||
|
|
1dfc38d078 | ||
|
|
387184b052 | ||
|
|
beec47d7ad | ||
|
|
6b554337ff | ||
|
|
08f95264d6 | ||
|
|
2802920e93 | ||
|
|
b04c47c15f | ||
|
|
56d96383e2 | ||
|
|
f2cbf846e7 | ||
|
|
0088e94293 | ||
|
|
a03a077e3d | ||
|
|
85d2b9e32a | ||
|
|
aa6471b3cc | ||
|
|
b462a85c43 | ||
|
|
9d8f30f955 | ||
|
|
420db828e9 | ||
|
|
fd8def705d | ||
|
|
39c4fa1bf0 | ||
|
|
32b964e9b7 | ||
|
|
2980515c85 | ||
|
|
36bee16781 | ||
|
|
4f5d3e00db | ||
|
|
6fb044f351 | ||
|
|
b403845d03 | ||
|
|
9d0e00acce | ||
|
|
d5afe8f4f2 | ||
|
|
3eb5ca3653 | ||
|
|
db07aa42ea | ||
|
|
081f51b9eb | ||
|
|
38d9c16aba | ||
|
|
392a426ddf | ||
|
|
d77b2751a6 | ||
|
|
3b28308291 | ||
|
|
574ecb5ad4 | ||
|
|
7a57a780d8 | ||
|
|
cf9842433e | ||
|
|
c6d089d701 | ||
|
|
ec37e2d2b5 | ||
|
|
e68b648a33 | ||
|
|
56179d8a54 | ||
|
|
47d232f694 | ||
|
|
fc26fb2149 | ||
|
|
92d828b04e | ||
|
|
6f9dc913d4 | ||
|
|
9ea0f7255f | ||
|
|
938628a05f | ||
|
|
a765dfc52e | ||
|
|
d58f0646cf | ||
|
|
792b963bda | ||
|
|
9a8bf3b881 | ||
|
|
1e02081cd2 | ||
|
|
3f24a87034 | ||
|
|
dd9f8021ff | ||
|
|
74978e84f8 | ||
|
|
6ef775390f | ||
|
|
1313c1b157 | ||
|
|
6d6c400b25 | ||
|
|
46bd1b9b18 | ||
|
|
dcea8bed6d | ||
|
|
e8b35f4623 | ||
|
|
ae263bb4db | ||
|
|
754ca7c8f2 | ||
|
|
804c02701a | ||
|
|
fbb4077812 | ||
|
|
961e1b89a2 | ||
|
|
cc449228f3 | ||
|
|
3984c47867 | ||
|
|
cfb80266c2 | ||
|
|
cc5198205d | ||
|
|
a27b44a3c2 | ||
|
|
937d064860 | ||
|
|
9c814d1584 | ||
|
|
415563dc6e | ||
|
|
bed653737b | ||
|
|
b53be942d4 | ||
|
|
1d26161d23 | ||
|
|
39afb58472 | ||
|
|
22cd18571b | ||
|
|
5753eb6682 | ||
|
|
e26a6ea71b | ||
|
|
6fbe765636 | ||
|
|
22b2ccd83d | ||
|
|
fc5aadd6dd | ||
|
|
5a9f0c24b4 | ||
|
|
0c12c072fa | ||
|
|
f7284caefd | ||
|
|
4e7f317679 | ||
|
|
3534d6fddc | ||
|
|
0b79e9cc9e | ||
|
|
3ce97ccaa8 | ||
|
|
407a340b2b | ||
|
|
532346ecfb | ||
|
|
cd25f5b6cb | ||
|
|
5a0ac6c2ac | ||
|
|
f0da08bbb1 | ||
|
|
fb3f6e2238 | ||
|
|
8b977252f3 | ||
|
|
9582f9b6e5 | ||
|
|
3f15a41006 | ||
|
|
e1c4cfd7eb | ||
|
|
a326f40bbf | ||
|
|
09c2fff8fc | ||
|
|
a4c1eb12b4 | ||
|
|
4e80758970 | ||
|
|
40ae408b3b | ||
|
|
84d8d4f622 | ||
|
|
5c04840312 | ||
|
|
bd28d5c48a | ||
|
|
f176d890c0 | ||
|
|
3b158a96b7 | ||
|
|
dcd0b2bf66 | ||
|
|
edd1c89ea1 | ||
|
|
32d858dce3 | ||
|
|
1e4bb53a34 | ||
|
|
f5f94097bf | ||
|
|
77a3218db3 | ||
|
|
c3ed40905a | ||
|
|
268ac0bde8 | ||
|
|
88e3f6af47 | ||
|
|
21a85832b3 | ||
|
|
254efdd122 | ||
|
|
62ac6e74d9 | ||
|
|
2c2268b39d | ||
|
|
41aa14eaf0 | ||
|
|
1f50f4658d | ||
|
|
d31481fd8b | ||
|
|
80ab28bc3a | ||
|
|
48b7ebe1c0 | ||
|
|
c59cf18337 | ||
|
|
b7b1260540 | ||
|
|
897c5634b0 | ||
|
|
78e3a05e14 | ||
|
|
1bb6367b79 | ||
|
|
f5512ef21b | ||
|
|
a0fa9937ba | ||
|
|
ef2345ea85 | ||
|
|
dd8fd09470 | ||
|
|
a779e2aeca | ||
|
|
aaaf25d578 | ||
|
|
2e65c852c3 | ||
|
|
1b206fe94c | ||
|
|
8b93c97a09 | ||
|
|
6247b55fc3 | ||
|
|
12d9d49fa4 | ||
|
|
aef3f097e4 | ||
|
|
1a415d5fa7 | ||
|
|
e4d46aee97 | ||
|
|
d3a88e59b9 | ||
|
|
44e3811520 | ||
|
|
e0574d2861 | ||
|
|
d4f66da793 | ||
|
|
c7e3289396 | ||
|
|
9cb7aeb32d | ||
|
|
4537370a54 | ||
|
|
9d2bc1142f | ||
|
|
c44caa5c96 | ||
|
|
77dc587686 | ||
|
|
ce768241da | ||
|
|
5f9e50175f | ||
|
|
34ec457a47 | ||
|
|
dd1651f2d1 | ||
|
|
c3c529b001 | ||
|
|
aa569304bc | ||
|
|
3d57fd3227 | ||
|
|
c18a6a6577 | ||
|
|
9720301d01 | ||
|
|
5ea75499fe | ||
|
|
b45bbb77ef | ||
|
|
d29b86baf0 | ||
|
|
1730aff2b9 | ||
|
|
1b03484b04 | ||
|
|
f3749753ad | ||
|
|
ae80e81b75 | ||
|
|
8581e980e9 | ||
|
|
7234aa601f | ||
|
|
a8a2b7d7bb | ||
|
|
9a901c7add | ||
|
|
1a0b809ec5 | ||
|
|
802c7254b4 | ||
|
|
a99e3be44e | ||
|
|
d7cf203547 | ||
|
|
3364ce53e6 | ||
|
|
2a2bcc8984 | ||
|
|
6b8339e9b4 | ||
|
|
32613ba544 | ||
|
|
8e9d91fedc | ||
|
|
41bb1c8535 | ||
|
|
cb5e34f58a | ||
|
|
f7c3cf5d78 | ||
|
|
fdc0832506 | ||
|
|
4413f816e6 | ||
|
|
02224bb5fe | ||
|
|
c37c4d8c6d | ||
|
|
00ccbdaae9 | ||
|
|
777189d7bd | ||
|
|
c5dfc432d9 | ||
|
|
144f7d6813 | ||
|
|
6a19b7c1b0 | ||
|
|
741b204fc7 | ||
|
|
aa394754b6 | ||
|
|
492f8f58bb | ||
|
|
b9e1a5708d | ||
|
|
576a9e4654 | ||
|
|
a7bbbad185 | ||
|
|
12ec05aed2 | ||
|
|
2bde6612d1 | ||
|
|
8460aa7d6a | ||
|
|
64efbe703a | ||
|
|
d272b0cbf3 | ||
|
|
d52c95a15f | ||
|
|
2b8414a453 | ||
|
|
bc3d019ecf | ||
|
|
bc9d44e5d7 | ||
|
|
d1a8177778 | ||
|
|
8d9aa6388d | ||
|
|
6d317d300c | ||
|
|
f647b3f0e0 | ||
|
|
059b75cdbb | ||
|
|
35fa42ca56 | ||
|
|
4b450bab11 | ||
|
|
4394a05243 | ||
|
|
fd11ad95f6 | ||
|
|
07b57de03e | ||
|
|
a2a5f5df3f | ||
|
|
148f2210ca | ||
|
|
93f072d1fc | ||
|
|
1104a385fa | ||
|
|
f5b2febf13 | ||
|
|
1b03e55cc3 | ||
|
|
4a3f020cd9 | ||
|
|
2acd23b14c | ||
|
|
339a2f4a6f | ||
|
|
0fef796757 | ||
|
|
f0e5fb04fc | ||
|
|
156be19c2d | ||
|
|
956c486345 | ||
|
|
062235f3a7 | ||
|
|
945b357ed8 | ||
|
|
c95ec8e99f | ||
|
|
61eb631e59 | ||
|
|
1ec349f7c9 | ||
|
|
c4bc9616af | ||
|
|
de050991d4 | ||
|
|
6521951e82 | ||
|
|
e818ddf152 | ||
|
|
b6499e5248 | ||
|
|
3cb809b444 | ||
|
|
12b7e56261 | ||
|
|
0007343175 | ||
|
|
8e49c433e8 | ||
|
|
16b2169965 | ||
|
|
6a7fa52879 | ||
|
|
4afc7438a6 | ||
|
|
b80ce2a32f | ||
|
|
386f88c9b2 | ||
|
|
563f09d9de | ||
|
|
423bdc6af1 | ||
|
|
0d0413e027 | ||
|
|
d4942858ba | ||
|
|
81bb7009ea | ||
|
|
310dc03e2a | ||
|
|
362e0bf1ec | ||
|
|
3bb330fcc4 | ||
|
|
18f5d0241e | ||
|
|
97f5b3f74d | ||
|
|
f794acdb5b | ||
|
|
f20909ba04 | ||
|
|
f495ce13b1 | ||
|
|
cac1d67b75 | ||
|
|
548111e2ff | ||
|
|
f5c0706c2e | ||
|
|
e6ef11575b | ||
|
|
7563e04743 | ||
|
|
33e51cc38b | ||
|
|
ef09596648 | ||
|
|
93db5a091f | ||
|
|
978ab2cdaa | ||
|
|
cb09ae5cc0 | ||
|
|
6bd275669d | ||
|
|
51485396c7 | ||
|
|
73e1f238cf | ||
|
|
37487c243e | ||
|
|
c05627a49e | ||
|
|
4ba6791043 | ||
|
|
54bec54765 | ||
|
|
5c8c4e0aad | ||
|
|
5cca26a565 | ||
|
|
a347f02545 | ||
|
|
a45cc0a048 | ||
|
|
52ec5cf8e7 | ||
|
|
3e4d0954b5 | ||
|
|
e76bcce3bb | ||
|
|
5a06b34b1d | ||
|
|
79dcb0359f | ||
|
|
5dc540b894 | ||
|
|
0bbb2786f8 | ||
|
|
c1fb1ba94c | ||
|
|
974331b825 | ||
|
|
f74567fbab | ||
|
|
c77c01e437 | ||
|
|
2b63680f55 | ||
|
|
114d32a28f | ||
|
|
cba5bca842 | ||
|
|
e99351d4db | ||
|
|
2524829023 | ||
|
|
a7bcc4c00d | ||
|
|
58ca6ec6aa | ||
|
|
bb2599eb30 | ||
|
|
2a95273b79 | ||
|
|
d393bc45a4 | ||
|
|
3a14eb9363 | ||
|
|
3586e53fd9 | ||
|
|
db75734774 | ||
|
|
137cbbd141 | ||
|
|
24f142df1d | ||
|
|
b16ee1a3a6 | ||
|
|
1b8580f12b | ||
|
|
aefdf15a45 | ||
|
|
99af697cd7 | ||
|
|
30ff15ec0b | ||
|
|
13ce39058d | ||
|
|
4ed7f5eb67 | ||
|
|
2df0c02dfd | ||
|
|
158ca9746c | ||
|
|
c58a2e8e46 | ||
|
|
41117578c5 | ||
|
|
a8ea6c2c66 | ||
|
|
7652f4272c | ||
|
|
04f1f5f94b | ||
|
|
f78e17ce02 | ||
|
|
c1518dc728 | ||
|
|
899f7da032 | ||
|
|
eec4334a78 | ||
|
|
2ad9eafeaf | ||
|
|
9dc9103d6c | ||
|
|
ed0e880913 | ||
|
|
a70e74e478 | ||
|
|
4d34abeeef | ||
|
|
621810c409 | ||
|
|
62d2c7efc3 | ||
|
|
c410dce07a | ||
|
|
d2709c681f | ||
|
|
0e6625f719 | ||
|
|
cc6a408d5d | ||
|
|
d10e3d8498 | ||
|
|
c89bab0ff1 | ||
|
|
6bc8963e8e | ||
|
|
34db64234f | ||
|
|
b43f0a4536 | ||
|
|
45ba07c214 | ||
|
|
4d72bfd495 | ||
|
|
925eaa1db0 | ||
|
|
69957dac3d | ||
|
|
b1c2f4a8d6 | ||
|
|
aba6c222cb | ||
|
|
b8da674816 | ||
|
|
f06dba5007 | ||
|
|
8dfcee9039 | ||
|
|
be355bf7b4 | ||
|
|
a1eedd496c | ||
|
|
9f7bad82e5 | ||
|
|
4e17d11da6 | ||
|
|
f38d5a9c06 | ||
|
|
696efcdf20 | ||
|
|
728c3a72ad | ||
|
|
c8c839a569 | ||
|
|
6827dacb4b | ||
|
|
246271fd9d | ||
|
|
4305ebc5b0 | ||
|
|
39bff8bfbc | ||
|
|
fc6a0c1006 | ||
|
|
d6dc9af5ff | ||
|
|
e62045eb7f | ||
|
|
bda3e53511 | ||
|
|
6295d0fc6c | ||
|
|
3271e65d56 | ||
|
|
f8225141dc | ||
|
|
8e9e583b79 | ||
|
|
bbadfce65a | ||
|
|
9e191d681f | ||
|
|
a3236997be | ||
|
|
be961cd60e | ||
|
|
4efd363134 | ||
|
|
7bdee9ce05 | ||
|
|
7dc9a9bf2f | ||
|
|
87245c7b33 | ||
|
|
db497a2ecf | ||
|
|
9d250bec05 | ||
|
|
1371f69810 | ||
|
|
82ee6aed7f | ||
|
|
d730fd0a5f | ||
|
|
cb4e4bb2db | ||
|
|
6fd3c0fbb4 | ||
|
|
13170fbb3c | ||
|
|
7e7295f259 | ||
|
|
d30cb2d4d9 | ||
|
|
f4100ac507 | ||
|
|
1a0415a100 | ||
|
|
1cc5bb5ec4 | ||
|
|
43661fff76 | ||
|
|
509bdceac2 | ||
|
|
835cb8caa5 | ||
|
|
ed32adc9bb | ||
|
|
44f8fecf84 | ||
|
|
0cee0e08e9 | ||
|
|
73cd595b73 | ||
|
|
c50b23e9ca | ||
|
|
d1a763bc21 | ||
|
|
de8ca5c1b5 | ||
|
|
14372ba7f3 | ||
|
|
ca861d8ff9 | ||
|
|
37a316e66c | ||
|
|
22c8be6645 | ||
|
|
8cce1b4f6c | ||
|
|
040bb9354a | ||
|
|
fb0f9cd1a1 | ||
|
|
3816db03f5 | ||
|
|
d802416dae | ||
|
|
2cd41773ba | ||
|
|
8ce5b087bc | ||
|
|
03fd74da4e | ||
|
|
ed178b702f | ||
|
|
b4567acc6b | ||
|
|
953f44bcc5 | ||
|
|
be4f259b71 | ||
|
|
e9531487d9 | ||
|
|
d715110961 | ||
|
|
3c31908e08 | ||
|
|
09d34a2129 | ||
|
|
e5e3f3c299 | ||
|
|
bcd1c793ea | ||
|
|
185152a74a | ||
|
|
ded99b9a09 | ||
|
|
55a04bbf2b | ||
|
|
7d5d7453c2 | ||
|
|
952f58153f | ||
|
|
d0d981435a | ||
|
|
fd83990d8a | ||
|
|
ad0c4caf1c | ||
|
|
126f0ed95d | ||
|
|
a2b499c460 | ||
|
|
5a5b3bf291 | ||
|
|
a4a6e7cf53 | ||
|
|
658db43ad3 | ||
|
|
cfecd063c9 | ||
|
|
a8fe063726 | ||
|
|
dcf637edd8 | ||
|
|
c0345f1088 | ||
|
|
0cbdfca141 | ||
|
|
cb1f696e22 | ||
|
|
cf7355e4d0 | ||
|
|
dab8c5ea56 | ||
|
|
4b889eac32 | ||
|
|
86835db8f2 | ||
|
|
bc317bf3f2 | ||
|
|
5c036eadf9 | ||
|
|
263474705b | ||
|
|
1e781ec78f | ||
|
|
ef1eabf033 | ||
|
|
2fa40555e6 | ||
|
|
2aae272d86 | ||
|
|
7db0900cc8 | ||
|
|
c1e2d66abd | ||
|
|
78b1ba56ce | ||
|
|
5385205b8e | ||
|
|
5c8bbb511e | ||
|
|
fde01f0b71 | ||
|
|
1c04ae3216 | ||
|
|
35b4907e52 | ||
|
|
2431b8e021 | ||
|
|
bd5c04b923 | ||
|
|
6e00b6e214 | ||
|
|
c9b079cbb5 | ||
|
|
9163372786 | ||
|
|
dabcd29fb6 | ||
|
|
d36e435801 | ||
|
|
a18fb27d0f | ||
|
|
2dbe511519 | ||
|
|
e031a5d28b | ||
|
|
f9b32474b0 | ||
|
|
53d268a7ef | ||
|
|
70da558802 | ||
|
|
11215374ff | ||
|
|
cb45a38838 | ||
|
|
bb4d430ebf | ||
|
|
c17f84ca23 | ||
|
|
0ae1f9ffc7 | ||
|
|
d15bcd9845 | ||
|
|
f79a11d993 | ||
|
|
9391d9d11b | ||
|
|
aee90a3116 | ||
|
|
ffac5279a7 | ||
|
|
318283fc70 | ||
|
|
3582ba0c77 | ||
|
|
985d0c786c | ||
|
|
9c8c282e08 | ||
|
|
93dc7a51c0 | ||
|
|
393577ee78 | ||
|
|
eef593a34e | ||
|
|
3790e924e9 | ||
|
|
2f165aade8 | ||
|
|
2c502aec45 | ||
|
|
6bbf246752 | ||
|
|
0b3e8e29cf | ||
|
|
d509ab7779 | ||
|
|
502a9aefdc | ||
|
|
ccba18aa8f | ||
|
|
586ebcd5be | ||
|
|
317b9a9c87 | ||
|
|
67f10ea7eb | ||
|
|
793edd3a2b | ||
|
|
5dabaf2fe9 | ||
|
|
1d9b25f771 | ||
|
|
74cd20116a | ||
|
|
867695eb4f | ||
|
|
9e3592ebf3 | ||
|
|
f8ec14d625 | ||
|
|
1f21e50663 | ||
|
|
7403545a48 | ||
|
|
1d95b317ba | ||
|
|
f7568b69d4 | ||
|
|
8d7649eaec | ||
|
|
10b1c7e8a3 | ||
|
|
b1c936164c | ||
|
|
74ad6abfc2 | ||
|
|
9786b2d096 | ||
|
|
ea02380c15 | ||
|
|
048d5dc914 | ||
|
|
aa6b63373e | ||
|
|
0b219bf8cb | ||
|
|
109b29aeb5 | ||
|
|
bc069b99ec | ||
|
|
16fa186b63 | ||
|
|
e70c0d3e2d | ||
|
|
8d47afb195 | ||
|
|
a01469fb08 | ||
|
|
929636ebd0 | ||
|
|
681ef1efec | ||
|
|
4f14f122bd | ||
|
|
67e9ed5d60 | ||
|
|
5c25497e16 | ||
|
|
626cbea9cf | ||
|
|
aa7ed319e9 | ||
|
|
580bd67278 | ||
|
|
9e44978aed | ||
|
|
64b5ec0b11 | ||
|
|
48f9ea3d9e | ||
|
|
798a0ca240 | ||
|
|
5ee6cbd4c8 | ||
|
|
3b219a6a9a | ||
|
|
536ff6f561 | ||
|
|
013b6aa44a | ||
|
|
0e7d3a7558 | ||
|
|
8bd4895538 | ||
|
|
465c77ddcf | ||
|
|
6ef2d4a4cc | ||
|
|
7ae7f046c2 | ||
|
|
f4051e810e | ||
|
|
e6c239d0f3 | ||
|
|
35a7a3c1ac | ||
|
|
a0991c8261 | ||
|
|
9520880568 | ||
|
|
719d793e22 | ||
|
|
c7fb65c78e | ||
|
|
dd74ea99a7 | ||
|
|
c6fe6eb7ab | ||
|
|
2cbee05c8a | ||
|
|
308b1d6039 | ||
|
|
4cd832c05a | ||
|
|
2af4925d95 | ||
|
|
5e52f0e2a8 | ||
|
|
a46a68d616 | ||
|
|
203d7c4b43 | ||
|
|
24703ffa57 | ||
|
|
5cd913a527 | ||
|
|
91844e48e9 | ||
|
|
6c2f3d1d17 | ||
|
|
9c222c7e5c | ||
|
|
2249da7976 | ||
|
|
a55288bda0 | ||
|
|
e645edbda7 | ||
|
|
aee7cd73c4 | ||
|
|
380a71dd21 | ||
|
|
23dd5cc160 | ||
|
|
847cb5b972 | ||
|
|
cc64091f9c | ||
|
|
b68eb44ca5 | ||
|
|
403540e8a1 | ||
|
|
c39497222f | ||
|
|
9e56e668e0 | ||
|
|
41ae93dba0 | ||
|
|
6c527c1bb4 | ||
|
|
90c7876341 | ||
|
|
10b77a8305 | ||
|
|
1902f4773b | ||
|
|
4b95be6a95 | ||
|
|
61323926e0 | ||
|
|
e30d18febe | ||
|
|
9f2f80ae4f | ||
|
|
bd6e0ceb81 | ||
|
|
673d7038d8 | ||
|
|
3a6231dcc1 | ||
|
|
9d54e46ce7 | ||
|
|
63e6d11892 | ||
|
|
0509bb9bb4 | ||
|
|
5c78908a5f | ||
|
|
5a2269c6c6 | ||
|
|
a7e9655e32 | ||
|
|
1ec82d2ddd | ||
|
|
98eaef621a | ||
|
|
74a6ca58ef | ||
|
|
747faa43ae | ||
|
|
32a53f7412 | ||
|
|
9c94e9813c | ||
|
|
19749bb37f | ||
|
|
5ab4c484a5 | ||
|
|
e602199bfb | ||
|
|
62e1c08dd6 | ||
|
|
37e2b60cd3 | ||
|
|
f299078585 | ||
|
|
bf0a0d5bad | ||
|
|
365cda386c | ||
|
|
5de5b7a66a | ||
|
|
d6de0a64ed | ||
|
|
26f8441f73 | ||
|
|
51bca08386 | ||
|
|
1579aad362 | ||
|
|
a2a580954a | ||
|
|
944c28f3b3 | ||
|
|
c13a597fe0 | ||
|
|
86e0ae0b93 | ||
|
|
f71ffed3c7 | ||
|
|
cae96d9023 | ||
|
|
676b649731 | ||
|
|
634ce34e00 | ||
|
|
6c3096f71f | ||
|
|
991ed2da72 | ||
|
|
907f02cd28 | ||
|
|
9ba78ce04a | ||
|
|
14a3d9f7fb | ||
|
|
f73a01295c | ||
|
|
ccfa3d3be1 | ||
|
|
727e4c0b37 | ||
|
|
6ce470b9b2 | ||
|
|
4b7e230531 | ||
|
|
aefe0d3ddd | ||
|
|
bbb23b515f | ||
|
|
976166a04a | ||
|
|
b20129c37e | ||
|
|
4fe1360b2c | ||
|
|
f0113c5ff5 | ||
|
|
a259016436 | ||
|
|
9a79c71e88 | ||
|
|
e62d22a50e | ||
|
|
402f2d939c | ||
|
|
374aee75d8 | ||
|
|
88ce65266e | ||
|
|
e0a6a623d2 | ||
|
|
d0310bd745 | ||
|
|
35c665156b | ||
|
|
3754a76f10 | ||
|
|
db2e6e5b95 | ||
|
|
d5f95db68d | ||
|
|
d96726c392 | ||
|
|
d1c54f55e6 | ||
|
|
176a73f4e9 | ||
|
|
54a9592e19 | ||
|
|
b2aa29e221 | ||
|
|
0eba0f8dd3 | ||
|
|
d8d0afff0b | ||
|
|
b799e8c0a6 | ||
|
|
583255e1a8 | ||
|
|
c68ccbf263 | ||
|
|
d658ec8de2 | ||
|
|
8727661c1c | ||
|
|
731e8bfe2b | ||
|
|
f88d9c06f5 | ||
|
|
248a0c1b6c | ||
|
|
434f1edb25 | ||
|
|
2a550e6466 | ||
|
|
4f4132943d | ||
|
|
62760d5b2d | ||
|
|
34aa501637 | ||
|
|
6330379f77 | ||
|
|
30c64baa7f | ||
|
|
cdbed0c615 | ||
|
|
f6e7034172 | ||
|
|
cd9c5b9c5d | ||
|
|
7833e21b01 | ||
|
|
12ba2b222f | ||
|
|
8583ca73e4 | ||
|
|
b588ae4e0e | ||
|
|
28abc15c00 | ||
|
|
4d663680d8 | ||
|
|
6505f8e94a | ||
|
|
ff3c20dda2 | ||
|
|
c698dee071 | ||
|
|
8891a41793 | ||
|
|
026f61f5aa | ||
|
|
af063dc2f2 | ||
|
|
3075f3cfe4 | ||
|
|
419f2dca15 | ||
|
|
5803f69605 | ||
|
|
bfd1cc99a0 | ||
|
|
a6a2cea414 | ||
|
|
10e857cebe | ||
|
|
ed76b54511 | ||
|
|
9659d056b7 | ||
|
|
b79b0952b4 | ||
|
|
fa44dc7d16 | ||
|
|
b0dc841e00 | ||
|
|
c72ae375c8 | ||
|
|
0fd6ae5330 | ||
|
|
c8792ccfa6 | ||
|
|
b689972e67 | ||
|
|
ae6d7bbfa3 | ||
|
|
2591bc90ac | ||
|
|
61fe000daa | ||
|
|
c864ebeabb | ||
|
|
24c530611f | ||
|
|
d44d00f0f4 | ||
|
|
dc3082e66d | ||
|
|
c61573a8b7 | ||
|
|
3b95560d32 | ||
|
|
990f68375e | ||
|
|
1290c98c9b | ||
|
|
08599afdd4 | ||
|
|
bb040918e3 | ||
|
|
6fac33ea7d | ||
|
|
e16c16b3ef | ||
|
|
1eeeead78f | ||
|
|
046a1a7af8 | ||
|
|
db89648f62 | ||
|
|
9c839cdc70 | ||
|
|
938b1ff06a | ||
|
|
ecd838bf01 | ||
|
|
855a31ff25 | ||
|
|
7464add904 | ||
|
|
7514607129 | ||
|
|
12a1d7b38d | ||
|
|
d6fe008b2c | ||
|
|
d920da6624 | ||
|
|
e2c86cef47 | ||
|
|
8e270dc246 | ||
|
|
22b6a25408 | ||
|
|
cde695d903 | ||
|
|
e98eb57e3e | ||
|
|
11d997c42b | ||
|
|
eab1ab0fac | ||
|
|
660b801775 | ||
|
|
eb80503bcc | ||
|
|
14ceb10555 | ||
|
|
d9a4688e98 | ||
|
|
092586c931 | ||
|
|
31478e9fb4 | ||
|
|
cc7630c236 | ||
|
|
c29810b2f6 | ||
|
|
c1240d3f2c | ||
|
|
654f1dd055 | ||
|
|
53c609c278 | ||
|
|
a2fcbb7e65 | ||
|
|
db14dc973a | ||
|
|
c29aaa047d | ||
|
|
49064ed56d | ||
|
|
72bc46d339 | ||
|
|
40b895d16b | ||
|
|
33cad9a824 | ||
|
|
e96d9e0ea1 | ||
|
|
ed63bda932 | ||
|
|
18107d567d | ||
|
|
608818fa9f | ||
|
|
9ae2440ec1 | ||
|
|
ef9c50e63a | ||
|
|
5d50b08351 | ||
|
|
123fc19c4e | ||
|
|
a9058e471c | ||
|
|
bd1f48d9fe | ||
|
|
66ab4d217d | ||
|
|
7a8b392607 | ||
|
|
bfdf069d2d | ||
|
|
b4b00a48d9 | ||
|
|
e5f226612e | ||
|
|
20619ad3c1 | ||
|
|
37dce7d4c3 | ||
|
|
a67b82e730 | ||
|
|
e0b8ad7911 | ||
|
|
dbb39d366e | ||
|
|
e1de36398d | ||
|
|
871c28aeeb | ||
|
|
c84dc6254d | ||
|
|
60cb1ad7c5 | ||
|
|
4a5ff5dcfb | ||
|
|
5c40307745 | ||
|
|
39426f03e6 | ||
|
|
92e5d2b8f5 | ||
|
|
09aa59f98b | ||
|
|
c9c1c89a27 | ||
|
|
96b76709e9 | ||
|
|
ca3107e21b | ||
|
|
2e4f223207 | ||
|
|
17df668186 | ||
|
|
aef70152de | ||
|
|
e0252f35be | ||
|
|
0f47534766 | ||
|
|
54feaa67e8 | ||
|
|
64ecfa49eb | ||
|
|
fdae613b14 | ||
|
|
c57c08b2c6 | ||
|
|
d2103995cb | ||
|
|
196fb0f16e | ||
|
|
c0afe7260a | ||
|
|
099c8703ae | ||
|
|
b03e480dbf | ||
|
|
8430353389 | ||
|
|
a123ec94ef | ||
|
|
4a2f54f6ff | ||
|
|
b5c85eaeca | ||
|
|
8b3b91d78d | ||
|
|
e1de3973fe | ||
|
|
c1993a6ffc | ||
|
|
ab26fc438a | ||
|
|
c37259b01d | ||
|
|
bde8cc3285 | ||
|
|
59ba5504d0 | ||
|
|
65eb5a3d05 | ||
|
|
b925322e9e | ||
|
|
f0c2ad00f8 | ||
|
|
fc53a25a4c | ||
|
|
58872d162b | ||
|
|
30f1b8f02a | ||
|
|
ee4f199a9f | ||
|
|
e3957f3bac | ||
|
|
2506673514 | ||
|
|
7cb12015fd | ||
|
|
caaac9b9ec | ||
|
|
1dac4d00c4 | ||
|
|
b41902f4df | ||
|
|
ada70dd683 | ||
|
|
7e5d8a8d54 | ||
|
|
e981cae27c | ||
|
|
d7c377c229 | ||
|
|
8772edcd33 | ||
|
|
254740cf68 | ||
|
|
c3775c0f56 | ||
|
|
5ecf40e967 | ||
|
|
45026df4bd | ||
|
|
b8830f4a09 | ||
|
|
811ee1d989 | ||
|
|
8c32102e99 | ||
|
|
1b135095c7 | ||
|
|
becf4396c9 | ||
|
|
929e066506 | ||
|
|
355ad9ac2c | ||
|
|
2cda0472bd | ||
|
|
4710753700 | ||
|
|
2b439ef209 | ||
|
|
2fc76e6d9e | ||
|
|
ff544450a5 | ||
|
|
b4590da686 | ||
|
|
6c4be0311a | ||
|
|
4a2cdc20f0 | ||
|
|
6ce79c62eb | ||
|
|
96994721ef | ||
|
|
f358bb1a96 | ||
|
|
1bce210c51 | ||
|
|
8a3c8d1b1c | ||
|
|
08b224de1f | ||
|
|
86c92c37d2 | ||
|
|
07053c3df7 | ||
|
|
4fb33c9b09 | ||
|
|
5e6a25c3c2 | ||
|
|
e14ef4a294 | ||
|
|
8b659f0f4c | ||
|
|
a6395c95d4 | ||
|
|
98e74dfd38 | ||
|
|
1d728186db | ||
|
|
2499f2ed80 | ||
|
|
963905adcd | ||
|
|
7c21ab0985 | ||
|
|
77d3712261 | ||
|
|
0376f22d41 | ||
|
|
007820b7c1 | ||
|
|
dc98711477 | ||
|
|
e98c6ff31b | ||
|
|
9eae74357a | ||
|
|
1ee88a2878 | ||
|
|
14189e6827 | ||
|
|
3dd6113a0a | ||
|
|
ae5cdea5af | ||
|
|
3c1f389e25 | ||
|
|
284d821233 | ||
|
|
78272e5592 | ||
|
|
098bc41083 | ||
|
|
c837090282 | ||
|
|
cdc1678e6f | ||
|
|
2a3d76b0cc | ||
|
|
29e89491de | ||
|
|
d63947aec4 | ||
|
|
512f0a67fb | ||
|
|
a7cd4657f5 | ||
|
|
bf02cde598 | ||
|
|
b7dbb35546 | ||
|
|
6fd5f0e3de | ||
|
|
e6469df065 | ||
|
|
d61fe357f6 | ||
|
|
965dd2ab67 | ||
|
|
78e011d558 | ||
|
|
76590d6c69 | ||
|
|
16dc6ce41a | ||
|
|
1dadcee5c9 | ||
|
|
bed3bb45f7 | ||
|
|
18d850d7d6 | ||
|
|
f920fd8b6f | ||
|
|
b9da6d9ef6 | ||
|
|
3be489c69e | ||
|
|
cab092aad7 | ||
|
|
0451cba343 | ||
|
|
12334cd9ad | ||
|
|
9d18a2dff4 | ||
|
|
869e1dc241 | ||
|
|
b878f3fc4a | ||
|
|
2aeabcc38b | ||
|
|
94778c0dac | ||
|
|
390491b37a | ||
|
|
f8ea825577 | ||
|
|
f77ad7d184 | ||
|
|
e467a53def | ||
|
|
9cd2e12915 | ||
|
|
627a6587b4 | ||
|
|
9b3b419f7a | ||
|
|
f37ed44837 | ||
|
|
dddc333407 | ||
|
|
75714fb784 | ||
|
|
5a6342a5fe | ||
|
|
e07379b406 | ||
|
|
54bfe34273 | ||
|
|
bef876e4b4 | ||
|
|
6ef9ab6a0b | ||
|
|
53ffdd8d68 | ||
|
|
ba198034cb | ||
|
|
58e66b9e1b | ||
|
|
ac7efcb43a | ||
|
|
b514b9ec7c | ||
|
|
f69942eb35 | ||
|
|
f28827f625 | ||
|
|
f20a6b3635 | ||
|
|
7f7ff3ce3e | ||
|
|
0abb0756ba | ||
|
|
070ed9396c | ||
|
|
307004a455 | ||
|
|
d097327bd8 | ||
|
|
f72f501931 | ||
|
|
e9584cfcab | ||
|
|
aec0e75d73 | ||
|
|
a6b4d49454 | ||
|
|
1cc726593e | ||
|
|
5001bd8810 | ||
|
|
46aa70aa09 | ||
|
|
41cf447c45 | ||
|
|
b328fd7aed | ||
|
|
917a24580b | ||
|
|
af36182879 | ||
|
|
160c721afc | ||
|
|
ee0040ef1c | ||
|
|
9e60a55dd7 | ||
|
|
57f27572ae | ||
|
|
ceae032112 | ||
|
|
d3ab367fcd | ||
|
|
0ceefb48c8 | ||
|
|
a9815ae1e9 | ||
|
|
79140fd132 | ||
|
|
6c0f48ce25 | ||
|
|
da7ab7057d | ||
|
|
499ae609dd | ||
|
|
e78b3644e4 | ||
|
|
ce6b39bd1c | ||
|
|
603f528b47 | ||
|
|
5fecd07045 | ||
|
|
8edd7ad32e | ||
|
|
3a27d0b849 | ||
|
|
b351536dee | ||
|
|
eeea8559b6 | ||
|
|
17591117c1 | ||
|
|
c4470fd1e6 | ||
|
|
45fc7ec01f | ||
|
|
dc54472ca5 | ||
|
|
4b3bf05aaf | ||
|
|
ec014a7ecf | ||
|
|
a8c87f3bab | ||
|
|
b39e76200a | ||
|
|
01deb9ef7d | ||
|
|
5c89568cca | ||
|
|
6119b44746 | ||
|
|
7af4a7bf88 | ||
|
|
04e4d69662 | ||
|
|
a87e0f028a | ||
|
|
31774a7711 | ||
|
|
a32f27a2aa | ||
|
|
805a409318 | ||
|
|
42d45bd14a | ||
|
|
5870709fbc | ||
|
|
0fdb7430ff | ||
|
|
4771f80d6f | ||
|
|
91405583fd | ||
|
|
f864303aac | ||
|
|
e5ebf4a2b2 | ||
|
|
725a36e37a | ||
|
|
78dacfa865 | ||
|
|
16547fb3c2 | ||
|
|
9eeabf79f9 | ||
|
|
81b71cc143 | ||
|
|
8301acd4d6 | ||
|
|
f80afb1755 | ||
|
|
71c6b60054 | ||
|
|
95a1b874d8 | ||
|
|
07de96ede9 | ||
|
|
209014b083 | ||
|
|
127f10e7a8 | ||
|
|
67615a0cbc | ||
|
|
dde20f0c76 | ||
|
|
ddced79475 | ||
|
|
e80795b7f0 | ||
|
|
080337a4e8 | ||
|
|
884af3c986 | ||
|
|
edc95b2cb7 | ||
|
|
96239e95ec | ||
|
|
331ad76675 | ||
|
|
29152d3022 | ||
|
|
73fa4b1cbd | ||
|
|
7766a91e8c | ||
|
|
b7bad765aa | ||
|
|
5af09a60e9 | ||
|
|
ef623cfbbd | ||
|
|
ce5faba185 | ||
|
|
40d9ed535b | ||
|
|
d5675765f3 | ||
|
|
65067c24cc | ||
|
|
e2463cb501 | ||
|
|
de2f2d7ef1 | ||
|
|
7cdb75e7ce | ||
|
|
1502e308b8 | ||
|
|
dd1fe68e5c | ||
|
|
85c1dc5a0c | ||
|
|
7b3a689aad | ||
|
|
2c28a12cca | ||
|
|
324021fd64 | ||
|
|
1dceae97c6 | ||
|
|
0bef925b51 | ||
|
|
0ba1f29e40 | ||
|
|
3b1e536822 | ||
|
|
9a83662a18 | ||
|
|
5bcead5b5c | ||
|
|
d8f9502ea2 | ||
|
|
020128b9ca | ||
|
|
1566a4e607 | ||
|
|
fbd59631ee | ||
|
|
6c36856499 | ||
|
|
f4cda49988 | ||
|
|
a6b49fe7d6 | ||
|
|
26966b2bf3 | ||
|
|
36a7429aa0 | ||
|
|
7be1fe09f1 | ||
|
|
62bf08d323 | ||
|
|
260c082c4e | ||
|
|
5e0ff7fd56 | ||
|
|
aac312ca34 | ||
|
|
a90401454a | ||
|
|
5679e6b3a9 | ||
|
|
194574bb0e | ||
|
|
337d2da38a | ||
|
|
df848aa084 | ||
|
|
0cc1615252 | ||
|
|
d4259fa8aa | ||
|
|
157d7e2b59 | ||
|
|
ee50904147 | ||
|
|
ec39aa3890 | ||
|
|
beb0fdf4b8 | ||
|
|
443fe813c3 | ||
|
|
69cdc5a9b9 | ||
|
|
dac54a6019 | ||
|
|
e5ca53e56c | ||
|
|
7a79236bab | ||
|
|
cd37e2908f | ||
|
|
3368e49aa7 | ||
|
|
9e31f05861 | ||
|
|
b52f4ed25b | ||
|
|
00eb764880 | ||
|
|
6d011a3700 | ||
|
|
f9d9caf417 | ||
|
|
b50702dd52 | ||
|
|
e74a82b9d6 | ||
|
|
12fa983242 | ||
|
|
1607df835a | ||
|
|
7b705dd670 | ||
|
|
323e3028b6 | ||
|
|
7e1b4692e2 | ||
|
|
c1dd971ce9 | ||
|
|
39610e6933 | ||
|
|
fdb189102d | ||
|
|
e54d095064 | ||
|
|
e6634c56d3 | ||
|
|
deb77a4dde | ||
|
|
90ea27c423 | ||
|
|
19533f87e3 | ||
|
|
e6c5c84133 | ||
|
|
404aaa5658 | ||
|
|
2c70df25f5 | ||
|
|
db959ac2eb | ||
|
|
bbf0dce390 | ||
|
|
1cbb8b9851 | ||
|
|
96387cce47 | ||
|
|
570fc68cb1 | ||
|
|
e6ce0057af | ||
|
|
a3f625fe39 | ||
|
|
bdfe459d64 | ||
|
|
95ec8ef5e1 | ||
|
|
b8198716d9 | ||
|
|
071a4e5f83 | ||
|
|
d99cd71408 | ||
|
|
030e6aa507 | ||
|
|
f146b01e3e | ||
|
|
c249ff9046 | ||
|
|
8b48560c81 | ||
|
|
bdbea2463b | ||
|
|
8fb2263471 | ||
|
|
ea55c36a3a | ||
|
|
37595ff3e7 | ||
|
|
cca008b73c | ||
|
|
b9dcbd9d33 | ||
|
|
b6bf8d5b2d | ||
|
|
43ed66cf26 | ||
|
|
57eae1be43 | ||
|
|
b394d184cc | ||
|
|
81e01b6f88 | ||
|
|
52efe32e0f | ||
|
|
e7886734c4 | ||
|
|
083c37a7b2 | ||
|
|
b24a10aa00 | ||
|
|
e1ef14d12b | ||
|
|
1a33de91e2 | ||
|
|
6e15e2d72a | ||
|
|
d58b715c52 | ||
|
|
804ff8b5a5 | ||
|
|
6e89d2f46a | ||
|
|
e99d69b7d9 | ||
|
|
f25416c3f5 | ||
|
|
a7bb6a2781 | ||
|
|
41f14e0e89 | ||
|
|
01bd10f485 | ||
|
|
476eacd5ca | ||
|
|
ca2fb74f41 | ||
|
|
8a2baf5b6e | ||
|
|
d44fc3aa1b | ||
|
|
7d78c42dfc | ||
|
|
a361180745 | ||
|
|
310dc10c4d | ||
|
|
96e02c4c2e | ||
|
|
afb3b9f029 | ||
|
|
d5e647a191 | ||
|
|
a1d37617a8 | ||
|
|
2541f85942 | ||
|
|
07fd23dc5e | ||
|
|
946311b2a3 | ||
|
|
4868032215 | ||
|
|
fb0cf64536 | ||
|
|
b37afcdba1 | ||
|
|
f644bee831 | ||
|
|
6fcc7e3e23 | ||
|
|
95602eb85d | ||
|
|
9f3afdf928 | ||
|
|
627a2412d2 | ||
|
|
acffd1e792 | ||
|
|
62ca4ba624 | ||
|
|
6ea8f35343 | ||
|
|
1bd8c67041 | ||
|
|
7425b382d6 | ||
|
|
5f61b57d63 | ||
|
|
9db73767d9 | ||
|
|
c562245c16 | ||
|
|
9525216d78 | ||
|
|
28a71a29e6 | ||
|
|
7b06d34ba4 | ||
|
|
656d24e477 | ||
|
|
df0f03d831 | ||
|
|
1db6d15677 | ||
|
|
08a0479c9e | ||
|
|
6682b7dfa5 | ||
|
|
aad5d98b43 | ||
|
|
a8a4a85dac | ||
|
|
ad71b969b2 | ||
|
|
8bcb10391e | ||
|
|
8e7d74bc3b | ||
|
|
629b6faa22 | ||
|
|
ef1e27966d | ||
|
|
ea0eb4ba09 | ||
|
|
f83ad77c08 | ||
|
|
af219ce9e7 | ||
|
|
2ac88a7fa6 | ||
|
|
c495ff8ad8 | ||
|
|
0e12215614 | ||
|
|
b4b13b0cb9 | ||
|
|
677cdfd6c8 | ||
|
|
e9f18c6d8d | ||
|
|
8640e18d1f | ||
|
|
300c4d8432 | ||
|
|
92c877493a | ||
|
|
ff4926be35 | ||
|
|
df3872f665 | ||
|
|
fcded2ea2b | ||
|
|
420f0109ee | ||
|
|
b4678a1cee | ||
|
|
5d3c90197c | ||
|
|
d5d517748c | ||
|
|
de3d3c15a5 | ||
|
|
3db253998b | ||
|
|
d3f978a19d | ||
|
|
a50ddd6d8c | ||
|
|
37700b4b1a | ||
|
|
a8f9871725 | ||
|
|
a4a5492782 | ||
|
|
9ebb80502a | ||
|
|
dcacbb37c3 | ||
|
|
34443da7ab | ||
|
|
09c14f1a10 | ||
|
|
e801647083 | ||
|
|
0122cee0ae | ||
|
|
1188b1be36 | ||
|
|
905c4932d1 | ||
|
|
6484da26f0 | ||
|
|
079041cf1f | ||
|
|
b8df7798bf | ||
|
|
e4af954a95 | ||
|
|
f3df62f27e | ||
|
|
4b3c9e826d | ||
|
|
ba4780cbd3 | ||
|
|
b1a6940188 | ||
|
|
3c8325f1f3 | ||
|
|
5fa9581db3 | ||
|
|
96001d86e1 | ||
|
|
963e808d98 | ||
|
|
d124ca377f | ||
|
|
a3d3d81447 | ||
|
|
244121d920 | ||
|
|
adf95fb90d | ||
|
|
f8446f1bfc | ||
|
|
b922035d4f | ||
|
|
b75fc2bde3 | ||
|
|
10e16cca22 | ||
|
|
6628214484 | ||
|
|
e3c9c46c1c | ||
|
|
6a1b341336 | ||
|
|
65f96494f8 | ||
|
|
6e3e2d9f29 | ||
|
|
25d8debb11 | ||
|
|
426b227f03 | ||
|
|
2fbd9b95a7 | ||
|
|
9758029c11 | ||
|
|
f21b8206af | ||
|
|
4f8f79a907 | ||
|
|
c9e2f16404 | ||
|
|
2658754295 | ||
|
|
77d8ff3620 | ||
|
|
c4621119b3 | ||
|
|
a2a292d48e | ||
|
|
8349b70ba8 | ||
|
|
dd6fe58475 | ||
|
|
386929e84b | ||
|
|
43a355e07a | ||
|
|
1a5d2ac459 | ||
|
|
21403b19e0 | ||
|
|
cb7778d433 | ||
|
|
07c0105c83 | ||
|
|
7da186d4e9 | ||
|
|
75885c3095 | ||
|
|
5d275389af | ||
|
|
75589b4450 | ||
|
|
a9b12d5d73 | ||
|
|
6a00a504d4 | ||
|
|
6b9cac928b | ||
|
|
b292384380 | ||
|
|
01ac5aeb0d | ||
|
|
1a41ee0c30 | ||
|
|
1950a67e15 | ||
|
|
65303d027a | ||
|
|
fae838b054 | ||
|
|
eab4c7bce9 | ||
|
|
6bb38b873b | ||
|
|
ba5860cdd6 | ||
|
|
ee62863759 | ||
|
|
4a3a61bea1 | ||
|
|
f3901eb668 | ||
|
|
b53d1df4df | ||
|
|
df9b0c548e | ||
|
|
3056fc4710 | ||
|
|
e411a4af21 | ||
|
|
3430012136 | ||
|
|
809295c03d | ||
|
|
c9783b38c4 | ||
|
|
44e4cbf04a | ||
|
|
049695b914 | ||
|
|
7b1aab3759 | ||
|
|
5e87fea1ee | ||
|
|
a187111a26 | ||
|
|
3044a6b517 | ||
|
|
824fbe09c2 | ||
|
|
764eed7599 | ||
|
|
ccb81919f7 | ||
|
|
4505c38b58 | ||
|
|
d9770dcffb | ||
|
|
9936f97fcb | ||
|
|
4cf09f46e7 | ||
|
|
6ffe3a424c | ||
|
|
641794d458 | ||
|
|
73d9ac6c01 | ||
|
|
3205d1e16a | ||
|
|
ec61004622 | ||
|
|
ae93d258a5 | ||
|
|
12845f0eef | ||
|
|
6b4f524620 | ||
|
|
dec0baa147 | ||
|
|
340609d149 | ||
|
|
9a30c3d722 | ||
|
|
e19a927579 | ||
|
|
53a595885a | ||
|
|
ebd7406386 | ||
|
|
66cdbc5cad | ||
|
|
060c049056 | ||
|
|
31270c36d2 | ||
|
|
0aa26e9134 | ||
|
|
b45a2d7335 | ||
|
|
07676d483d | ||
|
|
6436452bc9 | ||
|
|
60ad374514 | ||
|
|
b611ddda5e | ||
|
|
186bd156dd | ||
|
|
3628c81885 | ||
|
|
180000a531 | ||
|
|
5f995c64d4 | ||
|
|
fe2c2014de | ||
|
|
754df5ebe2 | ||
|
|
d8b7ac9044 | ||
|
|
5f3b04ecbd | ||
|
|
1f1aba4a32 | ||
|
|
1390cd4048 | ||
|
|
31cdac7bfb | ||
|
|
0705901bce | ||
|
|
87495575bc | ||
|
|
b292bba092 | ||
|
|
5c7f0a0ad8 | ||
|
|
2df86e1966 | ||
|
|
41406e3be2 | ||
|
|
7ed4bd67b8 | ||
|
|
8ce3531de8 | ||
|
|
579e53f02c | ||
|
|
1379c6b404 | ||
|
|
153d3045ed | ||
|
|
278686dc48 | ||
|
|
9b9f33bc8b | ||
|
|
24984458de | ||
|
|
e3eb4a20a5 | ||
|
|
001bbd36f5 | ||
|
|
382cc52baa | ||
|
|
2af368ab82 | ||
|
|
f514160fae | ||
|
|
30e4dcef90 | ||
|
|
4618e11406 | ||
|
|
f887b77e36 | ||
|
|
fb527139a0 | ||
|
|
261fbef516 | ||
|
|
32e8a9b377 | ||
|
|
5f95351074 | ||
|
|
1c57c0e651 | ||
|
|
ec6ee27f29 | ||
|
|
70234edd02 | ||
|
|
7e24a696bd | ||
|
|
c3ed936776 | ||
|
|
235ec7cb2e | ||
|
|
4f8586d81d | ||
|
|
6cd1e38425 | ||
|
|
f7512dc540 | ||
|
|
45415a742f | ||
|
|
2247065dc5 | ||
|
|
c550e2ccf5 | ||
|
|
bc75e5ec44 | ||
|
|
5b39496008 | ||
|
|
4016da6632 | ||
|
|
f2edcb9bdf | ||
|
|
3d8a87563d | ||
|
|
93bde0cae2 | ||
|
|
30179bb60d | ||
|
|
498bc21133 | ||
|
|
af7a34521f | ||
|
|
7fc2b33a0a | ||
|
|
a5d3f7785a | ||
|
|
f1cdce38a6 | ||
|
|
65e4652142 | ||
|
|
569797d7c5 | ||
|
|
35a7c94cd1 | ||
|
|
c8365b7444 | ||
|
|
8c5a343729 | ||
|
|
22ddec46ab | ||
|
|
a4e70ba4ca | ||
|
|
978ac65cae | ||
|
|
eb5a836822 | ||
|
|
ad1b9b71ae | ||
|
|
60257f422a | ||
|
|
6576d4852c | ||
|
|
c30661c44c | ||
|
|
2d9cf195d7 | ||
|
|
a21e76caab | ||
|
|
a534a6bf09 | ||
|
|
2b0f219bfd | ||
|
|
33dde63256 | ||
|
|
4dc9540325 | ||
|
|
2b1a661614 | ||
|
|
2138fc2349 | ||
|
|
cdc8dd88e9 | ||
|
|
2a5f8e84bb | ||
|
|
6f8540f25a | ||
|
|
151c699f5c | ||
|
|
9133f6d352 | ||
|
|
ab276e4e86 | ||
|
|
cdc4f85e47 | ||
|
|
b2fb84e361 | ||
|
|
4ab1e893f0 | ||
|
|
70bc3d178b | ||
|
|
777f145128 | ||
|
|
2f0181ac44 | ||
|
|
83b4b698a0 | ||
|
|
a10a294a2e | ||
|
|
34036a3b51 | ||
|
|
911bc03381 | ||
|
|
0240aeac8d | ||
|
|
a43be186d3 | ||
|
|
fb63f48d0a | ||
|
|
c0279df3c6 | ||
|
|
e614b993f2 | ||
|
|
8035f30305 | ||
|
|
af0b5e6176 | ||
|
|
1bf996c705 | ||
|
|
33f69481f7 | ||
|
|
354fb6b8c6 | ||
|
|
d30477b6f8 | ||
|
|
496b5d62ae | ||
|
|
3652002a68 | ||
|
|
c0e5719271 | ||
|
|
997f851031 | ||
|
|
eaf184b585 | ||
|
|
0616261a94 | ||
|
|
d2c45f428f | ||
|
|
6054ab35cb | ||
|
|
5ba0c6404b | ||
|
|
f738c2be9d | ||
|
|
12ac2e5534 | ||
|
|
60985b396a | ||
|
|
d9b33e01ee | ||
|
|
0335f2726a | ||
|
|
23717cc4d7 | ||
|
|
4422f301b4 | ||
|
|
1981da1b4e | ||
|
|
a130f50107 | ||
|
|
c40198046c | ||
|
|
e5b44a3970 | ||
|
|
5e7443706b | ||
|
|
39e75e932a | ||
|
|
b8a8edc513 | ||
|
|
b1051365fa | ||
|
|
a935cd0c5d | ||
|
|
62ce90795f | ||
|
|
6bcad45392 | ||
|
|
8747c0c58d | ||
|
|
b91d9c2867 | ||
|
|
140935bca3 | ||
|
|
67c7abaac3 | ||
|
|
11bd7dc7de | ||
|
|
406f65cb23 | ||
|
|
2344706462 | ||
|
|
fcc32fca7e | ||
|
|
7f587fd4da | ||
|
|
f47c0601ce | ||
|
|
3d638c692e | ||
|
|
e6ef3ea24f | ||
|
|
8dd880d0c8 | ||
|
|
393a3a079f | ||
|
|
ceb035e9b3 | ||
|
|
e38570437e | ||
|
|
f0203d1f19 | ||
|
|
e0c5a61be5 | ||
|
|
526a16298c | ||
|
|
443f02cd92 | ||
|
|
b682c8e052 | ||
|
|
d07c8dcd9c | ||
|
|
9bcce4f271 | ||
|
|
eab497a814 | ||
|
|
a8c8df6ee8 | ||
|
|
b3549f421d | ||
|
|
ae0accb5a4 | ||
|
|
d367566db9 | ||
|
|
35812208e7 | ||
|
|
afaff724f6 | ||
|
|
ec8dfb0cad | ||
|
|
7798da8dff | ||
|
|
89a49ce72e | ||
|
|
655dce6a4b | ||
|
|
2cf403a3e9 | ||
|
|
4351ad2414 | ||
|
|
fd2944fa8d | ||
|
|
f4626cf8ad | ||
|
|
65bf0d20e7 | ||
|
|
2f496de98a | ||
|
|
644b8304ab | ||
|
|
5c4570fcaa | ||
|
|
ac0bd3b116 | ||
|
|
7ad881d0ec | ||
|
|
9a25224890 | ||
|
|
3f6f597093 | ||
|
|
9ab22fe551 | ||
|
|
9794e71a86 | ||
|
|
8cb3884fae | ||
|
|
098bd4509b | ||
|
|
4dc5bac72f | ||
|
|
b150869b51 | ||
|
|
daf8be9f78 | ||
|
|
d14cf1e80b | ||
|
|
605d34008e | ||
|
|
d3135414cc | ||
|
|
b3f29b46cf | ||
|
|
4545719e18 | ||
|
|
cc60afa31a | ||
|
|
85743ede7e | ||
|
|
8e231cb2ec | ||
|
|
72405cd43f | ||
|
|
a9ad9d5e6d | ||
|
|
2d79c7333f | ||
|
|
7a79cfd76b | ||
|
|
6600d6b6d9 | ||
|
|
8ca25aed8b | ||
|
|
1db353a0fc | ||
|
|
2d384eb546 | ||
|
|
5d1de33026 | ||
|
|
f2ba49fa35 | ||
|
|
772ae1cae4 | ||
|
|
1d267f2642 | ||
|
|
cba299160a | ||
|
|
ea33193e73 | ||
|
|
ef69c228fd | ||
|
|
7ba0f07732 | ||
|
|
07696086a2 | ||
|
|
426581eb1e | ||
|
|
fe88811a40 | ||
|
|
30aaa6e26c | ||
|
|
4bd071bf3c | ||
|
|
309ac65447 | ||
|
|
fe246470ce | ||
|
|
660cfe707c | ||
|
|
a0d7d7bc4b | ||
|
|
758764ea75 | ||
|
|
6935c62a61 | ||
|
|
a34d4d9124 | ||
|
|
c49bb5aa03 | ||
|
|
427750d6af | ||
|
|
b9ad5f8727 | ||
|
|
c776826425 | ||
|
|
a8ede18db5 | ||
|
|
9aa84ec54a | ||
|
|
91a6520c3b | ||
|
|
45fde4888e | ||
|
|
484ef5f2f6 | ||
|
|
cdd354739a | ||
|
|
420f544bf3 | ||
|
|
15b33189ae | ||
|
|
685a9f36f6 | ||
|
|
3b57812e29 | ||
|
|
d583c48992 | ||
|
|
0e636ea67e | ||
|
|
c9ce1bd30f | ||
|
|
f2cbc3192c | ||
|
|
56d76791f1 | ||
|
|
df2cab877f | ||
|
|
27ad8305e5 | ||
|
|
c97b4dd48e | ||
|
|
59ecd610b1 | ||
|
|
42c1285ead | ||
|
|
622c1c9236 | ||
|
|
2d88508f9d | ||
|
|
86efba4ecc | ||
|
|
90fae00aa6 | ||
|
|
d497ed4040 | ||
|
|
c4a21ae5ad | ||
|
|
5e5798bee9 | ||
|
|
d793077b91 | ||
|
|
3e826f9249 | ||
|
|
2adfb60dc4 | ||
|
|
f7212cf80c | ||
|
|
69556643bb | ||
|
|
f0ebc84840 | ||
|
|
490eb59ea3 | ||
|
|
16a9391726 | ||
|
|
adf8ba67d2 | ||
|
|
c37b222cbf | ||
|
|
72f8f2beb1 | ||
|
|
16bb9c17f5 | ||
|
|
ca44977d92 | ||
|
|
bcf294475f | ||
|
|
3b31774dd3 | ||
|
|
c106347c59 | ||
|
|
2679be9d97 | ||
|
|
4d59368c7d | ||
|
|
3f29680fb6 | ||
|
|
8f1df14ae7 | ||
|
|
7159e360d9 | ||
|
|
65723bcac5 | ||
|
|
73ae451cb4 | ||
|
|
dc0ea3a248 | ||
|
|
9548cd8341 | ||
|
|
8410fc38c9 | ||
|
|
0c9c2168b3 | ||
|
|
2690f54743 | ||
|
|
22783813c5 | ||
|
|
b5ed46a5af | ||
|
|
1a3f17e296 | ||
|
|
9281129f07 | ||
|
|
0e9ddd3b99 | ||
|
|
d87db04e55 | ||
|
|
906ec3c8a9 | ||
|
|
449575ceae | ||
|
|
6255c77eba | ||
|
|
c33622f2b3 | ||
|
|
e4860acb58 | ||
|
|
e96867f757 | ||
|
|
57bd43baf3 | ||
|
|
3a4e595d32 | ||
|
|
dcad22bfa8 | ||
|
|
acba0e47d8 | ||
|
|
dd19459e18 | ||
|
|
8be3c5ed21 | ||
|
|
51726d8de7 | ||
|
|
8f41c6bad8 | ||
|
|
e294abc567 | ||
|
|
9f9518c872 | ||
|
|
5faf7cb59e | ||
|
|
4b095d532c | ||
|
|
4288761235 | ||
|
|
73388f30fd | ||
|
|
2d9ddd4bc8 | ||
|
|
55aa0cf303 | ||
|
|
52ca15b514 | ||
|
|
bd383888de | ||
|
|
1bd349485f | ||
|
|
155f9dc1b1 | ||
|
|
fe7ee1edc3 | ||
|
|
c11cbff605 | ||
|
|
fbc629266f | ||
|
|
75d44dca6b | ||
|
|
3e6b794a33 | ||
|
|
a757ce48a1 | ||
|
|
507f29a7bd | ||
|
|
e5f4f77073 | ||
|
|
f07fee538d | ||
|
|
fa1420b384 | ||
|
|
724a2bd72f | ||
|
|
d525d02348 | ||
|
|
4920cf2b98 | ||
|
|
f41d0938a4 | ||
|
|
924a405829 | ||
|
|
fc9225e24a | ||
|
|
ac202cbdd3 | ||
|
|
cedd68c942 | ||
|
|
4231c879ef | ||
|
|
42e58a4b72 | ||
|
|
ebd1bc83c9 | ||
|
|
0bc9d7455f | ||
|
|
5274166f8c | ||
|
|
5566aa4588 | ||
|
|
ab638a4f54 | ||
|
|
86a8452ab7 | ||
|
|
8c96c16eac | ||
|
|
45c2e6575d | ||
|
|
a786a7dc55 | ||
|
|
6adf5cbee5 | ||
|
|
1964b54627 | ||
|
|
c9d0e82c52 | ||
|
|
1a4948f0f2 | ||
|
|
66da3f5668 | ||
|
|
9ebeb64570 | ||
|
|
571aaece2e | ||
|
|
f60fb954a2 | ||
|
|
1d136cacfb | ||
|
|
994021d77f | ||
|
|
8899325a0e | ||
|
|
43876a9357 | ||
|
|
d6cace32f5 | ||
|
|
2388de455b | ||
|
|
b14b3ab276 | ||
|
|
32ccb779c6 | ||
|
|
52536e94e5 | ||
|
|
5d6b3abd26 | ||
|
|
79682bbcb0 | ||
|
|
770ff19313 | ||
|
|
8d4855f100 | ||
|
|
7d4e14f384 | ||
|
|
f162dd7e87 | ||
|
|
06dc12e217 | ||
|
|
36edff9e15 | ||
|
|
34a0c079de | ||
|
|
84b581ae13 | ||
|
|
587c93eadf | ||
|
|
b5aa549e0c | ||
|
|
5fb6738612 | ||
|
|
685262fc5e | ||
|
|
8802f6cf23 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -19,6 +19,8 @@ configure
|
|||||||
data/50-gnome-shell-*.xml
|
data/50-gnome-shell-*.xml
|
||||||
data/gnome-shell.desktop
|
data/gnome-shell.desktop
|
||||||
data/gnome-shell.desktop.in
|
data/gnome-shell.desktop.in
|
||||||
|
data/gnome-shell-wayland.desktop
|
||||||
|
data/gnome-shell-wayland.desktop.in
|
||||||
data/gnome-shell-extension-prefs.desktop
|
data/gnome-shell-extension-prefs.desktop
|
||||||
data/gnome-shell-extension-prefs.desktop.in
|
data/gnome-shell-extension-prefs.desktop.in
|
||||||
data/gschemas.compiled
|
data/gschemas.compiled
|
||||||
@@ -41,6 +43,8 @@ docs/reference/*/xml/
|
|||||||
docs/reference/shell/doc-gen-*
|
docs/reference/shell/doc-gen-*
|
||||||
gtk-doc.make
|
gtk-doc.make
|
||||||
js/misc/config.js
|
js/misc/config.js
|
||||||
|
js/js-resources.c
|
||||||
|
js/js-resources.h
|
||||||
intltool-extract.in
|
intltool-extract.in
|
||||||
intltool-merge.in
|
intltool-merge.in
|
||||||
intltool-update.in
|
intltool-update.in
|
||||||
@@ -71,13 +75,14 @@ src/calendar-server/evolution-calendar.desktop.in
|
|||||||
src/calendar-server/org.gnome.Shell.CalendarServer.service
|
src/calendar-server/org.gnome.Shell.CalendarServer.service
|
||||||
src/gnome-shell
|
src/gnome-shell
|
||||||
src/gnome-shell-calendar-server
|
src/gnome-shell-calendar-server
|
||||||
src/gnome-shell-extension-tool
|
|
||||||
src/gnome-shell-extension-prefs
|
src/gnome-shell-extension-prefs
|
||||||
|
src/gnome-shell-extension-tool
|
||||||
src/gnome-shell-hotplug-sniffer
|
src/gnome-shell-hotplug-sniffer
|
||||||
src/gnome-shell-jhbuild
|
src/gnome-shell-jhbuild
|
||||||
src/gnome-shell-perf-helper
|
src/gnome-shell-perf-helper
|
||||||
src/gnome-shell-perf-tool
|
src/gnome-shell-perf-tool
|
||||||
src/gnome-shell-real
|
src/gnome-shell-real
|
||||||
|
src/gnome-shell-wayland
|
||||||
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
|
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
|
||||||
src/run-js-test
|
src/run-js-test
|
||||||
src/test-recorder
|
src/test-recorder
|
||||||
|
|||||||
25
COPYING
25
COPYING
@@ -1,8 +1,8 @@
|
|||||||
GNU GENERAL PUBLIC LICENSE
|
GNU GENERAL PUBLIC LICENSE
|
||||||
Version 2, June 1991
|
Version 2, June 1991
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
|
|||||||
General Public License applies to most of the Free Software
|
General Public License applies to most of the Free Software
|
||||||
Foundation's software and to any other program whose authors commit to
|
Foundation's software and to any other program whose authors commit to
|
||||||
using it. (Some other Free Software Foundation software is covered by
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
the GNU Library General Public License instead.) You can apply it to
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
your programs, too.
|
your programs, too.
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
When we speak of free software, we are referring to freedom, not
|
||||||
@@ -55,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all.
|
|||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
The precise terms and conditions for copying, distribution and
|
||||||
modification follow.
|
modification follow.
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
GNU GENERAL PUBLIC LICENSE
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
|
|||||||
License. (Exception: if the Program itself is interactive but
|
License. (Exception: if the Program itself is interactive but
|
||||||
does not normally print such an announcement, your work based on
|
does not normally print such an announcement, your work based on
|
||||||
the Program is not required to print an announcement.)
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
These requirements apply to the modified work as a whole. If
|
||||||
identifiable sections of that work are not derived from the Program,
|
identifiable sections of that work are not derived from the Program,
|
||||||
and can be reasonably considered independent and separate works in
|
and can be reasonably considered independent and separate works in
|
||||||
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
|
|||||||
access to copy the source code from the same place counts as
|
access to copy the source code from the same place counts as
|
||||||
distribution of the source code, even though third parties are not
|
distribution of the source code, even though third parties are not
|
||||||
compelled to copy the source along with the object code.
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
4. You may not copy, modify, sublicense, or distribute the Program
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
except as expressly provided under this License. Any attempt
|
except as expressly provided under this License. Any attempt
|
||||||
otherwise to copy, modify, sublicense or distribute the Program is
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
@@ -225,7 +225,7 @@ impose that choice.
|
|||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
This section is intended to make thoroughly clear what is believed to
|
||||||
be a consequence of the rest of this License.
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
8. If the distribution and/or use of the Program is restricted in
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
original copyright holder who places the Program under this License
|
original copyright holder who places the Program under this License
|
||||||
@@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
|||||||
POSSIBILITY OF SUCH DAMAGES.
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
If you develop a new program, and you want it to be of the greatest
|
||||||
@@ -303,10 +303,9 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License along
|
||||||
along with this program; if not, write to the Free Software
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
@@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
|
|||||||
This General Public License does not permit incorporating your program into
|
This General Public License does not permit incorporating your program into
|
||||||
proprietary programs. If your program is a subroutine library, you may
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
consider it more useful to permit linking proprietary applications with the
|
consider it more useful to permit linking proprietary applications with the
|
||||||
library. If this is what you want to do, use the GNU Library General
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
Public License instead of this License.
|
Public License instead of this License.
|
||||||
|
|||||||
4
HACKING
4
HACKING
@@ -138,8 +138,8 @@ GObjects, although this feature isn't used very often in the Shell itself.
|
|||||||
|
|
||||||
_init: function(icon, label) {
|
_init: function(icon, label) {
|
||||||
this.parent({ reactive: false });
|
this.parent({ reactive: false });
|
||||||
this.addActor(icon);
|
this.actor.add_child(icon);
|
||||||
this.addActor(label);
|
this.actor.add_child(label);
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function() {
|
open: function() {
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
# Point to our macro directory and pick up user flags from the environment
|
# Point to our macro directory and pick up user flags from the environment
|
||||||
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
|
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
|
||||||
|
|
||||||
SUBDIRS = data js src browser-plugin tests po docs
|
SUBDIRS = data js src tests po docs
|
||||||
|
|
||||||
|
if BUILD_BROWSER_PLUGIN
|
||||||
|
SUBDIRS += browser-plugin
|
||||||
|
endif
|
||||||
|
|
||||||
if ENABLE_MAN
|
if ENABLE_MAN
|
||||||
SUBDIRS += man
|
SUBDIRS += man
|
||||||
|
|||||||
760
NEWS
760
NEWS
@@ -1,3 +1,763 @@
|
|||||||
|
3.11.4
|
||||||
|
======
|
||||||
|
* Fix removal of workspacaes that are not at the end [Giovanni; #721417]
|
||||||
|
* Allow session mode to be specified in the environment [Ray; #720894]
|
||||||
|
* Special-case launching of terminals [Debarshi; #695010]
|
||||||
|
* Always show arrow if app switcher is scrollable [Jonh; #711467]
|
||||||
|
* Implement new app folders system [Jasper; #722117]
|
||||||
|
* Remove arrow from background menu [Tarun; #699608]
|
||||||
|
* Misc bug fixes and cleanups [Giovanni, Andika, Florian, Ray; #721039,
|
||||||
|
#721439, #721507, #721629, #721868, #722210]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Piotr Drąg, Tarun Kumar Joshi, Florian Müllner,
|
||||||
|
Debarshi Ray, Jasper St. Pierre, Ray Strode, Andika Triwidada, Jonh Wendell
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Dušan Kazik [sk], Tong Hui [zh_CN], Benjamin Steinwender [de],
|
||||||
|
Matej Urbančič [sl], Jorge Pérez Pérez [an], Kjartan Maraas [nb],
|
||||||
|
Milo Casagrande [it], Rafael Ferreira [pt_BR], Marek Černocký [cs],
|
||||||
|
Daniel Mustieles [es], Adorilson Bezerra [pt_BR], Christian Kirbach [de],
|
||||||
|
Aurimas Černius [lt], Andika Triwidada [id], Baurzhan Muftakhidinov [kk],
|
||||||
|
Victor Ibragimov [tg], Yosef Or Boczko [he], Dimitris Spingos [el],
|
||||||
|
Fran Diéguez [gl]
|
||||||
|
|
||||||
|
3.11.3
|
||||||
|
======
|
||||||
|
* Fix fade effect of desktop icons [Florian; #707671]
|
||||||
|
* Fix issues with background management code [Jasper; #709313]
|
||||||
|
* Use new Glib facilities for application search [Jasper; #711631]
|
||||||
|
* Add focus indication to session menu button [Sebastien; #710539]
|
||||||
|
* Fix hover tracking for StEntries [Jasper; #706749]
|
||||||
|
* Fix reentrancy issue in message tray [Jasper; #711694]
|
||||||
|
* Tone down zoom animation on login/unlock [Jasper; #712362]
|
||||||
|
* Allow specifying monitor for OSD [Carlos; #712664]
|
||||||
|
* Fix resetting prompt on user switch [Ray; #710456]
|
||||||
|
* Stop using gnome-bluetooth-applet [Bastien; #719341]
|
||||||
|
* Add support for EAP-FAST password requests [Dan; #719813]
|
||||||
|
* Fix entry focus of chat notifications [Jasper; #709853]
|
||||||
|
* Make window previews keyboard navigatable [Jasper; #644306]
|
||||||
|
* Fix app switcher order with dialog windows [Florian; #719824]
|
||||||
|
* Allow remote search providers without icons [Debarshi; #719965]
|
||||||
|
* Fix various alignment issues in RTL locales [Yosef; #712638, #712596,
|
||||||
|
#712594, #712600, #712579]
|
||||||
|
* Misc. bug fixes and cleanups [Jasper, Florian, Giovanni, Dan; #712727,
|
||||||
|
#712753, #719378, #719730, #719803, #710115, #720017, #719815, #719567,
|
||||||
|
#720298]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Carlos Garnacho, Sebastien Lafargue, Tim Lunn,
|
||||||
|
Florian Müllner, Bastien Nocera, Yosef Or Boczko, Debarshi Ray,
|
||||||
|
Jasper St. Pierre, Ray Strode, Dan Williams
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Kjartan Maraas [nb], Reinout van Schouwen [nl], Rafael Ferreira [pt_BR],
|
||||||
|
Mattias Põldaru [et], Emin Tufan Çetin [tr], Jiri Grönroos [fi],
|
||||||
|
Khaled Hosny [ar], Fran Diéguez [gl], Victor Ibragimov [tg],
|
||||||
|
Daniel Mustieles [es]
|
||||||
|
|
||||||
|
3.11.2
|
||||||
|
======
|
||||||
|
* Cache search result display actors [Jasper; #704912]
|
||||||
|
* Use username in userWidget if real name doesn't fit [Jasper; #706851]
|
||||||
|
* Support shell_global_reexec_self() on OpenBSD [Antoine; #709571]
|
||||||
|
* Support disabling browser plugin [Colin; #711218]
|
||||||
|
* Restore support for 'disable-restart-buttons' [Florian; #711244]
|
||||||
|
* Validate parameters of exposed DBus methods [Florian; #699752]
|
||||||
|
* Connect applications to systemd journal if available [Colin; #711626]
|
||||||
|
* Misc bug fixes and cleanups [Florian, Jasper; #711205, #698486, #711416,
|
||||||
|
#644306, #711555, #709806, #711631, #711732]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Cosimo Cecchi, Antoine Jacoutot, Florian Müllner, Jasper St. Pierre,
|
||||||
|
Rico Tzschichholz, Colin Walters
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Yuri Myasoedov [ru], Kjartan Maraas [nb], Efstathios Iosifidis [el],
|
||||||
|
Benjamin Steinwender [de], eternalhui [zh_CN], Shantha kumar [ta]
|
||||||
|
|
||||||
|
3.11.1
|
||||||
|
======
|
||||||
|
* power: Use UPower directly instead of gnome-settings-daemon [Bastien; #710273]
|
||||||
|
* Implement support for new GTK+ notification API [Jasper, Giovanni, Florian;
|
||||||
|
#710137, #710596]
|
||||||
|
* gdm: Don't allow user-list to fill up the entire screen [Florian; #710555]
|
||||||
|
* Don't autostart remote search providers at login [Giovanni; #708830]
|
||||||
|
* Fix spacing in end-session dialog [Sebastien; #710543]
|
||||||
|
* Prepare for js24 [Tim; #711052]
|
||||||
|
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Tim, Sebastien; #710347,
|
||||||
|
#710144, #710541, #691409, #710745, #688331, #704912]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Adel Gadllah, Sebastien Lafargue, Tim Lunn,
|
||||||
|
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Stas Solovey [ru], Yosef Or Boczko [he], Rafael Ferreira [pt_BR]
|
||||||
|
|
||||||
|
3.10.1
|
||||||
|
======
|
||||||
|
* Make sure lock screen is drawn once before switching user [Giovanni; #708051]
|
||||||
|
* Fix signal strength indicators in network selector [Jasper; #708442]
|
||||||
|
* Scroll search results when focusing provider icons [Jasper; #708868]
|
||||||
|
* Add separate hover/active states to page indicators [Carlos; #708852]
|
||||||
|
* Tweak appearance of user name and avatar [Yash; #702309]
|
||||||
|
* Hide "Turn On" in network menu when disabled by hardware [Giovanni; #709635]
|
||||||
|
* Cancel open keyring prompts when the screen is locked [Florian; #708910]
|
||||||
|
* Differentiate "Not Connected" and "Off" in network menu [Giovanni; #709043]
|
||||||
|
* Make network settings items point to the right device [Giovanni; #709246]
|
||||||
|
* Remove animation of window preview titles [Sebastien; #709392]
|
||||||
|
* Add 'Notifications' switch to tray menu [Florian; #707073]
|
||||||
|
* Make dropdown arrows consistent [Carlos; #709564]
|
||||||
|
* power: Use icon from primary device for status [Jasper; #709925]
|
||||||
|
* Fix XDND drags to overview [Adel; #708887]
|
||||||
|
* Fix workspace switcher disappearing with too many workspaces [Jasper; #694881]
|
||||||
|
* Handle search results with 'special:' prefix specially [Giovanni; #707055]
|
||||||
|
* gdm: Support pre-authenticated logins from oVirt [Vinzenz; #702162]
|
||||||
|
* Use ARROW role for labels representing arrows [Alejandro; #710120]
|
||||||
|
* Make selected view in app picker persistent [Florian; #710042]
|
||||||
|
* Make network selector navigable by keyboard [Alejandro; #710144]
|
||||||
|
* Misc bug fixes [Florian, Adel, Jasper, Aleksander, Giovanni, Dan, Michael,
|
||||||
|
Tim; #709034, #709263, #698486, #709286, #709248, #709543, #696564, #703265,
|
||||||
|
#709638, #709866, #709998, #710019, #710104, #710115]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Michael Catanzaro, Vinzenz Feenstra, Adel Gadllah,
|
||||||
|
Yash Girdhar, Sebastien Lafargue, Tim Lunn, Aleksander Morgado,
|
||||||
|
Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
|
||||||
|
Dieter Verfaillie, Dan Winship
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Inaki Larranaga Murgoitio [eu], Christian Kirbach [de], Muhammet Kara [tr],
|
||||||
|
Aurimas Černius [lt], Ryan Lortie [eo], Rūdolfs Mazurs [lv],
|
||||||
|
Dušan Kazik [sk], Fran Diéguez [gl], Enrico Nicoletto [pt_BR],
|
||||||
|
Kjartan Maraas [nb], Victor Ibragimov [tg], Matej Urbančič [sl],
|
||||||
|
A S Alam [pa], Nilamdyuti Goswami [as], Daniel Mustieles [es],
|
||||||
|
Cheng-Chia Tseng [zh_HK, zh_TW], Mattias Põldaru [et], Kenneth Nielsen [da],
|
||||||
|
Milo Casagrande [it], Marek Černocký [cs], Ihar Hrachyshka [be],
|
||||||
|
Мирослав Николић [sr, sr@latin], Arash Mousavi [fa], Yuri Myasoedov [ru],
|
||||||
|
Gil Forcada [ca], Carles Ferrando [ca@valencia], Andika Triwidada [id],
|
||||||
|
Timo Jyrinki [fi], Piotr Drąg [pl], Rafael Ferreira [pt_BR],
|
||||||
|
Gabor Kelemen [hu], Yosef Or Boczko [he], Daniel Korostil [uk],
|
||||||
|
Wouter Bolsterlee [nl], António Lima [pt]
|
||||||
|
|
||||||
|
3.10.0.1
|
||||||
|
=========
|
||||||
|
* Fix login screen [Ray; #708691]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Ray Strode, Giovanni Campagna, Jasper St. Pierree
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Kjartan Maraas [nb], Marek Černocký [cs], A S Alam [pa], Daniel Mustieles [es],
|
||||||
|
Ihar Hrachyshka [be], Chao-Hsiung Liao [zh_HK], Nilamdyuti Goswami [as],
|
||||||
|
Yuri Myasoedov [ru], Baurzhan Muftakhidinov [kk]
|
||||||
|
|
||||||
|
3.10.0
|
||||||
|
======
|
||||||
|
* Fix fade effect in ScrollViews [Carlos; #708256]
|
||||||
|
* network: Resync when activating connection changes [Jasper; #708322]
|
||||||
|
* Close run dialog when the screen locks [Florian; #708218]
|
||||||
|
* Fix entry growing out of password dialogs [Florian; #708324, #703833]
|
||||||
|
* Vertically center labels in submenu items [Jasper; #708330]
|
||||||
|
* https://bugzilla.gnome.org/show_bug.cgi?id=708387 [Mike; #708387]
|
||||||
|
* Fix bluetooth icon not being added to status menu [Jasper; #708541]
|
||||||
|
* Fix GNOME 2 keyring dialogs appearing on lock screen [Florian; #708187]
|
||||||
|
* Fix passwords being cleared twice when authentication fails [Florian; #708186]
|
||||||
|
* Fix message tray appearing in a11y popup on login screen [Florian; #708380]
|
||||||
|
* Increase width of aggregate menu popup [Adel; #708472]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Adel Gadllah, Mike Gorse, Ryan Lortie, Florian Müllner, Frédéric Péters,
|
||||||
|
Carlos Soriano, Jasper St. Pierre, Rico Tzschichholz
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Daniel Șerbănescu [ro], Ryan Lortie [eo], Ihar Hrachyshka [be],
|
||||||
|
A S Alam [pa], Jiro Matsuzawa [ja], Chao-Hsiung Liao [zh_HK, zh_TW],
|
||||||
|
Piotr Drąg [pl], Kristjan SCHMIDT [eo], Daniel Korostil [uk],
|
||||||
|
Rūdolfs Mazurs [lv], Reinout van Schouwen [nl], Yosef Or Boczko [he],
|
||||||
|
Fran Diéguez [gl], António Lima [pt], Andika Triwidada [id],
|
||||||
|
Alexandre Franke [fr], Rafael Ferreira [pt_BR], Milo Casagrande [it],
|
||||||
|
Kenneth Nielsen [da], Matej Urbančič [sl]
|
||||||
|
|
||||||
|
3.9.92
|
||||||
|
======
|
||||||
|
* Don't show page indicators if there's only one page [Florian; #707363]
|
||||||
|
* Make :active style of app and non-app results consistent [Jakub; #704714]
|
||||||
|
* Fade app pages when scrolled [Florian; #707409]
|
||||||
|
* Don't block scrolling on page indicators [Carlos; #707609]
|
||||||
|
* Tweak visual appearance of folder views [Florian; #707662]
|
||||||
|
* Don't put minimized apps at the end of the app switcher [Florian; #707663]
|
||||||
|
* Merge the wayland branch [Giovanni, Neil; #707467]
|
||||||
|
* Make search entry behave better in RTL locales [Matthias, Florian; #705779]
|
||||||
|
* Allow to change app pages with pageUp/pageDown keys [Carlos; #707979]
|
||||||
|
* Set approriate a11y states on expandable menu items [Alejandro; #708038]
|
||||||
|
* Improve page indicator animation [Carlos; #707565]
|
||||||
|
* Misc bug fixes and cleanups [Florian, Olivier, Jasper, Giovanni, Magdalen,
|
||||||
|
Adel, Carlos, Rico, Joanmarie; #707308, #707430, #707508, #707557, #707600,
|
||||||
|
#707614, #707666, #707814, #707806, #707801, #707889, #707892, #707935,
|
||||||
|
#707842, #707940, #707996, #708007, #708009, #708020, #707580, #708080]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Magdalen Berns, Olivier Blin, Giovanni Campagna, Matthias Clasen,
|
||||||
|
Joanmarie Diggs, Adel Gadllah, Florian Müllner, Alejandro Piñeiro,
|
||||||
|
Neil Roberts, Carlos Soriano, Jasper St. Pierre, Jakub Steiner,
|
||||||
|
Rico Tzschichholz
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Rafael Ferreira [pt_BR], Fran Diéguez [gl], Daniel Mustieles [es],
|
||||||
|
Aurimas Černius [lt], Luca Ferretti [it], Piotr Drąg [pl],
|
||||||
|
Chao-Hsiung Liao [zh_HK, zh_TW], Timo Jyrinki [fi], Daniel Korostil [uk],
|
||||||
|
Dušan Kazik [sk], Adam Matoušek [cs], Marek Černocký [cs],
|
||||||
|
Jiro Matsuzawa [ja], Yuri Myasoedov [ru], Tobias Endrigkeit [de],
|
||||||
|
Kjartan Maraas [nb], Victor Ibragimov [tg], Мирослав Николић [sr, sr@latin],
|
||||||
|
A S Alam [pa], Khaled Hosny [ar], Andika Triwidada [id],
|
||||||
|
Nilamdyuti Goswami [as], Ihar Hrachyshka [be], Rūdolfs Mazurs [lv],
|
||||||
|
Mattias Põldaru [et], Gabor Kelemen [hu], Bruce Cowan [en_GB],
|
||||||
|
Matej Urbančič [sl], Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
|
||||||
|
Changwoo Ryu [ko], Kris Thomsen [da], Alexandre Franke [fr],
|
||||||
|
Evgeny Bobkin [ru], Baurzhan Muftakhidinov [kk], Peter Mráz [sk],
|
||||||
|
Inaki Larranaga Murgoitio [eu], Yosef Or Boczko [he]
|
||||||
|
|
||||||
|
3.9.91
|
||||||
|
======
|
||||||
|
* Improve submenu styling [Jakub; #706037]
|
||||||
|
* Fix changing slider values via keyboard [Alejandro; #706386]
|
||||||
|
* Fix accessibility of sliders [Alejandro; #706391]
|
||||||
|
* Tweak system actions style [Jakub; #706638]
|
||||||
|
* Add support for auth without username / fix Not Listed? [Ray; #706607]
|
||||||
|
* Dash: Don't show tooltips for apps with open popups [Giovanni; #705611]
|
||||||
|
* Implement new end-session/power-off dialog design [Jasper, Matthias; #706612]
|
||||||
|
* Implement building separate binaries for x11 and wayland [Giovanni; #705497]
|
||||||
|
* authPrompt: Fix controls moving when showing messages [Ray; #706670]
|
||||||
|
* Tweak padding between system status icons [Allan; #706796]
|
||||||
|
* Add a generic accessible usable by JS code [Alejandro; #648623]
|
||||||
|
* Improve keynav and accessibility of the calendar [Alejandro; #706903]
|
||||||
|
* Update to new NetworkManager APIs [Jasper; #706098]
|
||||||
|
* Hide system actions section in the lock screen [Jasper; #706852]
|
||||||
|
* Don't show other logged in users at log out [Giovanni; #707124]
|
||||||
|
* Remove "Session" subtitle heading in login dialog [Jasper; #707072]
|
||||||
|
* dash: Reload favorites when installed apps change [Giovanni; #706878]
|
||||||
|
* Don't open overview after closing last window on workspace [Florian; #662581]
|
||||||
|
* Add FocusApp DBus method [Giovanni; #654086]
|
||||||
|
* Add ShowApplications DBus method [Giovanni; #698743]
|
||||||
|
* Implement new app picker design [Carlos, Florian; #706081]
|
||||||
|
* Improve frequent apps being empty by default [Carlos, Florian; #694710]
|
||||||
|
* Extend clickable area of page indicators [Giovanni; #707314]
|
||||||
|
|
||||||
|
* Misc bug fixes [Ray, Giovanni, Jasper, Emmanuele; #706542, #706654, #706005,
|
||||||
|
#706681, #706841, #706843, #707064, #706262, #707197, #707269]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Emmanuele Bassi, Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah,
|
||||||
|
Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
|
||||||
|
Jakub Steiner, Ray Strode, Seán de Búrca
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Piotr Drąg [pl], Kjartan Maraas [nb], Victor Ibragimov [tg],
|
||||||
|
Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
|
||||||
|
Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Seán de Búrca [ga],
|
||||||
|
Fran Diéguez [gl], Daniel Mustieles [es], Dušan Kazik [sk],
|
||||||
|
Matej Urbančič [sl], Andika Triwidada [id], Jordi Mas [ca],
|
||||||
|
Ihar Hrachyshka [be]
|
||||||
|
|
||||||
|
3.9.90
|
||||||
|
======
|
||||||
|
* workspaceThumbnails: Exclude transient windows when shifting workspaces
|
||||||
|
[Bradley; #705174]
|
||||||
|
* Never show a horizontal scrollbar on lock screen [Jasper; #704327]
|
||||||
|
* authPrompt: Fix disable-user-list / Not Listed? [Ray; #705370]
|
||||||
|
* Animate the lock screen notification transitions [Giovanni; #687660]
|
||||||
|
* Wake up the screen when new notifications appear [Giovanni; #703084]
|
||||||
|
* Use StartupWMClass for application matching [Giovanni; #673657, #705801]
|
||||||
|
* dateMenu: Add style class for the clock label [Jonh; #705634]
|
||||||
|
* keyboard: Translate IBus IME name if possible [Daiki; #695673]
|
||||||
|
* power: Display single digit minutes correctly [Sebastian; #705803]
|
||||||
|
* Implement new aggregate status menu [Jasper; #705845]
|
||||||
|
* Improve triangle animation when expanding sub-menus [Tarun; #703109]
|
||||||
|
* Fix alignment of search provider icons [Tarun; #695760]
|
||||||
|
* Slide dash and workspace switcher on overview transitions [Tarun; #694262]
|
||||||
|
* Respect always-show-universal-access-status setting [Tanner; #705733]
|
||||||
|
* Handle .desktop files with capital letters [Giovanni; #706252]
|
||||||
|
* authPrompt: Add smartcard support [Ray; #683437]
|
||||||
|
* Fix call notifications in busy mode [Emilio; #666221]
|
||||||
|
* Improve triangle animation when expanding sub-menus [Tarun; #703109]
|
||||||
|
* Move message tray menu to a tray button [Jasper; #699272]
|
||||||
|
* Wi-fi dialog improvements [Jasper, Allan; #705916, #706136]
|
||||||
|
* Work towards running as wayland compositor [Giovanni]
|
||||||
|
- Switch to Mutter abstraction layer for cursor tracking [#705911]
|
||||||
|
- Add confirmation dialog for display changes [#706208]
|
||||||
|
* Use a different background in screen shield [Giovanni; #688210]
|
||||||
|
* Add fade animation before blanking the screen [Giovanni; #699112]
|
||||||
|
* Misc. bugfixes and cleanups [Jasper, Giovanni, Adel, Colin, Ray, Florian,
|
||||||
|
Magdalen; #704448, #702536, #686855, #695581, #700901, #701761, #701495,
|
||||||
|
#701848, #697833, #701731, #705664, #705840, #705898, #706089, #706153,
|
||||||
|
#704646, #706262, #706324, #703810, #703811, #704015, #706232, #705917,
|
||||||
|
#706536]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Magdalen Berns, Giovanni Campagna, Allan Day, Tanner Doshier, Adel Gadllah,
|
||||||
|
Sebastian Keller, Tarun Kumar Joshi, Florian Müllner, Bradley Pankow,
|
||||||
|
Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
|
||||||
|
Daiki Ueno, Colin Walters, Jonh Wendell
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Kjartan Maraas [nb], Aurimas Černius [lt], Yaron Shahrabani [he],
|
||||||
|
Fran Diéguez [gl], Gabor Kelemen [hu],
|
||||||
|
Juan Diego Martins da Costa Cruz [pt_BR], Inaki Larranaga Murgoitio [eu],
|
||||||
|
Yuri Myasoedov [ru], Daniel Mustieles [es], Seán de Búrca [ga],
|
||||||
|
Khaled Hosny [ar], Victor Ibragimov [tg], Friedel Wolff [af],
|
||||||
|
Marek Černocký [cs], Matej Urbančič [sl], A S Alam [pa],
|
||||||
|
Rafael Ferreira [pt_BR], Andika Triwidada [id], Dušan Kazik [sk]
|
||||||
|
|
||||||
|
3.9.5
|
||||||
|
=====
|
||||||
|
* Fix width changes of the calendar popup [Florian; #704200]
|
||||||
|
* Work towards aggregate status menu [Jasper; #702539, #704336, #704368,
|
||||||
|
#704670]
|
||||||
|
* Update design of lock screen notifications [Allan; #702305]
|
||||||
|
* Don't show empty backgroundMenu [Michael; #703868]
|
||||||
|
* Add option to limit app switcher to current workspace [Adel; #703538]
|
||||||
|
* Consolidate design of login screen and unlock dialog [Ray; #702308, #704795]
|
||||||
|
* Respect hasWorkspace property of session mode [Jasper; #698593]
|
||||||
|
* Fix fade of app menu icon in RTL locales [Jasper; #704583]
|
||||||
|
* Destroy notifications when the close button is clicked [Adel; #687016]
|
||||||
|
* Fix clicks on legacy tray icons in the message tray [Florian; #704095]
|
||||||
|
* authPrompt: Fade out message if users start to type [Ray; #704817]
|
||||||
|
* Export timestamps of global shortcuts on DBus [Bastien; #704859]
|
||||||
|
* Fix duplicate search provider results [Jasper; #700283]
|
||||||
|
* Misc bug fixes and cleanups [Lionel, Florian, Emilio, Ray, Jasper; #703859,
|
||||||
|
#703540, #704077, #703997, #704318, #704347, #704265, #704411, #704430,
|
||||||
|
#704347, #704453, #704471, #704542, #704707, #703905, #705037]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Allan Day, Adel Gadllah, Lionel Landwerlin, Florian Müllner, Bastien Nocera,
|
||||||
|
Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Colin Walters,
|
||||||
|
Michael Wood
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
eternalhui [zh_CN], Victor Ibragimov [tg], Dušan Kazik [sk],
|
||||||
|
Jiro Matsuzawa [ja], Kjartan Maraas [nb], Milo Casagrande [it],
|
||||||
|
Marek Černocký [cs], Daniel Mustieles [es], Benjamin Steinwender [de]
|
||||||
|
|
||||||
|
3.9.4
|
||||||
|
=====
|
||||||
|
* Fix chat entries not being focused when expanded [Jasper; #698778]
|
||||||
|
* Fix alignment of "Not Listed?" label [Mathieu; #702307]
|
||||||
|
* Fix alignment of time stamps in chat notifications [Carlos; #687809]
|
||||||
|
* Round the ends of slider trough [Jasper; #702825]
|
||||||
|
* Add support for "box-shadow: none" [Cosimo; #702782]
|
||||||
|
* Keep chrome below popup windows [Florian; #702338]
|
||||||
|
* Move the session list to a popup menu [Ray; #702818]
|
||||||
|
* Fix autorun notifications for "non-native" volumes [Matthias; #703418]
|
||||||
|
* dnd: Speed up by not picking on each motion event [Jasper; #703443]
|
||||||
|
* Fix management of asynchronous background loading [Lionel; #703001]
|
||||||
|
* Rework focus handling [Jasper; #700735]
|
||||||
|
* Optimize box-shadow rendering [Lionel; #689858]
|
||||||
|
* Remove support for fixed positioning in BoxLayouts [Florian; #703808]
|
||||||
|
* Misc bug fixes and cleanups [Adel, Jasper, Florian, Ray, Lionel, Emilio;
|
||||||
|
#702849, #610279, #703132, #703105, #703160, #703126, #703304, #703403,
|
||||||
|
#698593, #703442, #703565, #700901, #703874, #703807, #703893, #703909]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Mathieu Bridon, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
|
||||||
|
Fran Diéguez, Adel Gadllah, Lionel Landwerlin, Florian Müllner,
|
||||||
|
Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre, Ray Strode
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Daniel Mustieles [es],
|
||||||
|
Fran Diéguez [gl], Kjartan Maraas [nb], Andika Triwidada [id],
|
||||||
|
Benjamin Steinwender [de], Nguyễn Thái Ngọc Duy [vi], Trần Ngọc Quân [vi]
|
||||||
|
|
||||||
|
3.9.3
|
||||||
|
=====
|
||||||
|
* Don't push window thumbs when workspace switcher is hidden [Jasper; #701167]
|
||||||
|
* Tweak timeout for activating windows during XDND [Adel; #700150]
|
||||||
|
* Fix ellipsization in control buttons in app picker [Carlos; #696307]
|
||||||
|
* Fix DND to empty dash [Florian; #684618]
|
||||||
|
* Fix OSD window appearing below system modal dialogs [Rui; #701269]
|
||||||
|
* Clear clipboard on screen lock to prevent information leak [Florian; #698922]
|
||||||
|
* Allow session mode specific overrides schema [Florian; #701717]
|
||||||
|
* window-switcher: Only show windows from current workspace by default
|
||||||
|
[Florian; #701214]
|
||||||
|
* logout dialog: Show the correct text right away [Matthias; #702056]
|
||||||
|
* bluetooth: Port to bluez 5 [Emilio; #700891]
|
||||||
|
* dateMenu: Allow events to span multiple lines [Giovanni; #701231]
|
||||||
|
* gdm: Clear message queue when no more messages are pending [Jonh; #702458]
|
||||||
|
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Giovanni; #693836,
|
||||||
|
#700972, #701386, #700877, #701755, #698918, #701224, #702125, #701954,
|
||||||
|
#701849, #702121]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Matthias Clasen, Fran Diéguez, Adel Gadllah, Rui Matos,
|
||||||
|
Florian Müllner, Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre,
|
||||||
|
Jonh Wendell
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Marek Černocký [cs], Victor Ibragimov [tg], Fran Diéguez [gl],
|
||||||
|
Benjamin Steinwender [de], Cheng-Chia Tseng [zh_HK, zh_TW],
|
||||||
|
eternalhui [zh_CN], Ivaylo Valkov [bg], Kjartan Maraas [nb],
|
||||||
|
Daniel Mustieles [es]
|
||||||
|
|
||||||
|
3.9.2
|
||||||
|
=====
|
||||||
|
* Use a symbolic icon for DESKTOP windows [Matthias; #697914]
|
||||||
|
* Move paint state cache into StWidget [Jasper; #697274]
|
||||||
|
* gdm: Fix regression where domain login hint not shown [Stef; #698200]
|
||||||
|
* Make calendar keyboard navigable [Tanner; #667434]
|
||||||
|
* Hide "Open Calendar" item when no calendar app is installed [Lionel; #697725]
|
||||||
|
* Update how branding appears on login screen [Florian; #694912, #699877]
|
||||||
|
* Allow OSD popups to grow if necessary [Marta; #696523]
|
||||||
|
* Fix offset of shadow offscreen rendering [Lionel; #698301]
|
||||||
|
* Fix insensitive button preventing empty keyring password [Stef; #696304]
|
||||||
|
* Allow cancelling keyring dialog between prompts [Stef; #682830]
|
||||||
|
* modalDialog: Show spinner while working [Stef; #684438]
|
||||||
|
* overview: Only show close buttons for windows that may close [Jasper; #699269]
|
||||||
|
* Add input purpose and hints to StEntry and StIMText [Daiki; #691392]
|
||||||
|
* Set input-purpose property for password entries [Rui; #700043]
|
||||||
|
* Provide a DBus API for screencasting [Florian; #696247]
|
||||||
|
* overview: Disable hotcorner during DND [Jasper; #698484]
|
||||||
|
* polkitAgent: Allow retrying after mistyped passwords [Stef; #684431]
|
||||||
|
* Add a way to get backtraces from criticals and warnings [Giovanni; #700262]
|
||||||
|
* Allow switch-to-workspace-n keybindings in overview [Florian; #649977]
|
||||||
|
* Update man page [Matthias; #700339]
|
||||||
|
* Add FocusSearch DBus method [Florian; #700536]
|
||||||
|
* Hide frequent view when app monitoring is disabled [Florian; #699714]
|
||||||
|
* Show switcher popup for switch-to-workspace-n keybindings [Elad; #659288]
|
||||||
|
* gdm: Update the session chooser style [Allan; #695742]
|
||||||
|
* Fix some app folders getting truncated at the top [Florian; #694371]
|
||||||
|
* Don't block the message tray while a notification is showing [Jasper; #700639]
|
||||||
|
* popupMenu: Allow for an optional border for slider handle [Florian; #697917]
|
||||||
|
* Re-lock screen when restarted after a crash [Colin; #691987]
|
||||||
|
* Synchronize input source switching with key events [Rui; #697007]
|
||||||
|
* Switch input source on modifiers-only accelerator [Rui; #697008]
|
||||||
|
* Allow input source switching in message tray [Rui; #697009]
|
||||||
|
* Misc bug fixes and cleanups [Alban, Jasper, Giovanni, Florian, Rui, Tomeu,
|
||||||
|
Stef, Gustavo; #698863, #699799, #699800, #676285, #699975, #700097, #698812,
|
||||||
|
#698486, #700194, #695314, #700257, #699678, #700356, #700322, #700394,
|
||||||
|
#700409, #700595, #700625, #691746, #700620, #700807, #659288, #700784,
|
||||||
|
#700842, #700847, #700488, #700735, #696159, #700900, #700853, #700923,
|
||||||
|
#700944, #697661, #700854, #700190, #699189, #701097]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Elad Alfassa, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
|
||||||
|
Tanner Doshier, Lionel Landwerlin, Rui Matos, Simon McVittie,
|
||||||
|
Marta Milakovic, Florian Müllner, Gustavo Padovan, Jasper St. Pierre,
|
||||||
|
Daiki Ueno, Tomeu Vizoso, Stef Walter, Colin Walters
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Matej Urbančič [sl], Kjartan Maraas [nb], Victor Ibragimov [tg],
|
||||||
|
Dušan Kazik [sk], Gil Forcada [ca], Daniel Mustieles [es]
|
||||||
|
|
||||||
|
3.9.1
|
||||||
|
=====
|
||||||
|
* Add additional toggle-overview keybinding [Matthias; #698251]
|
||||||
|
* Disable <super> shortcut when sticky keys are enabled [Matthias; #685974]
|
||||||
|
* Disable tray context menu while a notification displays [Jasper; #695800]
|
||||||
|
* Watch GApplication busy state [Cosimo; #697207]
|
||||||
|
* Disable style transitions if animations are disabled [Jasper; #698391]
|
||||||
|
* Filter out hidden applications from "Frequent" view [Giovanni; #696949]
|
||||||
|
* Fix window previews swapping place randomly [Jasper; #694469, #698776]
|
||||||
|
* Add support for serialized GIcons in remote search providers [Cosimo; #698761]
|
||||||
|
* Fix hotcorner regression in RTL locales [Jasper; #698884]
|
||||||
|
* Allow some keybindings to work while a top bar menu is open [Florian; #698938]
|
||||||
|
* Make open-app-menu keybinding a toggle action [Florian; #686756]
|
||||||
|
* Only recognize common URL schemes in notification messages [Monica; #661225]
|
||||||
|
* Misc fixes and cleanups [Tim, Jasper, Florian, Giovanni, Owen; #698531,
|
||||||
|
#698622, #698427, #698483, #698513, #697203, #698959, #698918, #699029,
|
||||||
|
#699075, #696720, #649748]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Monica Chelliah, Matthias Clasen, Tim Lunn,
|
||||||
|
Florian Müllner, Jasper St. Pierre, Michael Wood, Owen W. Taylor
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Fran Diéguez [gl], Muhammet Kara [tr], Daniel Mustieles [es],
|
||||||
|
Gil Forcada [ia], Anish A [ml], Dimitris Spingos [el], Marek Černocký [cs],
|
||||||
|
Žygimantas Beručka [lt]
|
||||||
|
|
||||||
|
3.8.1
|
||||||
|
=====
|
||||||
|
* Clip window group during startup animation [Jasper; #696323]
|
||||||
|
* Check for logind rather than systemd [Martin; #696252]
|
||||||
|
* Don't special-case last remote search provider position [Giovanni; #694974]
|
||||||
|
* Fix memory leaks [Ray, Jasper; ##697119, #697295, #697300, #697395]
|
||||||
|
* AppSwitcherPopup: Activate only the selected window if any [Rui; #697480]
|
||||||
|
* Enable screen recorder keybinding in all modes [Florian; #696200]
|
||||||
|
* Remove box-shadow from screen shield for performance reasons [Adel; #697274]
|
||||||
|
* Add support for -st-natural-width/height CSS properties [Giovanni; #664411]
|
||||||
|
* Remove excessive padding from notification buttons [Allan; #664411]
|
||||||
|
* Fix thumbnail dragging in overview [Jasper; #697504]
|
||||||
|
* theme-node: Add get_url()/lookup_url() methods [Florian; #693688]
|
||||||
|
* Misc bug fixes and cleanups [Jasper, Rui, Colin, David, Ray, Matthias:
|
||||||
|
#695859, #696259, #696585, #696436, #697432, #697435, #697560, #697722,
|
||||||
|
#697709]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah, David Gumberg,
|
||||||
|
Rui Matos, Florian Müllner, Martin Pitt, Jasper St. Pierre, Ray Strode,
|
||||||
|
Colin Walters
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Daniel Martinez [an], Bruce Cowan [en_GB], Khaled Hosny [ar],
|
||||||
|
Ihar Hrachyshka [be], Aron Xu [zh_CN], Wojciech Szczęsny [pl],
|
||||||
|
Inaki Larranaga Murgoitio [eu], Kjartan Maraas [nb],
|
||||||
|
Милош Поповић [sr, sr@latin], Trần Ngọc Quân [vi]
|
||||||
|
|
||||||
|
3.8.0.1
|
||||||
|
=======
|
||||||
|
* Background bug fixes [Ray; #696712]
|
||||||
|
|
||||||
|
3.8.0
|
||||||
|
=====
|
||||||
|
* Remove blur and desaturation from lock screen [Jasper; #696322]
|
||||||
|
* Remove scroll view fade near edges [Adel; #696404]
|
||||||
|
* dateMenu: Open calendar component when using Evolution [Florian; #696432]
|
||||||
|
* Fix unlocking on fast user switch [Cosimo; #696287]
|
||||||
|
* Tweak screen shield animation [Rui; #696380]
|
||||||
|
* Fix major memory leak when changing backgrounds [Ray; #696157]
|
||||||
|
* Miscellaneous bug fixes [Jasper, Adel, Florian; #696199, #696212, #696422,
|
||||||
|
#696447, #696235]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner,
|
||||||
|
Jasper St. Pierre, Ray Strode
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Alexandre Franke [fr], Victor Ibragimov [tg], Arash Mousavi [fa],
|
||||||
|
Gabor Kelemen [hu], Sandeep Sheshrao Shedmake [mr], ManojKumar Giri [or],
|
||||||
|
Shantha kumar [ta], Rajesh Ranjan [hi], Stas Solovey [ru],
|
||||||
|
Shankar Prasad [kn], Dušan Kazik [sk], Ihar Hrachyshka [be],
|
||||||
|
Wouter Bolsterlee [nl], Kris Thomsen [da], Jiro Matsuzawa [ja],
|
||||||
|
Daniel Korostil [uk], Ani Peter [ml], Krishnababu Krothapalli [te],
|
||||||
|
Mantas Kriaučiūnas [lt], Praveen Illa [te]
|
||||||
|
|
||||||
|
3.7.92
|
||||||
|
======
|
||||||
|
* Drop fallback lock implementation [Florian; #693403]
|
||||||
|
* Don't let the user trigger message-tray when in fullscreen [Jasper; #694997]
|
||||||
|
* Scroll search results when using keynav [Jasper; #689681]
|
||||||
|
* Allow raising the shield by starting to type the password [Jasper; #686740]
|
||||||
|
* Improve tracking of fullscreen windows [Owen; #649748]
|
||||||
|
* Improve animation of new windows in overview [Giovanni; #695582]
|
||||||
|
* workspace switcher: Animate new workspaces created by DND [Giovanni; #685285]
|
||||||
|
* Give user time to read messages on login screen [Ray; #694688]
|
||||||
|
* Misc bug fixes and cleanups [Jasper, Ray, Florian, Cosimo, Giovanni, Adel,
|
||||||
|
Stef, Takao, Rui, Neil; #695154, #694993, #695272, #691578, #694321, #695338,
|
||||||
|
#695409, #695458, #695526, #695601, #695471, #695324, #695650, #695656,
|
||||||
|
#695659, #695485, #695395, #694951, #695824, #695841, #695801, #694321,
|
||||||
|
#693708, #695800, #695607, #695882, #691578, #685851, #694371, #690857,
|
||||||
|
#694092, #695747, #696007, #693438, #696064, #696102
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Allan Day, Takao Fujiwara, Adel Gadllah,
|
||||||
|
Tim Lunn, Rui Matos, Florian Müllner, Neil Roberts, Jasper St. Pierre,
|
||||||
|
Ray Strode, Stef Walter, Colin Walters, Owen W. Taylor
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW],
|
||||||
|
Yuri Myasoedov [ru], Gheyret Kenji [ug], Baurzhan Muftakhidinov [kk],
|
||||||
|
Ville-Pekka Vainio [fi], Matej Urbančič [sl],
|
||||||
|
Мирослав Николић [sr, sr@latin], Rūdolfs Mazurs [lv], Christian Kirbach [de],
|
||||||
|
Andika Triwidada [id], Gil Forcada [ca], Mattias Põldaru [et],
|
||||||
|
Duarte Loreto [pt], Adam Matoušek [cs], Changwoo Ryu [ko],
|
||||||
|
Ihar Hrachyshka [be], Carles Ferrando [ca@valencia], Sweta Kothari [gu]
|
||||||
|
|
||||||
|
3.7.91
|
||||||
|
======
|
||||||
|
* overview: Fade out controls during DND that are not targets [Cosimo; #686984]
|
||||||
|
* overview: Keep open when a Control key is held [Florian; #686984]
|
||||||
|
* Improve login screen => session transition [Ray; #694321]
|
||||||
|
* Center application grid horizontally [Florian; #694261]
|
||||||
|
* Fix hiding panel when fullscreen windows span multiple monitors [Adel; 646861]
|
||||||
|
* Tweak thresholds of pressure barrier [Jasper; #694467]
|
||||||
|
* Tweak window picker layout [Jasper; #694902]
|
||||||
|
* Expose key grab DBus API to gnome-settings-daemon [Florian; #643111]
|
||||||
|
* Don't always show message tray in overview, add message indicator
|
||||||
|
[Cosimo; #687787]
|
||||||
|
* Tweak startup animation [Ray; #694326]
|
||||||
|
* Add OSD popups and expose them to gnome-settings-daemon [Florian; #613543]
|
||||||
|
* Move loupe icon to the start of the search entry [Jasper; #695069]
|
||||||
|
* Don't show the input switcher with less than 2 items [Rui; 695000]
|
||||||
|
* Fix auto-completion of system modals immediately upon display [Stef; #692937]
|
||||||
|
* Ignore workspaces in alt-tab [Florian; #661156]
|
||||||
|
* Disable copying text from password entries [Florian; #695104]
|
||||||
|
* Use standard styling for ibus candidate popups [Allan; #694796]
|
||||||
|
* Fix calendar changing height on month changes [Giovanni; #641383]
|
||||||
|
* Port the hot corner to use pressure barriers [Jasper; #663661]
|
||||||
|
* Misc bug fixes and cleanups: [Hashem, Giovanni, Alban, Jasper, Cosimo,
|
||||||
|
Florian, Adel, Daniel, Matthias, Ray, Rui, Guillaume, Stef; #685849, #690643,
|
||||||
|
#694292, #693814, #694234, #694365, #694287, #694336, #694256, #694261,
|
||||||
|
#663601, #694441, #694284, #694463, #694475, #687248, #694394, #694320,
|
||||||
|
#694701, #694784, #694858, #694906, #694327, #694876, #694905, #694969,
|
||||||
|
#694970, #694988, #695006, #695001, #694998, #695023, #695002, #695073,
|
||||||
|
#695126, #687748, #694837, #693907, #679851, #694988]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Alban Crequy, Allan Day,
|
||||||
|
Guillaume Desmottes, Adel Gadllah, Rui Matos, Daniel Mustieles,
|
||||||
|
Hashem Nasarat, Jasper St. Pierre, Ray Strode, Stef Walter
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Yuri Myasoedov [ru], Adam Matoušek [cs], Piotr Drąg [pl], Matej Urbančič [sl],
|
||||||
|
Sweta Kothari [gu], Kjartan Maraas [nb], Nguyễn Thái Ngọc Duy [vi],
|
||||||
|
Chao-Hsiung Liao [zh_HK, zh_TW], Dimitris Spingos [el],
|
||||||
|
Inaki Larranaga Murgoitio [eu], Luca Ferretti [it], A S Alam [pa],
|
||||||
|
Gheyret Kenji [ug], Stas Solovey [ru], Enrico Nicoletto [pt_BR],
|
||||||
|
Fran Diéguez [gl], Daniel Mustieles [es], Aurimas Černius [lt]
|
||||||
|
|
||||||
|
3.7.90
|
||||||
|
======
|
||||||
|
* Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339]
|
||||||
|
* Implement middle-click paste [Jasper; #645019]
|
||||||
|
* Fix top bar transition between session modes [Rui; #692966]
|
||||||
|
* Trigger the message tray with downward pressure [Jasper; #677215]
|
||||||
|
* Don't ask for a password on shutdown [Adel; #693385]
|
||||||
|
* Add a context menu to the message tray [Adel; #691035, #693887]
|
||||||
|
* Use unicode formatting in the date menu [Matthias; #689251]
|
||||||
|
* Use proper ellipsis instead of three dots [Jeremy; #689542]
|
||||||
|
* Tweak screen shield animation [Giovanni; #691964]
|
||||||
|
* Always hide the OSK when showing the message tray [Florian; #662687]
|
||||||
|
* Support sound in notifications [Giovanni; #642831]
|
||||||
|
* Place application popup menus above chrome [Jasper; #633620]
|
||||||
|
* Hide overview elements while searching [Cosimo; #682050]
|
||||||
|
* Implement updated IBus candidate popup designs [Rui; #691902]
|
||||||
|
* Add support for enable-animations preference [Cosimo; #655746]
|
||||||
|
* Don't always show the message tray in the overview [Cosimo; #693987]
|
||||||
|
* Improve arrangement of window previews [Adel; #690313]
|
||||||
|
* Remove builtin settings provider [Giovanni; #690824]
|
||||||
|
* Minimize fullscreen windows when they end up in the background [Adel; #693991]
|
||||||
|
* Add context menu to the background actor [Jasper; #681540]
|
||||||
|
* Handle backgrounds in the shell, improve startup animation [Ray; #682429]
|
||||||
|
* Hide universal access menu when not needed [Giovanni; #681528]
|
||||||
|
* Implement updated app picker designs [Florian; #694192]
|
||||||
|
* Improve login manager -> session transition [Ray; #694062]
|
||||||
|
* Don't use a grid layout in window picker [Adel; #694210]
|
||||||
|
* Use scroll wheel for workspace switching rather than zoom [Florian; #686639]
|
||||||
|
* Misc bug fixes and cleanups: [Jasper, Florian, Debarshi, Adel, Matthias,
|
||||||
|
Giovanni, Daiki, Rico, Bastien, Cosimo, Ray, Allan, Antonio; #693284,
|
||||||
|
#692680, #691746, #693303, #693162, #693161, #693522, #693385, #691715,
|
||||||
|
#688915, #689106, #682429, #693570, #693737, #693458, #692845, 693836,
|
||||||
|
#681540, #679925, #688227, #692773, #693909, #683288, #693854, #693746,
|
||||||
|
#693931, #693924, #693940, #693970, #693935, #693937, #693974, #693936,
|
||||||
|
#693975, #693822, #694030, #685849, #694052, #694035, #694038, #694079,
|
||||||
|
#694064, #681735, #694100, #694057, #694070, #693572, #693896, #686984,
|
||||||
|
#694123, #694125, #693756, #693757, #687556, #694215, 694062, #694227,
|
||||||
|
#694240, #694234, #694264, 694276, 694282, #694241, #689394, #694202,
|
||||||
|
#694265, #694289, #691806, #694290, #694296]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day,
|
||||||
|
António Fernandes, Adel Gadllah, Rui Matos, Florian Müllner, Bastien Nocera,
|
||||||
|
Debarshi Ray, Neil Roberts, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
|
||||||
|
Daiki Ueno
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Yasumichi Akahoshi [ja], Yoji TOYODA [ja], Dušan Kazik [sk],
|
||||||
|
Wouter Bolsterlee [nl], Matej Urbančič [sl], Gheyret Kenji [ug],
|
||||||
|
Ivaylo Valkov [bg], Daniel Korostil [uk], Gheyret Kenji [ug],
|
||||||
|
Daniel Mustieles [es], Anish A [ml], Gil Forcada [ca],
|
||||||
|
Carles Ferrando [ca@valencia], Мирослав Николић [sr, sr@latin],
|
||||||
|
Aurimas Černius [lt], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
|
||||||
|
Piotr Drąg [pl], Luca Ferretti [it], A S Alam [pa]
|
||||||
|
|
||||||
|
3.7.5
|
||||||
|
=====
|
||||||
|
* MessageTray: pass keyboard events to tray icons [Giovanni; #687425]
|
||||||
|
* network: add support for virtual devices (vlan, bond, bridge) [Dan; #677144]
|
||||||
|
* gdm: Allow right-clicking buttons for left-handed users [Jasper; #688748]
|
||||||
|
* Make list search results span all available horizontal space [Tanner; #691967]
|
||||||
|
* Make Show-Applications button depress when held down [Hashem; #692319]
|
||||||
|
* Set a max width on search results [Cosimo; #692453]
|
||||||
|
* Reserve scrollbar allocation for automatic policy [Cosimo; #686881]
|
||||||
|
* Improve scaling algorithm for window thumbnails [Jasper; #686944]
|
||||||
|
* Fix launching settings panels after g-c-c changes [Jasper; #692483]
|
||||||
|
* Stop launching applications from empty searches [Hashem; #692391]
|
||||||
|
* Implement per-source notification filtering [Giovanni; #685926]
|
||||||
|
* ScreenShield: Omit ActiveChanged() signal at end of fade [Giovanni; #691964]
|
||||||
|
* ScreenShield: Lower the shield on idle before locking [Giovanni; #692560]
|
||||||
|
* Make previews of minimized windows translucent in overview [Florian; #692999]
|
||||||
|
* windowManager: Respect icon geometry when minimizing [Florian; #692997]
|
||||||
|
* ScreenShield: Only show lock icon when actually locked [Giovanni; #693007]
|
||||||
|
* general: Use & instead of 'and' for Settings panels [Jeremy; #689590]
|
||||||
|
* network: Add support for new ModemManager1 interface [Aleksander; #687359]
|
||||||
|
* network: Handle LTE-only modems as GSM ones [Aleksander; #688144]
|
||||||
|
* mobile-providers: Port to libnm-gtk [Aleksander; #688943]
|
||||||
|
* general: Consistently use Title Case in top bar [Jeremy; #689589]
|
||||||
|
* panel: Add :overview pseudo class while in overview [Florian; #693218]
|
||||||
|
* sessionMode: Add support for mode-specific styling [Florian; #693219]
|
||||||
|
* loginManager: Make suspend a NOP in the ConsoleKit patch [Florian; #693162]
|
||||||
|
* screenShield: Inhibit suspend until the screen is locked [Florian; #686482]
|
||||||
|
* Misc bug fixes and cleanups [Jasper, Giovanni, Rui, Cosimo, Florian, Stefano,
|
||||||
|
Adel, Yanko; #691745, #691731, #690171, #689091, #691976, #691963, #684279,
|
||||||
|
#692052, #692091, #642831, #692454, #692715, #692678, #692723, #692677,
|
||||||
|
#683986, #692693, #692749, #692948, #692995, #692996, #692994, #677215,
|
||||||
|
#692586, #693067, #693031, #693049, #643111, #693161, #693220]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Tanner Doshier,
|
||||||
|
Stefano Facchini, Adel Gadllah, Yanko Kaneti, Rui Matos, Aleksander Morgado,
|
||||||
|
Florian Müllner, Hashem Nasarat, Jasper St. Pierre, Dan Winship
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Duarte Loreto [pt], Daniel Mustieles [es], Kjartan Maraas [nb],
|
||||||
|
Nilamdyuti Goswami [as], Мирослав Николић [sr,sr@latin],
|
||||||
|
Tobias Endrigkeit [de], Fabio Tomat [fur], Matej Urbančič [sl], A S Alam [pa],
|
||||||
|
Inaki Larranaga Murgoitio [eu], Piotr Drąg [pl], Wouter Bolsterlee [nl],
|
||||||
|
Gheyret Kenji [ug], Yaron Shahrabani [he], Chao-Hsiung Liao [zh_HK,zh_TW],
|
||||||
|
Milo Casagrande [it], Benjamin Steinwender [de]
|
||||||
|
|
||||||
|
3.7.4.1
|
||||||
|
=======
|
||||||
|
* userMenu: Use show-full-name-in-top-bar setting [Bastien; #689561]
|
||||||
|
* dateMenu: Add "Open Clocks" entry [Mathieu; #644390]
|
||||||
|
* screenshot: Immediately show the flash spot [Jasper; #691875]
|
||||||
|
* Misc. bug fixes [Rico, Jeremy]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Jeremy Bicha, Mathieu Bridon, Bastien Nocera, Jasper St. Pierre,
|
||||||
|
Rico Tzschichholz
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Ihar Hrachyshka [be]
|
||||||
|
|
||||||
|
3.7.4
|
||||||
|
=====
|
||||||
|
* Make menu separators crisp [Giovanni, Allan; #641745]
|
||||||
|
* power: Update for new D-Bus name [Bastien; #690506]
|
||||||
|
* Add smooth scrolling support [Jasper; #687573]
|
||||||
|
* Tweak notification layout [Allan; #688506]
|
||||||
|
* Ping the active window when using the app menu [Giovanni; #684340]
|
||||||
|
* Make password entries insensitive after submission [Jasper; #690594, #690895]
|
||||||
|
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
|
||||||
|
* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
|
||||||
|
* lookingGlass: Move to an inspect() function [Jasper; #690726]
|
||||||
|
* Make OSK animation quicker, snappier [Rui; #688642]
|
||||||
|
* Allow to close chat notifications with Escape [Jasper; #690897]
|
||||||
|
* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
|
||||||
|
* Add a SelectArea() DBus method [Cosimo; #687954]
|
||||||
|
* Support non-absolute paths when saving screenshots [Cosimo; #688004]
|
||||||
|
* OSK: Fix extended keys popups [Rui; #674955]
|
||||||
|
* Don't hide or show the keyboard immediately [Rui; #688646]
|
||||||
|
* Improve padding in power menu [Giovanni; #689297]
|
||||||
|
* Add per-window input source switching [Rui; #691414]
|
||||||
|
* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
|
||||||
|
#690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
|
||||||
|
#690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
|
||||||
|
#685513, #689263, #691553, #691720, #691743, #691750]
|
||||||
|
|
||||||
|
Contributors:
|
||||||
|
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
|
||||||
|
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
|
||||||
|
Dan Winship
|
||||||
|
|
||||||
|
Translations:
|
||||||
|
Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
|
||||||
|
Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
|
||||||
|
Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
|
||||||
|
Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
|
||||||
|
Aron Xu [zh_CN], Gheyret Kenji [ug]
|
||||||
|
|
||||||
3.7.3
|
3.7.3
|
||||||
=====
|
=====
|
||||||
* Add 'No Messages' label when message tray is empty [Victoria; #686738]
|
* Add 'No Messages' label when message tray is empty [Victoria; #686738]
|
||||||
|
|||||||
@@ -17,5 +17,4 @@ libgnome_shell_browser_plugin_la_SOURCES = \
|
|||||||
|
|
||||||
libgnome_shell_browser_plugin_la_CFLAGS = \
|
libgnome_shell_browser_plugin_la_CFLAGS = \
|
||||||
$(BROWSER_PLUGIN_CFLAGS) \
|
$(BROWSER_PLUGIN_CFLAGS) \
|
||||||
-DG_DISABLE_DEPRECATED \
|
|
||||||
-DG_LOG_DOMAIN=\"GnomeShellBrowserPlugin\"
|
-DG_LOG_DOMAIN=\"GnomeShellBrowserPlugin\"
|
||||||
|
|||||||
@@ -13,9 +13,7 @@
|
|||||||
* General Public License for more details.
|
* General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||||
|
|||||||
200
configure.ac
200
configure.ac
@@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ(2.63)
|
AC_PREREQ(2.63)
|
||||||
AC_INIT([gnome-shell],[3.7.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
AC_INIT([gnome-shell],[3.11.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||||
|
|
||||||
AC_CONFIG_HEADERS([config.h])
|
AC_CONFIG_HEADERS([config.h])
|
||||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||||
@@ -16,8 +16,7 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
|||||||
|
|
||||||
# Checks for programs.
|
# Checks for programs.
|
||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
# Needed for per-target cflags, like in gnomeshell-taskpanel
|
AC_PROG_CXX
|
||||||
AM_PROG_CC_C_O
|
|
||||||
|
|
||||||
# Initialize libtool
|
# Initialize libtool
|
||||||
LT_PREREQ([2.2.6])
|
LT_PREREQ([2.2.6])
|
||||||
@@ -26,9 +25,6 @@ LT_INIT([disable-static])
|
|||||||
# i18n
|
# i18n
|
||||||
IT_PROG_INTLTOOL([0.40])
|
IT_PROG_INTLTOOL([0.40])
|
||||||
|
|
||||||
AM_GNU_GETTEXT([external])
|
|
||||||
AM_GNU_GETTEXT_VERSION([0.17])
|
|
||||||
|
|
||||||
GETTEXT_PACKAGE=gnome-shell
|
GETTEXT_PACKAGE=gnome-shell
|
||||||
AC_SUBST(GETTEXT_PACKAGE)
|
AC_SUBST(GETTEXT_PACKAGE)
|
||||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
|
||||||
@@ -55,133 +51,122 @@ if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
|
|||||||
AC_MSG_RESULT(yes)
|
AC_MSG_RESULT(yes)
|
||||||
build_recorder=true
|
build_recorder=true
|
||||||
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
|
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
|
||||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
|
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0)
|
||||||
else
|
else
|
||||||
AC_MSG_RESULT(no)
|
AC_MSG_RESULT(no)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||||
|
|
||||||
CLUTTER_MIN_VERSION=1.11.11
|
AC_ARG_ENABLE([systemd],
|
||||||
|
AS_HELP_STRING([--enable-systemd], [Use systemd]),
|
||||||
|
[enable_systemd=$enableval],
|
||||||
|
[enable_systemd=auto])
|
||||||
|
AS_IF([test x$enable_systemd != xno], [
|
||||||
|
AC_MSG_CHECKING([for libsystemd-journal])
|
||||||
|
PKG_CHECK_EXISTS([libsystemd-journal],
|
||||||
|
[have_systemd=yes
|
||||||
|
AC_DEFINE([HAVE_SYSTEMD], [1], [Define if we have systemd])],
|
||||||
|
[have_systemd=no])
|
||||||
|
AC_MSG_RESULT($have_systemd)
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_MSG_RESULT($enable_systemd)
|
||||||
|
|
||||||
|
CLUTTER_MIN_VERSION=1.15.90
|
||||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||||
GJS_MIN_VERSION=1.33.2
|
GJS_MIN_VERSION=1.39.0
|
||||||
MUTTER_MIN_VERSION=3.7.3
|
MUTTER_MIN_VERSION=3.11.1
|
||||||
GTK_MIN_VERSION=3.3.9
|
GTK_MIN_VERSION=3.7.9
|
||||||
GIO_MIN_VERSION=2.35.0
|
GIO_MIN_VERSION=2.37.0
|
||||||
LIBECAL_MIN_VERSION=3.5.3
|
LIBECAL_MIN_VERSION=3.5.3
|
||||||
LIBEDATASERVER_MIN_VERSION=3.5.3
|
LIBEDATASERVER_MIN_VERSION=3.5.3
|
||||||
LIBEDATASERVERUI_MIN_VERSION=3.5.3
|
|
||||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
||||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
|
||||||
POLKIT_MIN_VERSION=0.100
|
POLKIT_MIN_VERSION=0.100
|
||||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||||
GCR_MIN_VERSION=3.3.90
|
GCR_MIN_VERSION=3.7.5
|
||||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
|
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
|
||||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
NETWORKMANAGER_MIN_VERSION=0.9.8
|
||||||
NETWORKMANAGER_MIN_VERSION=0.9.7
|
|
||||||
PULSE_MIN_VERS=2.0
|
PULSE_MIN_VERS=2.0
|
||||||
|
|
||||||
# Collect more than 20 libraries for a prize!
|
# Collect more than 20 libraries for a prize!
|
||||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||||
libxml-2.0
|
libxml-2.0
|
||||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||||
atk-bridge-2.0
|
atk-bridge-2.0
|
||||||
libmutter >= $MUTTER_MIN_VERSION
|
|
||||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||||
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
|
|
||||||
$recorder_modules
|
$recorder_modules
|
||||||
gdk-x11-3.0 libsoup-2.4
|
gdk-x11-3.0 libsoup-2.4
|
||||||
gl
|
xtst
|
||||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||||
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
||||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||||
libcanberra
|
libcanberra libcanberra-gtk3
|
||||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
polkit-agent-1 >= $POLKIT_MIN_VERSION
|
||||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
|
||||||
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
||||||
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||||
gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
|
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION"
|
||||||
|
if test x$have_systemd = xyes; then
|
||||||
|
SHARED_PCS="${SHARED_PCS} libsystemd-journal"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS)
|
||||||
|
PKG_CHECK_MODULES(MUTTER, libmutter >= $MUTTER_MIN_VERSION)
|
||||||
|
PKG_CHECK_MODULES(MUTTER_WAYLAND, [libmutter-wayland >= $MUTTER_MIN_VERSION],
|
||||||
|
[MUTTER_WAYLAND_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter-wayland`
|
||||||
|
AC_SUBST(MUTTER_WAYLAND_TYPELIB_DIR)
|
||||||
|
have_mutter_wayland=yes],
|
||||||
|
[have_mutter_wayland=no])
|
||||||
|
|
||||||
|
AM_CONDITIONAL(HAVE_MUTTER_WAYLAND, test $have_mutter_wayland != no)
|
||||||
|
|
||||||
|
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||||
|
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
||||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||||
|
|
||||||
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
||||||
|
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||||
|
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
|
||||||
|
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
|
||||||
|
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(browser-plugin,
|
||||||
|
[AS_HELP_STRING([--enable-browser-plugin],
|
||||||
|
[Enable browser plugin [default=yes]])],,
|
||||||
|
enable_browser_plugin=yes)
|
||||||
|
AS_IF([test x$enable_browser_plugin = xyes], [
|
||||||
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
||||||
|
])
|
||||||
|
AM_CONDITIONAL(BUILD_BROWSER_PLUGIN, test x$enable_browser_plugin = xyes)
|
||||||
|
|
||||||
|
PKG_CHECK_MODULES(BLUETOOTH, gnome-bluetooth-1.0 >= 3.9.0,
|
||||||
|
[AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
|
||||||
|
AC_SUBST([HAVE_BLUETOOTH],[1])],
|
||||||
|
[AC_DEFINE([HAVE_BLUETOOTH],[0])
|
||||||
|
AC_SUBST([HAVE_BLUETOOTH],[0])])
|
||||||
|
|
||||||
|
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
|
||||||
|
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
||||||
|
AC_SUBST(CALENDAR_SERVER_LIBS)
|
||||||
|
|
||||||
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
|
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
|
||||||
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||||
|
|
||||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||||
|
|
||||||
saved_CFLAGS=$CFLAGS
|
|
||||||
saved_LIBS=$LIBS
|
|
||||||
CFLAGS=$GNOME_SHELL_CFLAGS
|
|
||||||
LIBS=$GNOME_SHELL_LIBS
|
|
||||||
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
|
|
||||||
CFLAGS=$saved_CFLAGS
|
|
||||||
LIBS=$saved_LIBS
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
|
||||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
|
||||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
|
||||||
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
|
|
||||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2.2)
|
|
||||||
|
|
||||||
AC_MSG_CHECKING([for bluetooth support])
|
|
||||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
|
||||||
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
|
|
||||||
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
|
|
||||||
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
|
|
||||||
AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"])
|
|
||||||
AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
|
|
||||||
AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
|
|
||||||
AC_SUBST([HAVE_BLUETOOTH],[1])
|
|
||||||
AC_MSG_RESULT([yes])],
|
|
||||||
[AC_DEFINE([HAVE_BLUETOOTH],[0])
|
|
||||||
AC_SUBST([HAVE_BLUETOOTH],[0])
|
|
||||||
AC_MSG_RESULT([no])])
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
|
|
||||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
|
||||||
AC_SUBST(CALENDAR_SERVER_LIBS)
|
|
||||||
|
|
||||||
AC_ARG_WITH(systemd,
|
|
||||||
AS_HELP_STRING([--with-systemd],
|
|
||||||
[Add systemd support]),
|
|
||||||
[with_systemd=$withval], [with_systemd=auto])
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(SYSTEMD,
|
|
||||||
[libsystemd-login libsystemd-daemon],
|
|
||||||
[have_systemd=yes], [have_systemd=no])
|
|
||||||
|
|
||||||
if test "x$with_systemd" = "xauto" ; then
|
|
||||||
if test x$have_systemd = xno ; then
|
|
||||||
use_systemd=no
|
|
||||||
else
|
|
||||||
use_systemd=yes
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
use_systemd=$with_systemd
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test "x$use_systemd" = "xyes"; then
|
|
||||||
if test "x$have_systemd" = "xno"; then
|
|
||||||
AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
|
|
||||||
fi
|
|
||||||
|
|
||||||
AC_DEFINE(WITH_SYSTEMD, 1, [systemd support])
|
|
||||||
fi
|
|
||||||
|
|
||||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
|
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
|
||||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
|
|
||||||
AC_SUBST(MUTTER_GIR_DIR)
|
AC_SUBST(MUTTER_GIR_DIR)
|
||||||
|
|
||||||
|
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
|
||||||
AC_SUBST(MUTTER_TYPELIB_DIR)
|
AC_SUBST(MUTTER_TYPELIB_DIR)
|
||||||
|
|
||||||
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
|
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
|
||||||
AC_SUBST(GJS_CONSOLE)
|
AC_SUBST(GJS_CONSOLE)
|
||||||
|
|
||||||
|
GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable glib_compile_resources gio-2.0`
|
||||||
|
AC_SUBST(GLIB_COMPILE_RESOURCES)
|
||||||
|
|
||||||
AC_CHECK_FUNCS(fdwalk)
|
AC_CHECK_FUNCS(fdwalk)
|
||||||
AC_CHECK_FUNCS(mallinfo)
|
AC_CHECK_FUNCS(mallinfo)
|
||||||
AC_CHECK_HEADERS([sys/resource.h])
|
AC_CHECK_HEADERS([sys/resource.h])
|
||||||
@@ -199,16 +184,6 @@ fi
|
|||||||
|
|
||||||
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
|
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
|
||||||
AM_PATH_GLIB_2_0()
|
AM_PATH_GLIB_2_0()
|
||||||
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
|
|
||||||
AC_SUBST(G_IR_SCANNER)
|
|
||||||
G_IR_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
|
|
||||||
AC_SUBST(G_IR_COMPILER)
|
|
||||||
G_IR_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
|
|
||||||
AC_SUBST(G_IR_GENERATE)
|
|
||||||
GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
|
|
||||||
AC_SUBST(GIRDIR)
|
|
||||||
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
|
|
||||||
AC_SUBST(TYPELIBDIR)
|
|
||||||
|
|
||||||
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
|
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
|
||||||
|
|
||||||
@@ -224,36 +199,7 @@ if test "$enable_man" != no; then
|
|||||||
fi
|
fi
|
||||||
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
||||||
|
|
||||||
# Stay command-line compatible with the gnome-common configure option. Here
|
GNOME_COMPILE_WARNINGS([error])
|
||||||
# minimum/yes/maximum are the same, however.
|
|
||||||
AC_ARG_ENABLE(compile_warnings,
|
|
||||||
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
|
|
||||||
enable_compile_warnings=error)
|
|
||||||
|
|
||||||
changequote(,)dnl
|
|
||||||
if test "$enable_compile_warnings" != no ; then
|
|
||||||
if test "x$GCC" = "xyes"; then
|
|
||||||
case " $CFLAGS " in
|
|
||||||
*[\ \ ]-Wall[\ \ ]*) ;;
|
|
||||||
*) CFLAGS="$CFLAGS -Wall" ;;
|
|
||||||
esac
|
|
||||||
case " $CFLAGS " in
|
|
||||||
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
|
|
||||||
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
|
|
||||||
esac
|
|
||||||
if test "$enable_compile_warnings" = error ; then
|
|
||||||
case " $CFLAGS " in
|
|
||||||
*[\ \ ]-Werror[\ \ ]*) ;;
|
|
||||||
*) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
changequote([,])dnl
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(jhbuild-wrapper-script,
|
|
||||||
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
|
|
||||||
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
|
|
||||||
|
|
||||||
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
|
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
|
||||||
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
|
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<KeyListEntries schema="org.gnome.shell.keybindings"
|
|
||||||
group="system"
|
|
||||||
_name="Screenshots"
|
|
||||||
wm_name="GNOME Shell"
|
|
||||||
package="gnome-shell">
|
|
||||||
|
|
||||||
<KeyListEntry name="toggle-recording"
|
|
||||||
_description="Record a screencast"/>
|
|
||||||
|
|
||||||
</KeyListEntries>
|
|
||||||
|
|
||||||
@@ -11,6 +11,9 @@
|
|||||||
<KeyListEntry name="focus-active-notification"
|
<KeyListEntry name="focus-active-notification"
|
||||||
_description="Focus the active notification"/>
|
_description="Focus the active notification"/>
|
||||||
|
|
||||||
|
<KeyListEntry name="toggle-overview"
|
||||||
|
_description="Show the overview"/>
|
||||||
|
|
||||||
<KeyListEntry name="toggle-application-view"
|
<KeyListEntry name="toggle-application-view"
|
||||||
_description="Show all applications"/>
|
_description="Show all applications"/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
desktopdir=$(datadir)/applications
|
desktopdir=$(datadir)/applications
|
||||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||||
|
if HAVE_MUTTER_WAYLAND
|
||||||
|
desktop_DATA += gnome-shell-wayland.desktop
|
||||||
|
endif HAVE_MUTTER_WAYLAND
|
||||||
|
|
||||||
|
|
||||||
# We substitute in bindir so it works as an autostart
|
# We substitute in bindir so it works as an autostart
|
||||||
# file when built in a non-system prefix
|
# file when built in a non-system prefix
|
||||||
@@ -12,6 +16,8 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
|||||||
|
|
||||||
introspectiondir = $(datadir)/dbus-1/interfaces
|
introspectiondir = $(datadir)/dbus-1/interfaces
|
||||||
introspection_DATA = \
|
introspection_DATA = \
|
||||||
|
org.gnome.Shell.Screencast.xml \
|
||||||
|
org.gnome.Shell.Screenshot.xml \
|
||||||
org.gnome.ShellSearchProvider.xml \
|
org.gnome.ShellSearchProvider.xml \
|
||||||
org.gnome.ShellSearchProvider2.xml
|
org.gnome.ShellSearchProvider2.xml
|
||||||
|
|
||||||
@@ -36,6 +42,10 @@ dist_theme_DATA = \
|
|||||||
theme/message-tray-background.png \
|
theme/message-tray-background.png \
|
||||||
theme/more-results.svg \
|
theme/more-results.svg \
|
||||||
theme/noise-texture.png \
|
theme/noise-texture.png \
|
||||||
|
theme/page-indicator-active.svg \
|
||||||
|
theme/page-indicator-inactive.svg \
|
||||||
|
theme/page-indicator-checked.svg \
|
||||||
|
theme/page-indicator-hover.svg \
|
||||||
theme/panel-button-border.svg \
|
theme/panel-button-border.svg \
|
||||||
theme/panel-button-highlight-narrow.svg \
|
theme/panel-button-highlight-narrow.svg \
|
||||||
theme/panel-button-highlight-wide.svg \
|
theme/panel-button-highlight-wide.svg \
|
||||||
@@ -51,10 +61,7 @@ dist_theme_DATA = \
|
|||||||
theme/ws-switch-arrow-down.png
|
theme/ws-switch-arrow-down.png
|
||||||
|
|
||||||
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
|
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
|
||||||
keys_in_files = \
|
keys_in_files = 50-gnome-shell-system.xml.in
|
||||||
50-gnome-shell-screenshot.xml.in \
|
|
||||||
50-gnome-shell-system.xml.in \
|
|
||||||
$(NULL)
|
|
||||||
keys_DATA = $(keys_in_files:.xml.in=.xml)
|
keys_DATA = $(keys_in_files:.xml.in=.xml)
|
||||||
|
|
||||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
|
||||||
@@ -79,6 +86,7 @@ convert_DATA = gnome-shell-overrides.convert
|
|||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
gnome-shell.desktop.in.in \
|
gnome-shell.desktop.in.in \
|
||||||
|
gnome-shell-wayland.desktop.in.in \
|
||||||
gnome-shell-extension-prefs.desktop.in.in \
|
gnome-shell-extension-prefs.desktop.in.in \
|
||||||
$(introspection_DATA) \
|
$(introspection_DATA) \
|
||||||
$(menu_DATA) \
|
$(menu_DATA) \
|
||||||
@@ -88,6 +96,7 @@ EXTRA_DIST = \
|
|||||||
|
|
||||||
CLEANFILES = \
|
CLEANFILES = \
|
||||||
gnome-shell.desktop.in \
|
gnome-shell.desktop.in \
|
||||||
|
gnome-shell-wayland.desktop.in \
|
||||||
gnome-shell-extension-prefs.in \
|
gnome-shell-extension-prefs.in \
|
||||||
$(desktop_DATA) \
|
$(desktop_DATA) \
|
||||||
$(keys_DATA) \
|
$(keys_DATA) \
|
||||||
|
|||||||
15
data/gnome-shell-wayland.desktop.in.in
Normal file
15
data/gnome-shell-wayland.desktop.in.in
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
_Name=GNOME Shell (wayland compositor)
|
||||||
|
_Comment=Window management and application launching
|
||||||
|
Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland
|
||||||
|
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||||
|
X-GNOME-Bugzilla-Product=gnome-shell
|
||||||
|
X-GNOME-Bugzilla-Component=general
|
||||||
|
X-GNOME-Bugzilla-Version=@VERSION@
|
||||||
|
Categories=GNOME;GTK;Core;
|
||||||
|
OnlyShowIn=GNOME;
|
||||||
|
NoDisplay=true
|
||||||
|
X-GNOME-Autostart-Phase=DisplayServer
|
||||||
|
X-GNOME-Autostart-Notify=true
|
||||||
|
X-GNOME-AutoRestart=false
|
||||||
96
data/org.gnome.Shell.Screencast.xml
Normal file
96
data/org.gnome.Shell.Screencast.xml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<!DOCTYPE node PUBLIC
|
||||||
|
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||||
|
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||||
|
<node>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
org.gnome.Shell.Screencast:
|
||||||
|
@short_description: Screencast interface
|
||||||
|
|
||||||
|
The interface used to record screen contents.
|
||||||
|
-->
|
||||||
|
<interface name="org.gnome.Shell.Screencast">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Screencast:
|
||||||
|
@file_template: the template for the filename to use
|
||||||
|
@options: a dictionary of optional parameters
|
||||||
|
@success: whether the screencast was started successfully
|
||||||
|
@filename_used: the file where the screencast is being saved
|
||||||
|
|
||||||
|
Records a screencast of the whole screen and saves it
|
||||||
|
(by default) as webm video under a filename derived from
|
||||||
|
@file_template. The template is either a relative or absolute
|
||||||
|
filename which may contain some escape sequences - %d and %t
|
||||||
|
will be replaced by the start date and time of the recording.
|
||||||
|
If a relative name is used, the screencast will be saved in the
|
||||||
|
$XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
|
||||||
|
The actual filename of the saved video is returned in @filename_used.
|
||||||
|
The set of optional parameters in @options currently consists of:
|
||||||
|
'draw-cursor'(b): whether the cursor should be included in the
|
||||||
|
recording (true)
|
||||||
|
'framerate'(i): the number of frames per second that should be
|
||||||
|
recorded if possible (30)
|
||||||
|
'pipeline'(s): the GStreamer pipeline used to encode recordings
|
||||||
|
in gst-launch format; if not specified, the
|
||||||
|
recorder will produce vp8 (webm) video (unset)
|
||||||
|
-->
|
||||||
|
<method name="Screencast">
|
||||||
|
<arg type="s" direction="in" name="file_template"/>
|
||||||
|
<arg type="a{sv}" direction="in" name="options"/>
|
||||||
|
<arg type="b" direction="in" name="flash"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ScreencastArea:
|
||||||
|
@x: the X coordinate of the area to capture
|
||||||
|
@y: the Y coordinate of the area to capture
|
||||||
|
@width: the width of the area to capture
|
||||||
|
@height: the height of the area to capture
|
||||||
|
@file_template: the template for the filename to use
|
||||||
|
@options: a dictionary of optional parameters
|
||||||
|
@success: whether the screencast was started successfully
|
||||||
|
@filename_used: the file where the screencast is being saved
|
||||||
|
|
||||||
|
Records a screencast of the passed in area and saves it
|
||||||
|
(by default) as webm video under a filename derived from
|
||||||
|
@file_template. The template is either a relative or absolute
|
||||||
|
filename which may contain some escape sequences - %d and %t
|
||||||
|
will be replaced by the start date and time of the recording.
|
||||||
|
If a relative name is used, the screencast will be saved in the
|
||||||
|
$XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
|
||||||
|
The actual filename of the saved video is returned in @filename_used.
|
||||||
|
The set of optional parameters in @options currently consists of:
|
||||||
|
'draw-cursor'(b): whether the cursor should be included in the
|
||||||
|
recording (true)
|
||||||
|
'framerate'(i): the number of frames per second that should be
|
||||||
|
recorded if possible (30)
|
||||||
|
'pipeline'(s): the GStreamer pipeline used to encode recordings
|
||||||
|
in gst-launch format; if not specified, the
|
||||||
|
recorder will produce vp8 (webm) video (unset)
|
||||||
|
-->
|
||||||
|
<method name="ScreencastArea">
|
||||||
|
<arg type="i" direction="in" name="x"/>
|
||||||
|
<arg type="i" direction="in" name="y"/>
|
||||||
|
<arg type="i" direction="in" name="width"/>
|
||||||
|
<arg type="i" direction="in" name="height"/>
|
||||||
|
<arg type="s" direction="in" name="file_template"/>
|
||||||
|
<arg type="a{sv}" direction="in" name="options"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
StopScreencast:
|
||||||
|
@success: whether stopping the recording was successful
|
||||||
|
|
||||||
|
Stop the recording started by either Screencast or ScreencastArea.
|
||||||
|
-->
|
||||||
|
<method name="StopScreencast">
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
128
data/org.gnome.Shell.Screenshot.xml
Normal file
128
data/org.gnome.Shell.Screenshot.xml
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<!DOCTYPE node PUBLIC
|
||||||
|
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||||
|
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||||
|
<node>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
org.gnome.Shell.Screenshot:
|
||||||
|
@short_description: Screenshot interface
|
||||||
|
|
||||||
|
The interface used to capture pictures of the screen contents.
|
||||||
|
-->
|
||||||
|
<interface name="org.gnome.Shell.Screenshot">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Screenshot:
|
||||||
|
@filename: The filename for the screenshot
|
||||||
|
@include_cursor: Whether to include the cursor image or not
|
||||||
|
@flash: Whether to flash the screen or not
|
||||||
|
@success: whether the screenshot was captured
|
||||||
|
@filename_used: the file where the screenshot was saved
|
||||||
|
|
||||||
|
Takes a screenshot of the whole screen and saves it
|
||||||
|
in @filename as png image, it returns a boolean
|
||||||
|
indicating whether the operation was successful or not.
|
||||||
|
@filename can either be an absolute path or a basename, in
|
||||||
|
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||||
|
or the home directory if it doesn't exist. The filename used
|
||||||
|
to save the screenshot will be returned in @filename_used.
|
||||||
|
-->
|
||||||
|
<method name="Screenshot">
|
||||||
|
<arg type="b" direction="in" name="include_cursor"/>
|
||||||
|
<arg type="b" direction="in" name="flash"/>
|
||||||
|
<arg type="s" direction="in" name="filename"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ScreenshotWindow:
|
||||||
|
@include_frame: Whether to include the frame or not
|
||||||
|
@include_cursor: Whether to include the cursor image or not
|
||||||
|
@flash: Whether to flash the window area or not
|
||||||
|
@filename: The filename for the screenshot
|
||||||
|
@success: whether the screenshot was captured
|
||||||
|
@filename_used: the file where the screenshot was saved
|
||||||
|
|
||||||
|
Takes a screenshot of the focused window (optionally omitting the frame)
|
||||||
|
and saves it in @filename as png image, it returns a boolean
|
||||||
|
indicating whether the operation was successful or not.
|
||||||
|
@filename can either be an absolute path or a basename, in
|
||||||
|
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||||
|
or the home directory if it doesn't exist. The filename used
|
||||||
|
to save the screenshot will be returned in @filename_used.
|
||||||
|
-->
|
||||||
|
<method name="ScreenshotWindow">
|
||||||
|
<arg type="b" direction="in" name="include_frame"/>
|
||||||
|
<arg type="b" direction="in" name="include_cursor"/>
|
||||||
|
<arg type="b" direction="in" name="flash"/>
|
||||||
|
<arg type="s" direction="in" name="filename"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ScreenshotArea:
|
||||||
|
@x: the X coordinate of the area to capture
|
||||||
|
@y: the Y coordinate of the area to capture
|
||||||
|
@width: the width of the area to capture
|
||||||
|
@height: the height of the area to capture
|
||||||
|
@flash: whether to flash the area or not
|
||||||
|
@filename: the filename for the screenshot
|
||||||
|
@success: whether the screenshot was captured
|
||||||
|
@filename_used: the file where the screenshot was saved
|
||||||
|
|
||||||
|
Takes a screenshot of the passed in area and saves it
|
||||||
|
in @filename as png image, it returns a boolean
|
||||||
|
indicating whether the operation was successful or not.
|
||||||
|
@filename can either be an absolute path or a basename, in
|
||||||
|
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||||
|
or the home directory if it doesn't exist. The filename used
|
||||||
|
to save the screenshot will be returned in @filename_used.
|
||||||
|
-->
|
||||||
|
<method name="ScreenshotArea">
|
||||||
|
<arg type="i" direction="in" name="x"/>
|
||||||
|
<arg type="i" direction="in" name="y"/>
|
||||||
|
<arg type="i" direction="in" name="width"/>
|
||||||
|
<arg type="i" direction="in" name="height"/>
|
||||||
|
<arg type="b" direction="in" name="flash"/>
|
||||||
|
<arg type="s" direction="in" name="filename"/>
|
||||||
|
<arg type="b" direction="out" name="success"/>
|
||||||
|
<arg type="s" direction="out" name="filename_used"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
FlashArea:
|
||||||
|
@x: the X coordinate of the area to flash
|
||||||
|
@y: the Y coordinate of the area to flash
|
||||||
|
@width: the width of the area to flash
|
||||||
|
@height: the height of the area to flash
|
||||||
|
|
||||||
|
Renders a flash spot effect in the specified rectangle of the screen.
|
||||||
|
-->
|
||||||
|
<method name="FlashArea">
|
||||||
|
<arg type="i" direction="in" name="x"/>
|
||||||
|
<arg type="i" direction="in" name="y"/>
|
||||||
|
<arg type="i" direction="in" name="width"/>
|
||||||
|
<arg type="i" direction="in" name="height"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SelectArea:
|
||||||
|
@x: the X coordinate of the selected area
|
||||||
|
@y: the Y coordinate of the selected area
|
||||||
|
@width: the width of the selected area
|
||||||
|
@height: the height of the selected area
|
||||||
|
|
||||||
|
Interactively allows the user to select a rectangular area of
|
||||||
|
the screen, and returns its coordinates.
|
||||||
|
-->
|
||||||
|
<method name="SelectArea">
|
||||||
|
<arg type="i" direction="out" name="x"/>
|
||||||
|
<arg type="i" direction="out" name="y"/>
|
||||||
|
<arg type="i" direction="out" name="width"/>
|
||||||
|
<arg type="i" direction="out" name="height"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
<!--
|
<!--
|
||||||
GetResultMetas:
|
GetResultMetas:
|
||||||
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||||
@metas: A dictionary describing the given search result, containing 'id' and 'name' (both strings). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images).
|
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
|
||||||
|
|
||||||
Return an array of meta data used to display each given result
|
Return an array of meta data used to display each given result
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
<!--
|
<!--
|
||||||
GetResultMetas:
|
GetResultMetas:
|
||||||
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||||
@metas: A dictionary describing the given search result, containing 'id' and 'name' (both strings). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
|
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, 'icon' (a serialized GIcon as obtained by g_icon_serialize) can be specified if the result can be better served with a thumbnail of the content (such as with images). 'gicon' (a serialized GIcon as obtained by g_icon_to_string) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) are deprecated values that can also be used for that purpose. A 'description' field (string) may also be specified if more context would help the user find the desired result.
|
||||||
|
|
||||||
Return an array of meta data used to display each given result
|
Return an array of meta data used to display each given result
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -13,22 +13,12 @@
|
|||||||
</key>
|
</key>
|
||||||
<key name="enabled-extensions" type="as">
|
<key name="enabled-extensions" type="as">
|
||||||
<default>[]</default>
|
<default>[]</default>
|
||||||
<_summary>Uuids of extensions to enable</_summary>
|
<_summary>UUIDs of extensions to enable</_summary>
|
||||||
<_description>
|
<_description>
|
||||||
GNOME Shell extensions have a uuid property; this key lists extensions
|
GNOME Shell extensions have a UUID property; this key lists extensions
|
||||||
which should be loaded. Any extension that wants to be loaded needs
|
which should be loaded. Any extension that wants to be loaded needs
|
||||||
to be in this list. You can also manipulate this list with the
|
to be in this list. You can also manipulate this list with the
|
||||||
EnableExtension and DisableExtension DBus methods on org.gnome.Shell.
|
EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell.
|
||||||
</_description>
|
|
||||||
</key>
|
|
||||||
<key name="enable-app-monitoring" type="b">
|
|
||||||
<default>true</default>
|
|
||||||
<_summary>Whether to collect stats about applications usage</_summary>
|
|
||||||
<_description>
|
|
||||||
The shell normally monitors active applications in order to present
|
|
||||||
the most used ones (e.g. in launchers). While this data will be
|
|
||||||
kept private, you may want to disable this for privacy reasons.
|
|
||||||
Please note that doing so won't remove already saved data.
|
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key name="favorite-apps" type="as">
|
<key name="favorite-apps" type="as">
|
||||||
@@ -39,6 +29,13 @@
|
|||||||
will be displayed in the favorites area.
|
will be displayed in the favorites area.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
|
<key name="app-picker-view" type="u">
|
||||||
|
<default>0</default>
|
||||||
|
<summary>App Picker View</summary>
|
||||||
|
<description>
|
||||||
|
Index of the currently selected view in the application picker.
|
||||||
|
</description>
|
||||||
|
</key>
|
||||||
<key name="command-history" type="as">
|
<key name="command-history" type="as">
|
||||||
<default>[]</default>
|
<default>[]</default>
|
||||||
<_summary>History for command (Alt-F2) dialog</_summary>
|
<_summary>History for command (Alt-F2) dialog</_summary>
|
||||||
@@ -47,16 +44,6 @@
|
|||||||
<default>[]</default>
|
<default>[]</default>
|
||||||
<_summary>History for the looking glass dialog</_summary>
|
<_summary>History for the looking glass dialog</_summary>
|
||||||
</key>
|
</key>
|
||||||
<key name="saved-im-presence" type="i">
|
|
||||||
<default>1</default>
|
|
||||||
<_summary>Internally used to store the last IM presence explicitly set by the user. The
|
|
||||||
value here is from the TpConnectionPresenceType enumeration.</_summary>
|
|
||||||
</key>
|
|
||||||
<key name="saved-session-presence" type="i">
|
|
||||||
<default>0</default>
|
|
||||||
<_summary>Internally used to store the last session presence status for the user. The
|
|
||||||
value here is from the GsmPresenceStatus enumeration.</_summary>
|
|
||||||
</key>
|
|
||||||
<key name="always-show-log-out" type="b">
|
<key name="always-show-log-out" type="b">
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
<_summary>Always show the 'Log out' menu item in the user menu.</_summary>
|
<_summary>Always show the 'Log out' menu item in the user menu.</_summary>
|
||||||
@@ -65,11 +52,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
menu item in single-user, single-session situations.
|
menu item in single-user, single-session situations.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key name="show-full-name" type="b">
|
|
||||||
<default>true</default>
|
|
||||||
<_summary>Show full name in the user menu</_summary>
|
|
||||||
<_description>Whether the users full name is shown in the user menu or not.</_description>
|
|
||||||
</key>
|
|
||||||
<key name="remember-mount-password" type="b">
|
<key name="remember-mount-password" type="b">
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>
|
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>
|
||||||
@@ -81,7 +63,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
<child name="calendar" schema="org.gnome.shell.calendar"/>
|
||||||
<child name="recorder" schema="org.gnome.shell.recorder"/>
|
|
||||||
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
|
||||||
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
|
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
|
||||||
</schema>
|
</schema>
|
||||||
@@ -114,6 +95,13 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
Overview.
|
Overview.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
|
<key name="toggle-overview" type="as">
|
||||||
|
<default>["<Super>s"]</default>
|
||||||
|
<_summary>Keybinding to open the overview</_summary>
|
||||||
|
<_description>
|
||||||
|
Keybinding to open the Activities Overview.
|
||||||
|
</_description>
|
||||||
|
</key>
|
||||||
<key name="toggle-message-tray" type="as">
|
<key name="toggle-message-tray" type="as">
|
||||||
<default>["<Super>m"]</default>
|
<default>["<Super>m"]</default>
|
||||||
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
|
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
|
||||||
@@ -128,12 +116,10 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
Keybinding to focus the active notification.
|
Keybinding to focus the active notification.
|
||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key name="toggle-recording" type="as">
|
<key name="pause-resume-tweens" type="as">
|
||||||
<default><![CDATA[['<Control><Shift><Alt>r']]]></default>
|
<default>[]</default>
|
||||||
<_summary>Keybinding to toggle the screen recorder</_summary>
|
<summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</summary>
|
||||||
<_description>
|
<description></description>
|
||||||
Keybinding to start/stop the builtin screen recorder.
|
|
||||||
</_description>
|
|
||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
@@ -148,41 +134,16 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
|
<schema id="org.gnome.shell.app-switcher"
|
||||||
|
path="/org/gnome/shell/app-switcher/"
|
||||||
gettext-domain="@GETTEXT_PACKAGE@">
|
gettext-domain="@GETTEXT_PACKAGE@">
|
||||||
<key name="framerate" type="i">
|
<key type="b" name="current-workspace-only">
|
||||||
<default>30</default>
|
<default>false</default>
|
||||||
<_summary>Framerate used for recording screencasts.</_summary>
|
<summary>Limit switcher to current workspace.</summary>
|
||||||
<_description>
|
<description>
|
||||||
The framerate of the resulting screencast recordered
|
If true, only applications that have windows on the current workspace are shown in the switcher.
|
||||||
by GNOME Shell's screencast recorder in frames-per-second.
|
Otherwise, all applications are included.
|
||||||
</_description>
|
</description>
|
||||||
</key>
|
|
||||||
<key name="pipeline" type="s">
|
|
||||||
<default>''</default>
|
|
||||||
<_summary>The gstreamer pipeline used to encode the screencast</_summary>
|
|
||||||
<_description>
|
|
||||||
Sets the GStreamer pipeline used to encode recordings.
|
|
||||||
It follows the syntax used for gst-launch. The pipeline should have
|
|
||||||
an unconnected sink pad where the recorded video is recorded. It will
|
|
||||||
normally have a unconnected source pad; output from that pad
|
|
||||||
will be written into the output file. However the pipeline can also
|
|
||||||
take care of its own output - this might be used to send the output
|
|
||||||
to an icecast server via shout2send or similar. When unset or set
|
|
||||||
to an empty value, the default pipeline will be used. This is currently
|
|
||||||
'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
|
|
||||||
and records to WEBM using the VP8 codec. %T is used as a placeholder
|
|
||||||
for a guess at the optimal thread count on the system.
|
|
||||||
</_description>
|
|
||||||
</key>
|
|
||||||
<key name="file-extension" type="s">
|
|
||||||
<default>'webm'</default>
|
|
||||||
<_summary>File extension used for storing the screencast</_summary>
|
|
||||||
<_description>
|
|
||||||
The filename for recorded screencasts will be a unique filename
|
|
||||||
based on the current date, and use this extension. It should be
|
|
||||||
changed when recording to a different container format.
|
|
||||||
</_description>
|
|
||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
@@ -204,7 +165,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
</_description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
<key type="b" name="current-workspace-only">
|
<key type="b" name="current-workspace-only">
|
||||||
<default>false</default>
|
<default>true</default>
|
||||||
<summary>Limit switcher to current workspace.</summary>
|
<summary>Limit switcher to current workspace.</summary>
|
||||||
<description>
|
<description>
|
||||||
If true, only windows from the current workspace are shown in the switcher.
|
If true, only windows from the current workspace are shown in the switcher.
|
||||||
@@ -259,10 +220,10 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
|||||||
|
|
||||||
<key name="focus-change-on-pointer-rest" type="b">
|
<key name="focus-change-on-pointer-rest" type="b">
|
||||||
<default>true</default>
|
<default>true</default>
|
||||||
<summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
|
<_summary>Delay focus changes in mouse mode until the pointer stops moving</_summary>
|
||||||
<description>
|
<_description>
|
||||||
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
This key overrides the key in org.gnome.mutter when running GNOME Shell.
|
||||||
</description>
|
</_description>
|
||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
</schemalist>
|
</schemalist>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@
|
|||||||
height="16"
|
height="16"
|
||||||
id="svg12430"
|
id="svg12430"
|
||||||
version="1.1"
|
version="1.1"
|
||||||
inkscape:version="0.48.3.1 r9886"
|
inkscape:version="0.48.4 r9939"
|
||||||
sodipodi:docname="more-results.svg">
|
sodipodi:docname="more-results.svg">
|
||||||
<defs
|
<defs
|
||||||
id="defs12432" />
|
id="defs12432" />
|
||||||
@@ -25,18 +25,18 @@
|
|||||||
borderopacity="1.0"
|
borderopacity="1.0"
|
||||||
inkscape:pageopacity="1"
|
inkscape:pageopacity="1"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="1"
|
inkscape:zoom="90.509668"
|
||||||
inkscape:cx="8.3155237"
|
inkscape:cx="6.5009792"
|
||||||
inkscape:cy="0.89548874"
|
inkscape:cy="8.3589595"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
inkscape:current-layer="g14642-3-0"
|
inkscape:current-layer="g14642-3-0"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
borderlayer="true"
|
borderlayer="true"
|
||||||
inkscape:showpageshadow="false"
|
inkscape:showpageshadow="false"
|
||||||
inkscape:window-width="2560"
|
inkscape:window-width="1440"
|
||||||
inkscape:window-height="1376"
|
inkscape:window-height="840"
|
||||||
inkscape:window-x="1200"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="187"
|
inkscape:window-y="27"
|
||||||
inkscape:window-maximized="1">
|
inkscape:window-maximized="1">
|
||||||
<inkscape:grid
|
<inkscape:grid
|
||||||
type="xygrid"
|
type="xygrid"
|
||||||
@@ -90,6 +90,11 @@
|
|||||||
transform="translate(-2,0)"
|
transform="translate(-2,0)"
|
||||||
width="16"
|
width="16"
|
||||||
height="16" />
|
height="16" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
|
||||||
|
d="M 7 5 L 7 7 L 5 7 L 5 9 L 7 9 L 7 11 L 9 11 L 9 9 L 11 9 L 11 7 L 9 7 L 9 5 L 7 5 z "
|
||||||
|
transform="translate(141.99984,397.99107)"
|
||||||
|
id="rect3757" />
|
||||||
<path
|
<path
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
|
style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.4 KiB |
71
data/theme/page-indicator-active.svg
Normal file
71
data/theme/page-indicator-active.svg
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
id="svg4703"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.4 r9939"
|
||||||
|
sodipodi:docname="page-indicator-pushed.svg">
|
||||||
|
<defs
|
||||||
|
id="defs4705" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="31.392433"
|
||||||
|
inkscape:cx="1.0245308"
|
||||||
|
inkscape:cy="13.3715"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:grid-bbox="true"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1374"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid6140" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata4708">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
id="layer1"
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
transform="translate(0,2)">
|
||||||
|
<path
|
||||||
|
transform="matrix(0.54617904,0,0,0.62523128,-1131.9904,-392.39214)"
|
||||||
|
d="m 2099.9808,638.83099 a 10.985409,9.5964489 0 1 1 -21.9708,0 10.985409,9.5964489 0 1 1 21.9708,0 z"
|
||||||
|
sodipodi:ry="9.5964489"
|
||||||
|
sodipodi:rx="10.985409"
|
||||||
|
sodipodi:cy="638.83099"
|
||||||
|
sodipodi:cx="2088.9954"
|
||||||
|
id="path4711"
|
||||||
|
style="fill:#fdffff;fill-opacity:1;stroke:none"
|
||||||
|
sodipodi:type="arc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
67
data/theme/page-indicator-checked.svg
Normal file
67
data/theme/page-indicator-checked.svg
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
id="svg4703"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.4 r9939"
|
||||||
|
sodipodi:docname="page-indicator-active.svg">
|
||||||
|
<defs
|
||||||
|
id="defs4705" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="22.197802"
|
||||||
|
inkscape:cx="2.1522887"
|
||||||
|
inkscape:cy="16.782904"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:grid-bbox="true"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1021"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata4708">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
id="layer1"
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
transform="translate(0,2)">
|
||||||
|
<path
|
||||||
|
transform="matrix(0.72823872,0,0,0.8336417,-1512.2872,-525.55618)"
|
||||||
|
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
|
||||||
|
sodipodi:ry="9.5964489"
|
||||||
|
sodipodi:rx="10.985409"
|
||||||
|
sodipodi:cy="638.83099"
|
||||||
|
sodipodi:cx="2088.9954"
|
||||||
|
id="path4711"
|
||||||
|
style="fill:#fdffff;fill-opacity:0.94117647;stroke:none"
|
||||||
|
sodipodi:type="arc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
67
data/theme/page-indicator-hover.svg
Normal file
67
data/theme/page-indicator-hover.svg
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
id="svg5266"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.4 r9939"
|
||||||
|
sodipodi:docname="page-indicator-inactive.svg">
|
||||||
|
<defs
|
||||||
|
id="defs5268" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="11.313709"
|
||||||
|
inkscape:cx="-2.307566"
|
||||||
|
inkscape:cy="17.859535"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:grid-bbox="true"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1374"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5271">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
id="layer1"
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
transform="translate(0,2)">
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||||
|
id="path5274"
|
||||||
|
sodipodi:cx="2088.9954"
|
||||||
|
sodipodi:cy="638.83099"
|
||||||
|
sodipodi:rx="10.985409"
|
||||||
|
sodipodi:ry="9.5964489"
|
||||||
|
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
|
||||||
|
transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
67
data/theme/page-indicator-inactive.svg
Normal file
67
data/theme/page-indicator-inactive.svg
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
id="svg5266"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.4 r9939"
|
||||||
|
sodipodi:docname="page-indicator-inactive.svg">
|
||||||
|
<defs
|
||||||
|
id="defs5268" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="11.313709"
|
||||||
|
inkscape:cx="-2.307566"
|
||||||
|
inkscape:cy="17.859535"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:grid-bbox="true"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1374"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5271">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
id="layer1"
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
transform="translate(0,2)">
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:0.39215686000000000;stroke-dasharray:none"
|
||||||
|
id="path5274"
|
||||||
|
sodipodi:cx="2088.9954"
|
||||||
|
sodipodi:cy="638.83099"
|
||||||
|
sodipodi:rx="10.985409"
|
||||||
|
sodipodi:ry="9.5964489"
|
||||||
|
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
|
||||||
|
transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -88,11 +88,18 @@ doc-gen-org.gnome.Shell.SearchProvider2.xml: $(top_srcdir)/data/org.gnome.ShellS
|
|||||||
--generate-docbook doc-gen \
|
--generate-docbook doc-gen \
|
||||||
$(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
|
$(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
|
||||||
|
|
||||||
|
doc-gen-org.gnome.Shell.Screenshot.xml: $(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
|
||||||
|
gdbus-codegen \
|
||||||
|
--interface-prefix org.gnome.Shell.Screenshot. \
|
||||||
|
--generate-docbook doc-gen \
|
||||||
|
$(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
|
||||||
|
|
||||||
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
|
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
|
||||||
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
|
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
|
||||||
content_files= \
|
content_files= \
|
||||||
doc-gen-org.gnome.Shell.SearchProvider.xml \
|
doc-gen-org.gnome.Shell.SearchProvider.xml \
|
||||||
doc-gen-org.gnome.Shell.SearchProvider2.xml
|
doc-gen-org.gnome.Shell.SearchProvider2.xml \
|
||||||
|
doc-gen-org.gnome.Shell.Screenshot.xml
|
||||||
|
|
||||||
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
|
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
|
||||||
# These files must be listed here *and* in content_files
|
# These files must be listed here *and* in content_files
|
||||||
@@ -105,7 +112,7 @@ expand_content_files=
|
|||||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
|
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
|
||||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
|
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
|
||||||
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
|
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
|
||||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell.la
|
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la
|
||||||
|
|
||||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
|
# This includes the standard gtk-doc make rules, copied by gtkdocize.
|
||||||
include $(top_srcdir)/gtk-doc.make
|
include $(top_srcdir)/gtk-doc.make
|
||||||
|
|||||||
@@ -46,11 +46,10 @@
|
|||||||
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider.xml"/>
|
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider.xml"/>
|
||||||
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider2.xml"/>
|
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider2.xml"/>
|
||||||
<xi:include href="xml/shell-global.xml"/>
|
<xi:include href="xml/shell-global.xml"/>
|
||||||
|
<xi:include href="xml/shell-keybinding-modes.xml"/>
|
||||||
<xi:include href="xml/shell-wm.xml"/>
|
<xi:include href="xml/shell-wm.xml"/>
|
||||||
<xi:include href="xml/shell-xfixes-cursor.xml"/>
|
|
||||||
<xi:include href="xml/shell-util.xml"/>
|
<xi:include href="xml/shell-util.xml"/>
|
||||||
<xi:include href="xml/shell-mount-operation.xml"/>
|
<xi:include href="xml/shell-mount-operation.xml"/>
|
||||||
<xi:include href="xml/shell-mobile-providers.xml"/>
|
|
||||||
<xi:include href="xml/shell-network-agent.xml"/>
|
<xi:include href="xml/shell-network-agent.xml"/>
|
||||||
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
|
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
|
||||||
<xi:include href="xml/shell-tp-client.xml"/>
|
<xi:include href="xml/shell-tp-client.xml"/>
|
||||||
|
|||||||
117
js/Makefile.am
117
js/Makefile.am
@@ -1,7 +1,5 @@
|
|||||||
NULL =
|
NULL =
|
||||||
|
BUILT_SOURCES =
|
||||||
EXTRA_DIST = misc/config.js.in
|
|
||||||
CLEANFILES = misc/config.js
|
|
||||||
|
|
||||||
misc/config.js: misc/config.js.in Makefile
|
misc/config.js: misc/config.js.in Makefile
|
||||||
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
|
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
|
||||||
@@ -14,97 +12,26 @@ misc/config.js: misc/config.js.in Makefile
|
|||||||
-e "s|[@]sysconfdir@|$(sysconfdir)|g" \
|
-e "s|[@]sysconfdir@|$(sysconfdir)|g" \
|
||||||
$< > $@
|
$< > $@
|
||||||
|
|
||||||
jsdir = $(pkgdatadir)/js
|
js_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/js-resources.gresource.xml)
|
||||||
|
js-resources.h: js-resources.gresource.xml $(js_resource_files) misc/config.js
|
||||||
|
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name shell_js_resources $<
|
||||||
|
js-resources.c: js-resources.gresource.xml $(js_resource_files) misc/config.js
|
||||||
|
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name shell_js_resources $<
|
||||||
|
|
||||||
nobase_dist_js_DATA = \
|
js_built_sources = js-resources.c js-resources.h
|
||||||
gdm/batch.js \
|
|
||||||
gdm/fingerprint.js \
|
BUILT_SOURCES += $(js_built_sources)
|
||||||
gdm/loginDialog.js \
|
|
||||||
gdm/powerMenu.js \
|
all-local: $(js_built_sources)
|
||||||
gdm/realmd.js \
|
|
||||||
gdm/util.js \
|
js_resource_dist_files = $(filter-out misc/config.js, $(js_resource_files))
|
||||||
extensionPrefs/main.js \
|
|
||||||
misc/config.js \
|
EXTRA_DIST = \
|
||||||
misc/extensionUtils.js \
|
$(js_resource_dist_files) \
|
||||||
misc/fileUtils.js \
|
js-resources.gresource.xml \
|
||||||
misc/gnomeSession.js \
|
misc/config.js.in \
|
||||||
misc/history.js \
|
$(NULL)
|
||||||
misc/jsParse.js \
|
|
||||||
misc/loginManager.js \
|
CLEANFILES = \
|
||||||
misc/modemManager.js \
|
$(js_built_sources) \
|
||||||
misc/params.js \
|
|
||||||
misc/util.js \
|
|
||||||
perf/core.js \
|
|
||||||
ui/altTab.js \
|
|
||||||
ui/appDisplay.js \
|
|
||||||
ui/appFavorites.js \
|
|
||||||
ui/boxpointer.js \
|
|
||||||
ui/calendar.js \
|
|
||||||
ui/checkBox.js \
|
|
||||||
ui/ctrlAltTab.js \
|
|
||||||
ui/dash.js \
|
|
||||||
ui/dateMenu.js \
|
|
||||||
ui/dnd.js \
|
|
||||||
ui/endSessionDialog.js \
|
|
||||||
ui/extensionSystem.js \
|
|
||||||
ui/extensionDownloader.js \
|
|
||||||
ui/environment.js \
|
|
||||||
ui/flashspot.js \
|
|
||||||
ui/ibusCandidatePopup.js\
|
|
||||||
ui/grabHelper.js \
|
|
||||||
ui/iconGrid.js \
|
|
||||||
ui/keyboard.js \
|
|
||||||
ui/layout.js \
|
|
||||||
ui/lightbox.js \
|
|
||||||
ui/lookingGlass.js \
|
|
||||||
ui/magnifier.js \
|
|
||||||
ui/magnifierDBus.js \
|
|
||||||
ui/main.js \
|
|
||||||
ui/messageTray.js \
|
|
||||||
ui/modalDialog.js \
|
|
||||||
ui/separator.js \
|
|
||||||
ui/sessionMode.js \
|
|
||||||
ui/shellEntry.js \
|
|
||||||
ui/shellMountOperation.js \
|
|
||||||
ui/notificationDaemon.js \
|
|
||||||
ui/overview.js \
|
|
||||||
ui/panel.js \
|
|
||||||
ui/panelMenu.js \
|
|
||||||
ui/pointerWatcher.js \
|
|
||||||
ui/popupMenu.js \
|
|
||||||
ui/remoteSearch.js \
|
|
||||||
ui/runDialog.js \
|
|
||||||
ui/screenShield.js \
|
|
||||||
ui/scripting.js \
|
|
||||||
ui/search.js \
|
|
||||||
ui/searchDisplay.js \
|
|
||||||
ui/shellDBus.js \
|
|
||||||
ui/status/accessibility.js \
|
|
||||||
ui/status/keyboard.js \
|
|
||||||
ui/status/lockScreenMenu.js \
|
|
||||||
ui/status/network.js \
|
|
||||||
ui/status/power.js \
|
|
||||||
ui/status/volume.js \
|
|
||||||
ui/status/bluetooth.js \
|
|
||||||
ui/switcherPopup.js \
|
|
||||||
ui/tweener.js \
|
|
||||||
ui/unlockDialog.js \
|
|
||||||
ui/userMenu.js \
|
|
||||||
ui/viewSelector.js \
|
|
||||||
ui/wanda.js \
|
|
||||||
ui/windowAttentionHandler.js \
|
|
||||||
ui/windowManager.js \
|
|
||||||
ui/workspace.js \
|
|
||||||
ui/workspaceThumbnail.js \
|
|
||||||
ui/workspacesView.js \
|
|
||||||
ui/workspaceSwitcherPopup.js \
|
|
||||||
ui/xdndHandler.js \
|
|
||||||
ui/components/__init__.js \
|
|
||||||
ui/components/autorunManager.js \
|
|
||||||
ui/components/automountManager.js \
|
|
||||||
ui/components/networkAgent.js \
|
|
||||||
ui/components/polkitAgent.js \
|
|
||||||
ui/components/recorder.js \
|
|
||||||
ui/components/telepathyClient.js \
|
|
||||||
ui/components/keyring.js \
|
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|||||||
@@ -13,13 +13,15 @@ const _ = Gettext.gettext;
|
|||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
|
||||||
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
|
const GnomeShellIface = '<node> \
|
||||||
<signal name="ExtensionStatusChanged">
|
<interface name="org.gnome.Shell.Extensions"> \
|
||||||
<arg type="s" name="uuid"/>
|
<signal name="ExtensionStatusChanged"> \
|
||||||
<arg type="i" name="state"/>
|
<arg type="s" name="uuid"/> \
|
||||||
<arg type="s" name="error"/>
|
<arg type="i" name="state"/> \
|
||||||
</signal>
|
<arg type="s" name="error"/> \
|
||||||
</interface>;
|
</signal> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
|
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
|
||||||
|
|
||||||
@@ -45,6 +47,7 @@ const Application = new Lang.Class({
|
|||||||
this._extensionPrefsModules = {};
|
this._extensionPrefsModules = {};
|
||||||
|
|
||||||
this._extensionIters = {};
|
this._extensionIters = {};
|
||||||
|
this._startupUuid = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_buildModel: function() {
|
_buildModel: function() {
|
||||||
@@ -204,14 +207,20 @@ const Application = new Lang.Class({
|
|||||||
let finder = new ExtensionUtils.ExtensionFinder();
|
let finder = new ExtensionUtils.ExtensionFinder();
|
||||||
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
|
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
|
||||||
finder.scanExtensions();
|
finder.scanExtensions();
|
||||||
|
this._extensionsLoaded();
|
||||||
},
|
},
|
||||||
|
|
||||||
_extensionFound: function(signals, extension) {
|
_extensionFound: function(finder, extension) {
|
||||||
let iter = this._model.append();
|
let iter = this._model.append();
|
||||||
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
|
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
|
||||||
this._extensionIters[extension.uuid] = iter;
|
this._extensionIters[extension.uuid] = iter;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_extensionsLoaded: function() {
|
||||||
|
if (this._startupUuid && this._extensionAvailable(this._startupUuid))
|
||||||
|
this._selectExtension(this._startupUuid);
|
||||||
|
this._startupUuid = null;
|
||||||
|
},
|
||||||
|
|
||||||
_onActivate: function() {
|
_onActivate: function() {
|
||||||
this._window.present();
|
this._window.present();
|
||||||
@@ -232,10 +241,10 @@ const Application = new Lang.Class({
|
|||||||
// Strip off "extension:///" prefix which fakes a URI, if it exists
|
// Strip off "extension:///" prefix which fakes a URI, if it exists
|
||||||
uuid = stripPrefix(uuid, "extension:///");
|
uuid = stripPrefix(uuid, "extension:///");
|
||||||
|
|
||||||
if (!this._extensionAvailable(uuid))
|
if (this._extensionAvailable(uuid))
|
||||||
return 1;
|
|
||||||
|
|
||||||
this._selectExtension(uuid);
|
this._selectExtension(uuid);
|
||||||
|
else
|
||||||
|
this._startupUuid = uuid;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
506
js/gdm/authPrompt.js
Normal file
506
js/gdm/authPrompt.js
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Animation = imports.ui.animation;
|
||||||
|
const Batch = imports.gdm.batch;
|
||||||
|
const GdmUtil = imports.gdm.util;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const ShellEntry = imports.ui.shellEntry;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
const UserWidget = imports.ui.userWidget;
|
||||||
|
|
||||||
|
const DEFAULT_BUTTON_WELL_ICON_SIZE = 24;
|
||||||
|
const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
|
||||||
|
const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3;
|
||||||
|
|
||||||
|
const MESSAGE_FADE_OUT_ANIMATION_TIME = 0.5;
|
||||||
|
|
||||||
|
const AuthPromptMode = {
|
||||||
|
UNLOCK_ONLY: 0,
|
||||||
|
UNLOCK_OR_LOG_IN: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const AuthPromptStatus = {
|
||||||
|
NOT_VERIFYING: 0,
|
||||||
|
VERIFYING: 1,
|
||||||
|
VERIFICATION_FAILED: 2,
|
||||||
|
VERIFICATION_SUCCEEDED: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
const BeginRequestType = {
|
||||||
|
PROVIDE_USERNAME: 0,
|
||||||
|
DONT_PROVIDE_USERNAME: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const AuthPrompt = new Lang.Class({
|
||||||
|
Name: 'AuthPrompt',
|
||||||
|
|
||||||
|
_init: function(gdmClient, mode) {
|
||||||
|
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||||
|
|
||||||
|
this._gdmClient = gdmClient;
|
||||||
|
this._mode = mode;
|
||||||
|
|
||||||
|
let reauthenticationOnly;
|
||||||
|
if (this._mode == AuthPromptMode.UNLOCK_ONLY)
|
||||||
|
reauthenticationOnly = true;
|
||||||
|
else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN)
|
||||||
|
reauthenticationOnly = false;
|
||||||
|
|
||||||
|
this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly });
|
||||||
|
|
||||||
|
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
|
||||||
|
this._userVerifier.connect('show-message', Lang.bind(this, this._onShowMessage));
|
||||||
|
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
|
||||||
|
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||||
|
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
|
||||||
|
this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
|
||||||
|
this._userVerifier.connect('ovirt-user-authenticated', Lang.bind(this, this._onOVirtUserAuthenticated));
|
||||||
|
this.smartcardDetected = this._userVerifier.smartcardDetected;
|
||||||
|
|
||||||
|
this.connect('next', Lang.bind(this, function() {
|
||||||
|
this.updateSensitivity(false);
|
||||||
|
this.startSpinning();
|
||||||
|
if (this._queryingService) {
|
||||||
|
this._userVerifier.answerQuery(this._queryingService, this._entry.text);
|
||||||
|
} else {
|
||||||
|
this._preemptiveAnswer = this._entry.text;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.actor = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
|
||||||
|
vertical: true });
|
||||||
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
this.actor.connect('key-press-event',
|
||||||
|
Lang.bind(this, function(actor, event) {
|
||||||
|
if (event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||||
|
this.cancel();
|
||||||
|
}
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._userWell = new St.Bin({ x_fill: true,
|
||||||
|
x_align: St.Align.START });
|
||||||
|
this.actor.add(this._userWell,
|
||||||
|
{ x_align: St.Align.START,
|
||||||
|
x_fill: true,
|
||||||
|
y_fill: true,
|
||||||
|
expand: true });
|
||||||
|
this._label = new St.Label({ style_class: 'login-dialog-prompt-label' });
|
||||||
|
|
||||||
|
this.actor.add(this._label,
|
||||||
|
{ expand: true,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: true,
|
||||||
|
x_align: St.Align.START });
|
||||||
|
this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||||
|
can_focus: true });
|
||||||
|
ShellEntry.addContextMenu(this._entry, { isPassword: true });
|
||||||
|
|
||||||
|
this.actor.add(this._entry,
|
||||||
|
{ expand: true,
|
||||||
|
x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START });
|
||||||
|
|
||||||
|
this._entry.grab_key_focus();
|
||||||
|
|
||||||
|
this._message = new St.Label({ opacity: 0,
|
||||||
|
styleClass: 'login-dialog-message' });
|
||||||
|
this._message.clutter_text.line_wrap = true;
|
||||||
|
this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START });
|
||||||
|
|
||||||
|
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
|
||||||
|
vertical: false });
|
||||||
|
this.actor.add(this._buttonBox,
|
||||||
|
{ expand: true,
|
||||||
|
x_align: St.Align.MIDDLE,
|
||||||
|
y_align: St.Align.END });
|
||||||
|
|
||||||
|
this._defaultButtonWell = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||||
|
this._defaultButtonWellActor = null;
|
||||||
|
|
||||||
|
this._initButtons();
|
||||||
|
|
||||||
|
let spinnerIcon = global.datadir + '/theme/process-working.svg';
|
||||||
|
this._spinner = new Animation.AnimatedIcon(spinnerIcon, DEFAULT_BUTTON_WELL_ICON_SIZE);
|
||||||
|
this._spinner.actor.opacity = 0;
|
||||||
|
this._spinner.actor.show();
|
||||||
|
this._defaultButtonWell.add_child(this._spinner.actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDestroy: function() {
|
||||||
|
this._userVerifier.clear();
|
||||||
|
this._userVerifier.disconnectAll();
|
||||||
|
this._userVerifier = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_initButtons: function() {
|
||||||
|
this.cancelButton = new St.Button({ style_class: 'modal-dialog-button',
|
||||||
|
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||||
|
reactive: true,
|
||||||
|
can_focus: true,
|
||||||
|
label: _("Cancel") });
|
||||||
|
this.cancelButton.connect('clicked',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this.cancel();
|
||||||
|
}));
|
||||||
|
this._buttonBox.add(this.cancelButton,
|
||||||
|
{ expand: false,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.END });
|
||||||
|
|
||||||
|
this._buttonBox.add(this._defaultButtonWell,
|
||||||
|
{ expand: true,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
this.nextButton = new St.Button({ style_class: 'modal-dialog-button',
|
||||||
|
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||||
|
reactive: true,
|
||||||
|
can_focus: true,
|
||||||
|
label: _("Next") });
|
||||||
|
this.nextButton.connect('clicked',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this.emit('next');
|
||||||
|
}));
|
||||||
|
this.nextButton.add_style_pseudo_class('default');
|
||||||
|
this._buttonBox.add(this.nextButton,
|
||||||
|
{ expand: false,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.END });
|
||||||
|
|
||||||
|
this._updateNextButtonSensitivity(this._entry.text.length > 0);
|
||||||
|
|
||||||
|
this._entry.clutter_text.connect('text-changed',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
if (!this._userVerifier.hasPendingMessages)
|
||||||
|
this._fadeOutMessage();
|
||||||
|
|
||||||
|
this._updateNextButtonSensitivity(this._entry.text.length > 0);
|
||||||
|
}));
|
||||||
|
this._entry.clutter_text.connect('activate', Lang.bind(this, function() {
|
||||||
|
this.emit('next');
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
|
||||||
|
if (this._preemptiveAnswer) {
|
||||||
|
if (this._queryingService)
|
||||||
|
this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
|
||||||
|
this._preemptiveAnswer = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._queryingService)
|
||||||
|
this.clear();
|
||||||
|
|
||||||
|
this._queryingService = serviceName;
|
||||||
|
this.setPasswordChar(passwordChar);
|
||||||
|
this.setQuestion(question);
|
||||||
|
|
||||||
|
if (passwordChar) {
|
||||||
|
if (this._userVerifier.reauthenticating)
|
||||||
|
this.nextButton.label = _("Unlock");
|
||||||
|
else
|
||||||
|
this.nextButton.label = C_("button", "Sign In");
|
||||||
|
} else {
|
||||||
|
this.nextButton.label = _("Next");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateSensitivity(true);
|
||||||
|
this.emit('prompted');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onOVirtUserAuthenticated: function() {
|
||||||
|
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSmartcardStatusChanged: function() {
|
||||||
|
this.smartcardDetected = this._userVerifier.smartcardDetected;
|
||||||
|
|
||||||
|
// Most of the time we want to reset if the user inserts or removes
|
||||||
|
// a smartcard. Smartcard insertion "preempts" what the user was
|
||||||
|
// doing, and smartcard removal aborts the preemption.
|
||||||
|
// The exceptions are: 1) Don't reset on smartcard insertion if we're already verifying
|
||||||
|
// with a smartcard
|
||||||
|
// 2) Don't reset if we've already succeeded at verification and
|
||||||
|
// the user is getting logged in.
|
||||||
|
if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
|
||||||
|
this.verificationStatus == AuthPromptStatus.VERIFYING &&
|
||||||
|
this.smartcardDetected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onShowMessage: function(userVerifier, message, type) {
|
||||||
|
this.setMessage(message, type);
|
||||||
|
this.emit('prompted');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVerificationFailed: function() {
|
||||||
|
this._queryingService = null;
|
||||||
|
this.clear();
|
||||||
|
|
||||||
|
this.updateSensitivity(true);
|
||||||
|
this.setActorInDefaultButtonWell(null);
|
||||||
|
this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVerificationComplete: function() {
|
||||||
|
this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onReset: function() {
|
||||||
|
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
|
||||||
|
addActorToDefaultButtonWell: function(actor) {
|
||||||
|
this._defaultButtonWell.add_child(actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
setActorInDefaultButtonWell: function(actor, animate) {
|
||||||
|
if (!this._defaultButtonWellActor &&
|
||||||
|
!actor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let oldActor = this._defaultButtonWellActor;
|
||||||
|
|
||||||
|
if (oldActor)
|
||||||
|
Tweener.removeTweens(oldActor);
|
||||||
|
|
||||||
|
let isSpinner;
|
||||||
|
if (actor == this._spinner.actor)
|
||||||
|
isSpinner = true;
|
||||||
|
else
|
||||||
|
isSpinner = false;
|
||||||
|
|
||||||
|
if (this._defaultButtonWellActor != actor && oldActor) {
|
||||||
|
if (!animate) {
|
||||||
|
oldActor.opacity = 0;
|
||||||
|
} else {
|
||||||
|
Tweener.addTween(oldActor,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
|
||||||
|
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
|
||||||
|
transition: 'linear',
|
||||||
|
onCompleteScope: this,
|
||||||
|
onComplete: function() {
|
||||||
|
if (isSpinner) {
|
||||||
|
if (this._spinner)
|
||||||
|
this._spinner.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actor) {
|
||||||
|
if (isSpinner)
|
||||||
|
this._spinner.play();
|
||||||
|
|
||||||
|
if (!animate)
|
||||||
|
actor.opacity = 255;
|
||||||
|
else
|
||||||
|
Tweener.addTween(actor,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
|
||||||
|
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
|
||||||
|
transition: 'linear' });
|
||||||
|
}
|
||||||
|
|
||||||
|
this._defaultButtonWellActor = actor;
|
||||||
|
},
|
||||||
|
|
||||||
|
startSpinning: function() {
|
||||||
|
this.setActorInDefaultButtonWell(this._spinner.actor, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
stopSpinning: function() {
|
||||||
|
this.setActorInDefaultButtonWell(null, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function() {
|
||||||
|
this._entry.text = '';
|
||||||
|
this.stopSpinning();
|
||||||
|
},
|
||||||
|
|
||||||
|
setPasswordChar: function(passwordChar) {
|
||||||
|
this._entry.clutter_text.set_password_char(passwordChar);
|
||||||
|
this._entry.menu.isPassword = passwordChar != '';
|
||||||
|
},
|
||||||
|
|
||||||
|
setQuestion: function(question) {
|
||||||
|
this._label.set_text(question);
|
||||||
|
|
||||||
|
this._label.show();
|
||||||
|
this._entry.show();
|
||||||
|
|
||||||
|
this._entry.grab_key_focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
getAnswer: function() {
|
||||||
|
let text;
|
||||||
|
|
||||||
|
if (this._preemptiveAnswer) {
|
||||||
|
text = this._preemptiveAnswer;
|
||||||
|
this._preemptiveAnswer = null;
|
||||||
|
} else {
|
||||||
|
text = this._entry.get_text();
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
|
||||||
|
_fadeOutMessage: function() {
|
||||||
|
if (this._message.opacity == 0)
|
||||||
|
return;
|
||||||
|
Tweener.removeTweens(this._message);
|
||||||
|
Tweener.addTween(this._message,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: MESSAGE_FADE_OUT_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setMessage: function(message, type) {
|
||||||
|
if (type == GdmUtil.MessageType.ERROR)
|
||||||
|
this._message.add_style_class_name('login-dialog-message-warning');
|
||||||
|
else
|
||||||
|
this._message.remove_style_class_name('login-dialog-message-warning');
|
||||||
|
|
||||||
|
if (type == GdmUtil.MessageType.HINT)
|
||||||
|
this._message.add_style_class_name('login-dialog-message-hint');
|
||||||
|
else
|
||||||
|
this._message.remove_style_class_name('login-dialog-message-hint');
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
Tweener.removeTweens(this._message);
|
||||||
|
this._message.text = message;
|
||||||
|
this._message.opacity = 255;
|
||||||
|
} else {
|
||||||
|
this._message.opacity = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateNextButtonSensitivity: function(sensitive) {
|
||||||
|
this.nextButton.reactive = sensitive;
|
||||||
|
this.nextButton.can_focus = sensitive;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSensitivity: function(sensitive) {
|
||||||
|
this._updateNextButtonSensitivity(sensitive);
|
||||||
|
this._entry.reactive = sensitive;
|
||||||
|
this._entry.clutter_text.editable = sensitive;
|
||||||
|
},
|
||||||
|
|
||||||
|
hide: function() {
|
||||||
|
this.setActorInDefaultButtonWell(null, true);
|
||||||
|
this.actor.hide();
|
||||||
|
this._message.opacity = 0;
|
||||||
|
|
||||||
|
this.setUser(null);
|
||||||
|
|
||||||
|
this.updateSensitivity(true);
|
||||||
|
this._entry.set_text('');
|
||||||
|
},
|
||||||
|
|
||||||
|
setUser: function(user) {
|
||||||
|
if (user) {
|
||||||
|
let userWidget = new UserWidget.UserWidget(user);
|
||||||
|
this._userWell.set_child(userWidget.actor);
|
||||||
|
} else {
|
||||||
|
this._userWell.set_child(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: function() {
|
||||||
|
let oldStatus = this.verificationStatus;
|
||||||
|
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||||
|
|
||||||
|
if (oldStatus == AuthPromptStatus.VERIFYING)
|
||||||
|
this._userVerifier.cancel();
|
||||||
|
|
||||||
|
this._queryingService = null;
|
||||||
|
this.clear();
|
||||||
|
this._message.opacity = 0;
|
||||||
|
this.setUser(null);
|
||||||
|
this.stopSpinning();
|
||||||
|
|
||||||
|
if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED)
|
||||||
|
this.emit('failed');
|
||||||
|
|
||||||
|
let beginRequestType;
|
||||||
|
|
||||||
|
if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
|
||||||
|
// The user is constant at the unlock screen, so it will immediately
|
||||||
|
// respond to the request with the username
|
||||||
|
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||||||
|
} else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
|
||||||
|
(this.smartcardDetected &&
|
||||||
|
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME))) {
|
||||||
|
// We don't need to know the username if the user preempted the login screen
|
||||||
|
// with a smartcard or with preauthenticated oVirt credentials
|
||||||
|
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
|
||||||
|
} else {
|
||||||
|
// In all other cases, we should get the username up front.
|
||||||
|
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('reset', beginRequestType);
|
||||||
|
},
|
||||||
|
|
||||||
|
addCharacter: function(unichar) {
|
||||||
|
if (!this._entry.visible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._entry.grab_key_focus();
|
||||||
|
this._entry.clutter_text.insert_unichar(unichar);
|
||||||
|
},
|
||||||
|
|
||||||
|
begin: function(params) {
|
||||||
|
params = Params.parse(params, { userName: null,
|
||||||
|
hold: null });
|
||||||
|
|
||||||
|
this.updateSensitivity(false);
|
||||||
|
|
||||||
|
let hold = params.hold;
|
||||||
|
if (!hold)
|
||||||
|
hold = new Batch.Hold();
|
||||||
|
|
||||||
|
this._userVerifier.begin(params.userName, hold);
|
||||||
|
this.verificationStatus = AuthPromptStatus.VERIFYING;
|
||||||
|
},
|
||||||
|
|
||||||
|
finish: function(onComplete) {
|
||||||
|
if (!this._userVerifier.hasPendingMessages) {
|
||||||
|
onComplete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let signalId = this._userVerifier.connect('no-more-messages',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._userVerifier.disconnect(signalId);
|
||||||
|
onComplete();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: function() {
|
||||||
|
this.reset();
|
||||||
|
this.emit('cancelled');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(AuthPrompt.prototype);
|
||||||
@@ -13,9 +13,7 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ const Lang = imports.lang;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
|
const FprintManagerIface = '<node> \
|
||||||
<method name='GetDefaultDevice'>
|
<interface name="net.reactivated.Fprint.Manager"> \
|
||||||
<arg type='o' direction='out' />
|
<method name="GetDefaultDevice"> \
|
||||||
</method>
|
<arg type="o" direction="out" /> \
|
||||||
</interface>;
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);
|
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
64
js/gdm/oVirt.js
Normal file
64
js/gdm/oVirt.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const OVirtCredentialsIface = '<node> \
|
||||||
|
<interface name="org.ovirt.vdsm.Credentials"> \
|
||||||
|
<signal name="UserAuthenticated"> \
|
||||||
|
<arg type="s" name="token"/> \
|
||||||
|
</signal> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
|
const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface);
|
||||||
|
|
||||||
|
let _oVirtCredentialsManager = null;
|
||||||
|
|
||||||
|
function OVirtCredentials() {
|
||||||
|
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
|
||||||
|
g_interface_name: OVirtCredentialsInfo.name,
|
||||||
|
g_interface_info: OVirtCredentialsInfo,
|
||||||
|
g_name: 'org.ovirt.vdsm.Credentials',
|
||||||
|
g_object_path: '/org/ovirt/vdsm/Credentials',
|
||||||
|
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||||
|
self.init(null);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OVirtCredentialsManager = new Lang.Class({
|
||||||
|
Name: 'OVirtCredentialsManager',
|
||||||
|
_init: function() {
|
||||||
|
this._token = null;
|
||||||
|
|
||||||
|
this._credentials = new OVirtCredentials();
|
||||||
|
this._credentials.connectSignal('UserAuthenticated',
|
||||||
|
Lang.bind(this, this._onUserAuthenticated));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onUserAuthenticated: function(proxy, sender, [token]) {
|
||||||
|
this._token = token;
|
||||||
|
this.emit('user-authenticated', token);
|
||||||
|
},
|
||||||
|
|
||||||
|
hasToken: function() {
|
||||||
|
return this._token != null;
|
||||||
|
},
|
||||||
|
|
||||||
|
getToken: function() {
|
||||||
|
return this._token;
|
||||||
|
},
|
||||||
|
|
||||||
|
resetToken: function() {
|
||||||
|
this._token = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(OVirtCredentialsManager.prototype);
|
||||||
|
|
||||||
|
function getOVirtCredentialsManager() {
|
||||||
|
if (!_oVirtCredentialsManager)
|
||||||
|
_oVirtCredentialsManager = new OVirtCredentialsManager();
|
||||||
|
|
||||||
|
return _oVirtCredentialsManager;
|
||||||
|
}
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
/*
|
|
||||||
* Copyright 2011 Red Hat, Inc
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
|
|
||||||
const LoginManager = imports.misc.loginManager;
|
|
||||||
|
|
||||||
const GdmUtil = imports.gdm.util;
|
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
|
||||||
|
|
||||||
const PowerMenuButton = new Lang.Class({
|
|
||||||
Name: 'PowerMenuButton',
|
|
||||||
Extends: PanelMenu.SystemStatusButton,
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
/* Translators: accessible name of the power menu in the login screen */
|
|
||||||
this.parent('system-shutdown-symbolic', _("Power"));
|
|
||||||
|
|
||||||
this._loginManager = LoginManager.getLoginManager();
|
|
||||||
|
|
||||||
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
|
||||||
this._settings.connect('changed::disable-restart-buttons',
|
|
||||||
Lang.bind(this, this._updateVisibility));
|
|
||||||
|
|
||||||
this._createSubMenu();
|
|
||||||
|
|
||||||
// ConsoleKit doesn't send notifications when shutdown/reboot
|
|
||||||
// are disabled, so we update the menu item each time the menu opens
|
|
||||||
this.menu.connect('open-state-changed', Lang.bind(this,
|
|
||||||
function(menu, open) {
|
|
||||||
if (open) {
|
|
||||||
this._updateHaveShutdown();
|
|
||||||
this._updateHaveRestart();
|
|
||||||
this._updateHaveSuspend();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
this._updateHaveShutdown();
|
|
||||||
this._updateHaveRestart();
|
|
||||||
this._updateHaveSuspend();
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateVisibility: function() {
|
|
||||||
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
|
|
||||||
this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons');
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateHaveShutdown: function() {
|
|
||||||
this._loginManager.canPowerOff(Lang.bind(this, function(result) {
|
|
||||||
this._haveShutdown = result;
|
|
||||||
this._powerOffItem.actor.visible = this._haveShutdown;
|
|
||||||
this._updateVisibility();
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateHaveRestart: function() {
|
|
||||||
this._loginManager.canReboot(Lang.bind(this, function(result) {
|
|
||||||
this._haveRestart = result;
|
|
||||||
this._restartItem.actor.visible = this._haveRestart;
|
|
||||||
this._updateVisibility();
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateHaveSuspend: function() {
|
|
||||||
this._loginManager.canSuspend(Lang.bind(this, function(result) {
|
|
||||||
this._haveSuspend = result;
|
|
||||||
this._suspendItem.actor.visible = this._haveSuspend;
|
|
||||||
this._updateVisibility();
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
_createSubMenu: function() {
|
|
||||||
let item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Suspend"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onActivateSuspend));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._suspendItem = item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Restart"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onActivateRestart));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._restartItem = item;
|
|
||||||
|
|
||||||
item = new PopupMenu.PopupMenuItem(_("Power Off"));
|
|
||||||
item.connect('activate', Lang.bind(this, this._onActivatePowerOff));
|
|
||||||
this.menu.addMenuItem(item);
|
|
||||||
this._powerOffItem = item;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onActivateSuspend: function() {
|
|
||||||
if (!this._haveSuspend)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._loginManager.suspend();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onActivateRestart: function() {
|
|
||||||
if (!this._haveRestart)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._loginManager.reboot();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onActivatePowerOff: function() {
|
|
||||||
if (!this._haveShutdown)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._loginManager.powerOff();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
106
js/gdm/realmd.js
106
js/gdm/realmd.js
@@ -5,52 +5,58 @@ const Lang = imports.lang;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const ProviderIface = <interface name='org.freedesktop.realmd.Provider'>
|
const ProviderIface = '<node> \
|
||||||
<property name="Name" type="s" access="read"/>
|
<interface name="org.freedesktop.realmd.Provider"> \
|
||||||
<property name="Version" type="s" access="read"/>
|
<property name="Name" type="s" access="read"/> \
|
||||||
<property name="Realms" type="ao" access="read"/>
|
<property name="Version" type="s" access="read"/> \
|
||||||
<method name="Discover">
|
<property name="Realms" type="ao" access="read"/> \
|
||||||
<arg name="string" type="s" direction="in"/>
|
<method name="Discover"> \
|
||||||
<arg name="options" type="a{sv}" direction="in"/>
|
<arg name="string" type="s" direction="in"/> \
|
||||||
<arg name="relevance" type="i" direction="out"/>
|
<arg name="options" type="a{sv}" direction="in"/> \
|
||||||
<arg name="realm" type="ao" direction="out"/>
|
<arg name="relevance" type="i" direction="out"/> \
|
||||||
</method>
|
<arg name="realm" type="ao" direction="out"/> \
|
||||||
</interface>;
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
|
const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
|
||||||
|
|
||||||
const ServiceIface = <interface name="org.freedesktop.realmd.Service">
|
const ServiceIface = '<node> \
|
||||||
<method name="Cancel">
|
<interface name="org.freedesktop.realmd.Service"> \
|
||||||
<arg name="operation" type="s" direction="in"/>
|
<method name="Cancel"> \
|
||||||
</method>
|
<arg name="operation" type="s" direction="in"/> \
|
||||||
<method name="Release" />
|
</method> \
|
||||||
<method name="SetLocale">
|
<method name="Release" /> \
|
||||||
<arg name="locale" type="s" direction="in"/>
|
<method name="SetLocale"> \
|
||||||
</method>
|
<arg name="locale" type="s" direction="in"/> \
|
||||||
<signal name="Diagnostics">
|
</method> \
|
||||||
<arg name="data" type="s"/>
|
<signal name="Diagnostics"> \
|
||||||
<arg name="operation" type="s"/>
|
<arg name="data" type="s"/> \
|
||||||
</signal>
|
<arg name="operation" type="s"/> \
|
||||||
</interface>;
|
</signal> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
|
const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
|
||||||
|
|
||||||
const RealmIface = <interface name="org.freedesktop.realmd.Realm">
|
const RealmIface = '<node> \
|
||||||
<property name="Name" type="s" access="read"/>
|
<interface name="org.freedesktop.realmd.Realm"> \
|
||||||
<property name="Configured" type="s" access="read"/>
|
<property name="Name" type="s" access="read"/> \
|
||||||
<property name="Details" type="a(ss)" access="read"/>
|
<property name="Configured" type="s" access="read"/> \
|
||||||
<property name="LoginFormats" type="as" access="read"/>
|
<property name="Details" type="a(ss)" access="read"/> \
|
||||||
<property name="LoginPolicy" type="s" access="read"/>
|
<property name="LoginFormats" type="as" access="read"/> \
|
||||||
<property name="PermittedLogins" type="as" access="read"/>
|
<property name="LoginPolicy" type="s" access="read"/> \
|
||||||
<property name="SupportedInterfaces" type="as" access="read"/>
|
<property name="PermittedLogins" type="as" access="read"/> \
|
||||||
<method name="ChangeLoginPolicy">
|
<property name="SupportedInterfaces" type="as" access="read"/> \
|
||||||
<arg name="login_policy" type="s" direction="in"/>
|
<method name="ChangeLoginPolicy"> \
|
||||||
<arg name="permitted_add" type="as" direction="in"/>
|
<arg name="login_policy" type="s" direction="in"/> \
|
||||||
<arg name="permitted_remove" type="as" direction="in"/>
|
<arg name="permitted_add" type="as" direction="in"/> \
|
||||||
<arg name="options" type="a{sv}" direction="in"/>
|
<arg name="permitted_remove" type="as" direction="in"/> \
|
||||||
</method>
|
<arg name="options" type="a{sv}" direction="in"/> \
|
||||||
<method name="Deconfigure">
|
</method> \
|
||||||
<arg name="options" type="a{sv}" direction="in"/>
|
<method name="Deconfigure"> \
|
||||||
</method>
|
<arg name="options" type="a{sv}" direction="in"/> \
|
||||||
</interface>;
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
|
const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
|
||||||
|
|
||||||
const Manager = new Lang.Class({
|
const Manager = new Lang.Class({
|
||||||
@@ -63,7 +69,7 @@ const Manager = new Lang.Class({
|
|||||||
Lang.bind(this, this._reloadRealms))
|
Lang.bind(this, this._reloadRealms))
|
||||||
this._realms = {};
|
this._realms = {};
|
||||||
|
|
||||||
this._aggregateProvider.connect('g-properties-changed',
|
this._signalId = this._aggregateProvider.connect('g-properties-changed',
|
||||||
Lang.bind(this, function(proxy, properties) {
|
Lang.bind(this, function(proxy, properties) {
|
||||||
if ('Realms' in properties.deep_unpack())
|
if ('Realms' in properties.deep_unpack())
|
||||||
this._reloadRealms();
|
this._reloadRealms();
|
||||||
@@ -106,7 +112,7 @@ const Manager = new Lang.Class({
|
|||||||
realm.connect('g-properties-changed',
|
realm.connect('g-properties-changed',
|
||||||
Lang.bind(this, function(proxy, properties) {
|
Lang.bind(this, function(proxy, properties) {
|
||||||
if ('Configured' in properties.deep_unpack())
|
if ('Configured' in properties.deep_unpack())
|
||||||
this._reloadRealm();
|
this._reloadRealm(realm);
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -134,6 +140,18 @@ const Manager = new Lang.Class({
|
|||||||
this._updateLoginFormat();
|
this._updateLoginFormat();
|
||||||
|
|
||||||
return this._loginFormat;
|
return this._loginFormat;
|
||||||
|
},
|
||||||
|
|
||||||
|
release: function() {
|
||||||
|
Service(Gio.DBus.system,
|
||||||
|
'org.freedesktop.realmd',
|
||||||
|
'/org/freedesktop/realmd',
|
||||||
|
function(service) {
|
||||||
|
service.ReleaseRemote();
|
||||||
|
});
|
||||||
|
this._aggregateProvider.disconnect(this._signalId);
|
||||||
|
this._realms = { };
|
||||||
|
this._updateLoginFormat();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(Manager.prototype)
|
Signals.addSignalMethods(Manager.prototype)
|
||||||
|
|||||||
350
js/gdm/util.js
350
js/gdm/util.js
@@ -1,23 +1,33 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const Batch = imports.gdm.batch;
|
const Batch = imports.gdm.batch;
|
||||||
const Fprint = imports.gdm.fingerprint;
|
const Fprint = imports.gdm.fingerprint;
|
||||||
const Realmd = imports.gdm.realmd;
|
const OVirt = imports.gdm.oVirt;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
const ShellEntry = imports.ui.shellEntry;
|
||||||
|
const SmartcardManager = imports.misc.smartcardManager;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
const PASSWORD_SERVICE_NAME = 'gdm-password';
|
const PASSWORD_SERVICE_NAME = 'gdm-password';
|
||||||
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
||||||
|
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
|
||||||
|
const OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
|
||||||
const FADE_ANIMATION_TIME = 0.16;
|
const FADE_ANIMATION_TIME = 0.16;
|
||||||
|
const CLONE_FADE_ANIMATION_TIME = 0.25;
|
||||||
|
|
||||||
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
||||||
|
const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
|
||||||
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
||||||
|
const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
|
||||||
const BANNER_MESSAGE_KEY = 'banner-message-enable';
|
const BANNER_MESSAGE_KEY = 'banner-message-enable';
|
||||||
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
|
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
|
||||||
const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
||||||
@@ -25,6 +35,16 @@ const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
|||||||
const LOGO_KEY = 'logo';
|
const LOGO_KEY = 'logo';
|
||||||
const DISABLE_USER_LIST_KEY = 'disable-user-list';
|
const DISABLE_USER_LIST_KEY = 'disable-user-list';
|
||||||
|
|
||||||
|
// Give user 16ms to read each character of a PAM message
|
||||||
|
const USER_READ_TIME = 16
|
||||||
|
|
||||||
|
const MessageType = {
|
||||||
|
NONE: 0,
|
||||||
|
ERROR: 1,
|
||||||
|
INFO: 2,
|
||||||
|
HINT: 3
|
||||||
|
};
|
||||||
|
|
||||||
function fadeInActor(actor) {
|
function fadeInActor(actor) {
|
||||||
if (actor.opacity == 255 && actor.visible)
|
if (actor.opacity == 255 && actor.visible)
|
||||||
return null;
|
return null;
|
||||||
@@ -71,6 +91,34 @@ function fadeOutActor(actor) {
|
|||||||
return hold;
|
return hold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cloneAndFadeOutActor(actor) {
|
||||||
|
// Immediately hide actor so its sibling can have its space
|
||||||
|
// and position, but leave a non-reactive clone on-screen,
|
||||||
|
// so from the user's point of view it smoothly fades away
|
||||||
|
// and reveals its sibling.
|
||||||
|
actor.hide();
|
||||||
|
|
||||||
|
let clone = new Clutter.Clone({ source: actor,
|
||||||
|
reactive: false });
|
||||||
|
|
||||||
|
Main.uiGroup.add_child(clone);
|
||||||
|
|
||||||
|
let [x, y] = actor.get_transformed_position();
|
||||||
|
clone.set_position(x, y);
|
||||||
|
|
||||||
|
let hold = new Batch.Hold();
|
||||||
|
Tweener.addTween(clone,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: CLONE_FADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: function() {
|
||||||
|
clone.destroy();
|
||||||
|
hold.release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return hold;
|
||||||
|
}
|
||||||
|
|
||||||
const ShellUserVerifier = new Lang.Class({
|
const ShellUserVerifier = new Lang.Class({
|
||||||
Name: 'ShellUserVerifier',
|
Name: 'ShellUserVerifier',
|
||||||
|
|
||||||
@@ -81,17 +129,45 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
this._client = client;
|
this._client = client;
|
||||||
|
|
||||||
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
|
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
|
||||||
|
this._settings.connect('changed',
|
||||||
|
Lang.bind(this, this._updateDefaultService));
|
||||||
|
this._updateDefaultService();
|
||||||
|
|
||||||
this._fprintManager = new Fprint.FprintManager();
|
this._fprintManager = new Fprint.FprintManager();
|
||||||
this._realmManager = new Realmd.Manager();
|
this._smartcardManager = SmartcardManager.getSmartcardManager();
|
||||||
|
|
||||||
|
// We check for smartcards right away, since an inserted smartcard
|
||||||
|
// at startup should result in immediately initiating authentication.
|
||||||
|
// This is different than fingeprint readers, where we only check them
|
||||||
|
// after a user has been picked.
|
||||||
|
this._checkForSmartcard();
|
||||||
|
|
||||||
|
this._smartcardManager.connect('smartcard-inserted',
|
||||||
|
Lang.bind(this, this._checkForSmartcard));
|
||||||
|
this._smartcardManager.connect('smartcard-removed',
|
||||||
|
Lang.bind(this, this._checkForSmartcard));
|
||||||
|
|
||||||
|
this._messageQueue = [];
|
||||||
|
this._messageQueueTimeoutId = 0;
|
||||||
|
this.hasPendingMessages = false;
|
||||||
|
this.reauthenticating = false;
|
||||||
|
|
||||||
this._failCounter = 0;
|
this._failCounter = 0;
|
||||||
|
|
||||||
|
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
|
||||||
|
|
||||||
|
if (this._oVirtCredentialsManager.hasToken())
|
||||||
|
this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
|
||||||
|
|
||||||
|
this._oVirtCredentialsManager.connect('user-authenticated',
|
||||||
|
Lang.bind(this, this._oVirtUserAuthenticated));
|
||||||
},
|
},
|
||||||
|
|
||||||
begin: function(userName, hold) {
|
begin: function(userName, hold) {
|
||||||
this._cancellable = new Gio.Cancellable();
|
this._cancellable = new Gio.Cancellable();
|
||||||
this._hold = hold;
|
this._hold = hold;
|
||||||
this._userName = userName;
|
this._userName = userName;
|
||||||
|
this.reauthenticating = false;
|
||||||
|
|
||||||
this._checkForFingerprintReader();
|
this._checkForFingerprintReader();
|
||||||
|
|
||||||
@@ -109,8 +185,10 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
if (this._cancellable)
|
if (this._cancellable)
|
||||||
this._cancellable.cancel();
|
this._cancellable.cancel();
|
||||||
|
|
||||||
if (this._userVerifier)
|
if (this._userVerifier) {
|
||||||
this._userVerifier.call_cancel_sync(null);
|
this._userVerifier.call_cancel_sync(null);
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
clear: function() {
|
clear: function() {
|
||||||
@@ -123,33 +201,125 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
this._userVerifier.run_dispose();
|
this._userVerifier.run_dispose();
|
||||||
this._userVerifier = null;
|
this._userVerifier = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._clearMessageQueue();
|
||||||
},
|
},
|
||||||
|
|
||||||
answerQuery: function(serviceName, answer) {
|
answerQuery: function(serviceName, answer) {
|
||||||
// Clear any previous message
|
if (!this.hasPendingMessages) {
|
||||||
this.emit('show-message', null, null);
|
|
||||||
|
|
||||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||||
|
} else {
|
||||||
|
let signalId = this.connect('no-more-messages',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this.disconnect(signalId);
|
||||||
|
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_getIntervalForMessage: function(message) {
|
||||||
|
// We probably could be smarter here
|
||||||
|
return message.length * USER_READ_TIME;
|
||||||
|
},
|
||||||
|
|
||||||
|
finishMessageQueue: function() {
|
||||||
|
if (!this.hasPendingMessages)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._messageQueue = [];
|
||||||
|
|
||||||
|
this.hasPendingMessages = false;
|
||||||
|
this.emit('no-more-messages');
|
||||||
|
},
|
||||||
|
|
||||||
|
_queueMessageTimeout: function() {
|
||||||
|
if (this._messageQueue.length == 0) {
|
||||||
|
this.finishMessageQueue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._messageQueueTimeoutId != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let message = this._messageQueue.shift();
|
||||||
|
|
||||||
|
this.emit('show-message', message.text, message.type);
|
||||||
|
|
||||||
|
this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||||
|
message.interval,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._messageQueueTimeoutId = 0;
|
||||||
|
this._queueMessageTimeout();
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_queueMessage: function(message, messageType) {
|
||||||
|
let interval = this._getIntervalForMessage(message);
|
||||||
|
|
||||||
|
this.hasPendingMessages = true;
|
||||||
|
this._messageQueue.push({ text: message, type: messageType, interval: interval });
|
||||||
|
this._queueMessageTimeout();
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearMessageQueue: function() {
|
||||||
|
this.finishMessageQueue();
|
||||||
|
|
||||||
|
if (this._messageQueueTimeoutId != 0) {
|
||||||
|
GLib.source_remove(this._messageQueueTimeoutId);
|
||||||
|
this._messageQueueTimeoutId = 0;
|
||||||
|
}
|
||||||
|
this.emit('show-message', null, MessageType.NONE);
|
||||||
},
|
},
|
||||||
|
|
||||||
_checkForFingerprintReader: function() {
|
_checkForFingerprintReader: function() {
|
||||||
this._haveFingerprintReader = false;
|
this._haveFingerprintReader = false;
|
||||||
|
|
||||||
if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
|
if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) {
|
||||||
|
this._updateDefaultService();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
|
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
|
||||||
function(device, error) {
|
function(device, error) {
|
||||||
if (!error && device)
|
if (!error && device)
|
||||||
this._haveFingerprintReader = true;
|
this._haveFingerprintReader = true;
|
||||||
|
this._updateDefaultService();
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_oVirtUserAuthenticated: function(token) {
|
||||||
|
this._preemptingService = OVIRT_SERVICE_NAME;
|
||||||
|
this.emit('ovirt-user-authenticated');
|
||||||
|
},
|
||||||
|
|
||||||
|
_checkForSmartcard: function() {
|
||||||
|
let smartcardDetected;
|
||||||
|
|
||||||
|
if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
|
||||||
|
smartcardDetected = false;
|
||||||
|
else if (this.reauthenticating)
|
||||||
|
smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
|
||||||
|
else
|
||||||
|
smartcardDetected = this._smartcardManager.hasInsertedTokens();
|
||||||
|
|
||||||
|
if (smartcardDetected != this.smartcardDetected) {
|
||||||
|
this.smartcardDetected = smartcardDetected;
|
||||||
|
|
||||||
|
if (this.smartcardDetected)
|
||||||
|
this._preemptingService = SMARTCARD_SERVICE_NAME;
|
||||||
|
else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
|
||||||
|
this._preemptingService = null;
|
||||||
|
|
||||||
|
this.emit('smartcard-status-changed');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_reportInitError: function(where, error) {
|
_reportInitError: function(where, error) {
|
||||||
logError(error, where);
|
logError(error, where);
|
||||||
this._hold.release();
|
this._hold.release();
|
||||||
|
|
||||||
this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning');
|
this._queueMessage(_("Authentication error"), MessageType.ERROR);
|
||||||
this._verificationFailed(false);
|
this._verificationFailed(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -170,6 +340,7 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.reauthenticating = true;
|
||||||
this._connectSignals();
|
this._connectSignals();
|
||||||
this._beginVerification();
|
this._beginVerification();
|
||||||
this._hold.release();
|
this._hold.release();
|
||||||
@@ -200,11 +371,34 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
|
||||||
},
|
},
|
||||||
|
|
||||||
_beginVerification: function() {
|
_getForegroundService: function() {
|
||||||
this._hold.acquire();
|
if (this._preemptingService)
|
||||||
|
return this._preemptingService;
|
||||||
|
|
||||||
|
return this._defaultService;
|
||||||
|
},
|
||||||
|
|
||||||
|
serviceIsForeground: function(serviceName) {
|
||||||
|
return serviceName == this._getForegroundService();
|
||||||
|
},
|
||||||
|
|
||||||
|
serviceIsDefault: function(serviceName) {
|
||||||
|
return serviceName == this._defaultService;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateDefaultService: function() {
|
||||||
|
if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
|
||||||
|
this._defaultService = PASSWORD_SERVICE_NAME;
|
||||||
|
else if (this.smartcardDetected)
|
||||||
|
this._defaultService = SMARTCARD_SERVICE_NAME;
|
||||||
|
else if (this._haveFingerprintReader)
|
||||||
|
this._defaultService = FINGERPRINT_SERVICE_NAME;
|
||||||
|
},
|
||||||
|
|
||||||
|
_startService: function(serviceName) {
|
||||||
|
this._hold.acquire();
|
||||||
if (this._userName) {
|
if (this._userName) {
|
||||||
this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
|
this._userVerifier.call_begin_verification_for_user(serviceName,
|
||||||
this._userName,
|
this._userName,
|
||||||
this._cancellable,
|
this._cancellable,
|
||||||
Lang.bind(this, function(obj, result) {
|
Lang.bind(this, function(obj, result) {
|
||||||
@@ -219,28 +413,8 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
|
|
||||||
this._hold.release();
|
this._hold.release();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (this._haveFingerprintReader) {
|
|
||||||
this._hold.acquire();
|
|
||||||
|
|
||||||
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
|
|
||||||
this._userName,
|
|
||||||
this._cancellable,
|
|
||||||
Lang.bind(this, function(obj, result) {
|
|
||||||
try {
|
|
||||||
obj.call_begin_verification_for_user_finish(result);
|
|
||||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
|
||||||
return;
|
|
||||||
} catch(e) {
|
|
||||||
this._reportInitError('Failed to start fingerprint verification for user', e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._hold.release();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
|
this._userVerifier.call_begin_verification(serviceName,
|
||||||
this._cancellable,
|
this._cancellable,
|
||||||
Lang.bind(this, function(obj, result) {
|
Lang.bind(this, function(obj, result) {
|
||||||
try {
|
try {
|
||||||
@@ -257,69 +431,59 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_beginVerification: function() {
|
||||||
|
this._startService(this._getForegroundService());
|
||||||
|
|
||||||
|
if (this._userName && this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME))
|
||||||
|
this._startService(FINGERPRINT_SERVICE_NAME);
|
||||||
|
},
|
||||||
|
|
||||||
_onInfo: function(client, serviceName, info) {
|
_onInfo: function(client, serviceName, info) {
|
||||||
// We don't display fingerprint messages, because they
|
if (this.serviceIsForeground(serviceName)) {
|
||||||
// have words like UPEK in them. Instead we use the messages
|
this._queueMessage(info, MessageType.INFO);
|
||||||
// as a cue to display our own message.
|
} else if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
||||||
if (serviceName == FINGERPRINT_SERVICE_NAME &&
|
|
||||||
this._haveFingerprintReader) {
|
this._haveFingerprintReader) {
|
||||||
|
// We don't show fingerprint messages directly since it's
|
||||||
|
// not the main auth service. Instead we use the messages
|
||||||
|
// as a cue to display our own message.
|
||||||
|
|
||||||
// Translators: this message is shown below the password entry field
|
// Translators: this message is shown below the password entry field
|
||||||
// to indicate the user can swipe their finger instead
|
// to indicate the user can swipe their finger instead
|
||||||
this.emit('show-login-hint', _("(or swipe finger)"));
|
this._queueMessage(_("(or swipe finger)"), MessageType.HINT);
|
||||||
} else if (serviceName == PASSWORD_SERVICE_NAME) {
|
|
||||||
this.emit('show-message', info, 'login-dialog-message-info');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onProblem: function(client, serviceName, problem) {
|
_onProblem: function(client, serviceName, problem) {
|
||||||
// we don't want to show auth failed messages to
|
if (!this.serviceIsForeground(serviceName))
|
||||||
// users who haven't enrolled their fingerprint.
|
|
||||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
|
||||||
return;
|
return;
|
||||||
this.emit('show-message', problem, 'login-dialog-message-warning');
|
|
||||||
},
|
|
||||||
|
|
||||||
_showRealmLoginHint: function() {
|
this._queueMessage(problem, MessageType.ERROR);
|
||||||
if (this._realmManager.loginFormat) {
|
|
||||||
let hint = this._realmManager.loginFormat;
|
|
||||||
|
|
||||||
hint = hint.replace(/%U/g, 'user');
|
|
||||||
hint = hint.replace(/%D/g, 'DOMAIN');
|
|
||||||
hint = hint.replace(/%[^UD]/g, '');
|
|
||||||
|
|
||||||
// Translators: this message is shown below the username entry field
|
|
||||||
// to clue the user in on how to login to the local network realm
|
|
||||||
this.emit('show-login-hint',
|
|
||||||
_("(e.g., user or %s)").format(hint));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onInfoQuery: function(client, serviceName, question) {
|
_onInfoQuery: function(client, serviceName, question) {
|
||||||
// We only expect questions to come from the main auth service
|
if (!this.serviceIsForeground(serviceName))
|
||||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._showRealmLoginHint();
|
|
||||||
this._realmLoginHintSignalId = this._realmManager.connect('login-format-changed',
|
|
||||||
Lang.bind(this, this._showRealmLoginHint));
|
|
||||||
|
|
||||||
this.emit('ask-question', serviceName, question, '');
|
this.emit('ask-question', serviceName, question, '');
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
|
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
|
||||||
// We only expect secret requests to come from the main auth service
|
if (!this.serviceIsForeground(serviceName))
|
||||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (serviceName == OVIRT_SERVICE_NAME) {
|
||||||
|
// The only question asked by this service is "Token?"
|
||||||
|
this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
|
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
|
||||||
},
|
},
|
||||||
|
|
||||||
_onReset: function() {
|
_onReset: function() {
|
||||||
this.clear();
|
|
||||||
|
|
||||||
// Clear previous attempts to authenticate
|
// Clear previous attempts to authenticate
|
||||||
this._failCounter = 0;
|
this._failCounter = 0;
|
||||||
|
this._updateDefaultService();
|
||||||
|
|
||||||
this.emit('reset');
|
this.emit('reset');
|
||||||
},
|
},
|
||||||
@@ -328,6 +492,15 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
this.emit('verification-complete');
|
this.emit('verification-complete');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_cancelAndReset: function() {
|
||||||
|
this.cancel();
|
||||||
|
this._onReset();
|
||||||
|
},
|
||||||
|
|
||||||
|
_retry: function() {
|
||||||
|
this.begin(this._userName, new Batch.Hold());
|
||||||
|
},
|
||||||
|
|
||||||
_verificationFailed: function(retry) {
|
_verificationFailed: function(retry) {
|
||||||
// For Not Listed / enterprise logins, immediately reset
|
// For Not Listed / enterprise logins, immediately reset
|
||||||
// the dialog
|
// the dialog
|
||||||
@@ -339,34 +512,47 @@ const ShellUserVerifier = new Lang.Class({
|
|||||||
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
|
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
|
||||||
|
|
||||||
if (canRetry) {
|
if (canRetry) {
|
||||||
this.clear();
|
if (!this.hasPendingMessages) {
|
||||||
this.begin(this._userName, new Batch.Hold());
|
this._retry();
|
||||||
} else {
|
} else {
|
||||||
// Allow some time to see the message, then reset everything
|
let signalId = this.connect('no-more-messages',
|
||||||
Mainloop.timeout_add(3000, Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this.cancel();
|
this.disconnect(signalId);
|
||||||
|
this._retry();
|
||||||
this._onReset();
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (!this.hasPendingMessages) {
|
||||||
|
this._cancelAndReset();
|
||||||
|
} else {
|
||||||
|
let signalId = this.connect('no-more-messages',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this.disconnect(signalId);
|
||||||
|
this._cancelAndReset();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.emit('verification-failed');
|
this.emit('verification-failed');
|
||||||
},
|
},
|
||||||
|
|
||||||
_onConversationStopped: function(client, serviceName) {
|
_onConversationStopped: function(client, serviceName) {
|
||||||
|
// If the login failed with the preauthenticated oVirt credentials
|
||||||
|
// then discard the credentials and revert to default authentication
|
||||||
|
// mechanism.
|
||||||
|
if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) {
|
||||||
|
this._oVirtCredentialsManager.resetToken();
|
||||||
|
this._preemptingService = null;
|
||||||
|
this._verificationFailed(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if the password service fails, then cancel everything.
|
// if the password service fails, then cancel everything.
|
||||||
// But if, e.g., fingerprint fails, still give
|
// But if, e.g., fingerprint fails, still give
|
||||||
// password authentication a chance to succeed
|
// password authentication a chance to succeed
|
||||||
if (serviceName == PASSWORD_SERVICE_NAME) {
|
if (this.serviceIsForeground(serviceName)) {
|
||||||
this._verificationFailed(true);
|
this._verificationFailed(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('hide-login-hint');
|
|
||||||
|
|
||||||
if (this._realmLoginHintSignalId) {
|
|
||||||
this._realmManager.disconnect(this._realmLoginHintSignalId);
|
|
||||||
this._realmLoginHintSignalId = 0;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(ShellUserVerifier.prototype);
|
Signals.addSignalMethods(ShellUserVerifier.prototype);
|
||||||
|
|||||||
114
js/js-resources.gresource.xml
Normal file
114
js/js-resources.gresource.xml
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<gresources>
|
||||||
|
<gresource prefix="/org/gnome/shell">
|
||||||
|
<file>gdm/authPrompt.js</file>
|
||||||
|
<file>gdm/batch.js</file>
|
||||||
|
<file>gdm/fingerprint.js</file>
|
||||||
|
<file>gdm/loginDialog.js</file>
|
||||||
|
<file>gdm/oVirt.js</file>
|
||||||
|
<file>gdm/realmd.js</file>
|
||||||
|
<file>gdm/util.js</file>
|
||||||
|
|
||||||
|
<file>extensionPrefs/main.js</file>
|
||||||
|
|
||||||
|
<file>misc/config.js</file>
|
||||||
|
<file>misc/extensionUtils.js</file>
|
||||||
|
<file>misc/fileUtils.js</file>
|
||||||
|
<file>misc/gnomeSession.js</file>
|
||||||
|
<file>misc/history.js</file>
|
||||||
|
<file>misc/jsParse.js</file>
|
||||||
|
<file>misc/loginManager.js</file>
|
||||||
|
<file>misc/modemManager.js</file>
|
||||||
|
<file>misc/objectManager.js</file>
|
||||||
|
<file>misc/params.js</file>
|
||||||
|
<file>misc/smartcardManager.js</file>
|
||||||
|
<file>misc/util.js</file>
|
||||||
|
|
||||||
|
<file>perf/core.js</file>
|
||||||
|
|
||||||
|
<file>ui/altTab.js</file>
|
||||||
|
<file>ui/animation.js</file>
|
||||||
|
<file>ui/appDisplay.js</file>
|
||||||
|
<file>ui/appFavorites.js</file>
|
||||||
|
<file>ui/backgroundMenu.js</file>
|
||||||
|
<file>ui/background.js</file>
|
||||||
|
<file>ui/boxpointer.js</file>
|
||||||
|
<file>ui/calendar.js</file>
|
||||||
|
<file>ui/checkBox.js</file>
|
||||||
|
<file>ui/ctrlAltTab.js</file>
|
||||||
|
<file>ui/dash.js</file>
|
||||||
|
<file>ui/dateMenu.js</file>
|
||||||
|
<file>ui/dnd.js</file>
|
||||||
|
<file>ui/endSessionDialog.js</file>
|
||||||
|
<file>ui/environment.js</file>
|
||||||
|
<file>ui/extensionDownloader.js</file>
|
||||||
|
<file>ui/extensionSystem.js</file>
|
||||||
|
<file>ui/focusCaretTracker.js</file>
|
||||||
|
<file>ui/grabHelper.js</file>
|
||||||
|
<file>ui/ibusCandidatePopup.js</file>
|
||||||
|
<file>ui/iconGrid.js</file>
|
||||||
|
<file>ui/keyboard.js</file>
|
||||||
|
<file>ui/layout.js</file>
|
||||||
|
<file>ui/lightbox.js</file>
|
||||||
|
<file>ui/lookingGlass.js</file>
|
||||||
|
<file>ui/magnifier.js</file>
|
||||||
|
<file>ui/magnifierDBus.js</file>
|
||||||
|
<file>ui/main.js</file>
|
||||||
|
<file>ui/messageTray.js</file>
|
||||||
|
<file>ui/modalDialog.js</file>
|
||||||
|
<file>ui/notificationDaemon.js</file>
|
||||||
|
<file>ui/osdWindow.js</file>
|
||||||
|
<file>ui/overview.js</file>
|
||||||
|
<file>ui/overviewControls.js</file>
|
||||||
|
<file>ui/panel.js</file>
|
||||||
|
<file>ui/panelMenu.js</file>
|
||||||
|
<file>ui/pointerWatcher.js</file>
|
||||||
|
<file>ui/popupMenu.js</file>
|
||||||
|
<file>ui/remoteMenu.js</file>
|
||||||
|
<file>ui/remoteSearch.js</file>
|
||||||
|
<file>ui/runDialog.js</file>
|
||||||
|
<file>ui/screenShield.js</file>
|
||||||
|
<file>ui/screencast.js</file>
|
||||||
|
<file>ui/screenshot.js</file>
|
||||||
|
<file>ui/scripting.js</file>
|
||||||
|
<file>ui/search.js</file>
|
||||||
|
<file>ui/separator.js</file>
|
||||||
|
<file>ui/sessionMode.js</file>
|
||||||
|
<file>ui/shellDBus.js</file>
|
||||||
|
<file>ui/shellEntry.js</file>
|
||||||
|
<file>ui/shellMountOperation.js</file>
|
||||||
|
<file>ui/slider.js</file>
|
||||||
|
<file>ui/switcherPopup.js</file>
|
||||||
|
<file>ui/tweener.js</file>
|
||||||
|
<file>ui/unlockDialog.js</file>
|
||||||
|
<file>ui/userWidget.js</file>
|
||||||
|
<file>ui/viewSelector.js</file>
|
||||||
|
<file>ui/windowAttentionHandler.js</file>
|
||||||
|
<file>ui/windowManager.js</file>
|
||||||
|
<file>ui/workspace.js</file>
|
||||||
|
<file>ui/workspaceSwitcherPopup.js</file>
|
||||||
|
<file>ui/workspaceThumbnail.js</file>
|
||||||
|
<file>ui/workspacesView.js</file>
|
||||||
|
<file>ui/xdndHandler.js</file>
|
||||||
|
|
||||||
|
<file>ui/components/__init__.js</file>
|
||||||
|
<file>ui/components/autorunManager.js</file>
|
||||||
|
<file>ui/components/automountManager.js</file>
|
||||||
|
<file>ui/components/networkAgent.js</file>
|
||||||
|
<file>ui/components/polkitAgent.js</file>
|
||||||
|
<file>ui/components/telepathyClient.js</file>
|
||||||
|
<file>ui/components/keyring.js</file>
|
||||||
|
|
||||||
|
<file>ui/status/accessibility.js</file>
|
||||||
|
<file>ui/status/brightness.js</file>
|
||||||
|
<file>ui/status/location.js</file>
|
||||||
|
<file>ui/status/keyboard.js</file>
|
||||||
|
<file>ui/status/network.js</file>
|
||||||
|
<file>ui/status/power.js</file>
|
||||||
|
<file>ui/status/rfkill.js</file>
|
||||||
|
<file>ui/status/volume.js</file>
|
||||||
|
<file>ui/status/bluetooth.js</file>
|
||||||
|
<file>ui/status/screencast.js</file>
|
||||||
|
<file>ui/status/system.js</file>
|
||||||
|
</gresource>
|
||||||
|
</gresources>
|
||||||
@@ -176,10 +176,7 @@ const ExtensionFinder = new Lang.Class({
|
|||||||
|
|
||||||
scanExtensions: function() {
|
scanExtensions: function() {
|
||||||
let perUserDir = Gio.File.new_for_path(global.userdatadir);
|
let perUserDir = Gio.File.new_for_path(global.userdatadir);
|
||||||
FileUtils.collectFromDatadirsAsync('extensions',
|
FileUtils.collectFromDatadirs('extensions', true, Lang.bind(this, this._loadExtension, perUserDir));
|
||||||
{ processFile: Lang.bind(this, this._loadExtension),
|
|
||||||
includeUserDir: true,
|
|
||||||
data: perUserDir });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(ExtensionFinder.prototype);
|
Signals.addSignalMethods(ExtensionFinder.prototype);
|
||||||
|
|||||||
@@ -5,80 +5,27 @@ const GLib = imports.gi.GLib;
|
|||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
function listDirAsync(file, callback) {
|
function collectFromDatadirs(subdir, includeUserDir, processFile) {
|
||||||
let allFiles = [];
|
|
||||||
file.enumerate_children_async('standard::name,standard::type',
|
|
||||||
Gio.FileQueryInfoFlags.NONE,
|
|
||||||
GLib.PRIORITY_LOW, null, function (obj, res) {
|
|
||||||
let enumerator = obj.enumerate_children_finish(res);
|
|
||||||
function onNextFileComplete(obj, res) {
|
|
||||||
let files = obj.next_files_finish(res);
|
|
||||||
if (files.length) {
|
|
||||||
allFiles = allFiles.concat(files);
|
|
||||||
enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete);
|
|
||||||
} else {
|
|
||||||
enumerator.close(null);
|
|
||||||
callback(allFiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _collectFromDirectoryAsync(dir, loadState) {
|
|
||||||
function done() {
|
|
||||||
loadState.numLoading--;
|
|
||||||
if (loadState.loadedCallback &&
|
|
||||||
loadState.numLoading == 0)
|
|
||||||
loadState.loadedCallback(loadState.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
dir.query_info_async('standard::type', Gio.FileQueryInfoFlags.NONE,
|
|
||||||
GLib.PRIORITY_DEFAULT, null, function(object, res) {
|
|
||||||
try {
|
|
||||||
object.query_info_finish(res);
|
|
||||||
} catch (e) {
|
|
||||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
|
|
||||||
log(e.message);
|
|
||||||
done();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
listDirAsync(dir, Lang.bind(this, function(infos) {
|
|
||||||
for (let i = 0; i < infos.length; i++)
|
|
||||||
loadState.processFile(dir.get_child(infos[i].get_name()),
|
|
||||||
infos[i], loadState.data);
|
|
||||||
done();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectFromDatadirsAsync(subdir, params) {
|
|
||||||
params = Params.parse(params, { includeUserDir: false,
|
|
||||||
processFile: null,
|
|
||||||
loadedCallback: null,
|
|
||||||
data: null });
|
|
||||||
let loadState = { data: params.data,
|
|
||||||
numLoading: 0,
|
|
||||||
loadedCallback: params.loadedCallback,
|
|
||||||
processFile: params.processFile };
|
|
||||||
|
|
||||||
if (params.processFile == null) {
|
|
||||||
if (params.loadedCallback)
|
|
||||||
params.loadedCallback(params.data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let dataDirs = GLib.get_system_data_dirs();
|
let dataDirs = GLib.get_system_data_dirs();
|
||||||
if (params.includeUserDir)
|
if (includeUserDir)
|
||||||
dataDirs.unshift(GLib.get_user_data_dir());
|
dataDirs.unshift(GLib.get_user_data_dir());
|
||||||
loadState.numLoading = dataDirs.length;
|
|
||||||
|
|
||||||
for (let i = 0; i < dataDirs.length; i++) {
|
for (let i = 0; i < dataDirs.length; i++) {
|
||||||
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
|
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
|
||||||
let dir = Gio.File.new_for_path(path);
|
let dir = Gio.File.new_for_path(path);
|
||||||
|
|
||||||
_collectFromDirectoryAsync(dir, loadState);
|
let fileEnum;
|
||||||
|
try {
|
||||||
|
fileEnum = dir.enumerate_children('standard::name,standard::type',
|
||||||
|
Gio.FileQueryInfoFlags.NONE, null);
|
||||||
|
} catch (e) {
|
||||||
|
fileEnum = null;
|
||||||
|
}
|
||||||
|
if (fileEnum != null) {
|
||||||
|
let info;
|
||||||
|
while ((info = fileEnum.next_file(null)))
|
||||||
|
processFile(fileEnum.get_child(info), info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +64,6 @@ function recursivelyMoveDir(srcDir, destDir) {
|
|||||||
let type = info.get_file_type();
|
let type = info.get_file_type();
|
||||||
let srcChild = srcDir.get_child(info.get_name());
|
let srcChild = srcDir.get_child(info.get_name());
|
||||||
let destChild = destDir.get_child(info.get_name());
|
let destChild = destDir.get_child(info.get_name());
|
||||||
log([srcChild.get_path(), destChild.get_path()]);
|
|
||||||
if (type == Gio.FileType.REGULAR)
|
if (type == Gio.FileType.REGULAR)
|
||||||
srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
|
srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
|
||||||
else if (type == Gio.FileType.DIRECTORY)
|
else if (type == Gio.FileType.DIRECTORY)
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ const Gio = imports.gi.Gio;
|
|||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const PresenceIface = <interface name="org.gnome.SessionManager.Presence">
|
const PresenceIface = '<node> \
|
||||||
<method name="SetStatus">
|
<interface name="org.gnome.SessionManager.Presence"> \
|
||||||
<arg type="u" direction="in"/>
|
<method name="SetStatus"> \
|
||||||
</method>
|
<arg type="u" direction="in"/> \
|
||||||
<property name="status" type="u" access="readwrite"/>
|
</method> \
|
||||||
<signal name="StatusChanged">
|
<property name="status" type="u" access="readwrite"/> \
|
||||||
<arg type="u" direction="out"/>
|
<signal name="StatusChanged"> \
|
||||||
</signal>
|
<arg type="u" direction="out"/> \
|
||||||
</interface>;
|
</signal> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const PresenceStatus = {
|
const PresenceStatus = {
|
||||||
AVAILABLE: 0,
|
AVAILABLE: 0,
|
||||||
@@ -30,14 +32,16 @@ function Presence(initCallback, cancellable) {
|
|||||||
// Note inhibitors are immutable objects, so they don't
|
// Note inhibitors are immutable objects, so they don't
|
||||||
// change at runtime (changes always come in the form
|
// change at runtime (changes always come in the form
|
||||||
// of new inhibitors)
|
// of new inhibitors)
|
||||||
const InhibitorIface = <interface name="org.gnome.SessionManager.Inhibitor">
|
const InhibitorIface = '<node> \
|
||||||
<method name="GetAppId">
|
<interface name="org.gnome.SessionManager.Inhibitor"> \
|
||||||
<arg type="s" direction="out" />
|
<method name="GetAppId"> \
|
||||||
</method>
|
<arg type="s" direction="out" /> \
|
||||||
<method name="GetReason">
|
</method> \
|
||||||
<arg type="s" direction="out" />
|
<method name="GetReason"> \
|
||||||
</method>
|
<arg type="s" direction="out" /> \
|
||||||
</interface>;
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface);
|
var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface);
|
||||||
function Inhibitor(objectPath, initCallback, cancellable) {
|
function Inhibitor(objectPath, initCallback, cancellable) {
|
||||||
@@ -45,26 +49,29 @@ function Inhibitor(objectPath, initCallback, cancellable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Not the full interface, only the methods we use
|
// Not the full interface, only the methods we use
|
||||||
const SessionManagerIface = <interface name="org.gnome.SessionManager">
|
const SessionManagerIface = '<node> \
|
||||||
<method name="Logout">
|
<interface name="org.gnome.SessionManager"> \
|
||||||
<arg type="u" direction="in" />
|
<method name="Logout"> \
|
||||||
</method>
|
<arg type="u" direction="in" /> \
|
||||||
<method name="Shutdown" />
|
</method> \
|
||||||
<method name="Reboot" />
|
<method name="Shutdown" /> \
|
||||||
<method name="CanShutdown">
|
<method name="Reboot" /> \
|
||||||
<arg type="b" direction="out" />
|
<method name="CanShutdown"> \
|
||||||
</method>
|
<arg type="b" direction="out" /> \
|
||||||
<method name="IsInhibited">
|
</method> \
|
||||||
<arg type="u" direction="in" />
|
<method name="IsInhibited"> \
|
||||||
<arg type="b" direction="out" />
|
<arg type="u" direction="in" /> \
|
||||||
</method>
|
<arg type="b" direction="out" /> \
|
||||||
<signal name="InhibitorAdded">
|
</method> \
|
||||||
<arg type="o" direction="out"/>
|
<property name="SessionIsActive" type="b" access="read"/> \
|
||||||
</signal>
|
<signal name="InhibitorAdded"> \
|
||||||
<signal name="InhibitorRemoved">
|
<arg type="o" direction="out"/> \
|
||||||
<arg type="o" direction="out"/>
|
</signal> \
|
||||||
</signal>
|
<signal name="InhibitorRemoved"> \
|
||||||
</interface>;
|
<arg type="o" direction="out"/> \
|
||||||
|
</signal> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
|
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
|
||||||
function SessionManager(initCallback, cancellable) {
|
function SessionManager(initCallback, cancellable) {
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ const HistoryManager = new Lang.Class({
|
|||||||
} else if (symbol == Clutter.KEY_Down) {
|
} else if (symbol == Clutter.KEY_Down) {
|
||||||
return this._setNextItem(entry.get_text());
|
return this._setNextItem(entry.get_text());
|
||||||
}
|
}
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_indexChanged: function() {
|
_indexChanged: function() {
|
||||||
|
|||||||
@@ -5,67 +5,105 @@ const Gio = imports.gi.Gio;
|
|||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const UPowerGlib = imports.gi.UPowerGlib;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
|
const SystemdLoginManagerIface = '<node> \
|
||||||
<method name='PowerOff'>
|
<interface name="org.freedesktop.login1.Manager"> \
|
||||||
<arg type='b' direction='in'/>
|
<method name="Suspend"> \
|
||||||
</method>
|
<arg type="b" direction="in"/> \
|
||||||
<method name='Reboot'>
|
</method> \
|
||||||
<arg type='b' direction='in'/>
|
<method name="CanSuspend"> \
|
||||||
</method>
|
<arg type="s" direction="out"/> \
|
||||||
<method name='Suspend'>
|
</method> \
|
||||||
<arg type='b' direction='in'/>
|
<method name="Inhibit"> \
|
||||||
</method>
|
<arg type="s" direction="in"/> \
|
||||||
<method name='CanPowerOff'>
|
<arg type="s" direction="in"/> \
|
||||||
<arg type='s' direction='out'/>
|
<arg type="s" direction="in"/> \
|
||||||
</method>
|
<arg type="s" direction="in"/> \
|
||||||
<method name='CanReboot'>
|
<arg type="h" direction="out"/> \
|
||||||
<arg type='s' direction='out'/>
|
</method> \
|
||||||
</method>
|
<method name="GetSession"> \
|
||||||
<method name='CanSuspend'>
|
<arg type="s" direction="in"/> \
|
||||||
<arg type='s' direction='out'/>
|
<arg type="o" direction="out"/> \
|
||||||
</method>
|
</method> \
|
||||||
</interface>;
|
<method name="ListSessions"> \
|
||||||
|
<arg name="sessions" type="a(susso)" direction="out"/> \
|
||||||
|
</method> \
|
||||||
|
<signal name="PrepareForSleep"> \
|
||||||
|
<arg type="b" direction="out"/> \
|
||||||
|
</signal> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
const SystemdLoginSessionIface = '<node> \
|
||||||
<signal name='Lock' />
|
<interface name="org.freedesktop.login1.Session"> \
|
||||||
<signal name='Unlock' />
|
<signal name="Lock" /> \
|
||||||
</interface>;
|
<signal name="Unlock" /> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
|
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
|
||||||
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
||||||
|
|
||||||
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
|
const ConsoleKitManagerIface = '<node> \
|
||||||
<method name='CanRestart'>
|
<interface name="org.freedesktop.ConsoleKit.Manager"> \
|
||||||
<arg type='b' direction='out'/>
|
<method name="CanRestart"> \
|
||||||
</method>
|
<arg type="b" direction="out"/> \
|
||||||
<method name='CanStop'>
|
</method> \
|
||||||
<arg type='b' direction='out'/>
|
<method name="CanStop"> \
|
||||||
</method>
|
<arg type="b" direction="out"/> \
|
||||||
<method name='Restart' />
|
</method> \
|
||||||
<method name='Stop' />
|
<method name="Restart" /> \
|
||||||
<method name='GetCurrentSession'>
|
<method name="Stop" /> \
|
||||||
<arg type='o' direction='out' />
|
<method name="GetCurrentSession"> \
|
||||||
</method>
|
<arg type="o" direction="out" /> \
|
||||||
</interface>;
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
|
const ConsoleKitSessionIface = '<node> \
|
||||||
<method name='IsActive'>
|
<interface name="org.freedesktop.ConsoleKit.Session"> \
|
||||||
<arg type='b' direction='out' />
|
<signal name="Lock" /> \
|
||||||
</method>
|
<signal name="Unlock" /> \
|
||||||
<signal name='ActiveChanged'>
|
</interface> \
|
||||||
<arg type='b' direction='out' />
|
</node>';
|
||||||
</signal>
|
|
||||||
<signal name='Lock' />
|
|
||||||
<signal name='Unlock' />
|
|
||||||
</interface>;
|
|
||||||
|
|
||||||
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
|
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
|
||||||
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
|
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
|
||||||
|
|
||||||
function haveSystemd() {
|
function haveSystemd() {
|
||||||
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
|
return GLib.access("/run/systemd/seats", 0) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function versionCompare(required, reference) {
|
||||||
|
required = required.split('.');
|
||||||
|
reference = reference.split('.');
|
||||||
|
|
||||||
|
for (let i = 0; i < required.length; i++) {
|
||||||
|
let requiredInt = parseInt(required[i]);
|
||||||
|
let referenceInt = parseInt(reference[i]);
|
||||||
|
if (requiredInt != referenceInt)
|
||||||
|
return requiredInt < referenceInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canLock() {
|
||||||
|
try {
|
||||||
|
let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
|
||||||
|
let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
|
||||||
|
'/org/gnome/DisplayManager/Manager',
|
||||||
|
'org.freedesktop.DBus.Properties',
|
||||||
|
'Get', params, null,
|
||||||
|
Gio.DBusCallFlags.NONE,
|
||||||
|
-1, null);
|
||||||
|
|
||||||
|
let version = result.deep_unpack()[0].deep_unpack();
|
||||||
|
return versionCompare('3.5.91', version);
|
||||||
|
} catch(e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _loginManager = null;
|
let _loginManager = null;
|
||||||
@@ -93,42 +131,30 @@ const LoginManagerSystemd = new Lang.Class({
|
|||||||
this._proxy = new SystemdLoginManager(Gio.DBus.system,
|
this._proxy = new SystemdLoginManager(Gio.DBus.system,
|
||||||
'org.freedesktop.login1',
|
'org.freedesktop.login1',
|
||||||
'/org/freedesktop/login1');
|
'/org/freedesktop/login1');
|
||||||
|
this._proxy.connectSignal('PrepareForSleep',
|
||||||
|
Lang.bind(this, this._prepareForSleep));
|
||||||
},
|
},
|
||||||
|
|
||||||
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
||||||
// session objects have different interfaces - but in both cases there are
|
// session objects have different interfaces - but in both cases there are
|
||||||
// Lock/Unlock signals, and that's all we count upon at the moment.
|
// Lock/Unlock signals, and that's all we count upon at the moment.
|
||||||
getCurrentSessionProxy: function() {
|
getCurrentSessionProxy: function(callback) {
|
||||||
if (!this._currentSession) {
|
if (this._currentSession) {
|
||||||
this._currentSession = new SystemdLoginSession(Gio.DBus.system,
|
callback (this._currentSession);
|
||||||
'org.freedesktop.login1',
|
return;
|
||||||
'/org/freedesktop/login1/session/' +
|
|
||||||
GLib.getenv('XDG_SESSION_ID'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._currentSession;
|
this._proxy.GetSessionRemote(GLib.getenv('XDG_SESSION_ID'), Lang.bind(this,
|
||||||
},
|
function(result, error) {
|
||||||
|
if (error) {
|
||||||
get sessionActive() {
|
logError(error, 'Could not get a proxy for the current session');
|
||||||
return Shell.session_is_active_for_systemd();
|
} else {
|
||||||
},
|
this._currentSession = new SystemdLoginSession(Gio.DBus.system,
|
||||||
|
'org.freedesktop.login1',
|
||||||
canPowerOff: function(asyncCallback) {
|
result[0]);
|
||||||
this._proxy.CanPowerOffRemote(function(result, error) {
|
callback(this._currentSession);
|
||||||
if (error)
|
}
|
||||||
asyncCallback(false);
|
}));
|
||||||
else
|
|
||||||
asyncCallback(result[0] != 'no');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
canReboot: function(asyncCallback) {
|
|
||||||
this._proxy.CanRebootRemote(function(result, error) {
|
|
||||||
if (error)
|
|
||||||
asyncCallback(false);
|
|
||||||
else
|
|
||||||
asyncCallback(result[0] != 'no');
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
canSuspend: function(asyncCallback) {
|
canSuspend: function(asyncCallback) {
|
||||||
@@ -140,18 +166,44 @@ const LoginManagerSystemd = new Lang.Class({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
powerOff: function() {
|
listSessions: function(asyncCallback) {
|
||||||
this._proxy.PowerOffRemote(true);
|
this._proxy.ListSessionsRemote(function(result, error) {
|
||||||
},
|
if (error)
|
||||||
|
asyncCallback([]);
|
||||||
reboot: function() {
|
else
|
||||||
this._proxy.RebootRemote(true);
|
asyncCallback(result[0]);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
suspend: function() {
|
suspend: function() {
|
||||||
this._proxy.SuspendRemote(true);
|
this._proxy.SuspendRemote(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
inhibit: function(reason, callback) {
|
||||||
|
let inVariant = GLib.Variant.new('(ssss)',
|
||||||
|
['sleep',
|
||||||
|
'GNOME Shell',
|
||||||
|
reason,
|
||||||
|
'delay']);
|
||||||
|
this._proxy.call_with_unix_fd_list('Inhibit', inVariant, 0, -1, null, null,
|
||||||
|
Lang.bind(this, function(proxy, result) {
|
||||||
|
let fd = -1;
|
||||||
|
try {
|
||||||
|
let [outVariant, fdList] = proxy.call_with_unix_fd_list_finish(result);
|
||||||
|
fd = fdList.steal_fds(outVariant.deep_unpack())[0];
|
||||||
|
callback(new Gio.UnixInputStream({ fd: fd }));
|
||||||
|
} catch(e) {
|
||||||
|
logError(e, "Error getting systemd inhibitor");
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_prepareForSleep: function(proxy, sender, [aboutToSuspend]) {
|
||||||
|
this.emit('prepare-for-sleep', aboutToSuspend);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(LoginManagerSystemd.prototype);
|
||||||
|
|
||||||
const LoginManagerConsoleKit = new Lang.Class({
|
const LoginManagerConsoleKit = new Lang.Class({
|
||||||
Name: 'LoginManagerConsoleKit',
|
Name: 'LoginManagerConsoleKit',
|
||||||
@@ -160,70 +212,45 @@ const LoginManagerConsoleKit = new Lang.Class({
|
|||||||
this._proxy = new ConsoleKitManager(Gio.DBus.system,
|
this._proxy = new ConsoleKitManager(Gio.DBus.system,
|
||||||
'org.freedesktop.ConsoleKit',
|
'org.freedesktop.ConsoleKit',
|
||||||
'/org/freedesktop/ConsoleKit/Manager');
|
'/org/freedesktop/ConsoleKit/Manager');
|
||||||
this._upClient = new UPowerGlib.Client();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
||||||
// session objects have different interfaces - but in both cases there are
|
// session objects have different interfaces - but in both cases there are
|
||||||
// Lock/Unlock signals, and that's all we count upon at the moment.
|
// Lock/Unlock signals, and that's all we count upon at the moment.
|
||||||
getCurrentSessionProxy: function() {
|
getCurrentSessionProxy: function(callback) {
|
||||||
if (!this._currentSession) {
|
if (this._currentSession) {
|
||||||
let [currentSessionId] = this._proxy.GetCurrentSessionSync();
|
callback (this._currentSession);
|
||||||
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
|
return;
|
||||||
'org.freedesktop.ConsoleKit',
|
|
||||||
currentSessionId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._currentSession;
|
this._proxy.GetCurrentSessionRemote(Lang.bind(this,
|
||||||
},
|
function(result, error) {
|
||||||
|
if (error) {
|
||||||
get sessionActive() {
|
logError(error, 'Could not get a proxy for the current session');
|
||||||
if (this._sessionActive !== undefined)
|
} else {
|
||||||
return this._sessionActive;
|
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
|
||||||
|
'org.freedesktop.ConsoleKit',
|
||||||
let session = this.getCurrentSessionProxy();
|
result[0]);
|
||||||
session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) {
|
callback(this._currentSession);
|
||||||
this._sessionActive = isActive;
|
}
|
||||||
}));
|
}));
|
||||||
[this._sessionActive] = session.IsActiveSync();
|
|
||||||
|
|
||||||
return this._sessionActive;
|
|
||||||
},
|
|
||||||
|
|
||||||
canPowerOff: function(asyncCallback) {
|
|
||||||
this._proxy.CanStopRemote(function(result, error) {
|
|
||||||
if (error)
|
|
||||||
asyncCallback(false);
|
|
||||||
else
|
|
||||||
asyncCallback(result[0]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
canReboot: function(asyncCallback) {
|
|
||||||
this._proxy.CanRestartRemote(function(result, error) {
|
|
||||||
if (error)
|
|
||||||
asyncCallback(false);
|
|
||||||
else
|
|
||||||
asyncCallback(result[0]);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
canSuspend: function(asyncCallback) {
|
canSuspend: function(asyncCallback) {
|
||||||
Mainloop.idle_add(Lang.bind(this, function() {
|
asyncCallback(false);
|
||||||
asyncCallback(this._upClient.get_can_suspend());
|
|
||||||
return false;
|
|
||||||
}));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
powerOff: function() {
|
listSessions: function(asyncCallback) {
|
||||||
this._proxy.StopRemote();
|
asyncCallback([]);
|
||||||
},
|
|
||||||
|
|
||||||
reboot: function() {
|
|
||||||
this._proxy.RestartRemote();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
suspend: function() {
|
suspend: function() {
|
||||||
this._upClient.suspend_sync(null);
|
this.emit('prepare-for-sleep', true);
|
||||||
|
this.emit('prepare-for-sleep', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
inhibit: function(reason, callback) {
|
||||||
|
callback(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);
|
||||||
|
|||||||
@@ -2,116 +2,134 @@
|
|||||||
|
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const NMGtk = imports.gi.NMGtk;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
// _getMobileProvidersDatabase:
|
||||||
|
//
|
||||||
|
// Gets the database of mobile providers, with references between MCCMNC/SID and
|
||||||
|
// operator name
|
||||||
|
//
|
||||||
|
let _mpd;
|
||||||
|
function _getMobileProvidersDatabase() {
|
||||||
|
if (_mpd == null) {
|
||||||
|
try {
|
||||||
|
_mpd = new NMGtk.MobileProvidersDatabase();
|
||||||
|
_mpd.init(null);
|
||||||
|
} catch (e) {
|
||||||
|
log(e.message);
|
||||||
|
_mpd = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mpd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _findProviderForMccMnc:
|
||||||
|
// @operator_name: operator name
|
||||||
|
// @operator_code: operator code
|
||||||
|
//
|
||||||
|
// Given an operator name string (which may not be a real operator name) and an
|
||||||
|
// operator code string, tries to find a proper operator name to display.
|
||||||
|
//
|
||||||
|
function _findProviderForMccMnc(operator_name, operator_code) {
|
||||||
|
if (operator_name) {
|
||||||
|
if (operator_name.length != 0 &&
|
||||||
|
(operator_name.length > 6 || operator_name.length < 5)) {
|
||||||
|
// this looks like a valid name, i.e. not an MCCMNC (that some
|
||||||
|
// devices return when not yet connected
|
||||||
|
return operator_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(parseInt(operator_name))) {
|
||||||
|
// name is definitely not a MCCMNC, so it may be a name
|
||||||
|
// after all; return that
|
||||||
|
return operator_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let needle;
|
||||||
|
if ((!operator_name || operator_name.length == 0) && operator_code)
|
||||||
|
needle = operator_code;
|
||||||
|
else if (operator_name && (operator_name.length == 6 || operator_name.length == 5))
|
||||||
|
needle = operator_name;
|
||||||
|
else // nothing to search
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let mpd = _getMobileProvidersDatabase();
|
||||||
|
if (mpd) {
|
||||||
|
let provider = mpd.lookup_3gpp_mcc_mnc(needle);
|
||||||
|
if (provider)
|
||||||
|
return provider.get_name();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _findProviderForSid:
|
||||||
|
// @sid: System Identifier of the serving CDMA network
|
||||||
|
//
|
||||||
|
// Tries to find the operator name corresponding to the given SID
|
||||||
|
//
|
||||||
|
function _findProviderForSid(sid) {
|
||||||
|
if (sid == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let mpd = _getMobileProvidersDatabase();
|
||||||
|
if (mpd) {
|
||||||
|
let provider = mpd.lookup_cdma_sid(sid);
|
||||||
|
if (provider)
|
||||||
|
return provider.get_name();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Support for the old ModemManager interface (MM < 0.7)
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
// The following are not the complete interfaces, just the methods we need
|
// The following are not the complete interfaces, just the methods we need
|
||||||
// (or may need in the future)
|
// (or may need in the future)
|
||||||
|
|
||||||
const ModemGsmNetworkInterface = <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network">
|
const ModemGsmNetworkInterface = '<node> \
|
||||||
<method name="GetRegistrationInfo">
|
<interface name="org.freedesktop.ModemManager.Modem.Gsm.Network"> \
|
||||||
<arg type="(uss)" direction="out" />
|
<method name="GetRegistrationInfo"> \
|
||||||
</method>
|
<arg type="(uss)" direction="out" /> \
|
||||||
<method name="GetSignalQuality">
|
</method> \
|
||||||
<arg type="u" direction="out" />
|
<method name="GetSignalQuality"> \
|
||||||
</method>
|
<arg type="u" direction="out" /> \
|
||||||
<property name="AccessTechnology" type="u" access="read" />
|
</method> \
|
||||||
<signal name="SignalQuality">
|
<property name="AccessTechnology" type="u" access="read" /> \
|
||||||
<arg type="u" direction="out" />
|
<signal name="SignalQuality"> \
|
||||||
</signal>
|
<arg type="u" direction="out" /> \
|
||||||
<signal name="RegistrationInfo">
|
</signal> \
|
||||||
<arg type="u" direction="out" />
|
<signal name="RegistrationInfo"> \
|
||||||
<arg type="s" direction="out" />
|
<arg type="u" direction="out" /> \
|
||||||
<arg type="s" direction="out" />
|
<arg type="s" direction="out" /> \
|
||||||
</signal>
|
<arg type="s" direction="out" /> \
|
||||||
</interface>;
|
</signal> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface);
|
const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface);
|
||||||
|
|
||||||
const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.Cdma">
|
const ModemCdmaInterface = '<node> \
|
||||||
<method name="GetSignalQuality">
|
<interface name="org.freedesktop.ModemManager.Modem.Cdma"> \
|
||||||
<arg type="u" direction="out" />
|
<method name="GetSignalQuality"> \
|
||||||
</method>
|
<arg type="u" direction="out" /> \
|
||||||
<method name="GetServingSystem">
|
</method> \
|
||||||
<arg type="(usu)" direction="out" />
|
<method name="GetServingSystem"> \
|
||||||
</method>
|
<arg type="(usu)" direction="out" /> \
|
||||||
<signal name="SignalQuality">
|
</method> \
|
||||||
<arg type="u" direction="out" />
|
<signal name="SignalQuality"> \
|
||||||
</signal>
|
<arg type="u" direction="out" /> \
|
||||||
</interface>;
|
</signal> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
|
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
|
||||||
|
|
||||||
let _providersTable;
|
|
||||||
function _getProvidersTable() {
|
|
||||||
if (_providersTable)
|
|
||||||
return _providersTable;
|
|
||||||
return _providersTable = Shell.mobile_providers_parse(null,null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function findProviderForMCCMNC(table, needle) {
|
|
||||||
let needlemcc = needle.substring(0, 3);
|
|
||||||
let needlemnc = needle.substring(3, needle.length);
|
|
||||||
|
|
||||||
let name2, name3;
|
|
||||||
for (let iter in table) {
|
|
||||||
let country = table[iter];
|
|
||||||
let providers = country.get_providers();
|
|
||||||
|
|
||||||
// Search through each country's providers
|
|
||||||
for (let i = 0; i < providers.length; i++) {
|
|
||||||
let provider = providers[i];
|
|
||||||
|
|
||||||
// Search through MCC/MNC list
|
|
||||||
let list = provider.get_gsm_mcc_mnc();
|
|
||||||
for (let j = 0; j < list.length; j++) {
|
|
||||||
let mccmnc = list[j];
|
|
||||||
|
|
||||||
// Match both 2-digit and 3-digit MNC; prefer a
|
|
||||||
// 3-digit match if found, otherwise a 2-digit one.
|
|
||||||
if (mccmnc.mcc != needlemcc)
|
|
||||||
continue; // MCC was wrong
|
|
||||||
|
|
||||||
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
|
|
||||||
name3 = provider.name;
|
|
||||||
|
|
||||||
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
|
|
||||||
name2 = provider.name;
|
|
||||||
|
|
||||||
if (name2 && name3)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return name3 || name2 || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findProviderForSid(table, sid) {
|
|
||||||
if (sid == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Search through each country
|
|
||||||
for (let iter in table) {
|
|
||||||
let country = table[iter];
|
|
||||||
let providers = country.get_providers();
|
|
||||||
|
|
||||||
// Search through each country's providers
|
|
||||||
for (let i = 0; i < providers.length; i++) {
|
|
||||||
let provider = providers[i];
|
|
||||||
let cdma_sid = provider.get_cdma_sid();
|
|
||||||
|
|
||||||
// Search through CDMA SID list
|
|
||||||
for (let j = 0; j < cdma_sid.length; j++) {
|
|
||||||
if (cdma_sid[j] == sid)
|
|
||||||
return provider.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ModemGsm = new Lang.Class({
|
const ModemGsm = new Lang.Class({
|
||||||
Name: 'ModemGsm',
|
Name: 'ModemGsm',
|
||||||
|
|
||||||
@@ -127,7 +145,7 @@ const ModemGsm = new Lang.Class({
|
|||||||
this.emit('notify::signal-quality');
|
this.emit('notify::signal-quality');
|
||||||
}));
|
}));
|
||||||
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
|
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
|
||||||
this.operator_name = this._findOperatorName(name, code);
|
this.operator_name = _findProviderForMccMnc(name, code);
|
||||||
this.emit('notify::operator-name');
|
this.emit('notify::operator-name');
|
||||||
}));
|
}));
|
||||||
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
|
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
|
||||||
@@ -137,7 +155,7 @@ const ModemGsm = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let [status, code, name] = result;
|
let [status, code, name] = result;
|
||||||
this.operator_name = this._findOperatorName(name, code);
|
this.operator_name = _findProviderForMccMnc(name, code);
|
||||||
this.emit('notify::operator-name');
|
this.emit('notify::operator-name');
|
||||||
}));
|
}));
|
||||||
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
|
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
|
||||||
@@ -150,32 +168,6 @@ const ModemGsm = new Lang.Class({
|
|||||||
}
|
}
|
||||||
this.emit('notify::signal-quality');
|
this.emit('notify::signal-quality');
|
||||||
}));
|
}));
|
||||||
},
|
|
||||||
|
|
||||||
_findOperatorName: function(name, opCode) {
|
|
||||||
if (name) {
|
|
||||||
if (name && name.length != 0 && (name.length > 6 || name.length < 5)) {
|
|
||||||
// this looks like a valid name, i.e. not an MCCMNC (that some
|
|
||||||
// devices return when not yet connected
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
if (isNaN(parseInt(name))) {
|
|
||||||
// name is definitely not a MCCMNC, so it may be a name
|
|
||||||
// after all; return that
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let needle;
|
|
||||||
if ((name == null || name.length == 0) && opCode)
|
|
||||||
needle = opCode;
|
|
||||||
else if (name.length == 6 || name.length == 5)
|
|
||||||
needle = name;
|
|
||||||
else // nothing to search
|
|
||||||
return null;
|
|
||||||
|
|
||||||
let table = _getProvidersTable();
|
|
||||||
return findProviderForMCCMNC(table, needle);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(ModemGsm.prototype);
|
Signals.addSignalMethods(ModemGsm.prototype);
|
||||||
@@ -215,15 +207,105 @@ const ModemCdma = new Lang.Class({
|
|||||||
// it will return an error if the device is not connected
|
// it will return an error if the device is not connected
|
||||||
this.operator_name = null;
|
this.operator_name = null;
|
||||||
} else {
|
} else {
|
||||||
let [bandClass, band, id] = result;
|
let [bandClass, band, sid] = result;
|
||||||
if (name.length > 0) {
|
|
||||||
let table = _getProvidersTable();
|
this.operator_name = _findProviderForSid(sid)
|
||||||
this.operator_name = findProviderForSid(table, id);
|
|
||||||
} else
|
|
||||||
this.operator_name = null;
|
|
||||||
}
|
}
|
||||||
this.emit('notify::operator-name');
|
this.emit('notify::operator-name');
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(ModemCdma.prototype);
|
Signals.addSignalMethods(ModemCdma.prototype);
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Support for the new ModemManager1 interface (MM >= 0.7)
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const BroadbandModemInterface = '<node> \
|
||||||
|
<interface name="org.freedesktop.ModemManager1.Modem"> \
|
||||||
|
<property name="SignalQuality" type="(ub)" access="read" /> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
|
||||||
|
|
||||||
|
const BroadbandModem3gppInterface = '<node> \
|
||||||
|
<interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp"> \
|
||||||
|
<property name="OperatorCode" type="s" access="read" /> \
|
||||||
|
<property name="OperatorName" type="s" access="read" /> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
|
||||||
|
|
||||||
|
const BroadbandModemCdmaInterface = '<node> \
|
||||||
|
<interface name="org.freedesktop.ModemManager1.Modem.ModemCdma"> \
|
||||||
|
<property name="Sid" type="u" access="read" /> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
|
||||||
|
|
||||||
|
const BroadbandModem = new Lang.Class({
|
||||||
|
Name: 'BroadbandModem',
|
||||||
|
|
||||||
|
_init: function(path, capabilities) {
|
||||||
|
this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||||
|
this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||||
|
this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||||
|
this._capabilities = capabilities;
|
||||||
|
|
||||||
|
this._proxy.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||||
|
if ('SignalQuality' in properties.deep_unpack())
|
||||||
|
this._reloadSignalQuality();
|
||||||
|
}));
|
||||||
|
this._reloadSignalQuality();
|
||||||
|
|
||||||
|
this._proxy_3gpp.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||||
|
let unpacked = properties.deep_unpack();
|
||||||
|
if ('OperatorName' in unpacked || 'OperatorCode' in unpacked)
|
||||||
|
this._reload3gppOperatorName();
|
||||||
|
}));
|
||||||
|
this._reload3gppOperatorName();
|
||||||
|
|
||||||
|
this._proxy_cdma.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||||
|
let unpacked = properties.deep_unpack();
|
||||||
|
if ('Nid' in unpacked || 'Sid' in unpacked)
|
||||||
|
this._reloadCdmaOperatorName();
|
||||||
|
}));
|
||||||
|
this._reloadCdmaOperatorName();
|
||||||
|
},
|
||||||
|
|
||||||
|
_reloadSignalQuality: function() {
|
||||||
|
let [quality, recent] = this._proxy.SignalQuality;
|
||||||
|
this.signal_quality = quality;
|
||||||
|
this.emit('notify::signal-quality');
|
||||||
|
},
|
||||||
|
|
||||||
|
_reloadOperatorName: function() {
|
||||||
|
let new_name = "";
|
||||||
|
if (this.operator_name_3gpp && this.operator_name_3gpp.length > 0)
|
||||||
|
new_name += this.operator_name_3gpp;
|
||||||
|
|
||||||
|
if (this.operator_name_cdma && this.operator_name_cdma.length > 0) {
|
||||||
|
if (new_name != "")
|
||||||
|
new_name += ", ";
|
||||||
|
new_name += this.operator_name_cdma;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.operator_name = new_name;
|
||||||
|
this.emit('notify::operator-name');
|
||||||
|
},
|
||||||
|
|
||||||
|
_reload3gppOperatorName: function() {
|
||||||
|
let name = this._proxy_3gpp.OperatorName;
|
||||||
|
let code = this._proxy_3gpp.OperatorCode;
|
||||||
|
this.operator_name_3gpp = _findProviderForMccMnc(name, code);
|
||||||
|
this._reloadOperatorName();
|
||||||
|
},
|
||||||
|
|
||||||
|
_reloadCdmaOperatorName: function() {
|
||||||
|
let sid = this._proxy_cdma.Sid;
|
||||||
|
this.operator_name_cdma = _findProviderForSid(sid);
|
||||||
|
this._reloadOperatorName();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(BroadbandModem.prototype);
|
||||||
|
|||||||
259
js/misc/objectManager.js
Normal file
259
js/misc/objectManager.js
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
// Specified in the D-Bus specification here:
|
||||||
|
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
|
||||||
|
const ObjectManagerIface = '<node> \
|
||||||
|
<interface name="org.freedesktop.DBus.ObjectManager"> \
|
||||||
|
<method name="GetManagedObjects"> \
|
||||||
|
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/> \
|
||||||
|
</method> \
|
||||||
|
<signal name="InterfacesAdded"> \
|
||||||
|
<arg name="objectPath" type="o"/> \
|
||||||
|
<arg name="interfaces" type="a{sa{sv}}" /> \
|
||||||
|
</signal> \
|
||||||
|
<signal name="InterfacesRemoved"> \
|
||||||
|
<arg name="objectPath" type="o"/> \
|
||||||
|
<arg name="interfaces" type="as" /> \
|
||||||
|
</signal> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
|
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
|
||||||
|
|
||||||
|
const ObjectManager = new Lang.Class({
|
||||||
|
Name: 'ObjectManager',
|
||||||
|
_init: function(params) {
|
||||||
|
params = Params.parse(params, { connection: null,
|
||||||
|
name: null,
|
||||||
|
objectPath: null,
|
||||||
|
knownInterfaces: null,
|
||||||
|
cancellable: null,
|
||||||
|
onLoaded: null });
|
||||||
|
|
||||||
|
this._connection = params.connection;
|
||||||
|
this._serviceName = params.name;
|
||||||
|
this._managerPath = params.objectPath;
|
||||||
|
this._cancellable = params.cancellable;
|
||||||
|
|
||||||
|
this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection,
|
||||||
|
g_interface_name: ObjectManagerInfo.name,
|
||||||
|
g_interface_info: ObjectManagerInfo,
|
||||||
|
g_name: this._serviceName,
|
||||||
|
g_object_path: this._managerPath,
|
||||||
|
g_flags: Gio.DBusProxyFlags.NONE });
|
||||||
|
|
||||||
|
this._interfaceInfos = {};
|
||||||
|
this._objects = {};
|
||||||
|
this._interfaces = {};
|
||||||
|
this._onLoaded = params.onLoaded;
|
||||||
|
|
||||||
|
if (params.knownInterfaces)
|
||||||
|
this._registerInterfaces(params.knownInterfaces);
|
||||||
|
|
||||||
|
// Start out inhibiting load until at least the proxy
|
||||||
|
// manager is loaded and the remote objects are fetched
|
||||||
|
this._numLoadInhibitors = 1;
|
||||||
|
this._managerProxy.init_async(GLib.PRIORITY_DEFAULT,
|
||||||
|
this._cancellable,
|
||||||
|
Lang.bind(this, this._onManagerProxyLoaded));
|
||||||
|
},
|
||||||
|
|
||||||
|
_tryToCompleteLoad: function() {
|
||||||
|
this._numLoadInhibitors--;
|
||||||
|
if (this._numLoadInhibitors == 0) {
|
||||||
|
if (this._onLoaded)
|
||||||
|
this._onLoaded();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_addInterface: function(objectPath, interfaceName, onFinished) {
|
||||||
|
let info = this._interfaceInfos[interfaceName];
|
||||||
|
|
||||||
|
if (!info) {
|
||||||
|
if (onFinished)
|
||||||
|
onFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let proxy = new Gio.DBusProxy({ g_connection: this._connection,
|
||||||
|
g_name: this._serviceName,
|
||||||
|
g_object_path: objectPath,
|
||||||
|
g_interface_name: interfaceName,
|
||||||
|
g_interface_info: info,
|
||||||
|
g_flags: Gio.DBusProxyFlags.NONE });
|
||||||
|
|
||||||
|
proxy.init_async(GLib.PRIORITY_DEFAULT,
|
||||||
|
this._cancellable,
|
||||||
|
Lang.bind(this, function(initable, result) {
|
||||||
|
let error = null;
|
||||||
|
try {
|
||||||
|
initable.init_finish(result);
|
||||||
|
} catch(e) {
|
||||||
|
logError(e, 'could not initialize proxy for interface ' + interfaceName);
|
||||||
|
|
||||||
|
if (onFinished)
|
||||||
|
onFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let isNewObject;
|
||||||
|
if (!this._objects[objectPath]) {
|
||||||
|
this._objects[objectPath] = {};
|
||||||
|
isNewObject = true;
|
||||||
|
} else {
|
||||||
|
isNewObject = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._objects[objectPath][interfaceName] = proxy;
|
||||||
|
|
||||||
|
if (!this._interfaces[interfaceName])
|
||||||
|
this._interfaces[interfaceName] = [];
|
||||||
|
|
||||||
|
this._interfaces[interfaceName].push(proxy);
|
||||||
|
|
||||||
|
if (isNewObject)
|
||||||
|
this.emit('object-added', objectPath);
|
||||||
|
|
||||||
|
this.emit('interface-added', interfaceName, proxy);
|
||||||
|
|
||||||
|
if (onFinished)
|
||||||
|
onFinished();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeInterface: function(objectPath, interfaceName) {
|
||||||
|
if (!this._objects[objectPath])
|
||||||
|
return;
|
||||||
|
|
||||||
|
let proxy = this._objects[objectPath][interfaceName];
|
||||||
|
|
||||||
|
if (this._interfaces[interfaceName]) {
|
||||||
|
let index = this._interfaces[interfaceName].indexOf(proxy);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
this._interfaces[interfaceName].splice(index, 1);
|
||||||
|
|
||||||
|
if (this._interfaces[interfaceName].length == 0)
|
||||||
|
delete this._interfaces[interfaceName];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('interface-removed', interfaceName, proxy);
|
||||||
|
|
||||||
|
this._objects[objectPath][interfaceName] = null;
|
||||||
|
|
||||||
|
if (Object.keys(this._objects[objectPath]).length == 0) {
|
||||||
|
delete this._objects[objectPath];
|
||||||
|
this.emit('object-removed', objectPath);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onManagerProxyLoaded: function(initable, result) {
|
||||||
|
let error = null;
|
||||||
|
try {
|
||||||
|
initable.init_finish(result);
|
||||||
|
} catch(e) {
|
||||||
|
logError(e, 'could not initialize object manager for object ' + params.name);
|
||||||
|
|
||||||
|
this._tryToCompleteLoad();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._managerProxy.connectSignal('InterfacesAdded',
|
||||||
|
Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) {
|
||||||
|
let interfaceNames = Object.keys(interfaces);
|
||||||
|
for (let i = 0; i < interfaceNames.length; i++)
|
||||||
|
this._addInterface(objectPath, interfaceNames[i]);
|
||||||
|
}));
|
||||||
|
this._managerProxy.connectSignal('InterfacesRemoved',
|
||||||
|
Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) {
|
||||||
|
for (let i = 0; i < interfaceNames.length; i++)
|
||||||
|
this._removeInterface(objectPath, interfaceNames[i]);
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (Object.keys(this._interfaceInfos).length == 0) {
|
||||||
|
this._tryToCompleteLoad();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) {
|
||||||
|
if (!result) {
|
||||||
|
if (error) {
|
||||||
|
logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tryToCompleteLoad();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let [objects] = result;
|
||||||
|
|
||||||
|
let objectPaths = Object.keys(objects);
|
||||||
|
for (let i = 0; i < objectPaths.length; i++) {
|
||||||
|
let objectPath = objectPaths[i];
|
||||||
|
let object = objects[objectPath];
|
||||||
|
|
||||||
|
let interfaceNames = Object.getOwnPropertyNames(object);
|
||||||
|
for (let j = 0; j < interfaceNames.length; j++) {
|
||||||
|
let interfaceName = interfaceNames[j];
|
||||||
|
|
||||||
|
// Prevent load from completing until the interface is loaded
|
||||||
|
this._numLoadInhibitors++;
|
||||||
|
this._addInterface(objectPath,
|
||||||
|
interfaceName,
|
||||||
|
Lang.bind(this, this._tryToCompleteLoad));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._tryToCompleteLoad();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_registerInterfaces: function(interfaces) {
|
||||||
|
for (let i = 0; i < interfaces.length; i++) {
|
||||||
|
let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
|
||||||
|
this._interfaceInfos[info.name] = info;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getProxy: function(objectPath, interfaceName) {
|
||||||
|
let object = this._objects[objectPath];
|
||||||
|
|
||||||
|
if (!object)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return object[interfaceName];
|
||||||
|
},
|
||||||
|
|
||||||
|
getProxiesForInterface: function(interfaceName) {
|
||||||
|
let proxyList = this._interfaces[interfaceName];
|
||||||
|
|
||||||
|
if (!proxyList)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return proxyList;
|
||||||
|
},
|
||||||
|
|
||||||
|
getAllProxies: function() {
|
||||||
|
let proxies = [];
|
||||||
|
|
||||||
|
let objectPaths = Object.keys(this._objects);
|
||||||
|
for (let i = 0; i < objectPaths.length; i++) {
|
||||||
|
let object = this._objects[objectPaths];
|
||||||
|
|
||||||
|
let interfaceNames = Object.keys(object);
|
||||||
|
for (let j = 0; i < interfaceNames.length; i++) {
|
||||||
|
let interfaceName = interfaceNames[i];
|
||||||
|
if (object[interfaceName])
|
||||||
|
proxies.push(object(interfaceName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return proxies;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(ObjectManager.prototype);
|
||||||
119
js/misc/smartcardManager.js
Normal file
119
js/misc/smartcardManager.js
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const ObjectManager = imports.misc.objectManager;
|
||||||
|
|
||||||
|
const SmartcardTokenIface = '<node> \
|
||||||
|
<interface name="org.gnome.SettingsDaemon.Smartcard.Token"> \
|
||||||
|
<property name="Name" type="s" access="read"/> \
|
||||||
|
<property name="Driver" type="o" access="read"/> \
|
||||||
|
<property name="IsInserted" type="b" access="read"/> \
|
||||||
|
<property name="UsedToLogin" type="b" access="read"/> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
|
let _smartcardManager = null;
|
||||||
|
|
||||||
|
function getSmartcardManager() {
|
||||||
|
if (_smartcardManager == null)
|
||||||
|
_smartcardManager = new SmartcardManager();
|
||||||
|
|
||||||
|
return _smartcardManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SmartcardManager = new Lang.Class({
|
||||||
|
Name: 'SmartcardManager',
|
||||||
|
_init: function() {
|
||||||
|
this._objectManager = new ObjectManager.ObjectManager({ connection: Gio.DBus.session,
|
||||||
|
name: "org.gnome.SettingsDaemon.Smartcard",
|
||||||
|
objectPath: '/org/gnome/SettingsDaemon/Smartcard',
|
||||||
|
knownInterfaces: [ SmartcardTokenIface ],
|
||||||
|
onLoaded: Lang.bind(this, this._onLoaded) });
|
||||||
|
this._insertedTokens = {};
|
||||||
|
this._loginToken = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onLoaded: function() {
|
||||||
|
let tokens = this._objectManager.getProxiesForInterface('org.gnome.SettingsDaemon.Smartcard.Token');
|
||||||
|
|
||||||
|
for (let i = 0; i < tokens.length; i++)
|
||||||
|
this._addToken(tokens[i]);
|
||||||
|
|
||||||
|
this._objectManager.connect('interface-added', Lang.bind(this, function(objectManager, interfaceName, proxy) {
|
||||||
|
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
|
||||||
|
this._addToken(proxy);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._objectManager.connect('interface-removed', Lang.bind(this, function(objectManager, interfaceName, proxy) {
|
||||||
|
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
|
||||||
|
this._removeToken(proxy);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateToken: function(token) {
|
||||||
|
let objectPath = token.get_object_path();
|
||||||
|
|
||||||
|
delete this._insertedTokens[objectPath];
|
||||||
|
|
||||||
|
if (token.IsInserted)
|
||||||
|
this._insertedTokens[objectPath] = token;
|
||||||
|
|
||||||
|
if (token.UsedToLogin)
|
||||||
|
this._loginToken = token;
|
||||||
|
},
|
||||||
|
|
||||||
|
_addToken: function(token) {
|
||||||
|
this._updateToken(token);
|
||||||
|
|
||||||
|
token.connect('g-properties-changed',
|
||||||
|
Lang.bind(this, function(proxy, properties) {
|
||||||
|
if ('IsInserted' in properties.deep_unpack()) {
|
||||||
|
this._updateToken(token);
|
||||||
|
|
||||||
|
if (token.IsInserted) {
|
||||||
|
this.emit('smartcard-inserted', token);
|
||||||
|
} else {
|
||||||
|
this.emit('smartcard-removed', token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Emit a smartcard-inserted at startup if it's already plugged in
|
||||||
|
if (token.IsInserted)
|
||||||
|
this.emit('smartcard-inserted', token);
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeToken: function(token) {
|
||||||
|
let objectPath = token.get_object_path();
|
||||||
|
|
||||||
|
if (this._insertedTokens[objectPath] == token) {
|
||||||
|
delete this._insertedTokens[objectPath];
|
||||||
|
this.emit('smartcard-removed', token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._loginToken == token)
|
||||||
|
this._loginToken = null;
|
||||||
|
|
||||||
|
token.disconnectAll();
|
||||||
|
},
|
||||||
|
|
||||||
|
hasInsertedTokens: function() {
|
||||||
|
return Object.keys(this._insertedTokens).length > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
hasInsertedLoginToken: function() {
|
||||||
|
if (!this._loginToken)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!this._loginToken.IsInserted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(SmartcardManager.prototype);
|
||||||
141
js/misc/util.js
141
js/misc/util.js
@@ -1,8 +1,15 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const SCROLL_TIME = 0.1;
|
||||||
|
|
||||||
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
|
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
|
||||||
const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)';
|
const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)';
|
||||||
@@ -13,7 +20,7 @@ const _urlRegexp = new RegExp(
|
|||||||
'(^|' + _leadingJunk + ')' +
|
'(^|' + _leadingJunk + ')' +
|
||||||
'(' +
|
'(' +
|
||||||
'(?:' +
|
'(?:' +
|
||||||
'[a-z][\\w-]+://' + // scheme://
|
'(?:http|https|ftp)://' + // scheme://
|
||||||
'|' +
|
'|' +
|
||||||
'www\\d{0,3}[.]' + // www.
|
'www\\d{0,3}[.]' + // www.
|
||||||
'|' +
|
'|' +
|
||||||
@@ -73,6 +80,22 @@ function spawnCommandLine(command_line) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// spawnApp:
|
||||||
|
// @argv: an argv array
|
||||||
|
//
|
||||||
|
// Runs @argv as if it was an application, handling startup notification
|
||||||
|
function spawnApp(argv) {
|
||||||
|
try {
|
||||||
|
let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
|
||||||
|
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
|
||||||
|
|
||||||
|
let context = global.create_app_launch_context(0, -1);
|
||||||
|
app.launch([], context);
|
||||||
|
} catch(err) {
|
||||||
|
_handleSpawnError(argv[0], err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// trySpawn:
|
// trySpawn:
|
||||||
// @argv: an argv array
|
// @argv: an argv array
|
||||||
//
|
//
|
||||||
@@ -130,35 +153,10 @@ function trySpawnCommandLine(command_line) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _handleSpawnError(command, err) {
|
function _handleSpawnError(command, err) {
|
||||||
let title = _("Execution of '%s' failed:").format(command);
|
let title = _("Execution of “%s” failed:").format(command);
|
||||||
Main.notifyError(title, err.message);
|
Main.notifyError(title, err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// killall:
|
|
||||||
// @processName: a process name
|
|
||||||
//
|
|
||||||
// Kills @processName. If no process with the given name is found,
|
|
||||||
// this will fail silently.
|
|
||||||
function killall(processName) {
|
|
||||||
try {
|
|
||||||
// pkill is more portable than killall, but on Linux at least
|
|
||||||
// it won't match if you pass more than 15 characters of the
|
|
||||||
// process name... However, if you use the '-f' flag to match
|
|
||||||
// the entire command line, it will work, but we have to be
|
|
||||||
// careful in that case that we can match
|
|
||||||
// '/usr/bin/processName' but not 'gedit processName.c' or
|
|
||||||
// whatever...
|
|
||||||
|
|
||||||
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
|
|
||||||
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null);
|
|
||||||
// It might be useful to return success/failure, but we'd need
|
|
||||||
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
|
|
||||||
// the current callers care, we don't bother.
|
|
||||||
} catch (e) {
|
|
||||||
logError(e, 'Failed to kill ' + processName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lowerBound:
|
// lowerBound:
|
||||||
// @array: an array or array-like object, already sorted
|
// @array: an array or array-like object, already sorted
|
||||||
// according to @cmp
|
// according to @cmp
|
||||||
@@ -208,3 +206,92 @@ function insertSorted(array, val, cmp) {
|
|||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CloseButton = new Lang.Class({
|
||||||
|
Name: 'CloseButton',
|
||||||
|
Extends: St.Button,
|
||||||
|
|
||||||
|
_init: function(boxpointer) {
|
||||||
|
this.parent({ style_class: 'notification-close'});
|
||||||
|
|
||||||
|
// This is a bit tricky. St.Bin has its own x-align/y-align properties
|
||||||
|
// that compete with Clutter's properties. This should be fixed for
|
||||||
|
// Clutter 2.0. Since St.Bin doesn't define its own setters, the
|
||||||
|
// setters are a workaround to get Clutter's version.
|
||||||
|
this.set_x_align(Clutter.ActorAlign.END);
|
||||||
|
this.set_y_align(Clutter.ActorAlign.START);
|
||||||
|
|
||||||
|
// XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
|
||||||
|
// to respect the alignments.
|
||||||
|
this.set_x_expand(true);
|
||||||
|
this.set_y_expand(true);
|
||||||
|
|
||||||
|
this._boxPointer = boxpointer;
|
||||||
|
if (boxpointer)
|
||||||
|
this._boxPointer.connect('arrow-side-changed', Lang.bind(this, this._sync));
|
||||||
|
},
|
||||||
|
|
||||||
|
_computeBoxPointerOffset: function() {
|
||||||
|
if (!this._boxPointer || !this._boxPointer.actor.get_stage())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
let side = this._boxPointer.arrowSide;
|
||||||
|
if (side == St.Side.TOP)
|
||||||
|
return this._boxPointer.getArrowHeight();
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_sync: function() {
|
||||||
|
let themeNode = this.get_theme_node();
|
||||||
|
|
||||||
|
let offY = this._computeBoxPointerOffset();
|
||||||
|
this.translation_x = themeNode.get_length('-shell-close-overlap-x')
|
||||||
|
this.translation_y = themeNode.get_length('-shell-close-overlap-y') + offY;
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_style_changed: function() {
|
||||||
|
this._sync();
|
||||||
|
this.parent();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeCloseButton(boxpointer) {
|
||||||
|
return new CloseButton(boxpointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureActorVisibleInScrollView(scrollView, actor) {
|
||||||
|
let adjustment = scrollView.vscroll.adjustment;
|
||||||
|
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
let vfade = scrollView.get_effect("fade");
|
||||||
|
if (vfade)
|
||||||
|
offset = vfade.vfade_offset;
|
||||||
|
|
||||||
|
let box = actor.get_allocation_box();
|
||||||
|
let y1 = box.y1, y2 = box.y2;
|
||||||
|
|
||||||
|
let parent = actor.get_parent();
|
||||||
|
while (parent != scrollView) {
|
||||||
|
if (!parent)
|
||||||
|
throw new Error("actor not in scroll view");
|
||||||
|
|
||||||
|
let box = parent.get_allocation_box();
|
||||||
|
y1 += box.y1;
|
||||||
|
y2 += box.y1;
|
||||||
|
parent = parent.get_parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y1 < value + offset)
|
||||||
|
value = Math.max(0, y1 - offset);
|
||||||
|
else if (y2 > value + pageSize - offset)
|
||||||
|
value = Math.min(upper, y2 + offset - pageSize);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
Tweener.addTween(adjustment,
|
||||||
|
{ value: value,
|
||||||
|
time: SCROLL_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
}
|
||||||
|
|||||||
143
js/ui/altTab.js
143
js/ui/altTab.js
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
@@ -98,44 +99,16 @@ const AppSwitcherPopup = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_getAppLists: function() {
|
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
|
||||||
let appSys = Shell.AppSystem.get_default();
|
|
||||||
let allApps = appSys.get_running ();
|
|
||||||
|
|
||||||
let screen = global.screen;
|
|
||||||
let display = screen.get_display();
|
|
||||||
let windows = display.get_tab_list(Meta.TabList.NORMAL_ALL, screen,
|
|
||||||
screen.get_active_workspace());
|
|
||||||
|
|
||||||
// windows is only the windows on the current workspace. For
|
|
||||||
// each one, if it corresponds to an app we know, move that
|
|
||||||
// app from allApps to apps.
|
|
||||||
let apps = [];
|
|
||||||
for (let i = 0; i < windows.length && allApps.length != 0; i++) {
|
|
||||||
let app = tracker.get_window_app(windows[i]);
|
|
||||||
let index = allApps.indexOf(app);
|
|
||||||
if (index != -1) {
|
|
||||||
apps.push(app);
|
|
||||||
allApps.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now @apps is a list of apps on the current workspace, in
|
|
||||||
// standard Alt+Tab order (MRU except for minimized windows),
|
|
||||||
// and allApps is a list of apps that only appear on other
|
|
||||||
// workspaces, sorted by user_time, which is good enough.
|
|
||||||
return [apps, allApps];
|
|
||||||
},
|
|
||||||
|
|
||||||
_createSwitcher: function() {
|
_createSwitcher: function() {
|
||||||
let [localApps, otherApps] = this._getAppLists();
|
let apps = Shell.AppSystem.get_default().get_running ();
|
||||||
|
|
||||||
if (localApps.length == 0 && otherApps.length == 0)
|
if (apps.length == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this._switcherList = new AppSwitcher(localApps, otherApps, this);
|
this._switcherList = new AppSwitcher(apps, this);
|
||||||
this._items = this._switcherList.icons;
|
this._items = this._switcherList.icons;
|
||||||
|
if (this._items.length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
@@ -262,15 +235,13 @@ const AppSwitcherPopup = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_finish : function(timestamp) {
|
_finish : function(timestamp) {
|
||||||
this.parent();
|
|
||||||
|
|
||||||
let appIcon = this._items[this._selectedIndex];
|
let appIcon = this._items[this._selectedIndex];
|
||||||
let window;
|
if (this._currentWindow < 0)
|
||||||
if (this._currentWindow >= 0)
|
appIcon.app.activate_window(appIcon.cachedWindows[0], timestamp);
|
||||||
window = appIcon.cachedWindows[this._currentWindow];
|
|
||||||
else
|
else
|
||||||
window = null;
|
Main.activateWindow(appIcon.cachedWindows[this._currentWindow], timestamp);
|
||||||
appIcon.app.activate_window(window, timestamp);
|
|
||||||
|
this.parent();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDestroy : function() {
|
_onDestroy : function() {
|
||||||
@@ -342,7 +313,7 @@ const AppSwitcherPopup = new Lang.Class({
|
|||||||
this._createThumbnails();
|
this._createThumbnails();
|
||||||
this._thumbnailTimeoutId = 0;
|
this._thumbnailTimeoutId = 0;
|
||||||
this._thumbnailsFocused = false;
|
this._thumbnailsFocused = false;
|
||||||
return false;
|
return GLib.SOURCE_REMOVE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_destroyThumbnails : function() {
|
_destroyThumbnails : function() {
|
||||||
@@ -387,10 +358,13 @@ const WindowSwitcherPopup = new Lang.Class({
|
|||||||
Name: 'WindowSwitcherPopup',
|
Name: 'WindowSwitcherPopup',
|
||||||
Extends: SwitcherPopup.SwitcherPopup,
|
Extends: SwitcherPopup.SwitcherPopup,
|
||||||
|
|
||||||
|
_init: function(items) {
|
||||||
|
this.parent(items);
|
||||||
|
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
|
||||||
|
},
|
||||||
|
|
||||||
_getWindowList: function() {
|
_getWindowList: function() {
|
||||||
let settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
|
let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
|
||||||
let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
|
|
||||||
: null;
|
|
||||||
return global.display.get_tab_list(Meta.TabList.NORMAL, global.screen, workspace);
|
return global.display.get_tab_list(Meta.TabList.NORMAL, global.screen, workspace);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -400,9 +374,13 @@ const WindowSwitcherPopup = new Lang.Class({
|
|||||||
if (windows.length == 0)
|
if (windows.length == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this._switcherList = new WindowList(windows);
|
let mode = this._settings.get_enum('app-icon-mode');
|
||||||
|
this._switcherList = new WindowList(windows, mode);
|
||||||
this._items = this._switcherList.icons;
|
this._items = this._switcherList.icons;
|
||||||
|
|
||||||
|
if (this._items.length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -429,9 +407,9 @@ const WindowSwitcherPopup = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_finish: function() {
|
_finish: function() {
|
||||||
this.parent();
|
|
||||||
|
|
||||||
Main.activateWindow(this._items[this._selectedIndex].window);
|
Main.activateWindow(this._items[this._selectedIndex].window);
|
||||||
|
|
||||||
|
this.parent();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -461,34 +439,32 @@ const AppSwitcher = new Lang.Class({
|
|||||||
Name: 'AppSwitcher',
|
Name: 'AppSwitcher',
|
||||||
Extends: SwitcherPopup.SwitcherList,
|
Extends: SwitcherPopup.SwitcherList,
|
||||||
|
|
||||||
_init : function(localApps, otherApps, altTabPopup) {
|
_init : function(apps, altTabPopup) {
|
||||||
this.parent(true);
|
this.parent(true);
|
||||||
|
|
||||||
// Construct the AppIcons, add to the popup
|
|
||||||
let activeWorkspace = global.screen.get_active_workspace();
|
|
||||||
let workspaceIcons = [];
|
|
||||||
let otherIcons = [];
|
|
||||||
for (let i = 0; i < localApps.length; i++) {
|
|
||||||
let appIcon = new AppIcon(localApps[i]);
|
|
||||||
// Cache the window list now; we don't handle dynamic changes here,
|
|
||||||
// and we don't want to be continually retrieving it
|
|
||||||
appIcon.cachedWindows = appIcon.app.get_windows();
|
|
||||||
workspaceIcons.push(appIcon);
|
|
||||||
}
|
|
||||||
for (let i = 0; i < otherApps.length; i++) {
|
|
||||||
let appIcon = new AppIcon(otherApps[i]);
|
|
||||||
appIcon.cachedWindows = appIcon.app.get_windows();
|
|
||||||
otherIcons.push(appIcon);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.icons = [];
|
this.icons = [];
|
||||||
this._arrows = [];
|
this._arrows = [];
|
||||||
for (let i = 0; i < workspaceIcons.length; i++)
|
|
||||||
this._addIcon(workspaceIcons[i]);
|
let windowTracker = Shell.WindowTracker.get_default();
|
||||||
if (workspaceIcons.length > 0 && otherIcons.length > 0)
|
let settings = new Gio.Settings({ schema: 'org.gnome.shell.app-switcher' });
|
||||||
this.addSeparator();
|
let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
|
||||||
for (let i = 0; i < otherIcons.length; i++)
|
: null;
|
||||||
this._addIcon(otherIcons[i]);
|
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
|
||||||
|
global.screen, workspace);
|
||||||
|
|
||||||
|
// Construct the AppIcons, add to the popup
|
||||||
|
for (let i = 0; i < apps.length; i++) {
|
||||||
|
let appIcon = new AppIcon(apps[i]);
|
||||||
|
// Cache the window list now; we don't handle dynamic changes here,
|
||||||
|
// and we don't want to be continually retrieving it
|
||||||
|
appIcon.cachedWindows = allWindows.filter(function(w) {
|
||||||
|
return windowTracker.get_window_app (w) == appIcon.app;
|
||||||
|
});
|
||||||
|
if (appIcon.cachedWindows.length > 0)
|
||||||
|
this._addIcon(appIcon);
|
||||||
|
else if (workspace == null)
|
||||||
|
throw new Error('%s appears to be running, but doesn\'t have any windows'.format(appIcon.app.get_name()));
|
||||||
|
}
|
||||||
|
|
||||||
this._curApp = -1;
|
this._curApp = -1;
|
||||||
this._iconSize = 0;
|
this._iconSize = 0;
|
||||||
@@ -514,8 +490,6 @@ const AppSwitcher = new Lang.Class({
|
|||||||
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
|
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
|
||||||
let iconSpacing = iconNaturalHeight + iconPadding + iconBorder;
|
let iconSpacing = iconNaturalHeight + iconPadding + iconBorder;
|
||||||
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||||
if (this._separator)
|
|
||||||
totalSpacing += this._separator.width + this._list.spacing;
|
|
||||||
|
|
||||||
// We just assume the whole screen here due to weirdness happing with the passed width
|
// We just assume the whole screen here due to weirdness happing with the passed width
|
||||||
let primary = Main.layoutManager.primaryMonitor;
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
@@ -575,7 +549,7 @@ const AppSwitcher = new Lang.Class({
|
|||||||
Lang.bind(this, function () {
|
Lang.bind(this, function () {
|
||||||
this._enterItem(index);
|
this._enterItem(index);
|
||||||
this._mouseTimeOutId = 0;
|
this._mouseTimeOutId = 0;
|
||||||
return false;
|
return GLib.SOURCE_REMOVE;
|
||||||
}));
|
}));
|
||||||
} else
|
} else
|
||||||
this._itemEntered(index);
|
this._itemEntered(index);
|
||||||
@@ -638,24 +612,12 @@ const ThumbnailList = new Lang.Class({
|
|||||||
_init : function(windows) {
|
_init : function(windows) {
|
||||||
this.parent(false);
|
this.parent(false);
|
||||||
|
|
||||||
let activeWorkspace = global.screen.get_active_workspace();
|
|
||||||
|
|
||||||
// We fake the value of 'separatorAdded' when the app has no window
|
|
||||||
// on the current workspace, to avoid displaying a useless separator in
|
|
||||||
// that case.
|
|
||||||
let separatorAdded = windows.length == 0 || windows[0].get_workspace() != activeWorkspace;
|
|
||||||
|
|
||||||
this._labels = new Array();
|
this._labels = new Array();
|
||||||
this._thumbnailBins = new Array();
|
this._thumbnailBins = new Array();
|
||||||
this._clones = new Array();
|
this._clones = new Array();
|
||||||
this._windows = windows;
|
this._windows = windows;
|
||||||
|
|
||||||
for (let i = 0; i < windows.length; i++) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
if (!separatorAdded && windows[i].get_workspace() != activeWorkspace) {
|
|
||||||
this.addSeparator();
|
|
||||||
separatorAdded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let box = new St.BoxLayout({ style_class: 'thumbnail-box',
|
let box = new St.BoxLayout({ style_class: 'thumbnail-box',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
|
|
||||||
@@ -712,7 +674,7 @@ const ThumbnailList = new Lang.Class({
|
|||||||
const WindowIcon = new Lang.Class({
|
const WindowIcon = new Lang.Class({
|
||||||
Name: 'WindowIcon',
|
Name: 'WindowIcon',
|
||||||
|
|
||||||
_init: function(window) {
|
_init: function(window, mode) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
|
this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
|
||||||
@@ -730,8 +692,7 @@ const WindowIcon = new Lang.Class({
|
|||||||
|
|
||||||
this._icon.destroy_all_children();
|
this._icon.destroy_all_children();
|
||||||
|
|
||||||
let settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
|
switch (mode) {
|
||||||
switch (settings.get_enum('app-icon-mode')) {
|
|
||||||
case AppIconMode.THUMBNAIL_ONLY:
|
case AppIconMode.THUMBNAIL_ONLY:
|
||||||
size = WINDOW_PREVIEW_SIZE;
|
size = WINDOW_PREVIEW_SIZE;
|
||||||
this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE));
|
this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE));
|
||||||
@@ -769,7 +730,7 @@ const WindowList = new Lang.Class({
|
|||||||
Name: 'WindowList',
|
Name: 'WindowList',
|
||||||
Extends: SwitcherPopup.SwitcherList,
|
Extends: SwitcherPopup.SwitcherList,
|
||||||
|
|
||||||
_init : function(windows) {
|
_init : function(windows, mode) {
|
||||||
this.parent(true);
|
this.parent(true);
|
||||||
|
|
||||||
this._label = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
|
this._label = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
|
||||||
@@ -781,7 +742,7 @@ const WindowList = new Lang.Class({
|
|||||||
|
|
||||||
for (let i = 0; i < windows.length; i++) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
let win = windows[i];
|
let win = windows[i];
|
||||||
let icon = new WindowIcon(win);
|
let icon = new WindowIcon(win, mode);
|
||||||
|
|
||||||
this.addItem(icon.actor, icon.label);
|
this.addItem(icon.actor, icon.label);
|
||||||
this.icons.push(icon);
|
this.icons.push(icon);
|
||||||
|
|||||||
85
js/ui/animation.js
Normal file
85
js/ui/animation.js
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const Atk = imports.gi.Atk;
|
||||||
|
|
||||||
|
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||||
|
|
||||||
|
const Animation = new Lang.Class({
|
||||||
|
Name: 'Animation',
|
||||||
|
|
||||||
|
_init: function(filename, width, height, speed) {
|
||||||
|
this.actor = new St.Bin();
|
||||||
|
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||||
|
this._speed = speed;
|
||||||
|
|
||||||
|
this._isLoaded = false;
|
||||||
|
this._isPlaying = false;
|
||||||
|
this._timeoutId = 0;
|
||||||
|
this._frame = 0;
|
||||||
|
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
|
||||||
|
Lang.bind(this, this._animationsLoaded));
|
||||||
|
this.actor.set_child(this._animations);
|
||||||
|
},
|
||||||
|
|
||||||
|
play: function() {
|
||||||
|
if (this._isLoaded && this._timeoutId == 0) {
|
||||||
|
if (this._frame == 0)
|
||||||
|
this._showFrame(0);
|
||||||
|
|
||||||
|
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isPlaying = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
stop: function() {
|
||||||
|
if (this._timeoutId > 0) {
|
||||||
|
Mainloop.source_remove(this._timeoutId);
|
||||||
|
this._timeoutId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isPlaying = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_showFrame: function(frame) {
|
||||||
|
let oldFrameActor = this._animations.get_child_at_index(this._frame);
|
||||||
|
if (oldFrameActor)
|
||||||
|
oldFrameActor.hide();
|
||||||
|
|
||||||
|
this._frame = (frame % this._animations.get_n_children());
|
||||||
|
|
||||||
|
let newFrameActor = this._animations.get_child_at_index(this._frame);
|
||||||
|
if (newFrameActor)
|
||||||
|
newFrameActor.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
_update: function() {
|
||||||
|
this._showFrame(this._frame + 1);
|
||||||
|
return GLib.SOURCE_CONTINUE;
|
||||||
|
},
|
||||||
|
|
||||||
|
_animationsLoaded: function() {
|
||||||
|
this._isLoaded = true;
|
||||||
|
|
||||||
|
if (this._isPlaying)
|
||||||
|
this.play();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDestroy: function() {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const AnimatedIcon = new Lang.Class({
|
||||||
|
Name: 'AnimatedIcon',
|
||||||
|
Extends: Animation,
|
||||||
|
|
||||||
|
_init: function(filename, size) {
|
||||||
|
this.parent(filename, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
|
||||||
|
}
|
||||||
|
});
|
||||||
1635
js/ui/appDisplay.js
1635
js/ui/appDisplay.js
File diff suppressed because it is too large
Load Diff
@@ -14,15 +14,15 @@ const AppFavorites = new Lang.Class({
|
|||||||
_init: function() {
|
_init: function() {
|
||||||
this._favorites = {};
|
this._favorites = {};
|
||||||
global.settings.connect('changed::' + this.FAVORITE_APPS_KEY, Lang.bind(this, this._onFavsChanged));
|
global.settings.connect('changed::' + this.FAVORITE_APPS_KEY, Lang.bind(this, this._onFavsChanged));
|
||||||
this._reload();
|
this.reload();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onFavsChanged: function() {
|
_onFavsChanged: function() {
|
||||||
this._reload();
|
this.reload();
|
||||||
this.emit('changed');
|
this.emit('changed');
|
||||||
},
|
},
|
||||||
|
|
||||||
_reload: function() {
|
reload: function() {
|
||||||
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
|
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
|
||||||
let appSys = Shell.AppSystem.get_default();
|
let appSys = Shell.AppSystem.get_default();
|
||||||
let apps = ids.map(function (id) {
|
let apps = ids.map(function (id) {
|
||||||
|
|||||||
786
js/ui/background.js
Normal file
786
js/ui/background.js
Normal file
@@ -0,0 +1,786 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GDesktopEnums = imports.gi.GDesktopEnums;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
|
||||||
|
const PRIMARY_COLOR_KEY = 'primary-color';
|
||||||
|
const SECONDARY_COLOR_KEY = 'secondary-color';
|
||||||
|
const COLOR_SHADING_TYPE_KEY = 'color-shading-type';
|
||||||
|
const BACKGROUND_STYLE_KEY = 'picture-options';
|
||||||
|
const PICTURE_OPACITY_KEY = 'picture-opacity';
|
||||||
|
const PICTURE_URI_KEY = 'picture-uri';
|
||||||
|
|
||||||
|
const FADE_ANIMATION_TIME = 1.0;
|
||||||
|
|
||||||
|
// These parameters affect how often we redraw.
|
||||||
|
// The first is how different (percent crossfaded) the slide show
|
||||||
|
// has to look before redrawing and the second is the minimum
|
||||||
|
// frequency (in seconds) we're willing to wake up
|
||||||
|
const ANIMATION_OPACITY_STEP_INCREMENT = 4.0;
|
||||||
|
const ANIMATION_MIN_WAKEUP_INTERVAL = 1.0;
|
||||||
|
|
||||||
|
let _backgroundCache = null;
|
||||||
|
|
||||||
|
const BackgroundCache = new Lang.Class({
|
||||||
|
Name: 'BackgroundCache',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._patterns = [];
|
||||||
|
this._images = [];
|
||||||
|
this._pendingFileLoads = [];
|
||||||
|
this._fileMonitors = {};
|
||||||
|
},
|
||||||
|
|
||||||
|
getPatternContent: function(params) {
|
||||||
|
params = Params.parse(params, { monitorIndex: 0,
|
||||||
|
color: null,
|
||||||
|
secondColor: null,
|
||||||
|
shadingType: null,
|
||||||
|
effects: Meta.BackgroundEffects.NONE });
|
||||||
|
|
||||||
|
let content = null;
|
||||||
|
|
||||||
|
let candidateContent = null;
|
||||||
|
for (let i = 0; i < this._patterns.length; i++) {
|
||||||
|
if (this._patterns[i].get_shading() != params.shadingType)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!params.color.equal(this._patterns[i].get_color()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (params.shadingType != GDesktopEnums.BackgroundShading.SOLID &&
|
||||||
|
!params.secondColor.equal(this._patterns[i].get_second_color()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
candidateContent = this._patterns[i];
|
||||||
|
|
||||||
|
if (params.effects != this._patterns[i].effects)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidateContent) {
|
||||||
|
content = candidateContent.copy(params.monitorIndex, params.effects);
|
||||||
|
} else {
|
||||||
|
content = new Meta.Background({ meta_screen: global.screen,
|
||||||
|
monitor: params.monitorIndex,
|
||||||
|
effects: params.effects });
|
||||||
|
|
||||||
|
if (params.shadingType == GDesktopEnums.BackgroundShading.SOLID) {
|
||||||
|
content.load_color(params.color);
|
||||||
|
} else {
|
||||||
|
content.load_gradient(params.shadingType, params.color, params.secondColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._patterns.push(content);
|
||||||
|
return content;
|
||||||
|
},
|
||||||
|
|
||||||
|
_monitorFile: function(filename) {
|
||||||
|
if (this._fileMonitors[filename])
|
||||||
|
return;
|
||||||
|
|
||||||
|
let file = Gio.File.new_for_path(filename);
|
||||||
|
let monitor = file.monitor(Gio.FileMonitorFlags.NONE, null);
|
||||||
|
|
||||||
|
let signalId = monitor.connect('changed',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
for (let i = 0; i < this._images.length; i++) {
|
||||||
|
if (this._images[i].get_filename() == filename)
|
||||||
|
this._images.splice(i, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor.disconnect(signalId);
|
||||||
|
|
||||||
|
this.emit('file-changed', filename);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._fileMonitors[filename] = monitor;
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeContent: function(contentList, content) {
|
||||||
|
let index = contentList.indexOf(content);
|
||||||
|
if (index < 0)
|
||||||
|
throw new Error("Trying to remove invalid content: " + content);
|
||||||
|
contentList.splice(index, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
removePatternContent: function(content) {
|
||||||
|
this._removeContent(this._patterns, content);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeImageContent: function(content) {
|
||||||
|
let filename = content.get_filename();
|
||||||
|
|
||||||
|
let hasOtherUsers = this._images.some(function(content) { return filename == content.get_filename(); });
|
||||||
|
if (!hasOtherUsers)
|
||||||
|
delete this._fileMonitors[filename];
|
||||||
|
|
||||||
|
this._removeContent(this._images, content);
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadImageContentInternal: function(filename, style) {
|
||||||
|
let cancellable = new Gio.Cancellable();
|
||||||
|
let content = new Meta.Background({ meta_screen: global.screen });
|
||||||
|
|
||||||
|
let info = { filename: filename,
|
||||||
|
style: style,
|
||||||
|
cancellable: cancellable,
|
||||||
|
callers: [] };
|
||||||
|
|
||||||
|
content.load_file_async(filename, style, cancellable, Lang.bind(this, function(object, result) {
|
||||||
|
if (cancellable.is_cancelled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
content.load_file_finish(result);
|
||||||
|
} catch(e) {
|
||||||
|
content = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
this._monitorFile(filename);
|
||||||
|
info.callers.forEach(Lang.bind(this, function(caller) {
|
||||||
|
let newContent = content.copy(caller.monitorIndex, caller.effects);
|
||||||
|
this._images.push(newContent);
|
||||||
|
caller.onFinished(newContent);
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
info.callers.forEach(Lang.bind(this, function(caller) {
|
||||||
|
caller.onFinished(null);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let idx = this._pendingFileLoads.indexOf(info);
|
||||||
|
this._pendingFileLoads.splice(idx, 1);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._pendingFileLoads.push(info);
|
||||||
|
return info;
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadImageContent: function(params) {
|
||||||
|
params = Params.parse(params, { monitorIndex: 0,
|
||||||
|
style: null,
|
||||||
|
filename: null,
|
||||||
|
effects: Meta.BackgroundEffects.NONE,
|
||||||
|
cancellable: null,
|
||||||
|
onFinished: null });
|
||||||
|
|
||||||
|
let caller = { monitorIndex: params.monitorIndex,
|
||||||
|
effects: params.effects,
|
||||||
|
cancellable: params.cancellable,
|
||||||
|
onFinished: params.onFinished };
|
||||||
|
|
||||||
|
let info = null;
|
||||||
|
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||||
|
let pendingLoad = this._pendingFileLoads[i];
|
||||||
|
if (pendingLoad.filename == params.filename && pendingLoad.style == params.style) {
|
||||||
|
info = pendingLoad;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info)
|
||||||
|
info = this._loadImageContentInternal(params.filename, params.style);
|
||||||
|
|
||||||
|
info.callers.push(caller);
|
||||||
|
|
||||||
|
if (caller.cancellable) {
|
||||||
|
caller.cancellable.connect(Lang.bind(this, function() {
|
||||||
|
let idx = info.callers.indexOf(caller);
|
||||||
|
info.callers.splice(idx, 1);
|
||||||
|
|
||||||
|
if (info.callers.length == 0) {
|
||||||
|
info.cancellable.cancel();
|
||||||
|
|
||||||
|
let idx = this._pendingFileLoads.indexOf(info);
|
||||||
|
this._pendingFileLoads.splice(idx, 1);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getImageContent: function(params) {
|
||||||
|
params = Params.parse(params, { monitorIndex: 0,
|
||||||
|
style: null,
|
||||||
|
filename: null,
|
||||||
|
effects: Meta.BackgroundEffects.NONE,
|
||||||
|
cancellable: null,
|
||||||
|
onFinished: null });
|
||||||
|
|
||||||
|
let content = null;
|
||||||
|
|
||||||
|
let candidateContent = null;
|
||||||
|
for (let i = 0; i < this._images.length; i++) {
|
||||||
|
if (this._images[i].get_style() != params.style)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (this._images[i].get_filename() != params.filename)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
|
||||||
|
this._images[i].monitor != params.monitorIndex)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
candidateContent = this._images[i];
|
||||||
|
|
||||||
|
if (params.effects != this._images[i].effects)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidateContent) {
|
||||||
|
content = candidateContent.copy(params.monitorIndex, params.effects);
|
||||||
|
|
||||||
|
if (params.cancellable && params.cancellable.is_cancelled())
|
||||||
|
content = null;
|
||||||
|
else
|
||||||
|
this._images.push(content);
|
||||||
|
|
||||||
|
if (params.onFinished)
|
||||||
|
params.onFinished(content);
|
||||||
|
} else {
|
||||||
|
this._loadImageContent({ filename: params.filename,
|
||||||
|
style: params.style,
|
||||||
|
effects: params.effects,
|
||||||
|
monitorIndex: params.monitorIndex,
|
||||||
|
cancellable: params.cancellable,
|
||||||
|
onFinished: params.onFinished });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getAnimation: function(params) {
|
||||||
|
params = Params.parse(params, { filename: null,
|
||||||
|
onLoaded: null });
|
||||||
|
|
||||||
|
if (this._animationFilename == params.filename) {
|
||||||
|
if (params.onLoaded) {
|
||||||
|
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||||
|
params.onLoaded(this._animation);
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let animation = new Animation({ filename: params.filename });
|
||||||
|
|
||||||
|
animation.load(Lang.bind(this, function() {
|
||||||
|
this._monitorFile(params.filename);
|
||||||
|
this._animationFilename = params.filename;
|
||||||
|
this._animation = animation;
|
||||||
|
|
||||||
|
if (params.onLoaded) {
|
||||||
|
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||||
|
params.onLoaded(this._animation);
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(BackgroundCache.prototype);
|
||||||
|
|
||||||
|
function getBackgroundCache() {
|
||||||
|
if (!_backgroundCache)
|
||||||
|
_backgroundCache = new BackgroundCache();
|
||||||
|
return _backgroundCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Background = new Lang.Class({
|
||||||
|
Name: 'Background',
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
params = Params.parse(params, { monitorIndex: 0,
|
||||||
|
layoutManager: Main.layoutManager,
|
||||||
|
effects: Meta.BackgroundEffects.NONE,
|
||||||
|
settings: null });
|
||||||
|
this.actor = new Meta.BackgroundGroup();
|
||||||
|
this.actor._delegate = this;
|
||||||
|
|
||||||
|
this._destroySignalId = this.actor.connect('destroy',
|
||||||
|
Lang.bind(this, this._destroy));
|
||||||
|
|
||||||
|
this._settings = params.settings;
|
||||||
|
this._monitorIndex = params.monitorIndex;
|
||||||
|
this._layoutManager = params.layoutManager;
|
||||||
|
this._effects = params.effects;
|
||||||
|
this._fileWatches = {};
|
||||||
|
this._pattern = null;
|
||||||
|
// contains a single image for static backgrounds and
|
||||||
|
// two images (from and to) for slide shows
|
||||||
|
this._images = {};
|
||||||
|
|
||||||
|
this._brightness = 1.0;
|
||||||
|
this._vignetteSharpness = 0.2;
|
||||||
|
this._cancellable = new Gio.Cancellable();
|
||||||
|
this.isLoaded = false;
|
||||||
|
|
||||||
|
this._settingsChangedSignalId = this._settings.connect('changed', Lang.bind(this, function() {
|
||||||
|
this.emit('changed');
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._load();
|
||||||
|
},
|
||||||
|
|
||||||
|
_destroy: function() {
|
||||||
|
this._cancellable.cancel();
|
||||||
|
|
||||||
|
if (this._updateAnimationTimeoutId) {
|
||||||
|
GLib.source_remove (this._updateAnimationTimeoutId);
|
||||||
|
this._updateAnimationTimeoutId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let i;
|
||||||
|
let keys = Object.keys(this._fileWatches);
|
||||||
|
for (i = 0; i < keys.length; i++) {
|
||||||
|
this._cache.disconnect(this._fileWatches[keys[i]]);
|
||||||
|
}
|
||||||
|
this._fileWatches = null;
|
||||||
|
|
||||||
|
if (this._pattern) {
|
||||||
|
if (this._pattern.content)
|
||||||
|
this._cache.removePatternContent(this._pattern.content);
|
||||||
|
|
||||||
|
this._pattern.destroy();
|
||||||
|
this._pattern = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = Object.keys(this._images);
|
||||||
|
for (i = 0; i < keys.length; i++) {
|
||||||
|
let actor = this._images[keys[i]];
|
||||||
|
|
||||||
|
if (actor.content)
|
||||||
|
this._cache.removeImageContent(actor.content);
|
||||||
|
|
||||||
|
actor.destroy();
|
||||||
|
this._images[keys[i]] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.actor.disconnect(this._destroySignalId);
|
||||||
|
this._destroySignalId = 0;
|
||||||
|
|
||||||
|
if (this._settingsChangedSignalId != 0)
|
||||||
|
this._settings.disconnect(this._settingsChangedSignalId);
|
||||||
|
this._settingsChangedSignalId = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_setLoaded: function() {
|
||||||
|
if (this.isLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.isLoaded = true;
|
||||||
|
|
||||||
|
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||||
|
this.emit('loaded');
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadPattern: function() {
|
||||||
|
let colorString, res, color, secondColor;
|
||||||
|
|
||||||
|
colorString = this._settings.get_string(PRIMARY_COLOR_KEY);
|
||||||
|
[res, color] = Clutter.Color.from_string(colorString);
|
||||||
|
colorString = this._settings.get_string(SECONDARY_COLOR_KEY);
|
||||||
|
[res, secondColor] = Clutter.Color.from_string(colorString);
|
||||||
|
|
||||||
|
let shadingType = this._settings.get_enum(COLOR_SHADING_TYPE_KEY);
|
||||||
|
|
||||||
|
let content = this._cache.getPatternContent({ monitorIndex: this._monitorIndex,
|
||||||
|
effects: this._effects,
|
||||||
|
color: color,
|
||||||
|
secondColor: secondColor,
|
||||||
|
shadingType: shadingType });
|
||||||
|
|
||||||
|
this._pattern = new Meta.BackgroundActor();
|
||||||
|
this.actor.add_child(this._pattern);
|
||||||
|
|
||||||
|
this._pattern.content = content;
|
||||||
|
},
|
||||||
|
|
||||||
|
_watchCacheFile: function(filename) {
|
||||||
|
if (this._fileWatches[filename])
|
||||||
|
return;
|
||||||
|
|
||||||
|
let signalId = this._cache.connect('file-changed',
|
||||||
|
Lang.bind(this, function(cache, changedFile) {
|
||||||
|
if (changedFile == filename) {
|
||||||
|
this.emit('changed');
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this._fileWatches[filename] = signalId;
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureImage: function(index) {
|
||||||
|
if (this._images[index])
|
||||||
|
return;
|
||||||
|
|
||||||
|
let actor = new Meta.BackgroundActor();
|
||||||
|
|
||||||
|
// The background pattern is the first actor in
|
||||||
|
// the group, and all images should be above that.
|
||||||
|
this.actor.insert_child_at_index(actor, index + 1);
|
||||||
|
this._images[index] = actor;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateImage: function(index, content, filename) {
|
||||||
|
content.brightness = this._brightness;
|
||||||
|
content.vignette_sharpness = this._vignetteSharpness;
|
||||||
|
|
||||||
|
let image = this._images[index];
|
||||||
|
if (image.content)
|
||||||
|
this._cache.removeImageContent(content);
|
||||||
|
image.content = content;
|
||||||
|
this._watchCacheFile(filename);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateAnimationProgress: function() {
|
||||||
|
if (this._images[1])
|
||||||
|
this._images[1].opacity = this._animation.transitionProgress * 255;
|
||||||
|
|
||||||
|
this._queueUpdateAnimation();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateAnimation: function() {
|
||||||
|
this._updateAnimationTimeoutId = 0;
|
||||||
|
|
||||||
|
this._animation.update(this._layoutManager.monitors[this._monitorIndex]);
|
||||||
|
let files = this._animation.keyFrameFiles;
|
||||||
|
|
||||||
|
if (files.length == 0) {
|
||||||
|
this._setLoaded();
|
||||||
|
this._queueUpdateAnimation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let numPendingImages = files.length;
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
if (this._images[i] && this._images[i].content &&
|
||||||
|
this._images[i].content.get_filename() == files[i]) {
|
||||||
|
|
||||||
|
numPendingImages--;
|
||||||
|
if (numPendingImages == 0)
|
||||||
|
this._updateAnimationProgress();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
|
||||||
|
effects: this._effects,
|
||||||
|
style: this._style,
|
||||||
|
filename: files[i],
|
||||||
|
cancellable: this._cancellable,
|
||||||
|
onFinished: Lang.bind(this, function(content, i) {
|
||||||
|
numPendingImages--;
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
this._setLoaded();
|
||||||
|
if (numPendingImages == 0)
|
||||||
|
this._updateAnimationProgress();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._ensureImage(i);
|
||||||
|
this._updateImage(i, content, files[i]);
|
||||||
|
|
||||||
|
if (numPendingImages == 0) {
|
||||||
|
this._setLoaded();
|
||||||
|
this._updateAnimationProgress();
|
||||||
|
}
|
||||||
|
}, i)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_queueUpdateAnimation: function() {
|
||||||
|
if (this._updateAnimationTimeoutId != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this._cancellable || this._cancellable.is_cancelled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this._animation.transitionDuration)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let nSteps = 255 / ANIMATION_OPACITY_STEP_INCREMENT;
|
||||||
|
let timePerStep = (this._animation.transitionDuration * 1000) / nSteps;
|
||||||
|
|
||||||
|
let interval = Math.max(ANIMATION_MIN_WAKEUP_INTERVAL * 1000,
|
||||||
|
timePerStep);
|
||||||
|
|
||||||
|
if (interval > GLib.MAXUINT32)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._updateAnimationTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||||
|
interval,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._updateAnimationTimeoutId = 0;
|
||||||
|
this._updateAnimation();
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadAnimation: function(filename) {
|
||||||
|
this._cache.getAnimation({ filename: filename,
|
||||||
|
onLoaded: Lang.bind(this, function(animation) {
|
||||||
|
this._animation = animation;
|
||||||
|
|
||||||
|
if (!this._animation || this._cancellable.is_cancelled()) {
|
||||||
|
this._setLoaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateAnimation();
|
||||||
|
this._watchCacheFile(filename);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadImage: function(filename) {
|
||||||
|
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
|
||||||
|
effects: this._effects,
|
||||||
|
style: this._style,
|
||||||
|
filename: filename,
|
||||||
|
cancellable: this._cancellable,
|
||||||
|
onFinished: Lang.bind(this, function(content) {
|
||||||
|
if (content) {
|
||||||
|
this._ensureImage(0);
|
||||||
|
this._updateImage(0, content, filename);
|
||||||
|
}
|
||||||
|
this._setLoaded();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadFile: function(filename) {
|
||||||
|
if (filename.endsWith('.xml'))
|
||||||
|
this._loadAnimation(filename);
|
||||||
|
else
|
||||||
|
this._loadImage(filename);
|
||||||
|
},
|
||||||
|
|
||||||
|
_load: function () {
|
||||||
|
this._cache = getBackgroundCache();
|
||||||
|
|
||||||
|
this._loadPattern();
|
||||||
|
|
||||||
|
this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
|
||||||
|
if (this._style == GDesktopEnums.BackgroundStyle.NONE) {
|
||||||
|
this._setLoaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uri = this._settings.get_string(PICTURE_URI_KEY);
|
||||||
|
let filename;
|
||||||
|
if (GLib.uri_parse_scheme(uri) != null)
|
||||||
|
filename = Gio.File.new_for_uri(uri).get_path();
|
||||||
|
else
|
||||||
|
filename = uri;
|
||||||
|
|
||||||
|
if (!filename) {
|
||||||
|
this._setLoaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._loadFile(filename);
|
||||||
|
},
|
||||||
|
|
||||||
|
get brightness() {
|
||||||
|
return this._brightness;
|
||||||
|
},
|
||||||
|
|
||||||
|
set brightness(factor) {
|
||||||
|
this._brightness = factor;
|
||||||
|
if (this._pattern && this._pattern.content)
|
||||||
|
this._pattern.content.brightness = factor;
|
||||||
|
|
||||||
|
let keys = Object.keys(this._images);
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
let image = this._images[keys[i]];
|
||||||
|
if (image && image.content)
|
||||||
|
image.content.brightness = factor;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get vignetteSharpness() {
|
||||||
|
return this._vignetteSharpness;
|
||||||
|
},
|
||||||
|
|
||||||
|
set vignetteSharpness(sharpness) {
|
||||||
|
this._vignetteSharpness = sharpness;
|
||||||
|
if (this._pattern && this._pattern.content)
|
||||||
|
this._pattern.content.vignette_sharpness = sharpness;
|
||||||
|
|
||||||
|
let keys = Object.keys(this._images);
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
let image = this._images[keys[i]];
|
||||||
|
if (image && image.content)
|
||||||
|
image.content.vignette_sharpness = sharpness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(Background.prototype);
|
||||||
|
|
||||||
|
const Animation = new Lang.Class({
|
||||||
|
Name: 'Animation',
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
params = Params.parse(params, { filename: null });
|
||||||
|
|
||||||
|
this.filename = params.filename;
|
||||||
|
this.keyFrameFiles = [];
|
||||||
|
this.transitionProgress = 0.0;
|
||||||
|
this.transitionDuration = 0.0;
|
||||||
|
this.loaded = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
load: function(callback) {
|
||||||
|
let file = Gio.File.new_for_path(this.filename);
|
||||||
|
|
||||||
|
this._show = new GnomeDesktop.BGSlideShow({ filename: this.filename });
|
||||||
|
|
||||||
|
this._show.load_async(null,
|
||||||
|
Lang.bind(this,
|
||||||
|
function(object, result) {
|
||||||
|
this.loaded = true;
|
||||||
|
if (callback)
|
||||||
|
callback();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
update: function(monitor) {
|
||||||
|
this.keyFrameFiles = [];
|
||||||
|
|
||||||
|
if (!this._show)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._show.get_num_slides() < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let [progress, duration, isFixed, file1, file2] = this._show.get_current_slide(monitor.width, monitor.height);
|
||||||
|
|
||||||
|
this.transitionDuration = duration;
|
||||||
|
this.transitionProgress = progress;
|
||||||
|
|
||||||
|
if (file1)
|
||||||
|
this.keyFrameFiles.push(file1);
|
||||||
|
|
||||||
|
if (file2)
|
||||||
|
this.keyFrameFiles.push(file2);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(Animation.prototype);
|
||||||
|
|
||||||
|
const BackgroundManager = new Lang.Class({
|
||||||
|
Name: 'BackgroundManager',
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
params = Params.parse(params, { container: null,
|
||||||
|
layoutManager: Main.layoutManager,
|
||||||
|
monitorIndex: null,
|
||||||
|
effects: Meta.BackgroundEffects.NONE,
|
||||||
|
controlPosition: true,
|
||||||
|
settingsSchema: BACKGROUND_SCHEMA });
|
||||||
|
|
||||||
|
this._settings = new Gio.Settings({ schema: params.settingsSchema });
|
||||||
|
this._container = params.container;
|
||||||
|
this._layoutManager = params.layoutManager;
|
||||||
|
this._effects = params.effects;
|
||||||
|
this._monitorIndex = params.monitorIndex;
|
||||||
|
this._controlPosition = params.controlPosition;
|
||||||
|
|
||||||
|
this.background = this._createBackground();
|
||||||
|
this._newBackground = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
if (this._newBackground) {
|
||||||
|
this._newBackground.actor.destroy();
|
||||||
|
this._newBackground = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.background) {
|
||||||
|
this.background.actor.destroy();
|
||||||
|
this.background = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateBackground: function() {
|
||||||
|
let newBackground = this._createBackground();
|
||||||
|
newBackground.vignetteSharpness = this.background.vignetteSharpness;
|
||||||
|
newBackground.brightness = this.background.brightness;
|
||||||
|
newBackground.visible = this.background.visible;
|
||||||
|
|
||||||
|
newBackground.loadedSignalId = newBackground.connect('loaded',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
newBackground.disconnect(newBackground.loadedSignalId);
|
||||||
|
newBackground.loadedSignalId = 0;
|
||||||
|
Tweener.addTween(this.background.actor,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: FADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function() {
|
||||||
|
if (this._newBackground != newBackground) {
|
||||||
|
/* Not interesting, we queued another load */
|
||||||
|
newBackground.actor.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.background.actor.destroy();
|
||||||
|
this.background = newBackground;
|
||||||
|
this._newBackground = null;
|
||||||
|
|
||||||
|
this.emit('changed');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._newBackground = newBackground;
|
||||||
|
},
|
||||||
|
|
||||||
|
_createBackground: function() {
|
||||||
|
let background = new Background({ monitorIndex: this._monitorIndex,
|
||||||
|
layoutManager: this._layoutManager,
|
||||||
|
effects: this._effects,
|
||||||
|
settings: this._settings });
|
||||||
|
this._container.add_child(background.actor);
|
||||||
|
|
||||||
|
let monitor = this._layoutManager.monitors[this._monitorIndex];
|
||||||
|
|
||||||
|
background.actor.set_size(monitor.width, monitor.height);
|
||||||
|
if (this._controlPosition) {
|
||||||
|
background.actor.set_position(monitor.x, monitor.y);
|
||||||
|
background.actor.lower_bottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
background.changeSignalId = background.connect('changed', Lang.bind(this, function() {
|
||||||
|
background.disconnect(background.changeSignalId);
|
||||||
|
background.changeSignalId = 0;
|
||||||
|
this._updateBackground();
|
||||||
|
}));
|
||||||
|
|
||||||
|
background.actor.connect('destroy', Lang.bind(this, function() {
|
||||||
|
if (background.changeSignalId)
|
||||||
|
background.disconnect(background.changeSignalId);
|
||||||
|
|
||||||
|
if (background.loadedSignalId)
|
||||||
|
background.disconnect(background.loadedSignalId);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return background;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(BackgroundManager.prototype);
|
||||||
63
js/ui/backgroundMenu.js
Normal file
63
js/ui/backgroundMenu.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
|
const BackgroundMenu = new Lang.Class({
|
||||||
|
Name: 'BackgroundMenu',
|
||||||
|
Extends: PopupMenu.PopupMenu,
|
||||||
|
|
||||||
|
_init: function(layoutManager) {
|
||||||
|
this.parent(layoutManager.dummyCursor, 0, St.Side.TOP);
|
||||||
|
|
||||||
|
this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
|
||||||
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
this.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop');
|
||||||
|
|
||||||
|
this.actor.add_style_class_name('background-menu');
|
||||||
|
|
||||||
|
layoutManager.menuGroup.add_actor(this.actor);
|
||||||
|
this.actor.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function addBackgroundMenu(actor, layoutManager) {
|
||||||
|
actor.reactive = true;
|
||||||
|
actor._backgroundMenu = new BackgroundMenu(layoutManager);
|
||||||
|
actor._backgroundManager = new PopupMenu.PopupMenuManager({ actor: actor });
|
||||||
|
actor._backgroundManager.addMenu(actor._backgroundMenu);
|
||||||
|
|
||||||
|
function openMenu() {
|
||||||
|
let [x, y] = global.get_pointer();
|
||||||
|
Main.layoutManager.setDummyCursorPosition(x, y);
|
||||||
|
actor._backgroundMenu.open(BoxPointer.PopupAnimation.NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
let clickAction = new Clutter.ClickAction();
|
||||||
|
clickAction.connect('long-press', function(action, actor, state) {
|
||||||
|
if (state == Clutter.LongPressState.QUERY)
|
||||||
|
return action.get_button() == 1 && !actor._backgroundMenu.isOpen;
|
||||||
|
if (state == Clutter.LongPressState.ACTIVATE) {
|
||||||
|
openMenu();
|
||||||
|
actor._backgroundManager.ignoreRelease();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
clickAction.connect('clicked', function(action) {
|
||||||
|
if (action.get_button() == 3)
|
||||||
|
openMenu();
|
||||||
|
});
|
||||||
|
actor.add_action(clickAction);
|
||||||
|
|
||||||
|
actor.connect('destroy', function() {
|
||||||
|
actor._backgroundMenu.destroy();
|
||||||
|
actor._backgroundMenu = null;
|
||||||
|
actor._backgroundManager = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -3,8 +3,9 @@
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const St = imports.gi.St;
|
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
@@ -38,6 +39,7 @@ const BoxPointer = new Lang.Class({
|
|||||||
this._arrowSide = arrowSide;
|
this._arrowSide = arrowSide;
|
||||||
this._userArrowSide = arrowSide;
|
this._userArrowSide = arrowSide;
|
||||||
this._arrowOrigin = 0;
|
this._arrowOrigin = 0;
|
||||||
|
this._arrowActor = null;
|
||||||
this.actor = new St.Bin({ x_fill: true,
|
this.actor = new St.Bin({ x_fill: true,
|
||||||
y_fill: true });
|
y_fill: true });
|
||||||
this._container = new Shell.GenericContainer();
|
this._container = new Shell.GenericContainer();
|
||||||
@@ -60,10 +62,14 @@ const BoxPointer = new Lang.Class({
|
|||||||
this._muteInput();
|
this._muteInput();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get arrowSide() {
|
||||||
|
return this._arrowSide;
|
||||||
|
},
|
||||||
|
|
||||||
_muteInput: function() {
|
_muteInput: function() {
|
||||||
if (this._capturedEventId == 0)
|
if (this._capturedEventId == 0)
|
||||||
this._capturedEventId = this.actor.connect('captured-event',
|
this._capturedEventId = this.actor.connect('captured-event',
|
||||||
function() { return true; });
|
function() { return Clutter.EVENT_STOP; });
|
||||||
},
|
},
|
||||||
|
|
||||||
_unmuteInput: function() {
|
_unmuteInput: function() {
|
||||||
@@ -115,6 +121,9 @@ const BoxPointer = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
hide: function(animate, onComplete) {
|
hide: function(animate, onComplete) {
|
||||||
|
if (!this.actor.visible)
|
||||||
|
return;
|
||||||
|
|
||||||
let xOffset = 0;
|
let xOffset = 0;
|
||||||
let yOffset = 0;
|
let yOffset = 0;
|
||||||
let themeNode = this.actor.get_theme_node();
|
let themeNode = this.actor.get_theme_node();
|
||||||
@@ -179,7 +188,9 @@ const BoxPointer = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||||
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth);
|
let themeNode = this.actor.get_theme_node();
|
||||||
|
let borderWidth = themeNode.get_length('-arrow-border-width');
|
||||||
|
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth - 2 * borderWidth);
|
||||||
alloc.min_size = minSize;
|
alloc.min_size = minSize;
|
||||||
alloc.natural_size = naturalSize;
|
alloc.natural_size = naturalSize;
|
||||||
this._adjustAllocationForArrow(false, alloc);
|
this._adjustAllocationForArrow(false, alloc);
|
||||||
@@ -220,31 +231,27 @@ const BoxPointer = new Lang.Class({
|
|||||||
this.bin.allocate(childBox, flags);
|
this.bin.allocate(childBox, flags);
|
||||||
|
|
||||||
if (this._sourceActor && this._sourceActor.mapped) {
|
if (this._sourceActor && this._sourceActor.mapped) {
|
||||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
this._reposition();
|
||||||
|
this._updateFlip();
|
||||||
if (this._shouldFlip()) {
|
|
||||||
switch (this._arrowSide) {
|
|
||||||
case St.Side.TOP:
|
|
||||||
this._arrowSide = St.Side.BOTTOM;
|
|
||||||
break;
|
|
||||||
case St.Side.BOTTOM:
|
|
||||||
this._arrowSide = St.Side.TOP;
|
|
||||||
break;
|
|
||||||
case St.Side.LEFT:
|
|
||||||
this._arrowSide = St.Side.RIGHT;
|
|
||||||
break;
|
|
||||||
case St.Side.RIGHT:
|
|
||||||
this._arrowSide = St.Side.LEFT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_drawBorder: function(area) {
|
_drawBorder: function(area) {
|
||||||
let themeNode = this.actor.get_theme_node();
|
let themeNode = this.actor.get_theme_node();
|
||||||
|
|
||||||
|
if (this._arrowActor) {
|
||||||
|
let [sourceX, sourceY] = this._arrowActor.get_transformed_position();
|
||||||
|
let [sourceWidth, sourceHeight] = this._arrowActor.get_transformed_size();
|
||||||
|
let [absX, absY] = this.actor.get_transformed_position();
|
||||||
|
|
||||||
|
if (this._arrowSide == St.Side.TOP ||
|
||||||
|
this._arrowSide == St.Side.BOTTOM) {
|
||||||
|
this._arrowOrigin = sourceX - absX + sourceWidth / 2;
|
||||||
|
} else {
|
||||||
|
this._arrowOrigin = sourceY - absY + sourceHeight / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let borderWidth = themeNode.get_length('-arrow-border-width');
|
let borderWidth = themeNode.get_length('-arrow-border-width');
|
||||||
let base = themeNode.get_length('-arrow-base');
|
let base = themeNode.get_length('-arrow-base');
|
||||||
let rise = themeNode.get_length('-arrow-rise');
|
let rise = themeNode.get_length('-arrow-rise');
|
||||||
@@ -280,6 +287,7 @@ const BoxPointer = new Lang.Class({
|
|||||||
let skipBottomLeft = false;
|
let skipBottomLeft = false;
|
||||||
let skipBottomRight = false;
|
let skipBottomRight = false;
|
||||||
|
|
||||||
|
if (rise) {
|
||||||
switch (this._arrowSide) {
|
switch (this._arrowSide) {
|
||||||
case St.Side.TOP:
|
case St.Side.TOP:
|
||||||
if (this._arrowOrigin == x1)
|
if (this._arrowOrigin == x1)
|
||||||
@@ -309,9 +317,10 @@ const BoxPointer = new Lang.Class({
|
|||||||
skipBottomLeft = true;
|
skipBottomLeft = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cr.moveTo(x1 + borderRadius, y1);
|
cr.moveTo(x1 + borderRadius, y1);
|
||||||
if (this._arrowSide == St.Side.TOP) {
|
if (this._arrowSide == St.Side.TOP && rise) {
|
||||||
if (skipTopLeft) {
|
if (skipTopLeft) {
|
||||||
cr.moveTo(x1, y2 - borderRadius);
|
cr.moveTo(x1, y2 - borderRadius);
|
||||||
cr.lineTo(x1, y1 - rise);
|
cr.lineTo(x1, y1 - rise);
|
||||||
@@ -333,7 +342,7 @@ const BoxPointer = new Lang.Class({
|
|||||||
3*Math.PI/2, Math.PI*2);
|
3*Math.PI/2, Math.PI*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.RIGHT) {
|
if (this._arrowSide == St.Side.RIGHT && rise) {
|
||||||
if (skipTopRight) {
|
if (skipTopRight) {
|
||||||
cr.lineTo(x2 + rise, y1);
|
cr.lineTo(x2 + rise, y1);
|
||||||
cr.lineTo(x2 + rise, y1 + halfBase);
|
cr.lineTo(x2 + rise, y1 + halfBase);
|
||||||
@@ -354,7 +363,7 @@ const BoxPointer = new Lang.Class({
|
|||||||
0, Math.PI/2);
|
0, Math.PI/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.BOTTOM) {
|
if (this._arrowSide == St.Side.BOTTOM && rise) {
|
||||||
if (skipBottomLeft) {
|
if (skipBottomLeft) {
|
||||||
cr.lineTo(x1 + halfBase, y2);
|
cr.lineTo(x1 + halfBase, y2);
|
||||||
cr.lineTo(x1, y2 + rise);
|
cr.lineTo(x1, y2 + rise);
|
||||||
@@ -375,7 +384,7 @@ const BoxPointer = new Lang.Class({
|
|||||||
Math.PI/2, Math.PI);
|
Math.PI/2, Math.PI);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._arrowSide == St.Side.LEFT) {
|
if (this._arrowSide == St.Side.LEFT && rise) {
|
||||||
if (skipTopLeft) {
|
if (skipTopLeft) {
|
||||||
cr.lineTo(x1, y1 + halfBase);
|
cr.lineTo(x1, y1 + halfBase);
|
||||||
cr.lineTo(x1 - rise, y1);
|
cr.lineTo(x1 - rise, y1);
|
||||||
@@ -405,11 +414,11 @@ const BoxPointer = new Lang.Class({
|
|||||||
cr.setLineWidth(borderWidth);
|
cr.setLineWidth(borderWidth);
|
||||||
cr.stroke();
|
cr.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cr.$dispose();
|
||||||
},
|
},
|
||||||
|
|
||||||
setPosition: function(sourceActor, alignment) {
|
setPosition: function(sourceActor, alignment) {
|
||||||
this._arrowSide = this._userArrowSide;
|
|
||||||
|
|
||||||
// We need to show it now to force an allocation,
|
// We need to show it now to force an allocation,
|
||||||
// so that we can query the correct size.
|
// so that we can query the correct size.
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
@@ -417,7 +426,8 @@ const BoxPointer = new Lang.Class({
|
|||||||
this._sourceActor = sourceActor;
|
this._sourceActor = sourceActor;
|
||||||
this._arrowAlignment = alignment;
|
this._arrowAlignment = alignment;
|
||||||
|
|
||||||
this._reposition(sourceActor, alignment);
|
this._reposition();
|
||||||
|
this._updateFlip();
|
||||||
},
|
},
|
||||||
|
|
||||||
setSourceAlignment: function(alignment) {
|
setSourceAlignment: function(alignment) {
|
||||||
@@ -429,7 +439,10 @@ const BoxPointer = new Lang.Class({
|
|||||||
this.setPosition(this._sourceActor, this._arrowAlignment);
|
this.setPosition(this._sourceActor, this._arrowAlignment);
|
||||||
},
|
},
|
||||||
|
|
||||||
_reposition: function(sourceActor, alignment) {
|
_reposition: function() {
|
||||||
|
let sourceActor = this._sourceActor;
|
||||||
|
let alignment = this._arrowAlignment;
|
||||||
|
|
||||||
// Position correctly relative to the sourceActor
|
// Position correctly relative to the sourceActor
|
||||||
let sourceNode = sourceActor.get_theme_node();
|
let sourceNode = sourceActor.get_theme_node();
|
||||||
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
||||||
@@ -550,10 +563,20 @@ const BoxPointer = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// @actor: an actor relative to which the arrow is positioned.
|
||||||
|
// Differently from setPosition, this will not move the boxpointer itself,
|
||||||
|
// on the arrow
|
||||||
|
setArrowActor: function(actor) {
|
||||||
|
if (this._arrowActor != actor) {
|
||||||
|
this._arrowActor = actor;
|
||||||
|
this._border.queue_repaint();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_shiftActor : function() {
|
_shiftActor : function() {
|
||||||
// Since the position of the BoxPointer depends on the allocated size
|
// Since the position of the BoxPointer depends on the allocated size
|
||||||
// of the BoxPointer and the position of the source actor, trying
|
// of the BoxPointer and the position of the source actor, trying
|
||||||
// to position the BoxPoiner via the x/y properties will result in
|
// to position the BoxPointer via the x/y properties will result in
|
||||||
// allocation loops and warnings. Instead we do the positioning via
|
// allocation loops and warnings. Instead we do the positioning via
|
||||||
// the anchor point, which is independent of allocation, and leave
|
// the anchor point, which is independent of allocation, and leave
|
||||||
// x == y == 0.
|
// x == y == 0.
|
||||||
@@ -561,37 +584,49 @@ const BoxPointer = new Lang.Class({
|
|||||||
-(this._yPosition + this._yOffset));
|
-(this._yPosition + this._yOffset));
|
||||||
},
|
},
|
||||||
|
|
||||||
_shouldFlip: function() {
|
_calculateArrowSide: function(arrowSide) {
|
||||||
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
|
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
|
||||||
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
|
let [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size();
|
||||||
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
|
|
||||||
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
|
|
||||||
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
||||||
|
|
||||||
switch (this._arrowSide) {
|
switch (arrowSide) {
|
||||||
case St.Side.TOP:
|
case St.Side.TOP:
|
||||||
if (boxAllocation.y2 > monitor.y + monitor.height &&
|
if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height &&
|
||||||
boxHeight < sourceAllocation.y1 - monitor.y)
|
boxHeight < sourceAllocation.y1 - monitor.y)
|
||||||
return true;
|
return St.Side.BOTTOM;
|
||||||
break;
|
break;
|
||||||
case St.Side.BOTTOM:
|
case St.Side.BOTTOM:
|
||||||
if (boxAllocation.y1 < monitor.y &&
|
if (sourceAllocation.y1 - boxHeight < monitor.y &&
|
||||||
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
|
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
|
||||||
return true;
|
return St.Side.TOP;
|
||||||
break;
|
break;
|
||||||
case St.Side.LEFT:
|
case St.Side.LEFT:
|
||||||
if (boxAllocation.x2 > monitor.x + monitor.width &&
|
if (sourceAllocation.x2 + boxWidth > monitor.x + monitor.width &&
|
||||||
boxWidth < sourceAllocation.x1 - monitor.x)
|
boxWidth < sourceAllocation.x1 - monitor.x)
|
||||||
return true;
|
return St.Side.RIGHT;
|
||||||
break;
|
break;
|
||||||
case St.Side.RIGHT:
|
case St.Side.RIGHT:
|
||||||
if (boxAllocation.x1 < monitor.x &&
|
if (sourceAllocation.x1 - boxWidth < monitor.x &&
|
||||||
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
||||||
return true;
|
return St.Side.LEFT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return arrowSide;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateFlip: function() {
|
||||||
|
let arrowSide = this._calculateArrowSide(this._userArrowSide);
|
||||||
|
if (this._arrowSide != arrowSide) {
|
||||||
|
this._arrowSide = arrowSide;
|
||||||
|
this._reposition();
|
||||||
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||||
|
this._container.queue_relayout();
|
||||||
return false;
|
return false;
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.emit('arrow-side-changed');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
set xOffset(offset) {
|
set xOffset(offset) {
|
||||||
@@ -618,5 +653,21 @@ const BoxPointer = new Lang.Class({
|
|||||||
|
|
||||||
get opacity() {
|
get opacity() {
|
||||||
return this.actor.opacity;
|
return this.actor.opacity;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateArrowSide: function(side) {
|
||||||
|
this._arrowSide = side;
|
||||||
|
this._border.queue_repaint();
|
||||||
|
|
||||||
|
this.emit('arrow-side-changed');
|
||||||
|
},
|
||||||
|
|
||||||
|
getPadding: function(side) {
|
||||||
|
return this.bin.get_theme_node().get_padding(side);
|
||||||
|
},
|
||||||
|
|
||||||
|
getArrowHeight: function() {
|
||||||
|
return this.actor.get_theme_node().get_length('-arrow-rise');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(BoxPointer.prototype);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
@@ -16,16 +17,18 @@ const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
|||||||
// in org.gnome.desktop.interface
|
// in org.gnome.desktop.interface
|
||||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||||
|
|
||||||
function _sameDay(dateA, dateB) {
|
|
||||||
return (dateA.getDate() == dateB.getDate() &&
|
|
||||||
dateA.getMonth() == dateB.getMonth() &&
|
|
||||||
dateA.getYear() == dateB.getYear());
|
|
||||||
}
|
|
||||||
|
|
||||||
function _sameYear(dateA, dateB) {
|
function _sameYear(dateA, dateB) {
|
||||||
return (dateA.getYear() == dateB.getYear());
|
return (dateA.getYear() == dateB.getYear());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _sameMonth(dateA, dateB) {
|
||||||
|
return _sameYear(dateA, dateB) && (dateA.getMonth() == dateB.getMonth());
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sameDay(dateA, dateB) {
|
||||||
|
return _sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: maybe needs config - right now we assume that Saturday and
|
/* TODO: maybe needs config - right now we assume that Saturday and
|
||||||
* Sunday are non-work days (not true in e.g. Israel, it's Sunday and
|
* Sunday are non-work days (not true in e.g. Israel, it's Sunday and
|
||||||
* Monday there)
|
* Monday there)
|
||||||
@@ -62,15 +65,18 @@ function _formatEventTime(event, clockFormat) {
|
|||||||
} else {
|
} else {
|
||||||
switch (clockFormat) {
|
switch (clockFormat) {
|
||||||
case '24h':
|
case '24h':
|
||||||
/* Translators: Shown in calendar event list, if 24h format */
|
/* Translators: Shown in calendar event list, if 24h format,
|
||||||
ret = event.date.toLocaleFormat(C_("event list time", "%H:%M"));
|
\u2236 is a ratio character, similar to : */
|
||||||
|
ret = event.date.toLocaleFormat(C_("event list time", "%H\u2236%M"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* explicit fall-through */
|
/* explicit fall-through */
|
||||||
case '12h':
|
case '12h':
|
||||||
/* Transators: Shown in calendar event list, if 12h format */
|
/* Translators: Shown in calendar event list, if 12h format,
|
||||||
ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p"));
|
\u2236 is a ratio character, similar to : and \u2009 is
|
||||||
|
a thin space */
|
||||||
|
ret = event.date.toLocaleFormat(C_("event list time", "%l\u2236%M\u2009%p"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,6 +170,12 @@ const EmptyEventSource = new Lang.Class({
|
|||||||
Name: 'EmptyEventSource',
|
Name: 'EmptyEventSource',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.isDummy = true;
|
||||||
|
this.hasCalendars = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
},
|
},
|
||||||
|
|
||||||
requestRange: function(begin, end) {
|
requestRange: function(begin, end) {
|
||||||
@@ -180,28 +192,27 @@ const EmptyEventSource = new Lang.Class({
|
|||||||
});
|
});
|
||||||
Signals.addSignalMethods(EmptyEventSource.prototype);
|
Signals.addSignalMethods(EmptyEventSource.prototype);
|
||||||
|
|
||||||
const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
|
const CalendarServerIface = '<node> \
|
||||||
<method name="GetEvents">
|
<interface name="org.gnome.Shell.CalendarServer"> \
|
||||||
<arg type="x" direction="in" />
|
<method name="GetEvents"> \
|
||||||
<arg type="x" direction="in" />
|
<arg type="x" direction="in" /> \
|
||||||
<arg type="b" direction="in" />
|
<arg type="x" direction="in" /> \
|
||||||
<arg type="a(sssbxxa{sv})" direction="out" />
|
<arg type="b" direction="in" /> \
|
||||||
</method>
|
<arg type="a(sssbxxa{sv})" direction="out" /> \
|
||||||
<signal name="Changed" />
|
</method> \
|
||||||
</interface>;
|
<property name="HasCalendars" type="b" access="read" /> \
|
||||||
|
<signal name="Changed" /> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
|
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
|
||||||
|
|
||||||
function CalendarServer() {
|
function CalendarServer() {
|
||||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
return new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
||||||
g_interface_name: CalendarServerInfo.name,
|
g_interface_name: CalendarServerInfo.name,
|
||||||
g_interface_info: CalendarServerInfo,
|
g_interface_info: CalendarServerInfo,
|
||||||
g_name: 'org.gnome.Shell.CalendarServer',
|
g_name: 'org.gnome.Shell.CalendarServer',
|
||||||
g_object_path: '/org/gnome/Shell/CalendarServer',
|
g_object_path: '/org/gnome/Shell/CalendarServer' });
|
||||||
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
|
|
||||||
|
|
||||||
self.init(null);
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _datesEqual(a, b) {
|
function _datesEqual(a, b) {
|
||||||
@@ -228,8 +239,19 @@ const DBusEventSource = new Lang.Class({
|
|||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._resetCache();
|
this._resetCache();
|
||||||
|
this.isLoading = false;
|
||||||
|
this.isDummy = false;
|
||||||
|
|
||||||
|
this._initialized = false;
|
||||||
this._dbusProxy = new CalendarServer();
|
this._dbusProxy = new CalendarServer();
|
||||||
|
this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(object, result) {
|
||||||
|
try {
|
||||||
|
this._dbusProxy.init_finish(result);
|
||||||
|
} catch(e) {
|
||||||
|
log('Error loading calendars: ' + e.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
|
this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
|
||||||
|
|
||||||
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
|
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
|
||||||
@@ -238,6 +260,26 @@ const DBusEventSource = new Lang.Class({
|
|||||||
else
|
else
|
||||||
this._onNameVanished();
|
this._onNameVanished();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this._dbusProxy.connect('g-properties-changed', Lang.bind(this, function() {
|
||||||
|
this.emit('notify::has-calendars');
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._initialized = true;
|
||||||
|
this.emit('notify::has-calendars');
|
||||||
|
this._onNameAppeared();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._dbusProxy.run_dispose();
|
||||||
|
},
|
||||||
|
|
||||||
|
get hasCalendars() {
|
||||||
|
if (this._initialized)
|
||||||
|
return this._dbusProxy.HasCalendars;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
_resetCache: function() {
|
_resetCache: function() {
|
||||||
@@ -279,29 +321,32 @@ const DBusEventSource = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._events = newEvents;
|
this._events = newEvents;
|
||||||
|
this.isLoading = false;
|
||||||
this.emit('changed');
|
this.emit('changed');
|
||||||
},
|
},
|
||||||
|
|
||||||
_loadEvents: function(forceReload) {
|
_loadEvents: function(forceReload) {
|
||||||
|
// Ignore while loading
|
||||||
|
if (!this._initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
if (this._curRequestBegin && this._curRequestEnd){
|
if (this._curRequestBegin && this._curRequestEnd){
|
||||||
let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
|
|
||||||
if (forceReload)
|
|
||||||
callFlags = Gio.DBusCallFlags.NONE;
|
|
||||||
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
|
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
|
||||||
this._curRequestEnd.getTime() / 1000,
|
this._curRequestEnd.getTime() / 1000,
|
||||||
forceReload,
|
forceReload,
|
||||||
Lang.bind(this, this._onEventsReceived),
|
Lang.bind(this, this._onEventsReceived),
|
||||||
callFlags);
|
Gio.DBusCallFlags.NONE);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
requestRange: function(begin, end, forceReload) {
|
requestRange: function(begin, end) {
|
||||||
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
||||||
|
this.isLoading = true;
|
||||||
this._lastRequestBegin = begin;
|
this._lastRequestBegin = begin;
|
||||||
this._lastRequestEnd = end;
|
this._lastRequestEnd = end;
|
||||||
this._curRequestBegin = begin;
|
this._curRequestBegin = begin;
|
||||||
this._curRequestEnd = end;
|
this._curRequestEnd = end;
|
||||||
this._loadEvents(forceReload);
|
this._loadEvents(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -371,31 +416,21 @@ const Calendar = new Lang.Class({
|
|||||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
||||||
setEventSource: function(eventSource) {
|
setEventSource: function(eventSource) {
|
||||||
if (this._eventSource) {
|
|
||||||
this._eventSource.disconnect(this._eventSourceChangedId);
|
|
||||||
this._eventSource = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._eventSource = eventSource;
|
this._eventSource = eventSource;
|
||||||
|
this._eventSource.connect('changed', Lang.bind(this, function() {
|
||||||
if (this._eventSource) {
|
this._update();
|
||||||
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() {
|
|
||||||
this._update(false);
|
|
||||||
}));
|
}));
|
||||||
this._update(true);
|
this._update();
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Sets the calendar to show a specific date
|
// Sets the calendar to show a specific date
|
||||||
setDate: function(date, forceReload) {
|
setDate: function(date) {
|
||||||
if (!_sameDay(date, this._selectedDate)) {
|
if (_sameDay(date, this._selectedDate))
|
||||||
|
return;
|
||||||
|
|
||||||
this._selectedDate = date;
|
this._selectedDate = date;
|
||||||
this._update(forceReload);
|
this._update();
|
||||||
this.emit('selected-date-changed', new Date(this._selectedDate));
|
this.emit('selected-date-changed', new Date(this._selectedDate));
|
||||||
} else {
|
|
||||||
if (forceReload)
|
|
||||||
this._update(forceReload);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_buildHeader: function() {
|
_buildHeader: function() {
|
||||||
@@ -407,16 +442,21 @@ const Calendar = new Lang.Class({
|
|||||||
this.actor.add(this._topBox,
|
this.actor.add(this._topBox,
|
||||||
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
||||||
|
|
||||||
let back = new St.Button({ style_class: 'calendar-change-month-back' });
|
this._backButton = new St.Button({ style_class: 'calendar-change-month-back',
|
||||||
this._topBox.add(back);
|
accessible_name: _("Previous month"),
|
||||||
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
|
can_focus: true });
|
||||||
|
this._topBox.add(this._backButton);
|
||||||
|
this._backButton.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
|
||||||
|
|
||||||
this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
|
this._monthLabel = new St.Label({style_class: 'calendar-month-label',
|
||||||
|
can_focus: true });
|
||||||
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
let forward = new St.Button({ style_class: 'calendar-change-month-forward' });
|
this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward',
|
||||||
this._topBox.add(forward);
|
accessible_name: _("Next month"),
|
||||||
forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
|
can_focus: true });
|
||||||
|
this._topBox.add(this._forwardButton);
|
||||||
|
this._forwardButton.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
|
||||||
|
|
||||||
// Add weekday labels...
|
// Add weekday labels...
|
||||||
//
|
//
|
||||||
@@ -454,6 +494,7 @@ const Calendar = new Lang.Class({
|
|||||||
this._onNextMonthButtonClicked();
|
this._onNextMonthButtonClicked();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onPrevMonthButtonClicked: function() {
|
_onPrevMonthButtonClicked: function() {
|
||||||
@@ -475,7 +516,9 @@ const Calendar = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setDate(newDate, false);
|
this._backButton.grab_key_focus();
|
||||||
|
|
||||||
|
this.setDate(newDate);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onNextMonthButtonClicked: function() {
|
_onNextMonthButtonClicked: function() {
|
||||||
@@ -497,57 +540,83 @@ const Calendar = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setDate(newDate, false);
|
this._forwardButton.grab_key_focus();
|
||||||
|
|
||||||
|
this.setDate(newDate);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSettingsChange: function() {
|
_onSettingsChange: function() {
|
||||||
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
||||||
this._buildHeader();
|
this._buildHeader();
|
||||||
this._update(false);
|
this._update();
|
||||||
},
|
},
|
||||||
|
|
||||||
_update: function(forceReload) {
|
_rebuildCalendar: function() {
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
|
|
||||||
if (_sameYear(this._selectedDate, now))
|
|
||||||
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
|
|
||||||
else
|
|
||||||
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
|
|
||||||
|
|
||||||
// Remove everything but the topBox and the weekday labels
|
// Remove everything but the topBox and the weekday labels
|
||||||
let children = this.actor.get_children();
|
let children = this.actor.get_children();
|
||||||
for (let i = this._firstDayIndex; i < children.length; i++)
|
for (let i = this._firstDayIndex; i < children.length; i++)
|
||||||
children[i].destroy();
|
children[i].destroy();
|
||||||
|
|
||||||
|
this._buttons = [];
|
||||||
|
|
||||||
// Start at the beginning of the week before the start of the month
|
// Start at the beginning of the week before the start of the month
|
||||||
|
//
|
||||||
|
// We want to show always 6 weeks (to keep the calendar menu at the same
|
||||||
|
// height if there are no events), so we pad it according to the following
|
||||||
|
// policy:
|
||||||
|
//
|
||||||
|
// 1 - If a month has 6 weeks, we place no padding (example: Dec 2012)
|
||||||
|
// 2 - If a month has 5 weeks and it starts on week start, we pad one week
|
||||||
|
// before it (example: Apr 2012)
|
||||||
|
// 3 - If a month has 5 weeks and it starts on any other day, we pad one week
|
||||||
|
// after it (example: Nov 2012)
|
||||||
|
// 4 - If a month has 4 weeks, we pad one week before and one after it
|
||||||
|
// (example: Feb 2010)
|
||||||
|
//
|
||||||
|
// Actually computing the number of weeks is complex, but we know that the
|
||||||
|
// problematic categories (2 and 4) always start on week start, and that
|
||||||
|
// all months at the end have 6 weeks.
|
||||||
let beginDate = new Date(this._selectedDate);
|
let beginDate = new Date(this._selectedDate);
|
||||||
beginDate.setDate(1);
|
beginDate.setDate(1);
|
||||||
beginDate.setSeconds(0);
|
beginDate.setSeconds(0);
|
||||||
beginDate.setHours(12);
|
beginDate.setHours(12);
|
||||||
|
|
||||||
|
this._calendarBegin = new Date(beginDate);
|
||||||
|
|
||||||
|
let year = beginDate.getYear();
|
||||||
|
|
||||||
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
|
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
|
||||||
beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
|
let startsOnWeekStart = daysToWeekStart == 0;
|
||||||
|
let weekPadding = startsOnWeekStart ? 7 : 0;
|
||||||
|
|
||||||
|
beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
|
||||||
|
|
||||||
let iter = new Date(beginDate);
|
let iter = new Date(beginDate);
|
||||||
let row = 2;
|
let row = 2;
|
||||||
while (true) {
|
// nRows here means 6 weeks + one header + one navbar
|
||||||
let button = new St.Button({ label: iter.getDate().toString() });
|
let nRows = 8;
|
||||||
|
while (row < 8) {
|
||||||
|
let button = new St.Button({ label: iter.getDate().toString(),
|
||||||
|
can_focus: true });
|
||||||
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
||||||
|
|
||||||
if (!this._eventSource)
|
if (this._eventSource.isDummy)
|
||||||
button.reactive = false;
|
button.reactive = false;
|
||||||
|
|
||||||
let iterStr = iter.toUTCString();
|
button._date = new Date(iter);
|
||||||
button.connect('clicked', Lang.bind(this, function() {
|
button.connect('clicked', Lang.bind(this, function() {
|
||||||
let newlySelectedDate = new Date(iterStr);
|
this.setDate(button._date);
|
||||||
this.setDate(newlySelectedDate, false);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
|
let hasEvents = this._eventSource.hasEvents(iter);
|
||||||
let styleClass = 'calendar-day-base calendar-day';
|
let styleClass = 'calendar-day-base calendar-day';
|
||||||
|
|
||||||
if (_isWorkDay(iter))
|
if (_isWorkDay(iter))
|
||||||
styleClass += ' calendar-work-day'
|
styleClass += ' calendar-work-day';
|
||||||
else
|
else
|
||||||
styleClass += ' calendar-nonwork-day'
|
styleClass += ' calendar-nonwork-day';
|
||||||
|
|
||||||
// Hack used in lieu of border-collapse - see gnome-shell.css
|
// Hack used in lieu of border-collapse - see gnome-shell.css
|
||||||
if (row == 2)
|
if (row == 2)
|
||||||
@@ -563,11 +632,8 @@ const Calendar = new Lang.Class({
|
|||||||
else if (iter.getMonth() != this._selectedDate.getMonth())
|
else if (iter.getMonth() != this._selectedDate.getMonth())
|
||||||
styleClass += ' calendar-other-month-day';
|
styleClass += ' calendar-other-month-day';
|
||||||
|
|
||||||
if (_sameDay(this._selectedDate, iter))
|
|
||||||
button.add_style_pseudo_class('active');
|
|
||||||
|
|
||||||
if (hasEvents)
|
if (hasEvents)
|
||||||
styleClass += ' calendar-day-with-events'
|
styleClass += ' calendar-day-with-events';
|
||||||
|
|
||||||
button.style_class = styleClass;
|
button.style_class = styleClass;
|
||||||
|
|
||||||
@@ -575,6 +641,8 @@ const Calendar = new Lang.Class({
|
|||||||
this.actor.add(button,
|
this.actor.add(button,
|
||||||
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
|
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
|
||||||
|
|
||||||
|
this._buttons.push(button);
|
||||||
|
|
||||||
if (this._useWeekdate && iter.getDay() == 4) {
|
if (this._useWeekdate && iter.getDay() == 4) {
|
||||||
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
|
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
|
||||||
style_class: 'calendar-day-base calendar-week-number'});
|
style_class: 'calendar-day-base calendar-week-number'});
|
||||||
@@ -583,17 +651,33 @@ const Calendar = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||||
if (iter.getDay() == this._weekStart) {
|
|
||||||
// We stop on the first "first day of the week" after the month we are displaying
|
if (iter.getDay() == this._weekStart)
|
||||||
if (iter.getMonth() > this._selectedDate.getMonth() || iter.getYear() > this._selectedDate.getYear())
|
|
||||||
break;
|
|
||||||
row++;
|
row++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Signal to the event source that we are interested in events
|
// Signal to the event source that we are interested in events
|
||||||
// only from this date range
|
// only from this date range
|
||||||
if (this._eventSource)
|
this._eventSource.requestRange(beginDate, iter);
|
||||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
},
|
||||||
|
|
||||||
|
_update: function() {
|
||||||
|
let now = new Date();
|
||||||
|
|
||||||
|
if (_sameYear(this._selectedDate, now))
|
||||||
|
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
|
||||||
|
else
|
||||||
|
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
|
||||||
|
|
||||||
|
if (!this._calendarBegin || !_sameMonth(this._selectedDate, this._calendarBegin))
|
||||||
|
this._rebuildCalendar();
|
||||||
|
|
||||||
|
this._buttons.forEach(Lang.bind(this, function(button) {
|
||||||
|
if (_sameDay(button._date, this._selectedDate))
|
||||||
|
button.add_style_pseudo_class('active');
|
||||||
|
else
|
||||||
|
button.remove_style_pseudo_class('active');
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -603,7 +687,7 @@ const EventsList = new Lang.Class({
|
|||||||
Name: 'EventsList',
|
Name: 'EventsList',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
|
this.actor = new St.Table({ style_class: 'events-table' });
|
||||||
this._date = new Date();
|
this._date = new Date();
|
||||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||||
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
||||||
@@ -611,70 +695,76 @@ const EventsList = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setEventSource: function(eventSource) {
|
setEventSource: function(eventSource) {
|
||||||
if (this._eventSource) {
|
|
||||||
this._eventSource.disconnect(this._eventSourceChangedId);
|
|
||||||
this._eventSource = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._eventSource = eventSource;
|
this._eventSource = eventSource;
|
||||||
|
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||||
if (this._eventSource) {
|
|
||||||
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
|
_addEvent: function(event, index, includeDayName) {
|
||||||
if (includeDayName) {
|
let dayString;
|
||||||
dayNameBox.add(new St.Label( { style_class: 'events-day-dayname',
|
if (includeDayName)
|
||||||
text: day } ),
|
dayString = _getEventDayAbbreviation(event.date.getDay());
|
||||||
{ x_fill: true } );
|
else
|
||||||
}
|
dayString = '';
|
||||||
timeBox.add(new St.Label( { style_class: 'events-day-time',
|
|
||||||
text: time} ),
|
let dayLabel = new St.Label({ style_class: 'events-day-dayname',
|
||||||
{ x_fill: true } );
|
text: dayString });
|
||||||
eventTitleBox.add(new St.Label( { style_class: 'events-day-task',
|
dayLabel.clutter_text.line_wrap = false;
|
||||||
text: desc} ));
|
dayLabel.clutter_text.ellipsize = false;
|
||||||
|
|
||||||
|
this.actor.add(dayLabel, { row: index, col: 0,
|
||||||
|
x_expand: false, x_align: St.Align.END,
|
||||||
|
y_fill: false, y_align: St.Align.START });
|
||||||
|
|
||||||
|
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
|
||||||
|
let timeString = _formatEventTime(event, clockFormat);
|
||||||
|
let timeLabel = new St.Label({ style_class: 'events-day-time',
|
||||||
|
text: timeString });
|
||||||
|
timeLabel.clutter_text.line_wrap = false;
|
||||||
|
timeLabel.clutter_text.ellipsize = false;
|
||||||
|
|
||||||
|
this.actor.add(timeLabel, { row: index, col: 1,
|
||||||
|
x_expand: false, x_align: St.Align.MIDDLE,
|
||||||
|
y_fill: false, y_align: St.Align.START });
|
||||||
|
|
||||||
|
let titleLabel = new St.Label({ style_class: 'events-day-task',
|
||||||
|
text: event.summary });
|
||||||
|
titleLabel.clutter_text.line_wrap = true;
|
||||||
|
titleLabel.clutter_text.ellipsize = false;
|
||||||
|
|
||||||
|
this.actor.add(titleLabel, { row: index, col: 2,
|
||||||
|
x_expand: true, x_align: St.Align.START,
|
||||||
|
y_fill: false, y_align: St.Align.START });
|
||||||
},
|
},
|
||||||
|
|
||||||
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
|
_addPeriod: function(header, index, begin, end, includeDayName, showNothingScheduled) {
|
||||||
if (!this._eventSource)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let events = this._eventSource.getEvents(begin, end);
|
let events = this._eventSource.getEvents(begin, end);
|
||||||
|
|
||||||
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
|
|
||||||
|
|
||||||
if (events.length == 0 && !showNothingScheduled)
|
if (events.length == 0 && !showNothingScheduled)
|
||||||
return;
|
return index;
|
||||||
|
|
||||||
let vbox = new St.BoxLayout( {vertical: true} );
|
this.actor.add(new St.Label({ style_class: 'events-day-header', text: header }),
|
||||||
this.actor.add(vbox);
|
{ row: index, col: 0, col_span: 3,
|
||||||
|
// In theory, x_expand should be true here, but x_expand
|
||||||
vbox.add(new St.Label({ style_class: 'events-day-header', text: header }));
|
// is a property of the column for StTable, ie all day cells
|
||||||
let box = new St.BoxLayout({style_class: 'events-header-hbox'});
|
// get it too
|
||||||
let dayNameBox = new St.BoxLayout({ vertical: true, style_class: 'events-day-name-box' });
|
x_expand: false, x_align: St.Align.START,
|
||||||
let timeBox = new St.BoxLayout({ vertical: true, style_class: 'events-time-box' });
|
y_fill: false, y_align: St.Align.START });
|
||||||
let eventTitleBox = new St.BoxLayout({ vertical: true, style_class: 'events-event-box' });
|
index++;
|
||||||
box.add(dayNameBox, {x_fill: false});
|
|
||||||
box.add(timeBox, {x_fill: false});
|
|
||||||
box.add(eventTitleBox, {expand: true});
|
|
||||||
vbox.add(box);
|
|
||||||
|
|
||||||
for (let n = 0; n < events.length; n++) {
|
for (let n = 0; n < events.length; n++) {
|
||||||
let event = events[n];
|
this._addEvent(events[n], index, includeDayName);
|
||||||
let dayString = _getEventDayAbbreviation(event.date.getDay());
|
index++;
|
||||||
let timeString = _formatEventTime(event, clockFormat);
|
|
||||||
let summaryString = event.summary;
|
|
||||||
this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events.length == 0 && showNothingScheduled) {
|
if (events.length == 0 && showNothingScheduled) {
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
/* Translators: Text to show if there are no events */
|
/* Translators: Text to show if there are no events */
|
||||||
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
|
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
|
||||||
let timeString = _formatEventTime(nothingEvent, clockFormat);
|
this._addEvent(nothingEvent, index, false);
|
||||||
this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
},
|
},
|
||||||
|
|
||||||
_showOtherDay: function(day) {
|
_showOtherDay: function(day) {
|
||||||
@@ -691,20 +781,21 @@ const EventsList = new Lang.Class({
|
|||||||
else
|
else
|
||||||
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
||||||
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
|
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
|
||||||
this._addPeriod(dayString, dayBegin, dayEnd, false, true);
|
this._addPeriod(dayString, 0, dayBegin, dayEnd, false, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
_showToday: function() {
|
_showToday: function() {
|
||||||
this.actor.destroy_all_children();
|
this.actor.destroy_all_children();
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
let dayBegin = _getBeginningOfDay(now);
|
let dayBegin = _getBeginningOfDay(now);
|
||||||
let dayEnd = _getEndOfDay(now);
|
let dayEnd = _getEndOfDay(now);
|
||||||
this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
|
index = this._addPeriod(_("Today"), index, dayBegin, dayEnd, false, true);
|
||||||
|
|
||||||
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
|
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
|
||||||
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
|
||||||
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
|
index = this._addPeriod(_("Tomorrow"), index, tomorrowBegin, tomorrowEnd, false, true);
|
||||||
|
|
||||||
let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
|
let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
|
||||||
|
|
||||||
@@ -715,7 +806,7 @@ const EventsList = new Lang.Class({
|
|||||||
*/
|
*/
|
||||||
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||||
let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
|
let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
|
||||||
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
|
index = this._addPeriod(_("This week"), index, thisWeekBegin, thisWeekEnd, true, false);
|
||||||
} else {
|
} else {
|
||||||
/* otherwise it's one of the two last days of the week ... show
|
/* otherwise it's one of the two last days of the week ... show
|
||||||
* "Next week" and include events up until and including *next*
|
* "Next week" and include events up until and including *next*
|
||||||
@@ -723,7 +814,7 @@ const EventsList = new Lang.Class({
|
|||||||
*/
|
*/
|
||||||
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
|
||||||
let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
|
let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
|
||||||
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
|
index = this._addPeriod(_("Next week"), index, nextWeekBegin, nextWeekEnd, true, false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -736,6 +827,9 @@ const EventsList = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_update: function() {
|
_update: function() {
|
||||||
|
if (this._eventSource.isLoading)
|
||||||
|
return;
|
||||||
|
|
||||||
let today = new Date();
|
let today = new Date();
|
||||||
if (_sameDay (this._date, today)) {
|
if (_sameDay (this._date, today)) {
|
||||||
this._showToday();
|
this._showToday();
|
||||||
|
|||||||
@@ -1,115 +1,40 @@
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
|
|
||||||
const CheckBoxContainer = new Lang.Class({
|
|
||||||
Name: 'CheckBoxContainer',
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
this.actor = new Shell.GenericContainer();
|
|
||||||
this.actor.connect('get-preferred-width',
|
|
||||||
Lang.bind(this, this._getPreferredWidth));
|
|
||||||
this.actor.connect('get-preferred-height',
|
|
||||||
Lang.bind(this, this._getPreferredHeight));
|
|
||||||
this.actor.connect('allocate',
|
|
||||||
Lang.bind(this, this._allocate));
|
|
||||||
this.actor.connect('style-changed', Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
let node = this.actor.get_theme_node();
|
|
||||||
this._spacing = node.get_length('spacing');
|
|
||||||
}));
|
|
||||||
this.actor.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
|
|
||||||
|
|
||||||
this._box = new St.Bin();
|
|
||||||
this.actor.add_actor(this._box);
|
|
||||||
|
|
||||||
this.label = new St.Label();
|
|
||||||
this.label.clutter_text.set_line_wrap(true);
|
|
||||||
this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
|
|
||||||
this.actor.add_actor(this.label);
|
|
||||||
|
|
||||||
this._spacing = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
|
||||||
let [minWidth, natWidth] = this._box.get_preferred_width(forHeight);
|
|
||||||
|
|
||||||
alloc.min_size = minWidth + this._spacing;
|
|
||||||
alloc.natural_size = natWidth + this._spacing;
|
|
||||||
},
|
|
||||||
|
|
||||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
|
||||||
/* FIXME: StBoxlayout currently does not handle
|
|
||||||
height-for-width children correctly, so hard-code
|
|
||||||
two lines for the label until that problem is fixed.
|
|
||||||
|
|
||||||
https://bugzilla.gnome.org/show_bug.cgi?id=672543 */
|
|
||||||
/*
|
|
||||||
let [minBoxHeight, natBoxHeight] =
|
|
||||||
this._box.get_preferred_height(forWidth);
|
|
||||||
let [minLabelHeight, natLabelHeight] =
|
|
||||||
this.label.get_preferred_height(forWidth);
|
|
||||||
|
|
||||||
alloc.min_size = Math.max(minBoxHeight, minLabelHeight);
|
|
||||||
alloc.natural_size = Math.max(natBoxHeight, natLabelHeight);
|
|
||||||
*/
|
|
||||||
let [minBoxHeight, natBoxHeight] =
|
|
||||||
this._box.get_preferred_height(-1);
|
|
||||||
let [minLabelHeight, natLabelHeight] =
|
|
||||||
this.label.get_preferred_height(-1);
|
|
||||||
|
|
||||||
alloc.min_size = Math.max(minBoxHeight, 2 * minLabelHeight);
|
|
||||||
alloc.natural_size = Math.max(natBoxHeight, 2 * natLabelHeight);
|
|
||||||
},
|
|
||||||
|
|
||||||
_allocate: function(actor, box, flags) {
|
|
||||||
let availWidth = box.x2 - box.x1;
|
|
||||||
let availHeight = box.y2 - box.y1;
|
|
||||||
|
|
||||||
let childBox = new Clutter.ActorBox();
|
|
||||||
let [minBoxWidth, natBoxWidth] =
|
|
||||||
this._box.get_preferred_width(-1);
|
|
||||||
let [minBoxHeight, natBoxHeight] =
|
|
||||||
this._box.get_preferred_height(-1);
|
|
||||||
childBox.x1 = box.x1;
|
|
||||||
childBox.x2 = box.x1 + natBoxWidth;
|
|
||||||
childBox.y1 = box.y1;
|
|
||||||
childBox.y2 = box.y1 + natBoxHeight;
|
|
||||||
this._box.allocate(childBox, flags);
|
|
||||||
|
|
||||||
childBox.x1 = box.x1 + natBoxWidth + this._spacing;
|
|
||||||
childBox.x2 = availWidth - childBox.x1;
|
|
||||||
childBox.y1 = box.y1;
|
|
||||||
childBox.y2 = box.y2;
|
|
||||||
this.label.allocate(childBox, flags);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const CheckBox = new Lang.Class({
|
const CheckBox = new Lang.Class({
|
||||||
Name: 'CheckBox',
|
Name: 'CheckBox',
|
||||||
|
|
||||||
_init: function(label) {
|
_init: function(label) {
|
||||||
|
let container = new St.BoxLayout();
|
||||||
this.actor = new St.Button({ style_class: 'check-box',
|
this.actor = new St.Button({ style_class: 'check-box',
|
||||||
|
child: container,
|
||||||
button_mask: St.ButtonMask.ONE,
|
button_mask: St.ButtonMask.ONE,
|
||||||
toggle_mode: true,
|
toggle_mode: true,
|
||||||
can_focus: true,
|
can_focus: true,
|
||||||
x_fill: true,
|
x_fill: true,
|
||||||
y_fill: true });
|
y_fill: true });
|
||||||
this._container = new CheckBoxContainer();
|
|
||||||
this.actor.set_child(this._container.actor);
|
this._box = new St.Bin();
|
||||||
|
this._box.set_y_align(Clutter.ActorAlign.START);
|
||||||
|
container.add_actor(this._box);
|
||||||
|
|
||||||
|
this._label = new St.Label();
|
||||||
|
this._label.clutter_text.set_line_wrap(true);
|
||||||
|
this._label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
|
||||||
|
container.add_actor(this._label);
|
||||||
|
|
||||||
if (label)
|
if (label)
|
||||||
this.setLabel(label);
|
this.setLabel(label);
|
||||||
},
|
},
|
||||||
|
|
||||||
setLabel: function(label) {
|
setLabel: function(label) {
|
||||||
this._container.label.set_text(label);
|
this._label.set_text(label);
|
||||||
},
|
},
|
||||||
|
|
||||||
getLabelActor: function() {
|
getLabelActor: function() {
|
||||||
return this._container.label;
|
return this._label;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ const Params = imports.misc.params;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
const GnomeSession = imports.misc.gnomeSession;
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
const LoginManager = imports.misc.loginManager;
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
|
|
||||||
@@ -33,7 +32,6 @@ const AutomountManager = new Lang.Class({
|
|||||||
Lang.bind(this, this._InhibitorsChanged));
|
Lang.bind(this, this._InhibitorsChanged));
|
||||||
this._inhibited = false;
|
this._inhibited = false;
|
||||||
|
|
||||||
this._loginManager = LoginManager.getLoginManager();
|
|
||||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -79,31 +77,35 @@ const AutomountManager = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
this._mountAllId = 0;
|
this._mountAllId = 0;
|
||||||
return false;
|
return GLib.SOURCE_REMOVE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDriveConnected: function() {
|
_onDriveConnected: function() {
|
||||||
// if we're not in the current ConsoleKit session,
|
// if we're not in the current ConsoleKit session,
|
||||||
// or screensaver is active, don't play sounds
|
// or screensaver is active, don't play sounds
|
||||||
if (!this._loginManager.sessionActive)
|
if (!this._session.SessionIsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
global.play_theme_sound(0, 'device-added-media');
|
global.play_theme_sound(0, 'device-added-media',
|
||||||
|
_("External drive connected"),
|
||||||
|
null);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDriveDisconnected: function() {
|
_onDriveDisconnected: function() {
|
||||||
// if we're not in the current ConsoleKit session,
|
// if we're not in the current ConsoleKit session,
|
||||||
// or screensaver is active, don't play sounds
|
// or screensaver is active, don't play sounds
|
||||||
if (!this._loginManager.sessionActive)
|
if (!this._session.SessionIsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
global.play_theme_sound(0, 'device-removed-media');
|
global.play_theme_sound(0, 'device-removed-media',
|
||||||
|
_("External drive disconnected"),
|
||||||
|
null);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDriveEjectButton: function(monitor, drive) {
|
_onDriveEjectButton: function(monitor, drive) {
|
||||||
// TODO: this code path is not tested, as the GVfs volume monitor
|
// TODO: this code path is not tested, as the GVfs volume monitor
|
||||||
// doesn't emit this signal just yet.
|
// doesn't emit this signal just yet.
|
||||||
if (!this._loginManager.sessionActive)
|
if (!this._session.SessionIsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// we force stop/eject in this case, so we don't have to pass a
|
// we force stop/eject in this case, so we don't have to pass a
|
||||||
@@ -143,7 +145,7 @@ const AutomountManager = new Lang.Class({
|
|||||||
if (params.checkSession) {
|
if (params.checkSession) {
|
||||||
// if we're not in the current ConsoleKit session,
|
// if we're not in the current ConsoleKit session,
|
||||||
// don't attempt automount
|
// don't attempt automount
|
||||||
if (!this._loginManager.sessionActive)
|
if (!this._session.SessionIsActive)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +236,7 @@ const AutomountManager = new Lang.Class({
|
|||||||
_allowAutorunExpire: function(volume) {
|
_allowAutorunExpire: function(volume) {
|
||||||
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
|
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
|
||||||
volume.allowAutorun = false;
|
volume.allowAutorun = false;
|
||||||
return false;
|
return GLib.SOURCE_REMOVE;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const Lang = imports.lang;
|
|||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const LoginManager = imports.misc.loginManager;
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
@@ -31,7 +31,7 @@ function shouldAutorunMount(mount, forTransient) {
|
|||||||
if (!volume || (!volume.allowAutorun && forTransient))
|
if (!volume || (!volume.allowAutorun && forTransient))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!root.is_native() || isMountRootHidden(root))
|
if (root.is_native() && isMountRootHidden(root))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -64,7 +64,7 @@ function startAppForMount(app, mount) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
retval = app.launch(files,
|
retval = app.launch(files,
|
||||||
global.create_app_launch_context())
|
global.create_app_launch_context(0, -1))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('Unable to launch the application ' + app.get_name()
|
log('Unable to launch the application ' + app.get_name()
|
||||||
+ ': ' + e.toString());
|
+ ': ' + e.toString());
|
||||||
@@ -75,12 +75,14 @@ function startAppForMount(app, mount) {
|
|||||||
|
|
||||||
/******************************************/
|
/******************************************/
|
||||||
|
|
||||||
const HotplugSnifferIface = <interface name="org.gnome.Shell.HotplugSniffer">
|
const HotplugSnifferIface = '<node> \
|
||||||
<method name="SniffURI">
|
<interface name="org.gnome.Shell.HotplugSniffer"> \
|
||||||
<arg type="s" direction="in" />
|
<method name="SniffURI"> \
|
||||||
<arg type="as" direction="out" />
|
<arg type="s" direction="in" /> \
|
||||||
</method>
|
<arg type="as" direction="out" /> \
|
||||||
</interface>;
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
|
const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
|
||||||
function HotplugSniffer() {
|
function HotplugSniffer() {
|
||||||
@@ -162,8 +164,7 @@ const AutorunManager = new Lang.Class({
|
|||||||
Name: 'AutorunManager',
|
Name: 'AutorunManager',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._loginManager = LoginManager.getLoginManager();
|
this._session = new GnomeSession.SessionManager();
|
||||||
|
|
||||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||||
|
|
||||||
this._transDispatcher = new AutorunTransientDispatcher(this);
|
this._transDispatcher = new AutorunTransientDispatcher(this);
|
||||||
@@ -215,7 +216,7 @@ const AutorunManager = new Lang.Class({
|
|||||||
_onMountAdded: function(monitor, mount) {
|
_onMountAdded: function(monitor, mount) {
|
||||||
// don't do anything if our session is not the currently
|
// don't do anything if our session is not the currently
|
||||||
// active one
|
// active one
|
||||||
if (!this._loginManager.sessionActive)
|
if (!this._session.SessionIsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._processMount(mount, true);
|
this._processMount(mount, true);
|
||||||
@@ -293,7 +294,7 @@ const AutorunResidentSource = new Lang.Class({
|
|||||||
|
|
||||||
_init: function(manager) {
|
_init: function(manager) {
|
||||||
this.parent(_("Removable Devices"), 'media-removable');
|
this.parent(_("Removable Devices"), 'media-removable');
|
||||||
this.showInLockScreen = false;
|
this.resident = true;
|
||||||
|
|
||||||
this._mounts = [];
|
this._mounts = [];
|
||||||
|
|
||||||
@@ -301,6 +302,10 @@ const AutorunResidentSource = new Lang.Class({
|
|||||||
this._notification = new AutorunResidentNotification(this._manager, this);
|
this._notification = new AutorunResidentNotification(this._manager, this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_createPolicy: function() {
|
||||||
|
return new MessageTray.NotificationPolicy({ showInLockScreen: false });
|
||||||
|
},
|
||||||
|
|
||||||
buildRightClickMenu: function() {
|
buildRightClickMenu: function() {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ const ModalDialog = imports.ui.modalDialog;
|
|||||||
const ShellEntry = imports.ui.shellEntry;
|
const ShellEntry = imports.ui.shellEntry;
|
||||||
const CheckBox = imports.ui.checkBox;
|
const CheckBox = imports.ui.checkBox;
|
||||||
|
|
||||||
let prompter = null;
|
|
||||||
|
|
||||||
const KeyringDialog = new Lang.Class({
|
const KeyringDialog = new Lang.Class({
|
||||||
Name: 'KeyringDialog',
|
Name: 'KeyringDialog',
|
||||||
Extends: ModalDialog.ModalDialog,
|
Extends: ModalDialog.ModalDialog,
|
||||||
@@ -25,7 +23,7 @@ const KeyringDialog = new Lang.Class({
|
|||||||
this.prompt = new Shell.KeyringPrompt();
|
this.prompt = new Shell.KeyringPrompt();
|
||||||
this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
|
this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
|
||||||
this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
|
this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
|
||||||
this.prompt.connect('hide-prompt', Lang.bind(this, this._onHidePrompt));
|
this.prompt.connect('prompt-close', Lang.bind(this, this._onHidePrompt));
|
||||||
|
|
||||||
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||||
vertical: false });
|
vertical: false });
|
||||||
@@ -47,7 +45,9 @@ const KeyringDialog = new Lang.Class({
|
|||||||
this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
|
this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||||
|
|
||||||
this._messageBox.add(subject,
|
this._messageBox.add(subject,
|
||||||
{ y_fill: false,
|
{ x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
|
|
||||||
let description = new St.Label({ style_class: 'prompt-dialog-description' });
|
let description = new St.Label({ style_class: 'prompt-dialog-description' });
|
||||||
@@ -60,41 +60,46 @@ const KeyringDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._controlTable = null;
|
this._controlTable = null;
|
||||||
|
|
||||||
let buttons = [{ label: '',
|
|
||||||
action: Lang.bind(this, this._onCancelButton),
|
|
||||||
key: Clutter.Escape
|
|
||||||
},
|
|
||||||
{ label: '',
|
|
||||||
action: Lang.bind(this, this._onContinueButton),
|
|
||||||
default: true
|
|
||||||
}]
|
|
||||||
|
|
||||||
this.setButtons(buttons);
|
this._cancelButton = this.addButton({ label: '',
|
||||||
this._cancelButton = buttons[0].button;
|
action: Lang.bind(this, this._onCancelButton),
|
||||||
this._continueButton = buttons[1].button;
|
key: Clutter.Escape },
|
||||||
|
{ expand: true, x_fill: false, x_align: St.Align.START });
|
||||||
|
this.placeSpinner({ expand: false,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
this._continueButton = this.addButton({ label: '',
|
||||||
|
action: Lang.bind(this, this._onContinueButton),
|
||||||
|
default: true },
|
||||||
|
{ expand: false, x_fill: false, x_align: St.Align.END });
|
||||||
|
|
||||||
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||||
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||||
},
|
},
|
||||||
|
|
||||||
_buildControlTable: function() {
|
_buildControlTable: function() {
|
||||||
let table = new St.Table({ style_class: 'keyring-dialog-control-table' });
|
let layout = new Clutter.TableLayout();
|
||||||
|
let table = new St.Widget({ style_class: 'keyring-dialog-control-table',
|
||||||
|
layout_manager: layout });
|
||||||
|
layout.hookup_style(table);
|
||||||
let row = 0;
|
let row = 0;
|
||||||
|
|
||||||
if (this.prompt.password_visible) {
|
if (this.prompt.password_visible) {
|
||||||
let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
let label = new St.Label({ style_class: 'prompt-dialog-password-label' });
|
||||||
label.set_text(_("Password:"));
|
label.set_text(_("Password:"));
|
||||||
table.add(label, { row: row, col: 0,
|
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
x_expand: false, x_fill: true,
|
layout.pack(label, 0, row);
|
||||||
x_align: St.Align.START,
|
layout.child_set(label, { x_expand: false, y_fill: false,
|
||||||
y_fill: false, y_align: St.Align.MIDDLE });
|
x_align: Clutter.TableAlignment.START });
|
||||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||||
text: '',
|
text: '',
|
||||||
can_focus: true });
|
can_focus: true });
|
||||||
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate));
|
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate));
|
||||||
table.add(this._passwordEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
|
layout.pack(this._passwordEntry, 1, row);
|
||||||
row++;
|
row++;
|
||||||
} else {
|
} else {
|
||||||
this._passwordEntry = null;
|
this._passwordEntry = null;
|
||||||
@@ -103,17 +108,16 @@ const KeyringDialog = new Lang.Class({
|
|||||||
if (this.prompt.confirm_visible) {
|
if (this.prompt.confirm_visible) {
|
||||||
var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
|
||||||
label.set_text(_("Type again:"));
|
label.set_text(_("Type again:"));
|
||||||
table.add(label, { row: row, col: 0,
|
layout.pack(label, 0, row);
|
||||||
x_expand: false, x_fill: true,
|
layout.child_set(label, { x_expand: false, y_fill: false,
|
||||||
x_align: St.Align.START,
|
x_align: Clutter.TableAlignment.START });
|
||||||
y_fill: false, y_align: St.Align.MIDDLE });
|
|
||||||
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||||
text: '',
|
text: '',
|
||||||
can_focus: true });
|
can_focus: true });
|
||||||
this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||||
ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
|
ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
|
||||||
this._confirmEntry.clutter_text.connect('activate', Lang.bind(this, this._onConfirmActivate));
|
this._confirmEntry.clutter_text.connect('activate', Lang.bind(this, this._onConfirmActivate));
|
||||||
table.add(this._confirmEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
|
layout.pack(this._confirmEntry, 1, row);
|
||||||
row++;
|
row++;
|
||||||
} else {
|
} else {
|
||||||
this._confirmEntry = null;
|
this._confirmEntry = null;
|
||||||
@@ -126,14 +130,15 @@ const KeyringDialog = new Lang.Class({
|
|||||||
let choice = new CheckBox.CheckBox();
|
let choice = new CheckBox.CheckBox();
|
||||||
this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
|
this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||||
this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
|
this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
|
||||||
table.add(choice.actor, { row: row, col: 1, x_expand: false, x_fill: true, x_align: St.Align.START });
|
layout.pack(choice.actor, 1, row);
|
||||||
row++;
|
row++;
|
||||||
}
|
}
|
||||||
|
|
||||||
let warning = new St.Label({ style_class: 'prompt-dialog-error-label' });
|
let warning = new St.Label({ style_class: 'prompt-dialog-error-label' });
|
||||||
warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
warning.clutter_text.line_wrap = true;
|
warning.clutter_text.line_wrap = true;
|
||||||
table.add(warning, { row: row, col: 1, x_expand: false, x_fill: false, x_align: St.Align.START });
|
layout.pack(warning, 1, row);
|
||||||
|
layout.child_set(warning, { x_fill: false, x_align: Clutter.TableAlignment.START });
|
||||||
this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||||
this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
|
this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||||
|
|
||||||
@@ -146,6 +151,22 @@ const KeyringDialog = new Lang.Class({
|
|||||||
this._messageBox.add(table, { x_fill: true, y_fill: true });
|
this._messageBox.add(table, { x_fill: true, y_fill: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateSensitivity: function(sensitive) {
|
||||||
|
if (this._passwordEntry) {
|
||||||
|
this._passwordEntry.reactive = sensitive;
|
||||||
|
this._passwordEntry.clutter_text.editable = sensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._confirmEntry) {
|
||||||
|
this._confirmEntry.reactive = sensitive;
|
||||||
|
this._confirmEntry.clutter_text.editable = sensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._continueButton.can_focus = sensitive;
|
||||||
|
this._continueButton.reactive = sensitive;
|
||||||
|
this.setWorking(!sensitive);
|
||||||
|
},
|
||||||
|
|
||||||
_ensureOpen: function() {
|
_ensureOpen: function() {
|
||||||
// NOTE: ModalDialog.open() is safe to call if the dialog is
|
// NOTE: ModalDialog.open() is safe to call if the dialog is
|
||||||
// already open - it just returns true without side-effects
|
// already open - it just returns true without side-effects
|
||||||
@@ -167,12 +188,14 @@ const KeyringDialog = new Lang.Class({
|
|||||||
_onShowPassword: function(prompt) {
|
_onShowPassword: function(prompt) {
|
||||||
this._buildControlTable();
|
this._buildControlTable();
|
||||||
this._ensureOpen();
|
this._ensureOpen();
|
||||||
|
this._updateSensitivity(true);
|
||||||
this._passwordEntry.grab_key_focus();
|
this._passwordEntry.grab_key_focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onShowConfirm: function(prompt) {
|
_onShowConfirm: function(prompt) {
|
||||||
this._buildControlTable();
|
this._buildControlTable();
|
||||||
this._ensureOpen();
|
this._ensureOpen();
|
||||||
|
this._updateSensitivity(true);
|
||||||
this._continueButton.grab_key_focus();
|
this._continueButton.grab_key_focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -192,6 +215,7 @@ const KeyringDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onContinueButton: function() {
|
_onContinueButton: function() {
|
||||||
|
this._updateSensitivity(false);
|
||||||
this.prompt.complete();
|
this.prompt.complete();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -200,27 +224,56 @@ const KeyringDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const KeyringDummyDialog = new Lang.Class({
|
||||||
|
Name: 'KeyringDummyDialog',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.prompt = new Shell.KeyringPrompt();
|
||||||
|
this.prompt.connect('show-password',
|
||||||
|
Lang.bind(this, this._cancelPrompt));
|
||||||
|
this.prompt.connect('show-confirm', Lang.bind(this,
|
||||||
|
this._cancelPrompt));
|
||||||
|
},
|
||||||
|
|
||||||
|
_cancelPrompt: function() {
|
||||||
|
this.prompt.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const KeyringPrompter = new Lang.Class({
|
const KeyringPrompter = new Lang.Class({
|
||||||
Name: 'KeyringPrompter',
|
Name: 'KeyringPrompter',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._prompter = new Gcr.SystemPrompter();
|
this._prompter = new Gcr.SystemPrompter();
|
||||||
this._prompter.connect('new-prompt', function(prompter) {
|
this._prompter.connect('new-prompt', Lang.bind(this,
|
||||||
let dialog = new KeyringDialog();
|
function() {
|
||||||
return dialog.prompt;
|
let dialog = this._enabled ? new KeyringDialog()
|
||||||
});
|
: new KeyringDummyDialog();
|
||||||
|
this._currentPrompt = dialog.prompt;
|
||||||
|
return this._currentPrompt;
|
||||||
|
}));
|
||||||
this._dbusId = null;
|
this._dbusId = null;
|
||||||
|
this._registered = false;
|
||||||
|
this._enabled = false;
|
||||||
|
this._currentPrompt = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
enable: function() {
|
enable: function() {
|
||||||
|
if (!this._registered) {
|
||||||
this._prompter.register(Gio.DBus.session);
|
this._prompter.register(Gio.DBus.session);
|
||||||
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
|
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
|
||||||
Gio.BusNameOwnerFlags.REPLACE, null, null);
|
Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
|
||||||
|
this._registered = true;
|
||||||
|
}
|
||||||
|
this._enabled = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
disable: function() {
|
disable: function() {
|
||||||
this._prompter.unregister(false);
|
this._enabled = false;
|
||||||
Gio.DBus.session.unown_name(this._dbusId);
|
|
||||||
|
if (this._prompter.prompting)
|
||||||
|
this._currentPrompt.cancel();
|
||||||
|
this._currentPrompt = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -62,14 +62,9 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
|
|
||||||
if (this._content.message != null) {
|
if (this._content.message != null) {
|
||||||
let descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
|
let descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
|
||||||
text: this._content.message,
|
text: this._content.message });
|
||||||
// HACK: for reasons unknown to me, the label
|
|
||||||
// is not asked the correct height for width,
|
|
||||||
// and thus is underallocated
|
|
||||||
// place a fixed height to avoid overflowing
|
|
||||||
style: 'height: 3em'
|
|
||||||
});
|
|
||||||
descriptionLabel.clutter_text.line_wrap = true;
|
descriptionLabel.clutter_text.line_wrap = true;
|
||||||
|
descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
|
|
||||||
messageBox.add(descriptionLabel,
|
messageBox.add(descriptionLabel,
|
||||||
{ y_fill: true,
|
{ y_fill: true,
|
||||||
@@ -77,13 +72,18 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
expand: true });
|
expand: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
let secretTable = new St.Table({ style_class: 'network-dialog-secret-table' });
|
let layout = new Clutter.TableLayout();
|
||||||
|
let secretTable = new St.Widget({ style_class: 'network-dialog-secret-table',
|
||||||
|
layout_manager: layout });
|
||||||
|
layout.hookup_style(secretTable);
|
||||||
|
|
||||||
let initialFocusSet = false;
|
let initialFocusSet = false;
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
for (let i = 0; i < this._content.secrets.length; i++) {
|
for (let i = 0; i < this._content.secrets.length; i++) {
|
||||||
let secret = this._content.secrets[i];
|
let secret = this._content.secrets[i];
|
||||||
let label = new St.Label({ style_class: 'prompt-dialog-password-label',
|
let label = new St.Label({ style_class: 'prompt-dialog-password-label',
|
||||||
text: secret.label });
|
text: secret.label });
|
||||||
|
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
|
|
||||||
let reactive = secret.key != null;
|
let reactive = secret.key != null;
|
||||||
|
|
||||||
@@ -116,11 +116,10 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
} else
|
} else
|
||||||
secret.valid = true;
|
secret.valid = true;
|
||||||
|
|
||||||
secretTable.add(label, { row: pos, col: 0,
|
layout.pack(label, 0, pos);
|
||||||
x_expand: false, x_fill: true,
|
layout.child_set(label, { x_expand: false, y_fill: false,
|
||||||
x_align: St.Align.START,
|
x_align: Clutter.TableAlignment.START });
|
||||||
y_fill: false, y_align: St.Align.MIDDLE });
|
layout.pack(secret.entry, 1, pos);
|
||||||
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
|
|
||||||
pos++;
|
pos++;
|
||||||
|
|
||||||
if (secret.password)
|
if (secret.password)
|
||||||
@@ -139,6 +138,8 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
key: Clutter.KEY_Escape,
|
key: Clutter.KEY_Escape,
|
||||||
},
|
},
|
||||||
this._okButton]);
|
this._okButton]);
|
||||||
|
|
||||||
|
this._updateOkButton();
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateOkButton: function() {
|
_updateOkButton: function() {
|
||||||
@@ -254,6 +255,7 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
case 'leap':
|
case 'leap':
|
||||||
case 'ttls':
|
case 'ttls':
|
||||||
case 'peap':
|
case 'peap':
|
||||||
|
case 'fast':
|
||||||
// TTLS and PEAP are actually much more complicated, but this complication
|
// TTLS and PEAP are actually much more complicated, but this complication
|
||||||
// is not visible here since we only care about phase2 authentication
|
// is not visible here since we only care about phase2 authentication
|
||||||
// (and don't even care of which one)
|
// (and don't even care of which one)
|
||||||
@@ -307,7 +309,7 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
wirelessSetting = this._connection.get_setting_wireless();
|
wirelessSetting = this._connection.get_setting_wireless();
|
||||||
ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
|
ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
|
||||||
content.title = _("Authentication required by wireless network");
|
content.title = _("Authentication required by wireless network");
|
||||||
content.message = _("Passwords or encryption keys are required to access the wireless network '%s'.").format(ssid);
|
content.message = _("Passwords or encryption keys are required to access the wireless network “%s”.").format(ssid);
|
||||||
this._getWirelessSecrets(content.secrets, wirelessSetting);
|
this._getWirelessSecrets(content.secrets, wirelessSetting);
|
||||||
break;
|
break;
|
||||||
case '802-3-ethernet':
|
case '802-3-ethernet':
|
||||||
@@ -334,7 +336,7 @@ const NetworkSecretDialog = new Lang.Class({
|
|||||||
case 'cdma':
|
case 'cdma':
|
||||||
case 'bluetooth':
|
case 'bluetooth':
|
||||||
content.title = _("Mobile broadband network password");
|
content.title = _("Mobile broadband network password");
|
||||||
content.message = _("A password is required to connect to '%s'.").format(connectionSetting.get_id());
|
content.message = _("A password is required to connect to “%s”.").format(connectionSetting.get_id());
|
||||||
this._getMobileSecrets(content.secrets, connectionType);
|
this._getMobileSecrets(content.secrets, connectionType);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -385,11 +387,7 @@ const VPNRequestHandler = new Lang.Class({
|
|||||||
this._childPid = pid;
|
this._childPid = pid;
|
||||||
this._stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true });
|
this._stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true });
|
||||||
this._stdout = new Gio.UnixInputStream({ fd: stdout, close_fd: true });
|
this._stdout = new Gio.UnixInputStream({ fd: stdout, close_fd: true });
|
||||||
// We need this one too, even if don't actually care of what the process
|
GLib.close(stderr);
|
||||||
// has to say on stderr, because otherwise the fd opened by g_spawn_async_with_pipes
|
|
||||||
// is kept open indefinitely
|
|
||||||
let stderrStream = new Gio.UnixInputStream({ fd: stderr, close_fd: true });
|
|
||||||
stderrStream.close(null);
|
|
||||||
this._dataStdout = new Gio.DataInputStream({ base_stream: this._stdout });
|
this._dataStdout = new Gio.DataInputStream({ base_stream: this._stdout });
|
||||||
|
|
||||||
if (this._newStylePlugin)
|
if (this._newStylePlugin)
|
||||||
@@ -437,6 +435,7 @@ const VPNRequestHandler = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_vpnChildFinished: function(pid, status, requestObj) {
|
_vpnChildFinished: function(pid, status, requestObj) {
|
||||||
|
this._childWatch = 0;
|
||||||
if (this._newStylePlugin) {
|
if (this._newStylePlugin) {
|
||||||
// For new style plugin, all work is done in the async reading functions
|
// For new style plugin, all work is done in the async reading functions
|
||||||
// Just reap the process here
|
// Just reap the process here
|
||||||
@@ -587,18 +586,19 @@ const NetworkAgent = new Lang.Class({
|
|||||||
Name: 'NetworkAgent',
|
Name: 'NetworkAgent',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._native = new Shell.NetworkAgent({ auto_register: false,
|
this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent' });
|
||||||
identifier: 'org.gnome.Shell.NetworkAgent' });
|
|
||||||
|
|
||||||
this._dialogs = { };
|
this._dialogs = { };
|
||||||
this._vpnRequests = { };
|
this._vpnRequests = { };
|
||||||
|
|
||||||
this._native.connect('new-request', Lang.bind(this, this._newRequest));
|
this._native.connect('new-request', Lang.bind(this, this._newRequest));
|
||||||
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||||
|
|
||||||
|
this._enabled = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
enable: function() {
|
enable: function() {
|
||||||
this._native.auto_register = true;
|
this._enabled = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
disable: function() {
|
disable: function() {
|
||||||
@@ -612,12 +612,15 @@ const NetworkAgent = new Lang.Class({
|
|||||||
this._vpnRequests[requestId].cancel(true);
|
this._vpnRequests[requestId].cancel(true);
|
||||||
this._vpnRequests = { };
|
this._vpnRequests = { };
|
||||||
|
|
||||||
this._native.auto_register = false;
|
this._enabled = false;
|
||||||
if (this._native.registered)
|
|
||||||
this._native.unregister();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
|
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
|
||||||
|
if (!this._enabled) {
|
||||||
|
agent.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (settingName == 'vpn') {
|
if (settingName == 'vpn') {
|
||||||
this._vpnRequest(requestId, connection, hints, flags);
|
this._vpnRequest(requestId, connection, hints, flags);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const PolkitAgent = imports.gi.PolkitAgent;
|
|||||||
const Components = imports.ui.components;
|
const Components = imports.ui.components;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
const ShellEntry = imports.ui.shellEntry;
|
const ShellEntry = imports.ui.shellEntry;
|
||||||
const UserMenu = imports.ui.userMenu;
|
const UserWidget = imports.ui.userWidget;
|
||||||
|
|
||||||
const DIALOG_ICON_SIZE = 48;
|
const DIALOG_ICON_SIZE = 48;
|
||||||
|
|
||||||
@@ -31,7 +31,6 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
this.userNames = userNames;
|
this.userNames = userNames;
|
||||||
this._wasDismissed = false;
|
this._wasDismissed = false;
|
||||||
this._completed = false;
|
|
||||||
|
|
||||||
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
|
||||||
vertical: false });
|
vertical: false });
|
||||||
@@ -55,7 +54,9 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
text: _("Authentication Required") });
|
text: _("Authentication Required") });
|
||||||
|
|
||||||
messageBox.add(this._subjectLabel,
|
messageBox.add(this._subjectLabel,
|
||||||
{ y_fill: false,
|
{ x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
|
|
||||||
this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
|
this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
|
||||||
@@ -64,7 +65,9 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
this._descriptionLabel.clutter_text.line_wrap = true;
|
this._descriptionLabel.clutter_text.line_wrap = true;
|
||||||
|
|
||||||
messageBox.add(this._descriptionLabel,
|
messageBox.add(this._descriptionLabel,
|
||||||
{ y_fill: true,
|
{ x_fill: false,
|
||||||
|
y_fill: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
|
|
||||||
if (userNames.length > 1) {
|
if (userNames.length > 1) {
|
||||||
@@ -96,12 +99,13 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
if (userIsRoot) {
|
if (userIsRoot) {
|
||||||
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label',
|
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label',
|
||||||
text: userRealName }));
|
text: userRealName }));
|
||||||
messageBox.add(userLabel);
|
messageBox.add(userLabel, { x_fill: false,
|
||||||
|
x_align: St.Align.START });
|
||||||
} else {
|
} else {
|
||||||
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
|
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
|
||||||
vertical: false });
|
vertical: false });
|
||||||
messageBox.add(userBox);
|
messageBox.add(userBox);
|
||||||
this._userAvatar = new UserMenu.UserAvatarWidget(this._user,
|
this._userAvatar = new UserWidget.Avatar(this._user,
|
||||||
{ iconSize: DIALOG_ICON_SIZE,
|
{ iconSize: DIALOG_ICON_SIZE,
|
||||||
styleClass: 'polkit-dialog-user-icon' });
|
styleClass: 'polkit-dialog-user-icon' });
|
||||||
this._userAvatar.actor.hide();
|
this._userAvatar.actor.hide();
|
||||||
@@ -138,7 +142,7 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' });
|
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' });
|
||||||
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
this._errorMessageLabel.clutter_text.line_wrap = true;
|
this._errorMessageLabel.clutter_text.line_wrap = true;
|
||||||
messageBox.add(this._errorMessageLabel);
|
messageBox.add(this._errorMessageLabel, { x_fill: false, x_align: St.Align.START });
|
||||||
this._errorMessageLabel.hide();
|
this._errorMessageLabel.hide();
|
||||||
|
|
||||||
this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' });
|
this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' });
|
||||||
@@ -159,29 +163,34 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
messageBox.add(this._nullMessageLabel);
|
messageBox.add(this._nullMessageLabel);
|
||||||
this._nullMessageLabel.show();
|
this._nullMessageLabel.show();
|
||||||
|
|
||||||
this.setButtons([{ label: _("Cancel"),
|
this._cancelButton = this.addButton({ label: _("Cancel"),
|
||||||
action: Lang.bind(this, this.cancel),
|
action: Lang.bind(this, this.cancel),
|
||||||
key: Clutter.Escape
|
key: Clutter.Escape },
|
||||||
},
|
{ expand: true, x_fill: false, x_align: St.Align.START });
|
||||||
{ label: _("Authenticate"),
|
this.placeSpinner({ expand: false,
|
||||||
|
x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.END,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
this._okButton = this.addButton({ label: _("Authenticate"),
|
||||||
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||||
default: true
|
default: true },
|
||||||
}]);
|
{ expand: false, x_fill: false, x_align: St.Align.END });
|
||||||
|
|
||||||
this._doneEmitted = false;
|
this._doneEmitted = false;
|
||||||
|
|
||||||
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
|
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
|
||||||
this._cookie = cookie;
|
this._cookie = cookie;
|
||||||
|
},
|
||||||
|
|
||||||
|
performAuthentication: function() {
|
||||||
|
this.destroySession();
|
||||||
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
|
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
|
||||||
cookie: this._cookie });
|
cookie: this._cookie });
|
||||||
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
|
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
|
||||||
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
|
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
|
||||||
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
|
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
|
||||||
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
|
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
|
||||||
},
|
|
||||||
|
|
||||||
startAuthentication: function() {
|
|
||||||
this._session.initiate();
|
this._session.initiate();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -203,19 +212,29 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
log('polkitAuthenticationAgent: Failed to show modal dialog.' +
|
log('polkitAuthenticationAgent: Failed to show modal dialog.' +
|
||||||
' Dismissing authentication request for action-id ' + this.actionId +
|
' Dismissing authentication request for action-id ' + this.actionId +
|
||||||
' cookie ' + this._cookie);
|
' cookie ' + this._cookie);
|
||||||
this._emitDone(false, true);
|
this._emitDone(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_emitDone: function(keepVisible, dismissed) {
|
_emitDone: function(dismissed) {
|
||||||
if (!this._doneEmitted) {
|
if (!this._doneEmitted) {
|
||||||
this._doneEmitted = true;
|
this._doneEmitted = true;
|
||||||
this.emit('done', keepVisible, dismissed);
|
this.emit('done', dismissed);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateSensitivity: function(sensitive) {
|
||||||
|
this._passwordEntry.reactive = sensitive;
|
||||||
|
this._passwordEntry.clutter_text.editable = sensitive;
|
||||||
|
|
||||||
|
this._okButton.can_focus = sensitive;
|
||||||
|
this._okButton.reactive = sensitive;
|
||||||
|
this.setWorking(!sensitive);
|
||||||
|
},
|
||||||
|
|
||||||
_onEntryActivate: function() {
|
_onEntryActivate: function() {
|
||||||
let response = this._passwordEntry.get_text();
|
let response = this._passwordEntry.get_text();
|
||||||
|
this._updateSensitivity(false);
|
||||||
this._session.response(response);
|
this._session.response(response);
|
||||||
// When the user responds, dismiss already shown info and
|
// When the user responds, dismiss already shown info and
|
||||||
// error texts (if any)
|
// error texts (if any)
|
||||||
@@ -229,12 +248,16 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onSessionCompleted: function(session, gainedAuthorization) {
|
_onSessionCompleted: function(session, gainedAuthorization) {
|
||||||
if (this._completed)
|
if (this._completed || this._doneEmitted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._completed = true;
|
this._completed = true;
|
||||||
|
|
||||||
if (!gainedAuthorization) {
|
/* Yay, all done */
|
||||||
|
if (gainedAuthorization) {
|
||||||
|
this._emitDone(false);
|
||||||
|
|
||||||
|
} else {
|
||||||
/* Unless we are showing an existing error message from the PAM
|
/* Unless we are showing an existing error message from the PAM
|
||||||
* module (the PAM module could be reporting the authentication
|
* module (the PAM module could be reporting the authentication
|
||||||
* error providing authentication-method specific information),
|
* error providing authentication-method specific information),
|
||||||
@@ -250,8 +273,10 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
this._infoMessageLabel.hide();
|
this._infoMessageLabel.hide();
|
||||||
this._nullMessageLabel.hide();
|
this._nullMessageLabel.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Try and authenticate again */
|
||||||
|
this.performAuthentication();
|
||||||
}
|
}
|
||||||
this._emitDone(!gainedAuthorization, false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSessionRequest: function(session, request, echo_on) {
|
_onSessionRequest: function(session, request, echo_on) {
|
||||||
@@ -269,6 +294,7 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
this._passwordBox.show();
|
this._passwordBox.show();
|
||||||
this._passwordEntry.set_text('');
|
this._passwordEntry.set_text('');
|
||||||
this._passwordEntry.grab_key_focus();
|
this._passwordEntry.grab_key_focus();
|
||||||
|
this._updateSensitivity(true);
|
||||||
this._ensureOpen();
|
this._ensureOpen();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -294,6 +320,7 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
if (this._session) {
|
if (this._session) {
|
||||||
if (!this._completed)
|
if (!this._completed)
|
||||||
this._session.cancel();
|
this._session.cancel();
|
||||||
|
this._completed = false;
|
||||||
this._session = null;
|
this._session = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -308,7 +335,7 @@ const AuthenticationDialog = new Lang.Class({
|
|||||||
cancel: function() {
|
cancel: function() {
|
||||||
this._wasDismissed = true;
|
this._wasDismissed = true;
|
||||||
this.close(global.get_current_time());
|
this.close(global.get_current_time());
|
||||||
this._emitDone(false, true);
|
this._emitDone(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(AuthenticationDialog.prototype);
|
Signals.addSignalMethods(AuthenticationDialog.prototype);
|
||||||
@@ -318,7 +345,6 @@ const AuthenticationAgent = new Lang.Class({
|
|||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._currentDialog = null;
|
this._currentDialog = null;
|
||||||
this._isCompleting = false;
|
|
||||||
this._handle = null;
|
this._handle = null;
|
||||||
this._native = new Shell.PolkitAuthenticationAgent();
|
this._native = new Shell.PolkitAuthenticationAgent();
|
||||||
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
|
||||||
@@ -326,11 +352,19 @@ const AuthenticationAgent = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
enable: function() {
|
enable: function() {
|
||||||
|
try {
|
||||||
this._native.register();
|
this._native.register();
|
||||||
|
} catch(e) {
|
||||||
|
log('Failed to register AuthenticationAgent');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
disable: function() {
|
disable: function() {
|
||||||
|
try {
|
||||||
this._native.unregister();
|
this._native.unregister();
|
||||||
|
} catch(e) {
|
||||||
|
log('Failed to unregister AuthenticationAgent');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
||||||
@@ -347,45 +381,24 @@ const AuthenticationAgent = new Lang.Class({
|
|||||||
// discussion.
|
// discussion.
|
||||||
|
|
||||||
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
|
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
|
||||||
this._currentDialog.startAuthentication();
|
this._currentDialog.performAuthentication();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCancel: function(nativeAgent) {
|
_onCancel: function(nativeAgent) {
|
||||||
this._completeRequest(false, false);
|
this._completeRequest(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDialogDone: function(dialog, keepVisible, dismissed) {
|
_onDialogDone: function(dialog, dismissed) {
|
||||||
this._completeRequest(keepVisible, dismissed);
|
this._completeRequest(dismissed);
|
||||||
},
|
},
|
||||||
|
|
||||||
_reallyCompleteRequest: function(dismissed) {
|
_completeRequest: function(dismissed) {
|
||||||
this._currentDialog.close();
|
this._currentDialog.close();
|
||||||
this._currentDialog.destroySession();
|
this._currentDialog.destroySession();
|
||||||
this._currentDialog = null;
|
this._currentDialog = null;
|
||||||
this._isCompleting = false;
|
|
||||||
|
|
||||||
this._native.complete(dismissed)
|
this._native.complete(dismissed);
|
||||||
},
|
},
|
||||||
|
|
||||||
_completeRequest: function(keepVisible, wasDismissed) {
|
|
||||||
if (this._isCompleting)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._isCompleting = true;
|
|
||||||
|
|
||||||
if (keepVisible) {
|
|
||||||
// Give the user 2 seconds to read 'Authentication Failure' before
|
|
||||||
// dismissing the dialog
|
|
||||||
Mainloop.timeout_add(2000,
|
|
||||||
Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
this._reallyCompleteRequest(wasDismissed);
|
|
||||||
return false;
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
this._reallyCompleteRequest(wasDismissed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const Component = AuthenticationAgent;
|
const Component = AuthenticationAgent;
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
|
|
||||||
const Lang = imports.lang;
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
|
|
||||||
const Gio = imports.gi.Gio;
|
|
||||||
const Meta = imports.gi.Meta;
|
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
|
|
||||||
const Recorder = new Lang.Class({
|
|
||||||
Name: 'Recorder',
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
|
||||||
this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
|
|
||||||
this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
|
|
||||||
this._recorder = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
enable: function() {
|
|
||||||
Main.wm.addKeybinding('toggle-recording',
|
|
||||||
this._bindingSettings,
|
|
||||||
Meta.KeyBindingFlags.NONE,
|
|
||||||
Main.KeybindingMode.NORMAL |
|
|
||||||
Main.KeybindingMode.OVERVIEW,
|
|
||||||
Lang.bind(this, this._toggleRecorder));
|
|
||||||
},
|
|
||||||
|
|
||||||
disable: function() {
|
|
||||||
Main.wm.removeKeybinding('toggle-recording');
|
|
||||||
},
|
|
||||||
|
|
||||||
_ensureRecorder: function() {
|
|
||||||
if (this._recorder == null)
|
|
||||||
this._recorder = new Shell.Recorder({ stage: global.stage });
|
|
||||||
return this._recorder;
|
|
||||||
},
|
|
||||||
|
|
||||||
_toggleRecorder: function() {
|
|
||||||
let recorder = this._ensureRecorder();
|
|
||||||
if (recorder.is_recording()) {
|
|
||||||
recorder.close();
|
|
||||||
Meta.enable_unredirect_for_screen(global.screen);
|
|
||||||
} else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
|
|
||||||
// read the parameters from GSettings always in case they have changed
|
|
||||||
recorder.set_framerate(this._recorderSettings.get_int('framerate'));
|
|
||||||
/* Translators: this is a filename used for screencast recording */
|
|
||||||
// xgettext:no-c-format
|
|
||||||
recorder.set_file_template(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension'));
|
|
||||||
let pipeline = this._recorderSettings.get_string('pipeline');
|
|
||||||
|
|
||||||
if (!pipeline.match(/^\s*$/))
|
|
||||||
recorder.set_pipeline(pipeline);
|
|
||||||
else
|
|
||||||
recorder.set_pipeline(null);
|
|
||||||
|
|
||||||
Meta.disable_unredirect_for_screen(global.screen);
|
|
||||||
recorder.record();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const Component = Recorder;
|
|
||||||
@@ -17,7 +17,7 @@ const Params = imports.misc.params;
|
|||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
// See Notification.appendMessage
|
// See Notification.appendMessage
|
||||||
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
|
const SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes
|
||||||
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
|
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
|
||||||
const SCROLLBACK_RECENT_LENGTH = 20;
|
const SCROLLBACK_RECENT_LENGTH = 20;
|
||||||
const SCROLLBACK_IDLE_LENGTH = 5;
|
const SCROLLBACK_IDLE_LENGTH = 5;
|
||||||
@@ -415,6 +415,8 @@ const TelepathyClient = new Lang.Class({
|
|||||||
_ensureAppSource: function() {
|
_ensureAppSource: function() {
|
||||||
if (this._appSource == null) {
|
if (this._appSource == null) {
|
||||||
this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
|
this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
|
||||||
|
this._appSource.policy = new MessageTray.NotificationApplicationPolicy('empathy');
|
||||||
|
|
||||||
Main.messageTray.add(this._appSource);
|
Main.messageTray.add(this._appSource);
|
||||||
this._appSource.connect('destroy', Lang.bind(this, function () {
|
this._appSource.connect('destroy', Lang.bind(this, function () {
|
||||||
this._appSource = null;
|
this._appSource = null;
|
||||||
@@ -444,6 +446,7 @@ const ChatSource = new Lang.Class({
|
|||||||
this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed));
|
this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed));
|
||||||
|
|
||||||
this._notification = new ChatNotification(this);
|
this._notification = new ChatNotification(this);
|
||||||
|
this._notification.connect('clicked', Lang.bind(this, this.open));
|
||||||
this._notification.setUrgency(MessageTray.Urgency.HIGH);
|
this._notification.setUrgency(MessageTray.Urgency.HIGH);
|
||||||
this._notifyTimeoutId = 0;
|
this._notifyTimeoutId = 0;
|
||||||
|
|
||||||
@@ -484,6 +487,10 @@ const ChatSource = new Lang.Class({
|
|||||||
return rightClickMenu;
|
return rightClickMenu;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_createPolicy: function() {
|
||||||
|
return new MessageTray.NotificationApplicationPolicy('empathy');
|
||||||
|
},
|
||||||
|
|
||||||
_updateAlias: function() {
|
_updateAlias: function() {
|
||||||
let oldAlias = this.title;
|
let oldAlias = this.title;
|
||||||
let newAlias = this._contact.get_alias();
|
let newAlias = this._contact.get_alias();
|
||||||
@@ -538,14 +545,13 @@ const ChatSource = new Lang.Class({
|
|||||||
this._notification.update(this._notification.title, null, { customContent: true });
|
this._notification.update(this._notification.title, null, { customContent: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function(notification) {
|
open: function() {
|
||||||
if (this._client.is_handling_channel(this._channel)) {
|
if (this._client.is_handling_channel(this._channel)) {
|
||||||
// We are handling the channel, try to pass it to Empathy
|
// We are handling the channel, try to pass it to Empathy
|
||||||
this._client.delegate_channels_async([this._channel],
|
this._client.delegate_channels_async([this._channel],
|
||||||
global.get_current_time(),
|
global.get_current_time(),
|
||||||
'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
|
'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// We are not the handler, just ask to present the channel
|
// We are not the handler, just ask to present the channel
|
||||||
let dbus = Tp.DBusDaemon.dup();
|
let dbus = Tp.DBusDaemon.dup();
|
||||||
let cd = Tp.ChannelDispatcher.new(dbus);
|
let cd = Tp.ChannelDispatcher.new(dbus);
|
||||||
@@ -615,7 +621,11 @@ const ChatSource = new Lang.Class({
|
|||||||
this.notify();
|
this.notify();
|
||||||
},
|
},
|
||||||
|
|
||||||
_channelClosed: function() {
|
destroy: function(reason) {
|
||||||
|
if (this._destroyed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._destroyed = true;
|
||||||
this._channel.disconnect(this._closedId);
|
this._channel.disconnect(this._closedId);
|
||||||
this._channel.disconnect(this._receivedId);
|
this._channel.disconnect(this._receivedId);
|
||||||
this._channel.disconnect(this._pendingId);
|
this._channel.disconnect(this._pendingId);
|
||||||
@@ -625,7 +635,14 @@ const ChatSource = new Lang.Class({
|
|||||||
this._contact.disconnect(this._notifyAvatarId);
|
this._contact.disconnect(this._notifyAvatarId);
|
||||||
this._contact.disconnect(this._presenceChangedId);
|
this._contact.disconnect(this._presenceChangedId);
|
||||||
|
|
||||||
this.destroy();
|
if (this._timestampTimeoutId)
|
||||||
|
Mainloop.source_remove(this._timestampTimeoutId);
|
||||||
|
|
||||||
|
this.parent(reason);
|
||||||
|
},
|
||||||
|
|
||||||
|
_channelClosed: function() {
|
||||||
|
this.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
|
||||||
},
|
},
|
||||||
|
|
||||||
/* All messages are new messages for Telepathy sources */
|
/* All messages are new messages for Telepathy sources */
|
||||||
@@ -633,6 +650,10 @@ const ChatSource = new Lang.Class({
|
|||||||
return this._pendingMessages.length;
|
return this._pendingMessages.length;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get indicatorCount() {
|
||||||
|
return this.count;
|
||||||
|
},
|
||||||
|
|
||||||
get unseenCount() {
|
get unseenCount() {
|
||||||
return this.count;
|
return this.count;
|
||||||
},
|
},
|
||||||
@@ -665,7 +686,7 @@ const ChatSource = new Lang.Class({
|
|||||||
|
|
||||||
this._notifyTimeoutId = 0;
|
this._notifyTimeoutId = 0;
|
||||||
|
|
||||||
return false;
|
return GLib.SOURCE_REMOVE;
|
||||||
},
|
},
|
||||||
|
|
||||||
// This is called for both messages we send from
|
// This is called for both messages we send from
|
||||||
@@ -950,19 +971,22 @@ const ChatNotification = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
appendTimestamp: function() {
|
appendTimestamp: function() {
|
||||||
|
this._timestampTimeoutId = 0;
|
||||||
|
|
||||||
let lastMessageTime = this._history[0].time;
|
let lastMessageTime = this._history[0].time;
|
||||||
let lastMessageDate = new Date(lastMessageTime * 1000);
|
let lastMessageDate = new Date(lastMessageTime * 1000);
|
||||||
|
|
||||||
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
|
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
|
||||||
group: 'meta',
|
group: 'meta',
|
||||||
styles: ['chat-meta-message'],
|
styles: ['chat-meta-message'],
|
||||||
childProps: { expand: true, x_fill: false },
|
childProps: { expand: true, x_fill: false,
|
||||||
|
x_align: St.Align.END },
|
||||||
noTimestamp: true,
|
noTimestamp: true,
|
||||||
timestamp: lastMessageTime });
|
timestamp: lastMessageTime });
|
||||||
|
|
||||||
this._filterMessages();
|
this._filterMessages();
|
||||||
|
|
||||||
return false;
|
return GLib.SOURCE_REMOVE;
|
||||||
},
|
},
|
||||||
|
|
||||||
appendAliasChange: function(oldAlias, newAlias) {
|
appendAliasChange: function(oldAlias, newAlias) {
|
||||||
@@ -1000,7 +1024,7 @@ const ChatNotification = new Lang.Class({
|
|||||||
|
|
||||||
this.source.setChatState(Tp.ChannelChatState.PAUSED);
|
this.source.setChatState(Tp.ChannelChatState.PAUSED);
|
||||||
|
|
||||||
return false;
|
return GLib.SOURCE_REMOVE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onEntryChanged: function() {
|
_onEntryChanged: function() {
|
||||||
@@ -1048,6 +1072,10 @@ const ApproverSource = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_createPolicy: function() {
|
||||||
|
return new MessageTray.NotificationApplicationPolicy('empathy');
|
||||||
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
if (this._invalidId != 0) {
|
if (this._invalidId != 0) {
|
||||||
this._dispatchOp.disconnect(this._invalidId);
|
this._dispatchOp.disconnect(this._invalidId);
|
||||||
@@ -1080,22 +1108,16 @@ const RoomInviteNotification = new Lang.Class({
|
|||||||
* for example. */
|
* for example. */
|
||||||
this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier()));
|
this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier()));
|
||||||
|
|
||||||
this.addButton('decline', _("Decline"));
|
this.addAction(_("Decline"), Lang.bind(this, function() {
|
||||||
this.addButton('accept', _("Accept"));
|
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
|
||||||
|
src.leave_channels_finish(result);
|
||||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
});
|
||||||
switch (action) {
|
this.destroy();
|
||||||
case 'decline':
|
}));
|
||||||
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
|
this.addAction(_("Accept"), Lang.bind(this, function() {
|
||||||
'', function(src, result) {
|
dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
|
||||||
src.leave_channels_finish(result)});
|
src.handle_with_time_finish(result);
|
||||||
break;
|
});
|
||||||
case 'accept':
|
|
||||||
dispatchOp.handle_with_time_async('', global.get_current_time(),
|
|
||||||
function(src, result) {
|
|
||||||
src.handle_with_time_finish(result)});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.destroy();
|
this.destroy();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -1119,23 +1141,19 @@ const AudioVideoNotification = new Lang.Class({
|
|||||||
this.parent(source, title, null, { customContent: true });
|
this.parent(source, title, null, { customContent: true });
|
||||||
this.setResident(true);
|
this.setResident(true);
|
||||||
|
|
||||||
this.addButton('reject', _("Decline"));
|
this.setUrgency(MessageTray.Urgency.CRITICAL);
|
||||||
/* translators: this is a button label (verb), not a noun */
|
|
||||||
this.addButton('answer', _("Answer"));
|
|
||||||
|
|
||||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
this.addAction(_("Decline"), Lang.bind(this, function() {
|
||||||
switch (action) {
|
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
|
||||||
case 'reject':
|
src.leave_channels_finish(result);
|
||||||
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
|
});
|
||||||
'', function(src, result) {
|
this.destroy();
|
||||||
src.leave_channels_finish(result)});
|
}));
|
||||||
break;
|
/* translators: this is a button label (verb), not a noun */
|
||||||
case 'answer':
|
this.addAction(_("Answer"), Lang.bind(this, function() {
|
||||||
dispatchOp.handle_with_time_async('', global.get_current_time(),
|
dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
|
||||||
function(src, result) {
|
src.handle_with_time_finish(result);
|
||||||
src.handle_with_time_finish(result)});
|
});
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.destroy();
|
this.destroy();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -1159,22 +1177,16 @@ const FileTransferNotification = new Lang.Class({
|
|||||||
{ customContent: true });
|
{ customContent: true });
|
||||||
this.setResident(true);
|
this.setResident(true);
|
||||||
|
|
||||||
this.addButton('decline', _("Decline"));
|
this.addAction(_("Decline"), Lang.bind(this, function() {
|
||||||
this.addButton('accept', _("Accept"));
|
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
|
||||||
|
src.leave_channels_finish(result);
|
||||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
});
|
||||||
switch (action) {
|
this.destroy();
|
||||||
case 'decline':
|
}));
|
||||||
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
|
this.addAction(_("Accept"), Lang.bind(this, function() {
|
||||||
'', function(src, result) {
|
dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
|
||||||
src.leave_channels_finish(result)});
|
src.handle_with_time_finish(result);
|
||||||
break;
|
});
|
||||||
case 'accept':
|
|
||||||
dispatchOp.handle_with_time_async('', global.get_current_time(),
|
|
||||||
function(src, result) {
|
|
||||||
src.handle_with_time_finish(result)});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.destroy();
|
this.destroy();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -1222,27 +1234,20 @@ const SubscriptionRequestNotification = new Lang.Class({
|
|||||||
|
|
||||||
this.addActor(layout);
|
this.addActor(layout);
|
||||||
|
|
||||||
this.addButton('decline', _("Decline"));
|
this.addAction(_("Decline"), Lang.bind(this, function() {
|
||||||
this.addButton('accept', _("Accept"));
|
|
||||||
|
|
||||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
|
||||||
switch (action) {
|
|
||||||
case 'decline':
|
|
||||||
contact.remove_async(function(src, result) {
|
contact.remove_async(function(src, result) {
|
||||||
src.remove_finish(result)});
|
src.remove_finish(result);
|
||||||
break;
|
});
|
||||||
case 'accept':
|
}));
|
||||||
|
this.addAction(_("Accept"), Lang.bind(this, function() {
|
||||||
// Authorize the contact and request to see his status as well
|
// Authorize the contact and request to see his status as well
|
||||||
contact.authorize_publication_async(function(src, result) {
|
contact.authorize_publication_async(function(src, result) {
|
||||||
src.authorize_publication_finish(result)});
|
src.authorize_publication_finish(result);
|
||||||
|
});
|
||||||
|
|
||||||
contact.request_subscription_async('', function(src, result) {
|
contact.request_subscription_async('', function(src, result) {
|
||||||
src.request_subscription_finish(result)});
|
src.request_subscription_finish(result);
|
||||||
break;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// rely on _subscriptionStatesChangedCb to destroy the
|
|
||||||
// notification
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._changedId = contact.connect('subscription-states-changed',
|
this._changedId = contact.connect('subscription-states-changed',
|
||||||
@@ -1341,19 +1346,11 @@ const AccountNotification = new Lang.Class({
|
|||||||
|
|
||||||
this._account = account;
|
this._account = account;
|
||||||
|
|
||||||
this.addButton('view', _("View account"));
|
this.addAction(_("View account"), Lang.bind(this, function() {
|
||||||
|
let cmd = 'empathy-accounts --select-account=' +
|
||||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
account.get_path_suffix();
|
||||||
switch (action) {
|
|
||||||
case 'view':
|
|
||||||
let cmd = '/usr/bin/empathy-accounts'
|
|
||||||
+ ' --select-account=%s'
|
|
||||||
.format(account.get_path_suffix());
|
|
||||||
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
|
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
|
||||||
app_info.launch([], global.create_app_launch_context());
|
app_info.launch([], global.create_app_launch_context(0, -1));
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.destroy();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._enabledId = account.connect('notify::enabled',
|
this._enabledId = account.connect('notify::enabled',
|
||||||
|
|||||||
@@ -58,15 +58,10 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
focusGroup: function(item, timestamp) {
|
focusGroup: function(item, timestamp) {
|
||||||
if (item.focusCallback) {
|
if (item.focusCallback)
|
||||||
item.focusCallback(timestamp);
|
item.focusCallback(timestamp);
|
||||||
} else {
|
else
|
||||||
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
|
|
||||||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
|
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
|
||||||
|
|
||||||
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Sort the items into a consistent order; panel first, tray last,
|
// Sort the items into a consistent order; panel first, tray last,
|
||||||
@@ -89,21 +84,33 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
let items = this._items.filter(function (item) { return item.proxy.mapped; });
|
let items = this._items.filter(function (item) { return item.proxy.mapped; });
|
||||||
|
|
||||||
// And add the windows metacity would show in its Ctrl-Alt-Tab list
|
// And add the windows metacity would show in its Ctrl-Alt-Tab list
|
||||||
if (!Main.overview.visible) {
|
if (Main.sessionMode.hasWindows && !Main.overview.visible) {
|
||||||
let screen = global.screen;
|
let screen = global.screen;
|
||||||
let display = screen.get_display();
|
let display = screen.get_display();
|
||||||
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
|
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
|
||||||
let windowTracker = Shell.WindowTracker.get_default();
|
let windowTracker = Shell.WindowTracker.get_default();
|
||||||
let textureCache = St.TextureCache.get_default();
|
let textureCache = St.TextureCache.get_default();
|
||||||
for (let i = 0; i < windows.length; i++) {
|
for (let i = 0; i < windows.length; i++) {
|
||||||
let icon;
|
let icon = null;
|
||||||
|
let iconName = null;
|
||||||
|
if (windows[i].get_window_type () == Meta.WindowType.DESKTOP) {
|
||||||
|
iconName = 'video-display-symbolic';
|
||||||
|
} else {
|
||||||
let app = windowTracker.get_window_app(windows[i]);
|
let app = windowTracker.get_window_app(windows[i]);
|
||||||
if (app)
|
if (app)
|
||||||
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
|
||||||
else
|
else
|
||||||
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
||||||
|
}
|
||||||
|
|
||||||
items.push({ name: windows[i].title,
|
items.push({ name: windows[i].title,
|
||||||
|
proxy: windows[i].get_compositor_private(),
|
||||||
|
focusCallback: Lang.bind(windows[i],
|
||||||
|
function(timestamp) {
|
||||||
|
Main.activateWindow(this, timestamp);
|
||||||
|
}),
|
||||||
iconActor: icon,
|
iconActor: icon,
|
||||||
|
iconName: iconName,
|
||||||
sortGroup: SortGroup.MIDDLE });
|
sortGroup: SortGroup.MIDDLE });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,8 +132,6 @@ const CtrlAltTabManager = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_focusWindows: function(timestamp) {
|
_focusWindows: function(timestamp) {
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
|
||||||
global.stage.key_focus = null;
|
|
||||||
global.screen.focus_default_window(timestamp);
|
global.screen.focus_default_window(timestamp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
284
js/ui/dash.js
284
js/ui/dash.js
@@ -1,6 +1,7 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
@@ -22,11 +23,8 @@ const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
|
|||||||
const DASH_ITEM_HOVER_TIMEOUT = 300;
|
const DASH_ITEM_HOVER_TIMEOUT = 300;
|
||||||
|
|
||||||
function getAppFromSource(source) {
|
function getAppFromSource(source) {
|
||||||
if (source instanceof AppDisplay.AppWellIcon) {
|
if (source instanceof AppDisplay.AppIcon) {
|
||||||
return source.app;
|
return source.app;
|
||||||
} else if (source.metaWindow) {
|
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
|
||||||
return tracker.get_window_app(source.metaWindow);
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -36,30 +34,26 @@ function getAppFromSource(source) {
|
|||||||
// when requesting a size
|
// when requesting a size
|
||||||
const DashItemContainer = new Lang.Class({
|
const DashItemContainer = new Lang.Class({
|
||||||
Name: 'DashItemContainer',
|
Name: 'DashItemContainer',
|
||||||
|
Extends: St.Widget,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.actor = new Shell.GenericContainer({ style_class: 'dash-item-container' });
|
this.parent({ style_class: 'dash-item-container' });
|
||||||
this.actor.connect('get-preferred-width',
|
|
||||||
Lang.bind(this, this._getPreferredWidth));
|
|
||||||
this.actor.connect('get-preferred-height',
|
|
||||||
Lang.bind(this, this._getPreferredHeight));
|
|
||||||
this.actor.connect('allocate',
|
|
||||||
Lang.bind(this, this._allocate));
|
|
||||||
this.actor._delegate = this;
|
|
||||||
|
|
||||||
this._labelText = "";
|
this._labelText = "";
|
||||||
this.label = new St.Label({ style_class: 'dash-label'});
|
this.label = new St.Label({ style_class: 'dash-label'});
|
||||||
this.label.hide();
|
this.label.hide();
|
||||||
Main.layoutManager.addChrome(this.label);
|
Main.layoutManager.addChrome(this.label);
|
||||||
this.actor.label_actor = this.label;
|
this.label_actor = this.label;
|
||||||
|
|
||||||
this.child = null;
|
this.child = null;
|
||||||
this._childScale = 1;
|
this._childScale = 0;
|
||||||
this._childOpacity = 255;
|
this._childOpacity = 0;
|
||||||
this.animatingOut = false;
|
this.animatingOut = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
_allocate: function(actor, box, flags) {
|
vfunc_allocate: function(box, flags) {
|
||||||
|
this.set_allocation(box, flags);
|
||||||
|
|
||||||
if (this.child == null)
|
if (this.child == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -81,28 +75,28 @@ const DashItemContainer = new Lang.Class({
|
|||||||
this.child.allocate(childBox, flags);
|
this.child.allocate(childBox, flags);
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
vfunc_get_preferred_height: function(forWidth) {
|
||||||
alloc.min_size = 0;
|
let themeNode = this.get_theme_node();
|
||||||
alloc.natural_size = 0;
|
|
||||||
|
|
||||||
if (this.child == null)
|
if (this.child == null)
|
||||||
return;
|
return [0, 0];
|
||||||
|
|
||||||
|
forWidth = themeNode.adjust_for_width(forWidth);
|
||||||
let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
|
let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
|
||||||
alloc.min_size += minHeight * this.child.scale_y;
|
return themeNode.adjust_preferred_height(minHeight * this.child.scale_y,
|
||||||
alloc.natural_size += natHeight * this.child.scale_y;
|
natHeight * this.child.scale_y);
|
||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
vfunc_get_preferred_width: function(forHeight) {
|
||||||
alloc.min_size = 0;
|
let themeNode = this.get_theme_node();
|
||||||
alloc.natural_size = 0;
|
|
||||||
|
|
||||||
if (this.child == null)
|
if (this.child == null)
|
||||||
return;
|
return [0, 0];
|
||||||
|
|
||||||
|
forHeight = themeNode.adjust_for_height(forHeight);
|
||||||
let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
|
let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
|
||||||
alloc.min_size = minWidth * this.child.scale_y;
|
return themeNode.adjust_preferred_width(minWidth * this.child.scale_y,
|
||||||
alloc.natural_size = natWidth * this.child.scale_y;
|
natWidth * this.child.scale_y);
|
||||||
},
|
},
|
||||||
|
|
||||||
showLabel: function() {
|
showLabel: function() {
|
||||||
@@ -113,9 +107,9 @@ const DashItemContainer = new Lang.Class({
|
|||||||
this.label.opacity = 0;
|
this.label.opacity = 0;
|
||||||
this.label.show();
|
this.label.show();
|
||||||
|
|
||||||
let [stageX, stageY] = this.actor.get_transformed_position();
|
let [stageX, stageY] = this.get_transformed_position();
|
||||||
|
|
||||||
let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
|
let itemHeight = this.allocation.y2 - this.allocation.y1;
|
||||||
|
|
||||||
let labelHeight = this.label.get_height();
|
let labelHeight = this.label.get_height();
|
||||||
let yOffset = Math.floor((itemHeight - labelHeight) / 2)
|
let yOffset = Math.floor((itemHeight - labelHeight) / 2)
|
||||||
@@ -129,7 +123,7 @@ const DashItemContainer = new Lang.Class({
|
|||||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||||
x = stageX - this.label.get_width() - xOffset;
|
x = stageX - this.label.get_width() - xOffset;
|
||||||
else
|
else
|
||||||
x = stageX + this.actor.get_width() + xOffset;
|
x = stageX + this.get_width() + xOffset;
|
||||||
|
|
||||||
this.label.set_position(x, y);
|
this.label.set_position(x, y);
|
||||||
Tweener.addTween(this.label,
|
Tweener.addTween(this.label,
|
||||||
@@ -159,22 +153,25 @@ const DashItemContainer = new Lang.Class({
|
|||||||
if (this.child == actor)
|
if (this.child == actor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.actor.destroy_all_children();
|
this.destroy_all_children();
|
||||||
|
|
||||||
this.child = actor;
|
this.child = actor;
|
||||||
this.actor.add_actor(this.child);
|
this.add_actor(this.child);
|
||||||
|
|
||||||
|
this.child.set_scale_with_gravity(this._childScale, this._childScale,
|
||||||
|
Clutter.Gravity.CENTER);
|
||||||
|
this.child.set_opacity(this._childOpacity);
|
||||||
},
|
},
|
||||||
|
|
||||||
animateIn: function() {
|
show: function(animate) {
|
||||||
if (this.child == null)
|
if (this.child == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.childScale = 0;
|
let time = animate ? DASH_ANIMATION_TIME : 0;
|
||||||
this.childOpacity = 0;
|
|
||||||
Tweener.addTween(this,
|
Tweener.addTween(this,
|
||||||
{ childScale: 1.0,
|
{ childScale: 1.0,
|
||||||
childOpacity: 255,
|
childOpacity: 255,
|
||||||
time: DASH_ANIMATION_TIME,
|
time: time,
|
||||||
transition: 'easeOutQuad'
|
transition: 'easeOutQuad'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -183,7 +180,7 @@ const DashItemContainer = new Lang.Class({
|
|||||||
if (this.label)
|
if (this.label)
|
||||||
this.label.destroy();
|
this.label.destroy();
|
||||||
|
|
||||||
this.actor.destroy();
|
this.parent();
|
||||||
},
|
},
|
||||||
|
|
||||||
animateOutAndDestroy: function() {
|
animateOutAndDestroy: function() {
|
||||||
@@ -191,19 +188,18 @@ const DashItemContainer = new Lang.Class({
|
|||||||
this.label.destroy();
|
this.label.destroy();
|
||||||
|
|
||||||
if (this.child == null) {
|
if (this.child == null) {
|
||||||
this.actor.destroy();
|
this.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.animatingOut = true;
|
this.animatingOut = true;
|
||||||
this.childScale = 1.0;
|
|
||||||
Tweener.addTween(this,
|
Tweener.addTween(this,
|
||||||
{ childScale: 0.0,
|
{ childScale: 0.0,
|
||||||
childOpacity: 0,
|
childOpacity: 0,
|
||||||
time: DASH_ANIMATION_TIME,
|
time: DASH_ANIMATION_TIME,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: Lang.bind(this, function() {
|
onComplete: Lang.bind(this, function() {
|
||||||
this.actor.destroy();
|
this.destroy();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -216,7 +212,7 @@ const DashItemContainer = new Lang.Class({
|
|||||||
|
|
||||||
this.child.set_scale_with_gravity(scale, scale,
|
this.child.set_scale_with_gravity(scale, scale,
|
||||||
Clutter.Gravity.CENTER);
|
Clutter.Gravity.CENTER);
|
||||||
this.actor.queue_relayout();
|
this.queue_relayout();
|
||||||
},
|
},
|
||||||
|
|
||||||
get childScale() {
|
get childScale() {
|
||||||
@@ -230,7 +226,7 @@ const DashItemContainer = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this.child.set_opacity(opacity);
|
this.child.set_opacity(opacity);
|
||||||
this.actor.queue_redraw();
|
this.queue_redraw();
|
||||||
},
|
},
|
||||||
|
|
||||||
get childOpacity() {
|
get childOpacity() {
|
||||||
@@ -292,13 +288,7 @@ const ShowAppsIcon = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleDragOver: function(source, actor, x, y, time) {
|
handleDragOver: function(source, actor, x, y, time) {
|
||||||
let app = getAppFromSource(source);
|
if (!this._canRemoveApp(getAppFromSource(source)))
|
||||||
if (app == null)
|
|
||||||
return DND.DragMotionResult.NO_DROP;
|
|
||||||
|
|
||||||
let id = app.get_id();
|
|
||||||
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
|
|
||||||
if (!isFavorite)
|
|
||||||
return DND.DragMotionResult.NO_DROP;
|
return DND.DragMotionResult.NO_DROP;
|
||||||
|
|
||||||
return DND.DragMotionResult.MOVE_DROP;
|
return DND.DragMotionResult.MOVE_DROP;
|
||||||
@@ -306,7 +296,7 @@ const ShowAppsIcon = new Lang.Class({
|
|||||||
|
|
||||||
acceptDrop: function(source, actor, x, y, time) {
|
acceptDrop: function(source, actor, x, y, time) {
|
||||||
let app = getAppFromSource(source);
|
let app = getAppFromSource(source);
|
||||||
if (app == null)
|
if (!this._canRemoveApp(app))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let id = app.get_id();
|
let id = app.get_id();
|
||||||
@@ -331,6 +321,16 @@ const DragPlaceholderItem = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const EmptyDropTargetItem = new Lang.Class({
|
||||||
|
Name: 'EmptyDropTargetItem',
|
||||||
|
Extends: DashItemContainer,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent();
|
||||||
|
this.setChild(new St.Bin({ style_class: 'empty-dash-drop-target' }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const DashActor = new Lang.Class({
|
const DashActor = new Lang.Class({
|
||||||
Name: 'DashActor',
|
Name: 'DashActor',
|
||||||
Extends: St.Widget,
|
Extends: St.Widget,
|
||||||
@@ -361,6 +361,23 @@ const DashActor = new Lang.Class({
|
|||||||
childBox.y1 = contentBox.y2 - showAppsNatHeight;
|
childBox.y1 = contentBox.y2 - showAppsNatHeight;
|
||||||
childBox.y2 = contentBox.y2;
|
childBox.y2 = contentBox.y2;
|
||||||
showAppsButton.allocate(childBox, flags);
|
showAppsButton.allocate(childBox, flags);
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_get_preferred_height: function(forWidth) {
|
||||||
|
// We want to request the natural height of all our children
|
||||||
|
// as our natural height, so we chain up to StWidget (which
|
||||||
|
// then calls BoxLayout), but we only request the showApps
|
||||||
|
// button as the minimum size
|
||||||
|
|
||||||
|
let [, natHeight] = this.parent(forWidth);
|
||||||
|
|
||||||
|
let themeNode = this.get_theme_node();
|
||||||
|
let adjustedForWidth = themeNode.adjust_for_width(forWidth);
|
||||||
|
let [, showAppsButton] = this.get_children();
|
||||||
|
let [minHeight, ] = showAppsButton.get_preferred_height(adjustedForWidth);
|
||||||
|
[minHeight, ] = themeNode.adjust_preferred_height(minHeight, natHeight);
|
||||||
|
|
||||||
|
return [minHeight, natHeight];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -386,12 +403,14 @@ const Dash = new Lang.Class({
|
|||||||
this._container.add_actor(this._box);
|
this._container.add_actor(this._box);
|
||||||
|
|
||||||
this._showAppsIcon = new ShowAppsIcon();
|
this._showAppsIcon = new ShowAppsIcon();
|
||||||
|
this._showAppsIcon.childScale = 1;
|
||||||
|
this._showAppsIcon.childOpacity = 255;
|
||||||
this._showAppsIcon.icon.setIconSize(this.iconSize);
|
this._showAppsIcon.icon.setIconSize(this.iconSize);
|
||||||
this._hookUpLabel(this._showAppsIcon);
|
this._hookUpLabel(this._showAppsIcon);
|
||||||
|
|
||||||
this.showAppsButton = this._showAppsIcon.toggleButton;
|
this.showAppsButton = this._showAppsIcon.toggleButton;
|
||||||
|
|
||||||
this._container.add_actor(this._showAppsIcon.actor);
|
this._container.add_actor(this._showAppsIcon);
|
||||||
|
|
||||||
this.actor = new St.Bin({ child: this._container });
|
this.actor = new St.Bin({ child: this._container });
|
||||||
this.actor.connect('notify::height', Lang.bind(this,
|
this.actor.connect('notify::height', Lang.bind(this,
|
||||||
@@ -405,7 +424,10 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
this._appSystem = Shell.AppSystem.get_default();
|
this._appSystem = Shell.AppSystem.get_default();
|
||||||
|
|
||||||
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
|
||||||
|
AppFavorites.getAppFavorites().reload();
|
||||||
|
this._queueRedisplay();
|
||||||
|
}));
|
||||||
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
|
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
|
||||||
this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
|
this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
|
||||||
|
|
||||||
@@ -415,12 +437,10 @@ const Dash = new Lang.Class({
|
|||||||
Lang.bind(this, this._onDragEnd));
|
Lang.bind(this, this._onDragEnd));
|
||||||
Main.overview.connect('item-drag-cancelled',
|
Main.overview.connect('item-drag-cancelled',
|
||||||
Lang.bind(this, this._onDragCancelled));
|
Lang.bind(this, this._onDragCancelled));
|
||||||
Main.overview.connect('window-drag-begin',
|
|
||||||
Lang.bind(this, this._onDragBegin));
|
// Translators: this is the name of the dock/favorites area on
|
||||||
Main.overview.connect('window-drag-cancelled',
|
// the left of the overview
|
||||||
Lang.bind(this, this._onDragCancelled));
|
Main.ctrlAltTabManager.addGroup(this.actor, _("Dash"), 'user-bookmarks-symbolic');
|
||||||
Main.overview.connect('window-drag-end',
|
|
||||||
Lang.bind(this, this._onDragEnd));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragBegin: function() {
|
_onDragBegin: function() {
|
||||||
@@ -429,6 +449,12 @@ const Dash = new Lang.Class({
|
|||||||
dragMotion: Lang.bind(this, this._onDragMotion)
|
dragMotion: Lang.bind(this, this._onDragMotion)
|
||||||
};
|
};
|
||||||
DND.addDragMonitor(this._dragMonitor);
|
DND.addDragMonitor(this._dragMonitor);
|
||||||
|
|
||||||
|
if (this._box.get_n_children() == 0) {
|
||||||
|
this._emptyDropTarget = new EmptyDropTargetItem();
|
||||||
|
this._box.insert_child_at_index(this._emptyDropTarget, 0);
|
||||||
|
this._emptyDropTarget.show(true);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragCancelled: function() {
|
_onDragCancelled: function() {
|
||||||
@@ -445,6 +471,7 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
_endDrag: function() {
|
_endDrag: function() {
|
||||||
this._clearDragPlaceholder();
|
this._clearDragPlaceholder();
|
||||||
|
this._clearEmptyDropTarget();
|
||||||
this._showAppsIcon.setDragApp(null);
|
this._showAppsIcon.setDragApp(null);
|
||||||
DND.removeDragMonitor(this._dragMonitor);
|
DND.removeDragMonitor(this._dragMonitor);
|
||||||
},
|
},
|
||||||
@@ -455,7 +482,7 @@ const Dash = new Lang.Class({
|
|||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
|
|
||||||
let showAppsHovered =
|
let showAppsHovered =
|
||||||
this._showAppsIcon.actor.contains(dragEvent.targetActor);
|
this._showAppsIcon.contains(dragEvent.targetActor);
|
||||||
|
|
||||||
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
|
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
|
||||||
this._clearDragPlaceholder();
|
this._clearDragPlaceholder();
|
||||||
@@ -479,19 +506,25 @@ const Dash = new Lang.Class({
|
|||||||
Main.queueDeferredWork(this._workId);
|
Main.queueDeferredWork(this._workId);
|
||||||
},
|
},
|
||||||
|
|
||||||
_hookUpLabel: function(item) {
|
_hookUpLabel: function(item, appIcon) {
|
||||||
item.child.connect('notify::hover', Lang.bind(this, function() {
|
item.child.connect('notify::hover', Lang.bind(this, function() {
|
||||||
this._onHover(item);
|
this._syncLabel(item, appIcon);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||||
this._labelShowing = false;
|
this._labelShowing = false;
|
||||||
item.hideLabel();
|
item.hideLabel();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
if (appIcon) {
|
||||||
|
appIcon.connect('sync-tooltip', Lang.bind(this, function() {
|
||||||
|
this._syncLabel(item, appIcon);
|
||||||
|
}));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_createAppItem: function(app) {
|
_createAppItem: function(app) {
|
||||||
let appIcon = new AppDisplay.AppWellIcon(app,
|
let appIcon = new AppDisplay.AppIcon(app,
|
||||||
{ setSizeManually: true,
|
{ setSizeManually: true,
|
||||||
showLabel: false });
|
showLabel: false });
|
||||||
appIcon._draggable.connect('drag-begin',
|
appIcon._draggable.connect('drag-begin',
|
||||||
@@ -510,13 +543,13 @@ const Dash = new Lang.Class({
|
|||||||
let item = new DashItemContainer();
|
let item = new DashItemContainer();
|
||||||
item.setChild(appIcon.actor);
|
item.setChild(appIcon.actor);
|
||||||
|
|
||||||
// Override default AppWellIcon label_actor, now the
|
// Override default AppIcon label_actor, now the
|
||||||
// accessible_name is set at DashItemContainer.setLabelText
|
// accessible_name is set at DashItemContainer.setLabelText
|
||||||
appIcon.actor.label_actor = null;
|
appIcon.actor.label_actor = null;
|
||||||
item.setLabelText(app.get_name());
|
item.setLabelText(app.get_name());
|
||||||
|
|
||||||
appIcon.icon.setIconSize(this.iconSize);
|
appIcon.icon.setIconSize(this.iconSize);
|
||||||
this._hookUpLabel(item);
|
this._hookUpLabel(item, appIcon);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
},
|
},
|
||||||
@@ -534,15 +567,18 @@ const Dash = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onHover: function (item) {
|
_syncLabel: function (item, appIcon) {
|
||||||
if (item.child.get_hover()) {
|
let shouldShow = appIcon ? appIcon.shouldShowTooltip() : item.child.get_hover();
|
||||||
|
|
||||||
|
if (shouldShow) {
|
||||||
if (this._showLabelTimeoutId == 0) {
|
if (this._showLabelTimeoutId == 0) {
|
||||||
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
|
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
|
||||||
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
|
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this._labelShowing = true;
|
this._labelShowing = true;
|
||||||
item.showLabel();
|
item.showLabel();
|
||||||
return false;
|
this._showLabelTimeoutId = 0;
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
}));
|
}));
|
||||||
if (this._resetHoverTimeoutId > 0) {
|
if (this._resetHoverTimeoutId > 0) {
|
||||||
Mainloop.source_remove(this._resetHoverTimeoutId);
|
Mainloop.source_remove(this._resetHoverTimeoutId);
|
||||||
@@ -558,7 +594,8 @@ const Dash = new Lang.Class({
|
|||||||
this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
|
this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this._labelShowing = false;
|
this._labelShowing = false;
|
||||||
return false;
|
this._resetHoverTimeoutId = 0;
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -570,13 +607,13 @@ const Dash = new Lang.Class({
|
|||||||
// animating out (which means they will be destroyed at the end of
|
// animating out (which means they will be destroyed at the end of
|
||||||
// the animation)
|
// the animation)
|
||||||
let iconChildren = this._box.get_children().filter(function(actor) {
|
let iconChildren = this._box.get_children().filter(function(actor) {
|
||||||
return actor._delegate.child &&
|
return actor.child &&
|
||||||
actor._delegate.child._delegate &&
|
actor.child._delegate &&
|
||||||
actor._delegate.child._delegate.icon &&
|
actor.child._delegate.icon &&
|
||||||
!actor._delegate.animatingOut;
|
!actor.animatingOut;
|
||||||
});
|
});
|
||||||
|
|
||||||
iconChildren.push(this._showAppsIcon.actor);
|
iconChildren.push(this._showAppsIcon);
|
||||||
|
|
||||||
if (this._maxHeight == -1)
|
if (this._maxHeight == -1)
|
||||||
return;
|
return;
|
||||||
@@ -589,23 +626,18 @@ const Dash = new Lang.Class({
|
|||||||
let availHeight = maxContent.y2 - maxContent.y1;
|
let availHeight = maxContent.y2 - maxContent.y1;
|
||||||
let spacing = themeNode.get_length('spacing');
|
let spacing = themeNode.get_length('spacing');
|
||||||
|
|
||||||
|
let firstButton = iconChildren[0].child;
|
||||||
let firstIcon = iconChildren[0]._delegate.child._delegate.icon;
|
let firstIcon = firstButton._delegate.icon;
|
||||||
|
|
||||||
let minHeight, natHeight;
|
let minHeight, natHeight;
|
||||||
|
|
||||||
// Enforce the current icon size during the size request if
|
// Enforce the current icon size during the size request
|
||||||
// the icon is animating
|
|
||||||
if (firstIcon._animating) {
|
|
||||||
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
|
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
|
||||||
|
|
||||||
firstIcon.icon.set_size(this.iconSize, this.iconSize);
|
firstIcon.icon.set_size(this.iconSize, this.iconSize);
|
||||||
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
|
[minHeight, natHeight] = firstButton.get_preferred_height(-1);
|
||||||
|
|
||||||
firstIcon.icon.set_size(currentWidth, currentHeight);
|
firstIcon.icon.set_size(currentWidth, currentHeight);
|
||||||
} else {
|
|
||||||
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtract icon padding and box spacing from the available height
|
// Subtract icon padding and box spacing from the available height
|
||||||
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
|
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
|
||||||
@@ -630,7 +662,7 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
let scale = oldIconSize / newIconSize;
|
let scale = oldIconSize / newIconSize;
|
||||||
for (let i = 0; i < iconChildren.length; i++) {
|
for (let i = 0; i < iconChildren.length; i++) {
|
||||||
let icon = iconChildren[i]._delegate.child._delegate.icon;
|
let icon = iconChildren[i].child._delegate.icon;
|
||||||
|
|
||||||
// Set the new size immediately, to keep the icons' sizes
|
// Set the new size immediately, to keep the icons' sizes
|
||||||
// in sync with this.iconSize
|
// in sync with this.iconSize
|
||||||
@@ -650,15 +682,11 @@ const Dash = new Lang.Class({
|
|||||||
icon.icon.set_size(icon.icon.width * scale,
|
icon.icon.set_size(icon.icon.width * scale,
|
||||||
icon.icon.height * scale);
|
icon.icon.height * scale);
|
||||||
|
|
||||||
icon._animating = true;
|
|
||||||
Tweener.addTween(icon.icon,
|
Tweener.addTween(icon.icon,
|
||||||
{ width: targetWidth,
|
{ width: targetWidth,
|
||||||
height: targetHeight,
|
height: targetHeight,
|
||||||
time: DASH_ANIMATION_TIME,
|
time: DASH_ANIMATION_TIME,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: function() {
|
|
||||||
icon._animating = false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -669,13 +697,13 @@ const Dash = new Lang.Class({
|
|||||||
let running = this._appSystem.get_running();
|
let running = this._appSystem.get_running();
|
||||||
|
|
||||||
let children = this._box.get_children().filter(function(actor) {
|
let children = this._box.get_children().filter(function(actor) {
|
||||||
return actor._delegate.child &&
|
return actor.child &&
|
||||||
actor._delegate.child._delegate &&
|
actor.child._delegate &&
|
||||||
actor._delegate.child._delegate.app;
|
actor.child._delegate.app;
|
||||||
});
|
});
|
||||||
// Apps currently in the dash
|
// Apps currently in the dash
|
||||||
let oldApps = children.map(function(actor) {
|
let oldApps = children.map(function(actor) {
|
||||||
return actor._delegate.child._delegate.app;
|
return actor.child._delegate.app;
|
||||||
});
|
});
|
||||||
// Apps supposed to be in the dash
|
// Apps supposed to be in the dash
|
||||||
let newApps = [];
|
let newApps = [];
|
||||||
@@ -741,7 +769,7 @@ const Dash = new Lang.Class({
|
|||||||
let insertHere = newApps[newIndex + 1] &&
|
let insertHere = newApps[newIndex + 1] &&
|
||||||
newApps[newIndex + 1] == oldApps[oldIndex];
|
newApps[newIndex + 1] == oldApps[oldIndex];
|
||||||
let alreadyRemoved = removedActors.reduce(function(result, actor) {
|
let alreadyRemoved = removedActors.reduce(function(result, actor) {
|
||||||
let removedApp = actor._delegate.child._delegate.app;
|
let removedApp = actor.child._delegate.app;
|
||||||
return result || removedApp == newApps[newIndex];
|
return result || removedApp == newApps[newIndex];
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
@@ -758,11 +786,11 @@ const Dash = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < addedItems.length; i++)
|
for (let i = 0; i < addedItems.length; i++)
|
||||||
this._box.insert_child_at_index(addedItems[i].item.actor,
|
this._box.insert_child_at_index(addedItems[i].item,
|
||||||
addedItems[i].pos);
|
addedItems[i].pos);
|
||||||
|
|
||||||
for (let i = 0; i < removedActors.length; i++) {
|
for (let i = 0; i < removedActors.length; i++) {
|
||||||
let item = removedActors[i]._delegate;
|
let item = removedActors[i];
|
||||||
|
|
||||||
// Don't animate item removal when the overview is transitioning
|
// Don't animate item removal when the overview is transitioning
|
||||||
// or hidden
|
// or hidden
|
||||||
@@ -776,25 +804,39 @@ const Dash = new Lang.Class({
|
|||||||
|
|
||||||
// Skip animations on first run when adding the initial set
|
// Skip animations on first run when adding the initial set
|
||||||
// of items, to avoid all items zooming in at once
|
// of items, to avoid all items zooming in at once
|
||||||
if (!this._shownInitially) {
|
|
||||||
|
let animate = this._shownInitially && Main.overview.visible &&
|
||||||
|
!Main.overview.animationInProgress;
|
||||||
|
|
||||||
|
if (!this._shownInitially)
|
||||||
this._shownInitially = true;
|
this._shownInitially = true;
|
||||||
return;
|
|
||||||
|
for (let i = 0; i < addedItems.length; i++) {
|
||||||
|
addedItems[i].item.show(animate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't animate item addition when the overview is transitioning
|
// Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
|
||||||
// or hidden
|
// Without it, StBoxLayout may use a stale size cache
|
||||||
if (!Main.overview.visible || Main.overview.animationInProgress)
|
this._box.queue_relayout();
|
||||||
return;
|
|
||||||
|
|
||||||
for (let i = 0; i < addedItems.length; i++)
|
|
||||||
addedItems[i].item.animateIn();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_clearDragPlaceholder: function() {
|
_clearDragPlaceholder: function() {
|
||||||
if (this._dragPlaceholder) {
|
if (this._dragPlaceholder) {
|
||||||
|
this._animatingPlaceholdersCount++;
|
||||||
this._dragPlaceholder.animateOutAndDestroy();
|
this._dragPlaceholder.animateOutAndDestroy();
|
||||||
|
this._dragPlaceholder.connect('destroy',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._animatingPlaceholdersCount--;
|
||||||
|
}));
|
||||||
this._dragPlaceholder = null;
|
this._dragPlaceholder = null;
|
||||||
|
}
|
||||||
this._dragPlaceholderPos = -1;
|
this._dragPlaceholderPos = -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearEmptyDropTarget: function() {
|
||||||
|
if (this._emptyDropTarget) {
|
||||||
|
this._emptyDropTarget.animateOutAndDestroy();
|
||||||
|
this._emptyDropTarget = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -818,27 +860,22 @@ const Dash = new Lang.Class({
|
|||||||
// the remove target has the same size as "normal" items, we don't
|
// the remove target has the same size as "normal" items, we don't
|
||||||
// need to do the same adjustment there.
|
// need to do the same adjustment there.
|
||||||
if (this._dragPlaceholder) {
|
if (this._dragPlaceholder) {
|
||||||
boxHeight -= this._dragPlaceholder.actor.height;
|
boxHeight -= this._dragPlaceholder.height;
|
||||||
numChildren--;
|
numChildren--;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pos = Math.floor(y * numChildren / boxHeight);
|
let pos;
|
||||||
|
if (!this._emptyDropTarget)
|
||||||
|
pos = Math.floor(y * numChildren / boxHeight);
|
||||||
|
else
|
||||||
|
pos = 0; // always insert at the top when dash is empty
|
||||||
|
|
||||||
if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
|
if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
|
||||||
this._dragPlaceholderPos = pos;
|
this._dragPlaceholderPos = pos;
|
||||||
|
|
||||||
// Don't allow positioning before or after self
|
// Don't allow positioning before or after self
|
||||||
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
|
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
|
||||||
if (this._dragPlaceholder) {
|
this._clearDragPlaceholder();
|
||||||
this._dragPlaceholder.animateOutAndDestroy();
|
|
||||||
this._animatingPlaceholdersCount++;
|
|
||||||
this._dragPlaceholder.actor.connect('destroy',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
this._animatingPlaceholdersCount--;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
this._dragPlaceholder = null;
|
|
||||||
|
|
||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,7 +884,7 @@ const Dash = new Lang.Class({
|
|||||||
// an animation
|
// an animation
|
||||||
let fadeIn;
|
let fadeIn;
|
||||||
if (this._dragPlaceholder) {
|
if (this._dragPlaceholder) {
|
||||||
this._dragPlaceholder.actor.destroy();
|
this._dragPlaceholder.destroy();
|
||||||
fadeIn = false;
|
fadeIn = false;
|
||||||
} else {
|
} else {
|
||||||
fadeIn = true;
|
fadeIn = true;
|
||||||
@@ -856,17 +893,16 @@ const Dash = new Lang.Class({
|
|||||||
this._dragPlaceholder = new DragPlaceholderItem();
|
this._dragPlaceholder = new DragPlaceholderItem();
|
||||||
this._dragPlaceholder.child.set_width (this.iconSize);
|
this._dragPlaceholder.child.set_width (this.iconSize);
|
||||||
this._dragPlaceholder.child.set_height (this.iconSize / 2);
|
this._dragPlaceholder.child.set_height (this.iconSize / 2);
|
||||||
this._box.insert_child_at_index(this._dragPlaceholder.actor,
|
this._box.insert_child_at_index(this._dragPlaceholder,
|
||||||
this._dragPlaceholderPos);
|
this._dragPlaceholderPos);
|
||||||
if (fadeIn)
|
this._dragPlaceholder.show(fadeIn);
|
||||||
this._dragPlaceholder.animateIn();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the drag placeholder if we are not in the
|
// Remove the drag placeholder if we are not in the
|
||||||
// "favorites zone"
|
// "favorites zone"
|
||||||
if (pos > numFavorites && this._dragPlaceholder) {
|
if (pos > numFavorites)
|
||||||
this._clearDragPlaceholder();
|
this._clearDragPlaceholder();
|
||||||
}
|
|
||||||
if (!this._dragPlaceholder)
|
if (!this._dragPlaceholder)
|
||||||
return DND.DragMotionResult.NO_DROP;
|
return DND.DragMotionResult.NO_DROP;
|
||||||
|
|
||||||
@@ -897,10 +933,10 @@ const Dash = new Lang.Class({
|
|||||||
let children = this._box.get_children();
|
let children = this._box.get_children();
|
||||||
for (let i = 0; i < this._dragPlaceholderPos; i++) {
|
for (let i = 0; i < this._dragPlaceholderPos; i++) {
|
||||||
if (this._dragPlaceholder &&
|
if (this._dragPlaceholder &&
|
||||||
children[i] == this._dragPlaceholder.actor)
|
children[i] == this._dragPlaceholder)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let childId = children[i]._delegate.child._delegate.app.get_id();
|
let childId = children[i].child._delegate.app.get_id();
|
||||||
if (childId == id)
|
if (childId == id)
|
||||||
continue;
|
continue;
|
||||||
if (childId in favorites)
|
if (childId in favorites)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ function _onVertSepRepaint (area)
|
|||||||
cr.setDash([1, 3], 1); // Hard-code for now
|
cr.setDash([1, 3], 1); // Hard-code for now
|
||||||
cr.setLineWidth(stippleWidth);
|
cr.setLineWidth(stippleWidth);
|
||||||
cr.stroke();
|
cr.stroke();
|
||||||
|
cr.$dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const DateMenuButton = new Lang.Class({
|
const DateMenuButton = new Lang.Class({
|
||||||
@@ -48,16 +49,13 @@ const DateMenuButton = new Lang.Class({
|
|||||||
menuAlignment = 1.0 - menuAlignment;
|
menuAlignment = 1.0 - menuAlignment;
|
||||||
this.parent(menuAlignment);
|
this.parent(menuAlignment);
|
||||||
|
|
||||||
// At this moment calendar menu is not keyboard navigable at
|
this._clockDisplay = new St.Label({ y_align: Clutter.ActorAlign.CENTER });
|
||||||
// all (so not accessible), so it doesn't make sense to set as
|
this.actor.label_actor = this._clockDisplay;
|
||||||
// role ATK_ROLE_MENU like other elements of the panel.
|
|
||||||
this.actor.accessible_role = Atk.Role.LABEL;
|
|
||||||
|
|
||||||
this._clockDisplay = new St.Label();
|
|
||||||
this.actor.add_actor(this._clockDisplay);
|
this.actor.add_actor(this._clockDisplay);
|
||||||
|
this.actor.add_style_class_name ('clock-display');
|
||||||
|
|
||||||
hbox = new St.BoxLayout({ name: 'calendarArea' });
|
hbox = new St.BoxLayout({ name: 'calendarArea' });
|
||||||
this.menu.addActor(hbox);
|
this.menu.box.add_child(hbox);
|
||||||
|
|
||||||
// Fill up the first column
|
// Fill up the first column
|
||||||
|
|
||||||
@@ -65,9 +63,8 @@ const DateMenuButton = new Lang.Class({
|
|||||||
hbox.add(vbox);
|
hbox.add(vbox);
|
||||||
|
|
||||||
// Date
|
// Date
|
||||||
this._date = new St.Label();
|
this._date = new St.Label({ style_class: 'datemenu-date-label',
|
||||||
this.actor.label_actor = this._clockDisplay;
|
can_focus: true });
|
||||||
this._date.style_class = 'datemenu-date-label';
|
|
||||||
vbox.add(this._date);
|
vbox.add(this._date);
|
||||||
|
|
||||||
this._eventList = new Calendar.EventsList();
|
this._eventList = new Calendar.EventsList();
|
||||||
@@ -83,14 +80,23 @@ const DateMenuButton = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
vbox.add(this._calendar.actor);
|
vbox.add(this._calendar.actor);
|
||||||
|
|
||||||
item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
|
|
||||||
if (item) {
|
|
||||||
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
||||||
separator.setColumnWidths(1);
|
|
||||||
vbox.add(separator.actor, { y_align: St.Align.END, expand: true, y_fill: false });
|
vbox.add(separator.actor, { y_align: St.Align.END, expand: true, y_fill: false });
|
||||||
|
|
||||||
|
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||||
|
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||||
|
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||||
|
|
||||||
|
this._openClocksItem = new PopupMenu.PopupMenuItem(_("Open Clocks"));
|
||||||
|
this._openClocksItem.connect('activate', Lang.bind(this, this._onOpenClocksActivate));
|
||||||
|
vbox.add(this._openClocksItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||||
|
|
||||||
|
Shell.AppSystem.get_default().connect('installed-changed',
|
||||||
|
Lang.bind(this, this._appInstalledChanged));
|
||||||
|
|
||||||
|
item = this.menu.addSettingsAction(_("Date & Time Settings"), 'gnome-datetime-panel.desktop');
|
||||||
|
if (item) {
|
||||||
item.actor.show_on_set_parent = false;
|
item.actor.show_on_set_parent = false;
|
||||||
item.actor.can_focus = false;
|
|
||||||
item.actor.reparent(vbox);
|
item.actor.reparent(vbox);
|
||||||
this._dateAndTimeSeparator = separator;
|
this._dateAndTimeSeparator = separator;
|
||||||
}
|
}
|
||||||
@@ -101,43 +107,13 @@ const DateMenuButton = new Lang.Class({
|
|||||||
hbox.add(this._separator);
|
hbox.add(this._separator);
|
||||||
|
|
||||||
// Fill up the second column
|
// Fill up the second column
|
||||||
vbox = new St.BoxLayout({ name: 'calendarEventsArea',
|
hbox.add(this._eventList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
|
||||||
vertical: true });
|
|
||||||
hbox.add(vbox, { expand: true });
|
|
||||||
|
|
||||||
// Event list
|
|
||||||
vbox.add(this._eventList.actor, { expand: true });
|
|
||||||
|
|
||||||
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
|
||||||
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
|
||||||
this._openCalendarItem.actor.can_focus = false;
|
|
||||||
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
|
||||||
|
|
||||||
this._calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
|
||||||
this._calendarSettings.connect('changed::exec',
|
|
||||||
Lang.bind(this, this._calendarSettingsChanged));
|
|
||||||
this._calendarSettingsChanged();
|
|
||||||
|
|
||||||
// Whenever the menu is opened, select today
|
// Whenever the menu is opened, select today
|
||||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
/* Passing true to setDate() forces events to be reloaded. We
|
this._calendar.setDate(now);
|
||||||
* want this behavior, because
|
|
||||||
*
|
|
||||||
* o It will cause activation of the calendar server which is
|
|
||||||
* useful if it has crashed
|
|
||||||
*
|
|
||||||
* o It will cause the calendar server to reload events which
|
|
||||||
* is useful if dynamic updates are not supported or not
|
|
||||||
* properly working
|
|
||||||
*
|
|
||||||
* Since this only happens when the menu is opened, the cost
|
|
||||||
* isn't very big.
|
|
||||||
*/
|
|
||||||
this._calendar.setDate(now, true);
|
|
||||||
// No need to update this._eventList as ::selected-date-changed
|
|
||||||
// signal will fire
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -151,30 +127,40 @@ const DateMenuButton = new Lang.Class({
|
|||||||
this._sessionUpdated();
|
this._sessionUpdated();
|
||||||
},
|
},
|
||||||
|
|
||||||
_calendarSettingsChanged: function() {
|
_appInstalledChanged: function() {
|
||||||
let exec = this._calendarSettings.get_string('exec');
|
this._calendarApp = undefined;
|
||||||
let fullExec = GLib.find_program_in_path(exec);
|
this._updateEventsVisibility();
|
||||||
this._openCalendarItem.actor.visible = fullExec != null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_setEventsVisibility: function(visible) {
|
_updateEventsVisibility: function() {
|
||||||
this._openCalendarItem.actor.visible = visible;
|
let visible = this._eventSource.hasCalendars;
|
||||||
|
this._openCalendarItem.actor.visible = visible &&
|
||||||
|
(this._getCalendarApp() != null);
|
||||||
|
this._openClocksItem.actor.visible = visible &&
|
||||||
|
(this._getClockApp() != null);
|
||||||
this._separator.visible = visible;
|
this._separator.visible = visible;
|
||||||
|
this._eventList.actor.visible = visible;
|
||||||
if (visible) {
|
if (visible) {
|
||||||
let alignment = 0.25;
|
let alignment = 0.25;
|
||||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||||
alignment = 1.0 - alignment;
|
alignment = 1.0 - alignment;
|
||||||
this.menu._arrowAlignment = alignment;
|
this.menu._arrowAlignment = alignment;
|
||||||
this._eventList.actor.get_parent().show();
|
|
||||||
} else {
|
} else {
|
||||||
this.menu._arrowAlignment = 0.5;
|
this.menu._arrowAlignment = 0.5;
|
||||||
this._eventList.actor.get_parent().hide();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_setEventSource: function(eventSource) {
|
_setEventSource: function(eventSource) {
|
||||||
|
if (this._eventSource)
|
||||||
|
this._eventSource.destroy();
|
||||||
|
|
||||||
this._calendar.setEventSource(eventSource);
|
this._calendar.setEventSource(eventSource);
|
||||||
this._eventList.setEventSource(eventSource);
|
this._eventList.setEventSource(eventSource);
|
||||||
|
|
||||||
|
this._eventSource = eventSource;
|
||||||
|
this._eventSource.connect('notify::has-calendars', Lang.bind(this, function() {
|
||||||
|
this._updateEventsVisibility();
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_sessionUpdated: function() {
|
_sessionUpdated: function() {
|
||||||
@@ -183,10 +169,10 @@ const DateMenuButton = new Lang.Class({
|
|||||||
if (showEvents) {
|
if (showEvents) {
|
||||||
eventSource = new Calendar.DBusEventSource();
|
eventSource = new Calendar.DBusEventSource();
|
||||||
} else {
|
} else {
|
||||||
eventSource = null;
|
eventSource = new Calendar.EmptyEventSource();
|
||||||
}
|
}
|
||||||
this._setEventSource(eventSource);
|
this._setEventSource(eventSource);
|
||||||
this._setEventsVisibility(showEvents);
|
this._updateEventsVisibility();
|
||||||
|
|
||||||
// This needs to be handled manually, as the code to
|
// This needs to be handled manually, as the code to
|
||||||
// autohide separators doesn't work across the vbox
|
// autohide separators doesn't work across the vbox
|
||||||
@@ -203,26 +189,34 @@ const DateMenuButton = new Lang.Class({
|
|||||||
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getCalendarApp: function() {
|
||||||
|
if (this._calendarApp !== undefined)
|
||||||
|
return this._calendarApp;
|
||||||
|
|
||||||
|
let apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
|
||||||
|
if (apps && (apps.length > 0))
|
||||||
|
this._calendarApp = apps[0];
|
||||||
|
else
|
||||||
|
this._calendarApp = null;
|
||||||
|
return this._calendarApp;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getClockApp: function() {
|
||||||
|
return Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
|
||||||
|
},
|
||||||
|
|
||||||
_onOpenCalendarActivate: function() {
|
_onOpenCalendarActivate: function() {
|
||||||
this.menu.close();
|
this.menu.close();
|
||||||
let tool = this._calendarSettings.get_string('exec');
|
|
||||||
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
|
let app = this._getCalendarApp();
|
||||||
// TODO: pass the selected day
|
if (app.get_id() == 'evolution.desktop')
|
||||||
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
|
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
|
||||||
|
app.launch([], global.create_app_launch_context(0, -1));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onOpenClocksActivate: function() {
|
||||||
|
this.menu.close();
|
||||||
|
let app = this._getClockApp();
|
||||||
app.activate();
|
app.activate();
|
||||||
} else {
|
|
||||||
let needTerm = this._calendarSettings.get_boolean('needs-term');
|
|
||||||
if (needTerm) {
|
|
||||||
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
|
|
||||||
let term = terminalSettings.get_string('exec');
|
|
||||||
let arg = terminalSettings.get_string('exec-arg');
|
|
||||||
if (arg != '')
|
|
||||||
Util.spawn([term, arg, tool]);
|
|
||||||
else
|
|
||||||
Util.spawn([term, tool]);
|
|
||||||
} else {
|
|
||||||
Util.spawnCommandLine(tool)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
188
js/ui/dnd.js
188
js/ui/dnd.js
@@ -1,9 +1,11 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
@@ -26,9 +28,9 @@ const DragMotionResult = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DRAG_CURSOR_MAP = {
|
const DRAG_CURSOR_MAP = {
|
||||||
0: Shell.Cursor.DND_UNSUPPORTED_TARGET,
|
0: Meta.Cursor.DND_UNSUPPORTED_TARGET,
|
||||||
1: Shell.Cursor.DND_COPY,
|
1: Meta.Cursor.DND_COPY,
|
||||||
2: Shell.Cursor.DND_MOVE
|
2: Meta.Cursor.DND_MOVE
|
||||||
};
|
};
|
||||||
|
|
||||||
const DragDropResult = {
|
const DragDropResult = {
|
||||||
@@ -43,10 +45,8 @@ let dragMonitors = [];
|
|||||||
|
|
||||||
function _getEventHandlerActor() {
|
function _getEventHandlerActor() {
|
||||||
if (!eventHandlerActor) {
|
if (!eventHandlerActor) {
|
||||||
eventHandlerActor = new Clutter.Rectangle();
|
eventHandlerActor = new Clutter.Actor({ width: 0, height: 0 });
|
||||||
eventHandlerActor.width = 0;
|
Main.layoutManager.sessionGroup.add_actor(eventHandlerActor);
|
||||||
eventHandlerActor.height = 0;
|
|
||||||
Main.uiGroup.add_actor(eventHandlerActor);
|
|
||||||
// We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
|
// We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
|
||||||
// when you've grabbed the pointer.
|
// when you've grabbed the pointer.
|
||||||
eventHandlerActor.connect('event',
|
eventHandlerActor.connect('event',
|
||||||
@@ -85,11 +85,8 @@ const _Draggable = new Lang.Class({
|
|||||||
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, function() {
|
this.actor.connect('destroy', Lang.bind(this, function() {
|
||||||
this._actorDestroyed = true;
|
this._actorDestroyed = true;
|
||||||
// If the drag actor is destroyed and we were going to fix
|
|
||||||
// up its hover state, fix up the parent hover state instead
|
if (this._dragInProgress && this._dragCancellable)
|
||||||
if (this.actor == this._firstLeaveActor)
|
|
||||||
this._firstLeaveActor = this._dragOrigParent;
|
|
||||||
if (this._dragInProgress)
|
|
||||||
this._cancelDrag(global.get_current_time());
|
this._cancelDrag(global.get_current_time());
|
||||||
this.disconnectAll();
|
this.disconnectAll();
|
||||||
}));
|
}));
|
||||||
@@ -102,22 +99,17 @@ const _Draggable = new Lang.Class({
|
|||||||
this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
|
this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
|
||||||
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
|
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
|
||||||
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
|
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
|
||||||
|
this._dragCancellable = true;
|
||||||
// During the drag, we eat enter/leave events so that actors don't prelight.
|
|
||||||
// But we remember the actors that we first left/last entered so we can
|
|
||||||
// fix up the hover state after the drag ends.
|
|
||||||
this._firstLeaveActor = null;
|
|
||||||
this._lastEnterActor = null;
|
|
||||||
|
|
||||||
this._eventsGrabbed = false;
|
this._eventsGrabbed = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onButtonPress : function (actor, event) {
|
_onButtonPress : function (actor, event) {
|
||||||
if (event.get_button() != 1)
|
if (event.get_button() != 1)
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
if (Tweener.getTweenCount(actor))
|
if (Tweener.getTweenCount(actor))
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
this._buttonDown = true;
|
this._buttonDown = true;
|
||||||
this._grabActor();
|
this._grabActor();
|
||||||
@@ -126,7 +118,7 @@ const _Draggable = new Lang.Class({
|
|||||||
this._dragStartX = stageX;
|
this._dragStartX = stageX;
|
||||||
this._dragStartY = stageY;
|
this._dragStartY = stageY;
|
||||||
|
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_grabActor: function() {
|
_grabActor: function() {
|
||||||
@@ -136,25 +128,26 @@ const _Draggable = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_ungrabActor: function() {
|
_ungrabActor: function() {
|
||||||
Clutter.ungrab_pointer();
|
|
||||||
if (!this._onEventId)
|
if (!this._onEventId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Clutter.ungrab_pointer();
|
||||||
this.actor.disconnect(this._onEventId);
|
this.actor.disconnect(this._onEventId);
|
||||||
this._onEventId = null;
|
this._onEventId = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_grabEvents: function() {
|
_grabEvents: function() {
|
||||||
if (!this._eventsGrabbed) {
|
if (!this._eventsGrabbed) {
|
||||||
|
this._eventsGrabbed = Main.pushModal(_getEventHandlerActor());
|
||||||
|
if (this._eventsGrabbed)
|
||||||
Clutter.grab_pointer(_getEventHandlerActor());
|
Clutter.grab_pointer(_getEventHandlerActor());
|
||||||
Clutter.grab_keyboard(_getEventHandlerActor());
|
|
||||||
this._eventsGrabbed = true;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_ungrabEvents: function() {
|
_ungrabEvents: function() {
|
||||||
if (this._eventsGrabbed) {
|
if (this._eventsGrabbed) {
|
||||||
Clutter.ungrab_pointer();
|
Clutter.ungrab_pointer();
|
||||||
Clutter.ungrab_keyboard();
|
Main.popModal(_getEventHandlerActor());
|
||||||
this._eventsGrabbed = false;
|
this._eventsGrabbed = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -171,11 +164,11 @@ const _Draggable = new Lang.Class({
|
|||||||
} else if (this._dragActor != null && !this._animationInProgress) {
|
} else if (this._dragActor != null && !this._animationInProgress) {
|
||||||
// Drag must have been cancelled with Esc.
|
// Drag must have been cancelled with Esc.
|
||||||
this._dragComplete();
|
this._dragComplete();
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
} else {
|
} else {
|
||||||
// Drag has never started.
|
// Drag has never started.
|
||||||
this._ungrabActor();
|
this._ungrabActor();
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
}
|
}
|
||||||
// We intercept MOTION event to figure out if the drag has started and to draw
|
// We intercept MOTION event to figure out if the drag has started and to draw
|
||||||
// this._dragActor under the pointer when dragging is in progress
|
// this._dragActor under the pointer when dragging is in progress
|
||||||
@@ -191,16 +184,24 @@ const _Draggable = new Lang.Class({
|
|||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
if (symbol == Clutter.Escape) {
|
if (symbol == Clutter.Escape) {
|
||||||
this._cancelDrag(event.get_time());
|
this._cancelDrag(event.get_time());
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
}
|
}
|
||||||
} else if (event.type() == Clutter.EventType.LEAVE) {
|
|
||||||
if (this._firstLeaveActor == null)
|
|
||||||
this._firstLeaveActor = event.get_source();
|
|
||||||
} else if (event.type() == Clutter.EventType.ENTER) {
|
|
||||||
this._lastEnterActor = event.get_source();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fakeRelease:
|
||||||
|
*
|
||||||
|
* Fake a release event.
|
||||||
|
* Must be called if you want to intercept release events on draggable
|
||||||
|
* actors for other purposes (for example if you're using
|
||||||
|
* PopupMenu.ignoreRelease())
|
||||||
|
*/
|
||||||
|
fakeRelease: function() {
|
||||||
|
this._buttonDown = false;
|
||||||
|
this._ungrabActor();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -228,14 +229,14 @@ const _Draggable = new Lang.Class({
|
|||||||
if (this._onEventId)
|
if (this._onEventId)
|
||||||
this._ungrabActor();
|
this._ungrabActor();
|
||||||
this._grabEvents();
|
this._grabEvents();
|
||||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
|
||||||
|
|
||||||
this._dragX = this._dragStartX = stageX;
|
this._dragX = this._dragStartX = stageX;
|
||||||
this._dragY = this._dragStartY = stageY;
|
this._dragY = this._dragStartY = stageY;
|
||||||
|
|
||||||
if (this.actor._delegate && this.actor._delegate.getDragActor) {
|
if (this.actor._delegate && this.actor._delegate.getDragActor) {
|
||||||
this._dragActor = this.actor._delegate.getDragActor();
|
this._dragActor = this.actor._delegate.getDragActor();
|
||||||
this._dragActor.reparent(Main.uiGroup);
|
Main.layoutManager.sessionGroup.add_child(this._dragActor);
|
||||||
this._dragActor.raise_top();
|
this._dragActor.raise_top();
|
||||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||||
|
|
||||||
@@ -274,19 +275,20 @@ const _Draggable = new Lang.Class({
|
|||||||
this._dragOrigY = this._dragActor.y;
|
this._dragOrigY = this._dragActor.y;
|
||||||
this._dragOrigScale = this._dragActor.scale_x;
|
this._dragOrigScale = this._dragActor.scale_x;
|
||||||
|
|
||||||
this._dragActor.reparent(Main.uiGroup);
|
// Set the actor's scale such that it will keep the same
|
||||||
this._dragActor.raise_top();
|
// transformed size when it's reparented to the sessionGroup
|
||||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
|
||||||
|
this._dragActor.set_scale(scaledWidth / this.actor.width,
|
||||||
|
scaledHeight / this.actor.height);
|
||||||
|
|
||||||
let [actorStageX, actorStageY] = this.actor.get_transformed_position();
|
let [actorStageX, actorStageY] = this.actor.get_transformed_position();
|
||||||
this._dragOffsetX = actorStageX - this._dragStartX;
|
this._dragOffsetX = actorStageX - this._dragStartX;
|
||||||
this._dragOffsetY = actorStageY - this._dragStartY;
|
this._dragOffsetY = actorStageY - this._dragStartY;
|
||||||
|
|
||||||
// Set the actor's scale such that it will keep the same
|
this._dragOrigParent.remove_actor(this._dragActor);
|
||||||
// transformed size when it's reparented to the uiGroup
|
Main.layoutManager.sessionGroup.add_child(this._dragActor);
|
||||||
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
|
this._dragActor.raise_top();
|
||||||
this.actor.set_scale(scaledWidth / this.actor.width,
|
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||||
scaledHeight / this.actor.height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dragOrigOpacity = this._dragActor.opacity;
|
this._dragOrigOpacity = this._dragActor.opacity;
|
||||||
@@ -343,25 +345,13 @@ const _Draggable = new Lang.Class({
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateDragPosition : function (event) {
|
_updateDragHover : function () {
|
||||||
let [stageX, stageY] = event.get_coords();
|
this._updateHoverId = 0;
|
||||||
this._dragX = stageX;
|
|
||||||
this._dragY = stageY;
|
|
||||||
|
|
||||||
// If we are dragging, update the position
|
|
||||||
if (this._dragActor) {
|
|
||||||
this._dragActor.set_position(stageX + this._dragOffsetX,
|
|
||||||
stageY + this._dragOffsetY);
|
|
||||||
|
|
||||||
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
|
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
|
||||||
stageX, stageY);
|
this._dragX, this._dragY);
|
||||||
|
|
||||||
// We call observers only once per motion with the innermost
|
|
||||||
// target actor. If necessary, the observer can walk the
|
|
||||||
// parent itself.
|
|
||||||
let dragEvent = {
|
let dragEvent = {
|
||||||
x: stageX,
|
x: this._dragX,
|
||||||
y: stageY,
|
y: this._dragY,
|
||||||
dragActor: this._dragActor,
|
dragActor: this._dragActor,
|
||||||
source: this.actor._delegate,
|
source: this.actor._delegate,
|
||||||
targetActor: target
|
targetActor: target
|
||||||
@@ -371,14 +361,15 @@ const _Draggable = new Lang.Class({
|
|||||||
if (motionFunc) {
|
if (motionFunc) {
|
||||||
let result = motionFunc(dragEvent);
|
let result = motionFunc(dragEvent);
|
||||||
if (result != DragMotionResult.CONTINUE) {
|
if (result != DragMotionResult.CONTINUE) {
|
||||||
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||||
return true;
|
return GLib.SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (target) {
|
while (target) {
|
||||||
if (target._delegate && target._delegate.handleDragOver) {
|
if (target._delegate && target._delegate.handleDragOver) {
|
||||||
let [r, targX, targY] = target.transform_stage_point(stageX, stageY);
|
let [r, targX, targY] = target.transform_stage_point(this._dragX, this._dragY);
|
||||||
// We currently loop through all parents on drag-over even if one of the children has handled it.
|
// We currently loop through all parents on drag-over even if one of the children has handled it.
|
||||||
// We can check the return value of the function and break the loop if it's true if we don't want
|
// We can check the return value of the function and break the loop if it's true if we don't want
|
||||||
// to continue checking the parents.
|
// to continue checking the parents.
|
||||||
@@ -386,17 +377,34 @@ const _Draggable = new Lang.Class({
|
|||||||
this._dragActor,
|
this._dragActor,
|
||||||
targX,
|
targX,
|
||||||
targY,
|
targY,
|
||||||
event.get_time());
|
0);
|
||||||
if (result != DragMotionResult.CONTINUE) {
|
if (result != DragMotionResult.CONTINUE) {
|
||||||
global.set_cursor(DRAG_CURSOR_MAP[result]);
|
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||||
return true;
|
return GLib.SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
target = target.get_parent();
|
target = target.get_parent();
|
||||||
}
|
}
|
||||||
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
|
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
|
||||||
}
|
return GLib.SOURCE_REMOVE;
|
||||||
|
},
|
||||||
|
|
||||||
|
_queueUpdateDragHover: function() {
|
||||||
|
if (this._updateHoverId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
|
||||||
|
Lang.bind(this, this._updateDragHover));
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateDragPosition : function (event) {
|
||||||
|
let [stageX, stageY] = event.get_coords();
|
||||||
|
this._dragX = stageX;
|
||||||
|
this._dragY = stageY;
|
||||||
|
this._dragActor.set_position(stageX + this._dragOffsetX,
|
||||||
|
stageY + this._dragOffsetY);
|
||||||
|
|
||||||
|
this._queueUpdateDragHover();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -425,6 +433,11 @@ const _Draggable = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// At this point it is too late to cancel a drag by destroying
|
||||||
|
// the actor, the fate of which is decided by acceptDrop and its
|
||||||
|
// side-effects
|
||||||
|
this._dragCancellable = false;
|
||||||
|
|
||||||
while (target) {
|
while (target) {
|
||||||
if (target._delegate && target._delegate.acceptDrop) {
|
if (target._delegate && target._delegate.acceptDrop) {
|
||||||
let [r, targX, targY] = target.transform_stage_point(dropX, dropY);
|
let [r, targX, targY] = target.transform_stage_point(dropX, dropY);
|
||||||
@@ -433,11 +446,9 @@ const _Draggable = new Lang.Class({
|
|||||||
targX,
|
targX,
|
||||||
targY,
|
targY,
|
||||||
event.get_time())) {
|
event.get_time())) {
|
||||||
if (this._actorDestroyed)
|
|
||||||
return true;
|
|
||||||
// If it accepted the drop without taking the actor,
|
// If it accepted the drop without taking the actor,
|
||||||
// handle it ourselves.
|
// handle it ourselves.
|
||||||
if (this._dragActor.get_parent() == Main.uiGroup) {
|
if (this._dragActor.get_parent() == Main.layoutManager.sessionGroup) {
|
||||||
if (this._restoreOnSuccess) {
|
if (this._restoreOnSuccess) {
|
||||||
this._restoreDragActor(event.get_time());
|
this._restoreDragActor(event.get_time());
|
||||||
return true;
|
return true;
|
||||||
@@ -446,7 +457,7 @@ const _Draggable = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._dragInProgress = false;
|
this._dragInProgress = false;
|
||||||
global.unset_cursor();
|
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||||
this.emit('drag-end', event.get_time(), true);
|
this.emit('drag-end', event.get_time(), true);
|
||||||
this._dragComplete();
|
this._dragComplete();
|
||||||
return true;
|
return true;
|
||||||
@@ -498,7 +509,7 @@ const _Draggable = new Lang.Class({
|
|||||||
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
|
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
|
||||||
|
|
||||||
if (this._actorDestroyed) {
|
if (this._actorDestroyed) {
|
||||||
global.unset_cursor();
|
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||||
if (!this._buttonDown)
|
if (!this._buttonDown)
|
||||||
this._dragComplete();
|
this._dragComplete();
|
||||||
this.emit('drag-end', eventTime, false);
|
this.emit('drag-end', eventTime, false);
|
||||||
@@ -546,13 +557,14 @@ const _Draggable = new Lang.Class({
|
|||||||
|
|
||||||
_onAnimationComplete : function (dragActor, eventTime) {
|
_onAnimationComplete : function (dragActor, eventTime) {
|
||||||
if (this._dragOrigParent) {
|
if (this._dragOrigParent) {
|
||||||
dragActor.reparent(this._dragOrigParent);
|
Main.layoutManager.sessionGroup.remove_child(this._dragActor);
|
||||||
|
this._dragOrigParent.add_actor(this._dragActor);
|
||||||
dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
|
dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
|
||||||
dragActor.set_position(this._dragOrigX, this._dragOrigY);
|
dragActor.set_position(this._dragOrigX, this._dragOrigY);
|
||||||
} else {
|
} else {
|
||||||
dragActor.destroy();
|
dragActor.destroy();
|
||||||
}
|
}
|
||||||
global.unset_cursor();
|
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||||
this.emit('drag-end', eventTime, false);
|
this.emit('drag-end', eventTime, false);
|
||||||
|
|
||||||
this._animationInProgress = false;
|
this._animationInProgress = false;
|
||||||
@@ -560,32 +572,16 @@ const _Draggable = new Lang.Class({
|
|||||||
this._dragComplete();
|
this._dragComplete();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Actor is an actor we have entered or left during the drag; call
|
|
||||||
// st_widget_sync_hover on all StWidget ancestors
|
|
||||||
_syncHover: function(actor) {
|
|
||||||
while (actor) {
|
|
||||||
let parent = actor.get_parent();
|
|
||||||
if (actor instanceof St.Widget)
|
|
||||||
actor.sync_hover();
|
|
||||||
|
|
||||||
actor = parent;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_dragComplete: function() {
|
_dragComplete: function() {
|
||||||
if (!this._actorDestroyed)
|
if (!this._actorDestroyed)
|
||||||
Shell.util_set_hidden_from_pick(this._dragActor, false);
|
Shell.util_set_hidden_from_pick(this._dragActor, false);
|
||||||
|
|
||||||
this._ungrabEvents();
|
this._ungrabEvents();
|
||||||
|
global.sync_pointer();
|
||||||
|
|
||||||
if (this._firstLeaveActor) {
|
if (this._updateHoverId) {
|
||||||
this._syncHover(this._firstLeaveActor);
|
GLib.source_remove(this._updateHoverId);
|
||||||
this._firstLeaveActor = null;
|
this._updateHoverId = 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (this._lastEnterActor) {
|
|
||||||
this._syncHover(this._lastEnterActor);
|
|
||||||
this._lastEnterActor = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dragActor = undefined;
|
this._dragActor = undefined;
|
||||||
|
|||||||
@@ -13,13 +13,11 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Signals = imports.signals;
|
const Mainloop = imports.mainloop;
|
||||||
|
|
||||||
const AccountsService = imports.gi.AccountsService;
|
const AccountsService = imports.gi.AccountsService;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
@@ -31,10 +29,10 @@ const St = imports.gi.St;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
const GnomeSession = imports.misc.gnomeSession;
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
const Main = imports.ui.main;
|
const LoginManager = imports.misc.loginManager;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const UserMenu = imports.ui.userMenu;
|
const UserWidget = imports.ui.userWidget;
|
||||||
|
|
||||||
let _endSessionDialog = null;
|
let _endSessionDialog = null;
|
||||||
|
|
||||||
@@ -43,80 +41,118 @@ const _DIALOG_ICON_SIZE = 32;
|
|||||||
|
|
||||||
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
|
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
|
||||||
|
|
||||||
const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessionDialog">
|
const EndSessionDialogIface = '<node> \
|
||||||
<method name="Open">
|
<interface name="org.gnome.SessionManager.EndSessionDialog"> \
|
||||||
<arg type="u" direction="in" />
|
<method name="Open"> \
|
||||||
<arg type="u" direction="in" />
|
<arg type="u" direction="in" /> \
|
||||||
<arg type="u" direction="in" />
|
<arg type="u" direction="in" /> \
|
||||||
<arg type="ao" direction="in" />
|
<arg type="u" direction="in" /> \
|
||||||
</method>
|
<arg type="ao" direction="in" /> \
|
||||||
<signal name="ConfirmedLogout" />
|
</method> \
|
||||||
<signal name="ConfirmedReboot" />
|
<method name="Close" /> \
|
||||||
<signal name="ConfirmedShutdown" />
|
<signal name="ConfirmedLogout" /> \
|
||||||
<signal name="Canceled" />
|
<signal name="ConfirmedReboot" /> \
|
||||||
<signal name="Closed" />
|
<signal name="ConfirmedShutdown" /> \
|
||||||
</interface>;
|
<signal name="Canceled" /> \
|
||||||
|
<signal name="Closed" /> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const logoutDialogContent = {
|
const logoutDialogContent = {
|
||||||
subjectWithUser: C_("title", "Log Out %s"),
|
subjectWithUser: C_("title", "Log Out %s"),
|
||||||
subject: C_("title", "Log Out"),
|
subject: C_("title", "Log Out"),
|
||||||
inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
|
descriptionWithUser: function(user, seconds) {
|
||||||
uninhibitedDescriptionWithUser: function(user, seconds) {
|
|
||||||
return ngettext("%s will be logged out automatically in %d second.",
|
return ngettext("%s will be logged out automatically in %d second.",
|
||||||
"%s will be logged out automatically in %d seconds.",
|
"%s will be logged out automatically in %d seconds.",
|
||||||
seconds).format(user, seconds);
|
seconds).format(user, seconds);
|
||||||
},
|
},
|
||||||
uninhibitedDescription: function(seconds) {
|
description: function(seconds) {
|
||||||
return ngettext("You will be logged out automatically in %d second.",
|
return ngettext("You will be logged out automatically in %d second.",
|
||||||
"You will be logged out automatically in %d seconds.",
|
"You will be logged out automatically in %d seconds.",
|
||||||
seconds).format(seconds);
|
seconds).format(seconds);
|
||||||
},
|
},
|
||||||
endDescription: _("Logging out of the system."),
|
|
||||||
confirmButtons: [{ signal: 'ConfirmedLogout',
|
confirmButtons: [{ signal: 'ConfirmedLogout',
|
||||||
label: C_("button", "Log Out") }],
|
label: C_("button", "Log Out") }],
|
||||||
iconStyleClass: 'end-session-dialog-logout-icon'
|
iconStyleClass: 'end-session-dialog-logout-icon',
|
||||||
|
showOtherSessions: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const shutdownDialogContent = {
|
const shutdownDialogContent = {
|
||||||
subject: C_("title", "Power Off"),
|
subject: C_("title", "Power Off"),
|
||||||
inhibitedDescription: _("Click Power Off to quit these applications and power off the system."),
|
description: function(seconds) {
|
||||||
uninhibitedDescription: function(seconds) {
|
|
||||||
return ngettext("The system will power off automatically in %d second.",
|
return ngettext("The system will power off automatically in %d second.",
|
||||||
"The system will power off automatically in %d seconds.",
|
"The system will power off automatically in %d seconds.",
|
||||||
seconds).format(seconds);
|
seconds).format(seconds);
|
||||||
},
|
},
|
||||||
endDescription: _("Powering off the system."),
|
|
||||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||||
label: C_("button", "Restart") },
|
label: C_("button", "Restart") },
|
||||||
{ signal: 'ConfirmedShutdown',
|
{ signal: 'ConfirmedShutdown',
|
||||||
label: C_("button", "Power Off") }],
|
label: C_("button", "Power Off") }],
|
||||||
iconName: 'system-shutdown-symbolic',
|
iconName: 'system-shutdown-symbolic',
|
||||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
iconStyleClass: 'end-session-dialog-shutdown-icon',
|
||||||
|
showOtherSessions: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const restartDialogContent = {
|
const restartDialogContent = {
|
||||||
subject: C_("title", "Restart"),
|
subject: C_("title", "Restart"),
|
||||||
inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
|
description: function(seconds) {
|
||||||
uninhibitedDescription: function(seconds) {
|
|
||||||
return ngettext("The system will restart automatically in %d second.",
|
return ngettext("The system will restart automatically in %d second.",
|
||||||
"The system will restart automatically in %d seconds.",
|
"The system will restart automatically in %d seconds.",
|
||||||
seconds).format(seconds);
|
seconds).format(seconds);
|
||||||
},
|
},
|
||||||
endDescription: _("Restarting the system."),
|
|
||||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||||
label: C_("button", "Restart") }],
|
label: C_("button", "Restart") }],
|
||||||
iconName: 'view-refresh-symbolic',
|
iconName: 'view-refresh-symbolic',
|
||||||
iconStyleClass: 'end-session-dialog-shutdown-icon'
|
iconStyleClass: 'end-session-dialog-shutdown-icon',
|
||||||
|
showOtherSessions: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const restartInstallDialogContent = {
|
||||||
|
|
||||||
|
subject: C_("title", "Restart & Install Updates"),
|
||||||
|
description: function(seconds) {
|
||||||
|
return ngettext("The system will automatically restart and install updates in %d second.",
|
||||||
|
"The system will automatically restart and install updates in %d seconds.",
|
||||||
|
seconds).format(seconds);
|
||||||
|
},
|
||||||
|
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||||
|
label: C_("button", "Restart & Install") }],
|
||||||
|
iconName: 'view-refresh-symbolic',
|
||||||
|
iconStyleClass: 'end-session-dialog-shutdown-icon',
|
||||||
|
showOtherSessions: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DialogContent = {
|
const DialogContent = {
|
||||||
0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent,
|
0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent,
|
||||||
1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent,
|
1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent,
|
||||||
2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent
|
2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent,
|
||||||
|
3: restartInstallDialogContent
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MAX_USERS_IN_SESSION_DIALOG = 5;
|
||||||
|
|
||||||
|
const LogindSessionIface = '<node> \
|
||||||
|
<interface name="org.freedesktop.login1.Session"> \
|
||||||
|
<property name="Id" type="s" access="read"/> \
|
||||||
|
<property name="Remote" type="b" access="read"/> \
|
||||||
|
<property name="Class" type="s" access="read"/> \
|
||||||
|
<property name="Type" type="s" access="read"/> \
|
||||||
|
<property name="State" type="s" access="read"/> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
|
const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
|
||||||
|
|
||||||
function findAppFromInhibitor(inhibitor) {
|
function findAppFromInhibitor(inhibitor) {
|
||||||
let [desktopFile] = inhibitor.GetAppIdSync();
|
let desktopFile;
|
||||||
|
try {
|
||||||
|
[desktopFile] = inhibitor.GetAppIdSync();
|
||||||
|
} catch(e) {
|
||||||
|
// XXX -- sometimes JIT inhibitors generated by gnome-session
|
||||||
|
// get removed too soon. Don't fail in this case.
|
||||||
|
log('gnome-session gave us a dead inhibitor: %s'.format(inhibitor.get_object_path()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!GLib.str_has_suffix(desktopFile, '.desktop'))
|
if (!GLib.str_has_suffix(desktopFile, '.desktop'))
|
||||||
desktopFile += '.desktop';
|
desktopFile += '.desktop';
|
||||||
@@ -124,58 +160,6 @@ function findAppFromInhibitor(inhibitor) {
|
|||||||
return Shell.AppSystem.get_default().lookup_heuristic_basename(desktopFile);
|
return Shell.AppSystem.get_default().lookup_heuristic_basename(desktopFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListItem = new Lang.Class({
|
|
||||||
Name: 'ListItem',
|
|
||||||
|
|
||||||
_init: function(app, reason) {
|
|
||||||
this._app = app;
|
|
||||||
this._reason = reason;
|
|
||||||
|
|
||||||
if (this._reason == null)
|
|
||||||
this._reason = '';
|
|
||||||
|
|
||||||
let layout = new St.BoxLayout({ vertical: false});
|
|
||||||
|
|
||||||
this.actor = new St.Button({ style_class: 'end-session-dialog-app-list-item',
|
|
||||||
can_focus: true,
|
|
||||||
child: layout,
|
|
||||||
reactive: true,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
x_fill: true });
|
|
||||||
|
|
||||||
this._icon = this._app.create_icon_texture(_ITEM_ICON_SIZE);
|
|
||||||
|
|
||||||
let iconBin = new St.Bin({ style_class: 'end-session-dialog-app-list-item-icon',
|
|
||||||
child: this._icon });
|
|
||||||
layout.add(iconBin);
|
|
||||||
|
|
||||||
let textLayout = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item-text-box',
|
|
||||||
vertical: true });
|
|
||||||
layout.add(textLayout);
|
|
||||||
|
|
||||||
this._nameLabel = new St.Label({ text: this._app.get_name(),
|
|
||||||
style_class: 'end-session-dialog-app-list-item-name' });
|
|
||||||
textLayout.add(this._nameLabel,
|
|
||||||
{ expand: false,
|
|
||||||
x_fill: true });
|
|
||||||
|
|
||||||
this._descriptionLabel = new St.Label({ text: this._reason,
|
|
||||||
style_class: 'end-session-dialog-app-list-item-description' });
|
|
||||||
this.actor.label_actor = this._nameLabel;
|
|
||||||
textLayout.add(this._descriptionLabel,
|
|
||||||
{ expand: true,
|
|
||||||
x_fill: true });
|
|
||||||
|
|
||||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
|
||||||
},
|
|
||||||
|
|
||||||
_onClicked: function() {
|
|
||||||
this.emit('activate');
|
|
||||||
this._app.activate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Signals.addSignalMethods(ListItem.prototype);
|
|
||||||
|
|
||||||
// The logout timer only shows updates every 10 seconds
|
// The logout timer only shows updates every 10 seconds
|
||||||
// until the last 10 seconds, then it shows updates every
|
// until the last 10 seconds, then it shows updates every
|
||||||
// second. This function takes a given time and returns
|
// second. This function takes a given time and returns
|
||||||
@@ -223,24 +207,26 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
Extends: ModalDialog.ModalDialog,
|
Extends: ModalDialog.ModalDialog,
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent({ styleClass: 'end-session-dialog' });
|
this.parent({ styleClass: 'end-session-dialog',
|
||||||
|
destroyOnClose: false });
|
||||||
|
|
||||||
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
|
this._loginManager = LoginManager.getLoginManager();
|
||||||
|
this._userManager = AccountsService.UserManager.get_default();
|
||||||
|
this._user = this._userManager.get_user(GLib.get_user_name());
|
||||||
|
this._updatesFile = Gio.File.new_for_path('/system-update');
|
||||||
|
|
||||||
this._secondsLeft = 0;
|
this._secondsLeft = 0;
|
||||||
this._totalSecondsToStayOpen = 0;
|
this._totalSecondsToStayOpen = 0;
|
||||||
this._inhibitors = [];
|
this._applications = [];
|
||||||
|
this._sessions = [];
|
||||||
|
|
||||||
this.connect('destroy',
|
this.connect('destroy',
|
||||||
Lang.bind(this, this._onDestroy));
|
Lang.bind(this, this._onDestroy));
|
||||||
this.connect('opened',
|
this.connect('opened',
|
||||||
Lang.bind(this, this._onOpened));
|
Lang.bind(this, this._onOpened));
|
||||||
|
|
||||||
this._userLoadedId = this._user.connect('notify::is_loaded',
|
this._userLoadedId = this._user.connect('notify::is_loaded', Lang.bind(this, this._sync));
|
||||||
Lang.bind(this, this._updateContent));
|
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._sync));
|
||||||
|
|
||||||
this._userChangedId = this._user.connect('changed',
|
|
||||||
Lang.bind(this, this._updateContent));
|
|
||||||
|
|
||||||
let mainContentLayout = new St.BoxLayout({ vertical: false });
|
let mainContentLayout = new St.BoxLayout({ vertical: false });
|
||||||
this.contentLayout.add(mainContentLayout,
|
this.contentLayout.add(mainContentLayout,
|
||||||
@@ -261,7 +247,9 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
|
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
|
||||||
|
|
||||||
messageLayout.add(this._subjectLabel,
|
messageLayout.add(this._subjectLabel,
|
||||||
{ y_fill: false,
|
{ x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
|
|
||||||
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
|
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
|
||||||
@@ -272,28 +260,30 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
{ y_fill: true,
|
{ y_fill: true,
|
||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
|
|
||||||
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list'});
|
this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' });
|
||||||
scrollView.set_policy(Gtk.PolicyType.NEVER,
|
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||||
Gtk.PolicyType.AUTOMATIC);
|
this.contentLayout.add(this._scrollView,
|
||||||
this.contentLayout.add(scrollView,
|
|
||||||
{ x_fill: true,
|
{ x_fill: true,
|
||||||
y_fill: true });
|
y_fill: true });
|
||||||
scrollView.hide();
|
this._scrollView.hide();
|
||||||
|
|
||||||
this._applicationList = new St.BoxLayout({ vertical: true });
|
this._inhibitorSection = new St.BoxLayout({ vertical: true,
|
||||||
scrollView.add_actor(this._applicationList);
|
style_class: 'end-session-dialog-inhibitor-layout' });
|
||||||
|
this._scrollView.add_actor(this._inhibitorSection);
|
||||||
|
|
||||||
this._applicationList.connect('actor-added',
|
this._applicationHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
|
||||||
Lang.bind(this, function() {
|
text: _("Some applications are busy or have unsaved work.") });
|
||||||
if (this._applicationList.get_n_children() == 1)
|
this._applicationList = new St.BoxLayout({ style_class: 'end-session-dialog-app-list',
|
||||||
scrollView.show();
|
vertical: true });
|
||||||
}));
|
this._inhibitorSection.add_actor(this._applicationHeader);
|
||||||
|
this._inhibitorSection.add_actor(this._applicationList);
|
||||||
|
|
||||||
this._applicationList.connect('actor-removed',
|
this._sessionHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
|
||||||
Lang.bind(this, function() {
|
text: _("Other users are logged in.") });
|
||||||
if (this._applicationList.get_n_children() == 0)
|
this._sessionList = new St.BoxLayout({ style_class: 'end-session-dialog-session-list',
|
||||||
scrollView.hide();
|
vertical: true });
|
||||||
}));
|
this._inhibitorSection.add_actor(this._sessionHeader);
|
||||||
|
this._inhibitorSection.add_actor(this._sessionList);
|
||||||
|
|
||||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
|
||||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
|
||||||
@@ -304,20 +294,19 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
this._user.disconnect(this._userChangedId);
|
this._user.disconnect(this._userChangedId);
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateDescription: function() {
|
_sync: function() {
|
||||||
if (this.state != ModalDialog.State.OPENING &&
|
let open = (this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED);
|
||||||
this.state != ModalDialog.State.OPENED)
|
if (!open)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (this._type == 2 && this._updatesFile.query_exists(null))
|
||||||
|
this._type = 3;
|
||||||
|
|
||||||
let dialogContent = DialogContent[this._type];
|
let dialogContent = DialogContent[this._type];
|
||||||
|
|
||||||
let subject = dialogContent.subject;
|
let subject = dialogContent.subject;
|
||||||
let description;
|
|
||||||
|
|
||||||
if (this._inhibitors.length > 0) {
|
let description;
|
||||||
this._stopTimer();
|
|
||||||
description = dialogContent.inhibitedDescription;
|
|
||||||
} else if (this._secondsLeft > 0 && this._inhibitors.length == 0) {
|
|
||||||
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
|
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
|
||||||
this._secondsLeft,
|
this._secondsLeft,
|
||||||
10);
|
10);
|
||||||
@@ -329,27 +318,18 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
if (dialogContent.subjectWithUser)
|
if (dialogContent.subjectWithUser)
|
||||||
subject = dialogContent.subjectWithUser.format(realName);
|
subject = dialogContent.subjectWithUser.format(realName);
|
||||||
|
|
||||||
if (dialogContent.uninhibitedDescriptionWithUser)
|
if (dialogContent.descriptionWithUser)
|
||||||
description = dialogContent.uninhibitedDescriptionWithUser(realName, displayTime);
|
description = dialogContent.descriptionWithUser(realName, displayTime);
|
||||||
else
|
else
|
||||||
description = dialogContent.uninhibitedDescription(displayTime);
|
description = dialogContent.description(displayTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!description)
|
if (!description)
|
||||||
description = dialogContent.uninhibitedDescription(displayTime);
|
description = dialogContent.description(displayTime);
|
||||||
} else {
|
|
||||||
description = dialogContent.endDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
_setLabelText(this._subjectLabel, subject);
|
|
||||||
_setLabelText(this._descriptionLabel, description);
|
_setLabelText(this._descriptionLabel, description);
|
||||||
},
|
_setLabelText(this._subjectLabel, subject);
|
||||||
|
|
||||||
_updateContent: function() {
|
|
||||||
if (this.state != ModalDialog.State.OPENING &&
|
|
||||||
this.state != ModalDialog.State.OPENED)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let dialogContent = DialogContent[this._type];
|
let dialogContent = DialogContent[this._type];
|
||||||
if (dialogContent.iconName) {
|
if (dialogContent.iconName) {
|
||||||
@@ -357,14 +337,18 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
icon_size: _DIALOG_ICON_SIZE,
|
icon_size: _DIALOG_ICON_SIZE,
|
||||||
style_class: dialogContent.iconStyleClass });
|
style_class: dialogContent.iconStyleClass });
|
||||||
} else {
|
} else {
|
||||||
let avatarWidget = new UserMenu.UserAvatarWidget(this._user,
|
let avatarWidget = new UserWidget.Avatar(this._user,
|
||||||
{ iconSize: _DIALOG_ICON_SIZE,
|
{ iconSize: _DIALOG_ICON_SIZE,
|
||||||
styleClass: dialogContent.iconStyleClass });
|
styleClass: dialogContent.iconStyleClass });
|
||||||
this._iconBin.child = avatarWidget.actor;
|
this._iconBin.child = avatarWidget.actor;
|
||||||
avatarWidget.update();
|
avatarWidget.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateDescription();
|
let hasApplications = this._applications.length > 0;
|
||||||
|
let hasSessions = this._sessions.length > 0;
|
||||||
|
this._scrollView.visible = hasApplications || hasSessions;
|
||||||
|
this._applicationHeader.visible = hasApplications;
|
||||||
|
this._sessionHeader.visible = hasSessions;
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateButtons: function() {
|
_updateButtons: function() {
|
||||||
@@ -377,7 +361,12 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
let signal = dialogContent.confirmButtons[i].signal;
|
let signal = dialogContent.confirmButtons[i].signal;
|
||||||
let label = dialogContent.confirmButtons[i].label;
|
let label = dialogContent.confirmButtons[i].label;
|
||||||
buttons.push({ action: Lang.bind(this, function() {
|
buttons.push({ action: Lang.bind(this, function() {
|
||||||
|
this.close(true);
|
||||||
|
let signalId = this.connect('closed',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this.disconnect(signalId);
|
||||||
this._confirm(signal);
|
this._confirm(signal);
|
||||||
|
}));
|
||||||
}),
|
}),
|
||||||
label: label });
|
label: label });
|
||||||
}
|
}
|
||||||
@@ -385,15 +374,17 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
this.setButtons(buttons);
|
this.setButtons(buttons);
|
||||||
},
|
},
|
||||||
|
|
||||||
close: function() {
|
close: function(skipSignal) {
|
||||||
this.parent();
|
this.parent();
|
||||||
|
|
||||||
|
if (!skipSignal)
|
||||||
this._dbusImpl.emit_signal('Closed', null);
|
this._dbusImpl.emit_signal('Closed', null);
|
||||||
},
|
},
|
||||||
|
|
||||||
cancel: function() {
|
cancel: function() {
|
||||||
this._stopTimer();
|
this._stopTimer();
|
||||||
this._dbusImpl.emit_signal('Canceled', null);
|
this._dbusImpl.emit_signal('Canceled', null);
|
||||||
this.close(global.get_current_time());
|
this.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
_confirm: function(signal) {
|
_confirm: function(signal) {
|
||||||
@@ -403,32 +394,69 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onOpened: function() {
|
_onOpened: function() {
|
||||||
if (this._inhibitors.length == 0)
|
this._sync();
|
||||||
this._startTimer();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_startTimer: function() {
|
_startTimer: function() {
|
||||||
|
let startTime = GLib.get_monotonic_time();
|
||||||
this._secondsLeft = this._totalSecondsToStayOpen;
|
this._secondsLeft = this._totalSecondsToStayOpen;
|
||||||
Tweener.addTween(this,
|
|
||||||
{ _secondsLeft: 0,
|
this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
|
||||||
time: this._secondsLeft,
|
function() {
|
||||||
transition: 'linear',
|
let currentTime = GLib.get_monotonic_time();
|
||||||
onUpdate: Lang.bind(this, this._updateDescription),
|
let secondsElapsed = ((currentTime - startTime) / 1000000);
|
||||||
onComplete: Lang.bind(this, function() {
|
|
||||||
|
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
|
||||||
|
if (this._secondsLeft > 0) {
|
||||||
|
this._sync();
|
||||||
|
return GLib.SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
let dialogContent = DialogContent[this._type];
|
let dialogContent = DialogContent[this._type];
|
||||||
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
||||||
this._confirm(button.signal);
|
this._confirm(button.signal);
|
||||||
}),
|
this._timerId = 0;
|
||||||
});
|
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_stopTimer: function() {
|
_stopTimer: function() {
|
||||||
Tweener.removeTweens(this);
|
if (this._timerId > 0) {
|
||||||
|
Mainloop.source_remove(this._timerId);
|
||||||
|
this._timerId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
this._secondsLeft = 0;
|
this._secondsLeft = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_constructListItemForApp: function(inhibitor, app) {
|
||||||
|
let actor = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item',
|
||||||
|
can_focus: true });
|
||||||
|
actor.add(app.create_icon_texture(_ITEM_ICON_SIZE));
|
||||||
|
|
||||||
|
let textLayout = new St.BoxLayout({ vertical: true,
|
||||||
|
y_expand: true,
|
||||||
|
y_align: Clutter.ActorAlign.CENTER });
|
||||||
|
actor.add(textLayout);
|
||||||
|
|
||||||
|
let nameLabel = new St.Label({ text: app.get_name(),
|
||||||
|
style_class: 'end-session-dialog-app-list-item-name' });
|
||||||
|
textLayout.add(nameLabel);
|
||||||
|
actor.label_actor = nameLabel;
|
||||||
|
|
||||||
|
let [reason] = inhibitor.GetReasonSync();
|
||||||
|
if (reason) {
|
||||||
|
let reasonLabel = new St.Label({ text: reason,
|
||||||
|
style_class: 'end-session-dialog-app-list-item-description' });
|
||||||
|
textLayout.add(reasonLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actor;
|
||||||
|
},
|
||||||
|
|
||||||
_onInhibitorLoaded: function(inhibitor) {
|
_onInhibitorLoaded: function(inhibitor) {
|
||||||
if (this._inhibitors.indexOf(inhibitor) < 0) {
|
if (this._applications.indexOf(inhibitor) < 0) {
|
||||||
// Stale inhibitor
|
// Stale inhibitor
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -436,29 +464,92 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
let app = findAppFromInhibitor(inhibitor);
|
let app = findAppFromInhibitor(inhibitor);
|
||||||
|
|
||||||
if (app) {
|
if (app) {
|
||||||
let [reason] = inhibitor.GetReasonSync();
|
let actor = this._constructListItemForApp(inhibitor, app);
|
||||||
let item = new ListItem(app, reason);
|
this._applicationList.add(actor);
|
||||||
item.connect('activate',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
this.close(global.get_current_time());
|
|
||||||
}));
|
|
||||||
this._applicationList.add(item.actor, { x_fill: true });
|
|
||||||
this._stopTimer();
|
|
||||||
} else {
|
} else {
|
||||||
// inhibiting app is a service, not an application
|
// inhibiting app is a service, not an application
|
||||||
this._inhibitors.splice(this._inhibitors.indexOf(inhibitor), 1);
|
this._applications.splice(this._applications.indexOf(inhibitor), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateContent();
|
this._sync();
|
||||||
|
},
|
||||||
|
|
||||||
|
_constructListItemForSession: function(session) {
|
||||||
|
let avatar = new UserWidget.Avatar(session.user, { iconSize: _ITEM_ICON_SIZE });
|
||||||
|
avatar.update();
|
||||||
|
|
||||||
|
let userName = session.user.get_real_name() ? session.user.get_real_name() : session.username;
|
||||||
|
let userLabelText;
|
||||||
|
|
||||||
|
if (session.remote)
|
||||||
|
/* Translators: Remote here refers to a remote session, like a ssh login */
|
||||||
|
userLabelText = _("%s (remote)").format(userName);
|
||||||
|
else if (session.type == "tty")
|
||||||
|
/* Translators: Console here refers to a tty like a VT console */
|
||||||
|
userLabelText = _("%s (console)").format(userName);
|
||||||
|
else
|
||||||
|
userLabelText = userName;
|
||||||
|
|
||||||
|
let actor = new St.BoxLayout({ style_class: 'end-session-dialog-session-list-item',
|
||||||
|
can_focus: true });
|
||||||
|
actor.add(avatar.actor);
|
||||||
|
|
||||||
|
let nameLabel = new St.Label({ text: userLabelText,
|
||||||
|
style_class: 'end-session-dialog-session-list-item-name',
|
||||||
|
y_expand: true,
|
||||||
|
y_align: Clutter.ActorAlign.CENTER });
|
||||||
|
actor.add(nameLabel);
|
||||||
|
actor.label_actor = nameLabel;
|
||||||
|
|
||||||
|
return actor;
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadSessions: function() {
|
||||||
|
this._loginManager.listSessions(Lang.bind(this, function(result) {
|
||||||
|
let n = 0;
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
let[id, uid, userName, seat, sessionPath] = result[i];
|
||||||
|
let proxy = new LogindSession(Gio.DBus.system, 'org.freedesktop.login1', sessionPath);
|
||||||
|
|
||||||
|
if (proxy.Class != 'user')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (proxy.State == 'closing')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let session = { user: this._userManager.get_user(userName),
|
||||||
|
username: userName,
|
||||||
|
type: proxy.Type,
|
||||||
|
remote: proxy.Remote };
|
||||||
|
this._sessions.push(session);
|
||||||
|
|
||||||
|
let actor = this._constructListItemForSession(session);
|
||||||
|
this._sessionList.add(actor);
|
||||||
|
|
||||||
|
// limit the number of entries
|
||||||
|
n++;
|
||||||
|
if (n == MAX_USERS_IN_SESSION_DIALOG)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._sync();
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
OpenAsync: function(parameters, invocation) {
|
OpenAsync: function(parameters, invocation) {
|
||||||
let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
|
let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
|
||||||
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
|
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
|
||||||
this._inhibitors = [];
|
|
||||||
this._applicationList.destroy_all_children();
|
|
||||||
this._type = type;
|
this._type = type;
|
||||||
|
|
||||||
|
this._applications = [];
|
||||||
|
this._applicationList.destroy_all_children();
|
||||||
|
|
||||||
|
this._sessions = [];
|
||||||
|
this._sessionList.destroy_all_children();
|
||||||
|
|
||||||
if (!(this._type in DialogContent)) {
|
if (!(this._type in DialogContent)) {
|
||||||
invocation.return_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
|
invocation.return_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
|
||||||
"Unknown dialog type requested");
|
"Unknown dialog type requested");
|
||||||
@@ -470,9 +561,12 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
this._onInhibitorLoaded(proxy);
|
this._onInhibitorLoaded(proxy);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._inhibitors.push(inhibitor);
|
this._applications.push(inhibitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DialogContent[type].showOtherSessions)
|
||||||
|
this._loadSessions();
|
||||||
|
|
||||||
this._updateButtons();
|
this._updateButtons();
|
||||||
|
|
||||||
if (!this.open(timestamp)) {
|
if (!this.open(timestamp)) {
|
||||||
@@ -481,12 +575,17 @@ const EndSessionDialog = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateContent();
|
this._startTimer();
|
||||||
|
this._sync();
|
||||||
|
|
||||||
let signalId = this.connect('opened',
|
let signalId = this.connect('opened',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
invocation.return_value(null);
|
invocation.return_value(null);
|
||||||
this.disconnect(signalId);
|
this.disconnect(signalId);
|
||||||
}));
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
Close: function(parameters, invocation) {
|
||||||
|
this.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const Clutter = imports.gi.Clutter;;
|
|||||||
const Gettext = imports.gettext;
|
const Gettext = imports.gettext;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
@@ -39,6 +40,22 @@ function _patchContainerClass(containerClass) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _patchLayoutClass(layoutClass, styleProps) {
|
||||||
|
if (styleProps)
|
||||||
|
layoutClass.prototype.hookup_style = function(container) {
|
||||||
|
container.connect('style-changed', Lang.bind(this, function() {
|
||||||
|
let node = container.get_theme_node();
|
||||||
|
for (let prop in styleProps)
|
||||||
|
this[prop] = node.get_length(styleProps[prop]);
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
layoutClass.prototype.child_set = function(actor, props) {
|
||||||
|
let meta = this.get_child_meta(actor.get_parent(), actor);
|
||||||
|
for (let prop in props)
|
||||||
|
meta[prop] = props[prop];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function _makeLoggingFunc(func) {
|
function _makeLoggingFunc(func) {
|
||||||
return function() {
|
return function() {
|
||||||
return func([].join.call(arguments, ', '));
|
return func([].join.call(arguments, ', '));
|
||||||
@@ -60,6 +77,12 @@ function init() {
|
|||||||
_patchContainerClass(St.BoxLayout);
|
_patchContainerClass(St.BoxLayout);
|
||||||
_patchContainerClass(St.Table);
|
_patchContainerClass(St.Table);
|
||||||
|
|
||||||
|
_patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows',
|
||||||
|
column_spacing: 'spacing-columns' });
|
||||||
|
_patchLayoutClass(Clutter.GridLayout, { row_spacing: 'spacing-rows',
|
||||||
|
column_spacing: 'spacing-columns' });
|
||||||
|
_patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' });
|
||||||
|
|
||||||
Clutter.Actor.prototype.toString = function() {
|
Clutter.Actor.prototype.toString = function() {
|
||||||
return St.describe_actor(this);
|
return St.describe_actor(this);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ const InstallExtensionDialog = new Lang.Class({
|
|||||||
default: true
|
default: true
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name);
|
let message = _("Download and install “%s” from extensions.gnome.org?").format(info.name);
|
||||||
|
|
||||||
let box = new St.BoxLayout();
|
let box = new St.BoxLayout();
|
||||||
this.contentLayout.add(box);
|
this.contentLayout.add(box);
|
||||||
@@ -215,7 +215,7 @@ const InstallExtensionDialog = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onCancelButtonPressed: function(button, event) {
|
_onCancelButtonPressed: function(button, event) {
|
||||||
this.close(global.get_current_time());
|
this.close();
|
||||||
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
|
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -257,7 +257,7 @@ const InstallExtensionDialog = new Lang.Class({
|
|||||||
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
|
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.close(global.get_current_time());
|
this.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,11 @@ function disableExtension(uuid) {
|
|||||||
theme.unload_stylesheet(extension.stylesheet.get_path());
|
theme.unload_stylesheet(extension.stylesheet.get_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
extension.stateObj.disable();
|
extension.stateObj.disable();
|
||||||
|
} catch(e) {
|
||||||
|
logExtensionError(uuid, e);
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < order.length; i++) {
|
for (let i = 0; i < order.length; i++) {
|
||||||
let uuid = order[i];
|
let uuid = order[i];
|
||||||
@@ -89,9 +93,11 @@ function disableExtension(uuid) {
|
|||||||
|
|
||||||
extensionOrder.splice(orderIdx, 1);
|
extensionOrder.splice(orderIdx, 1);
|
||||||
|
|
||||||
|
if ( extension.state != ExtensionState.ERROR ) {
|
||||||
extension.state = ExtensionState.DISABLED;
|
extension.state = ExtensionState.DISABLED;
|
||||||
_signals.emit('extension-state-changed', extension);
|
_signals.emit('extension-state-changed', extension);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function enableExtension(uuid) {
|
function enableExtension(uuid) {
|
||||||
let extension = ExtensionUtils.extensions[uuid];
|
let extension = ExtensionUtils.extensions[uuid];
|
||||||
@@ -106,17 +112,26 @@ function enableExtension(uuid) {
|
|||||||
|
|
||||||
extensionOrder.push(uuid);
|
extensionOrder.push(uuid);
|
||||||
|
|
||||||
let stylesheetFile = extension.dir.get_child('stylesheet.css');
|
let stylesheetNames = [global.session_mode + '.css', 'stylesheet.css'];
|
||||||
|
for (let i = 0; i < stylesheetNames.length; i++) {
|
||||||
|
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
|
||||||
if (stylesheetFile.query_exists(null)) {
|
if (stylesheetFile.query_exists(null)) {
|
||||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||||
theme.load_stylesheet(stylesheetFile.get_path());
|
theme.load_stylesheet(stylesheetFile.get_path());
|
||||||
extension.stylesheet = stylesheetFile;
|
extension.stylesheet = stylesheetFile;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
extension.stateObj.enable();
|
extension.stateObj.enable();
|
||||||
|
|
||||||
extension.state = ExtensionState.ENABLED;
|
extension.state = ExtensionState.ENABLED;
|
||||||
_signals.emit('extension-state-changed', extension);
|
_signals.emit('extension-state-changed', extension);
|
||||||
|
return;
|
||||||
|
} catch(e) {
|
||||||
|
logExtensionError(uuid, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function logExtensionError(uuid, error) {
|
function logExtensionError(uuid, error) {
|
||||||
@@ -146,7 +161,8 @@ function loadExtension(extension) {
|
|||||||
} else {
|
} else {
|
||||||
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
|
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
initExtension(extension.uuid);
|
if (!initExtension(extension.uuid))
|
||||||
|
return;
|
||||||
if (extension.state == ExtensionState.DISABLED)
|
if (extension.state == ExtensionState.DISABLED)
|
||||||
enableExtension(extension.uuid);
|
enableExtension(extension.uuid);
|
||||||
} else {
|
} else {
|
||||||
@@ -201,7 +217,12 @@ function initExtension(uuid) {
|
|||||||
extensionModule = extension.imports.extension;
|
extensionModule = extension.imports.extension;
|
||||||
|
|
||||||
if (extensionModule.init) {
|
if (extensionModule.init) {
|
||||||
|
try {
|
||||||
extensionState = extensionModule.init(extension);
|
extensionState = extensionModule.init(extension);
|
||||||
|
} catch(e) {
|
||||||
|
logExtensionError(uuid, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!extensionState)
|
if (!extensionState)
|
||||||
@@ -210,6 +231,7 @@ function initExtension(uuid) {
|
|||||||
|
|
||||||
extension.state = ExtensionState.DISABLED;
|
extension.state = ExtensionState.DISABLED;
|
||||||
_signals.emit('extension-loaded', uuid);
|
_signals.emit('extension-loaded', uuid);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEnabledExtensions() {
|
function getEnabledExtensions() {
|
||||||
@@ -231,11 +253,7 @@ function onEnabledExtensionsChanged() {
|
|||||||
newEnabledExtensions.filter(function(uuid) {
|
newEnabledExtensions.filter(function(uuid) {
|
||||||
return enabledExtensions.indexOf(uuid) == -1;
|
return enabledExtensions.indexOf(uuid) == -1;
|
||||||
}).forEach(function(uuid) {
|
}).forEach(function(uuid) {
|
||||||
try {
|
|
||||||
enableExtension(uuid);
|
enableExtension(uuid);
|
||||||
} catch(e) {
|
|
||||||
logExtensionError(uuid, e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Find and disable all the newly disabled extensions: UUIDs found in the
|
// Find and disable all the newly disabled extensions: UUIDs found in the
|
||||||
@@ -243,11 +261,7 @@ function onEnabledExtensionsChanged() {
|
|||||||
enabledExtensions.filter(function(item) {
|
enabledExtensions.filter(function(item) {
|
||||||
return newEnabledExtensions.indexOf(item) == -1;
|
return newEnabledExtensions.indexOf(item) == -1;
|
||||||
}).forEach(function(uuid) {
|
}).forEach(function(uuid) {
|
||||||
try {
|
|
||||||
disableExtension(uuid);
|
disableExtension(uuid);
|
||||||
} catch(e) {
|
|
||||||
logExtensionError(uuid, e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
enabledExtensions = newEnabledExtensions;
|
enabledExtensions = newEnabledExtensions;
|
||||||
@@ -258,12 +272,8 @@ function _loadExtensions() {
|
|||||||
enabledExtensions = getEnabledExtensions();
|
enabledExtensions = getEnabledExtensions();
|
||||||
|
|
||||||
let finder = new ExtensionUtils.ExtensionFinder();
|
let finder = new ExtensionUtils.ExtensionFinder();
|
||||||
finder.connect('extension-found', function(signals, extension) {
|
finder.connect('extension-found', function(finder, extension) {
|
||||||
try {
|
|
||||||
loadExtension(extension);
|
loadExtension(extension);
|
||||||
} catch(e) {
|
|
||||||
logExtensionError(extension.uuid, e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
finder.scanExtensions();
|
finder.scanExtensions();
|
||||||
}
|
}
|
||||||
@@ -288,7 +298,7 @@ function disableAllExtensions() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (initted) {
|
if (initted) {
|
||||||
enabledExtensions.forEach(function(uuid) {
|
extensionOrder.slice().reverse().forEach(function(uuid) {
|
||||||
disableExtension(uuid);
|
disableExtension(uuid);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
|
|
||||||
const Lang = imports.lang;
|
|
||||||
|
|
||||||
const Lightbox = imports.ui.lightbox;
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
const Tweener = imports.ui.tweener;
|
|
||||||
|
|
||||||
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
|
|
||||||
|
|
||||||
const Flashspot = new Lang.Class({
|
|
||||||
Name: 'Flashspot',
|
|
||||||
Extends: Lightbox.Lightbox,
|
|
||||||
|
|
||||||
_init: function(area) {
|
|
||||||
this.parent(Main.uiGroup, { inhibitEvents: true,
|
|
||||||
width: area.width,
|
|
||||||
height: area.height });
|
|
||||||
|
|
||||||
this.actor.style_class = 'flashspot';
|
|
||||||
this.actor.set_position(area.x, area.y);
|
|
||||||
},
|
|
||||||
|
|
||||||
fire: function() {
|
|
||||||
this.actor.opacity = 0;
|
|
||||||
Tweener.addTween(this.actor,
|
|
||||||
{ opacity: 255,
|
|
||||||
time: FLASHSPOT_ANIMATION_TIME,
|
|
||||||
transition: 'linear',
|
|
||||||
onComplete: Lang.bind(this, this._onFireShowComplete)
|
|
||||||
});
|
|
||||||
this.actor.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onFireShowComplete: function() {
|
|
||||||
Tweener.addTween(this.actor,
|
|
||||||
{ opacity: 0,
|
|
||||||
time: FLASHSPOT_ANIMATION_TIME,
|
|
||||||
transition: 'linear',
|
|
||||||
onComplete: Lang.bind(this, function() {
|
|
||||||
this.destroy();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
65
js/ui/focusCaretTracker.js
Normal file
65
js/ui/focusCaretTracker.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/** -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Inclusive Design Research Centre, OCAD University.
|
||||||
|
*
|
||||||
|
* This program 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 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Joseph Scheuhammer <clown@alum.mit.edu>
|
||||||
|
* Contributor:
|
||||||
|
* Magdalen Berns <m.berns@sms.ed.ac.uk>
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Atspi = imports.gi.Atspi;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const CARETMOVED = 'object:text-caret-moved';
|
||||||
|
const STATECHANGED = 'object:state-changed';
|
||||||
|
|
||||||
|
const FocusCaretTracker = new Lang.Class({
|
||||||
|
Name: 'FocusCaretTracker',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
Atspi.init();
|
||||||
|
Atspi.set_timeout(250, 250);
|
||||||
|
this._atspiListener = Atspi.EventListener.new(Lang.bind(this, this._onChanged));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onChanged: function(event) {
|
||||||
|
if (event.type.indexOf(STATECHANGED) == 0)
|
||||||
|
this.emit('focus-changed', event);
|
||||||
|
else if (event.type == CARETMOVED)
|
||||||
|
this.emit('caret-moved', event);
|
||||||
|
},
|
||||||
|
|
||||||
|
registerFocusListener: function() {
|
||||||
|
return this._atspiListener.register(STATECHANGED + ':focused') &&
|
||||||
|
this._atspiListener.register(STATECHANGED + ':selected');
|
||||||
|
},
|
||||||
|
|
||||||
|
registerCaretListener: function() {
|
||||||
|
return this._atspiListener.register(CARETMOVED);
|
||||||
|
},
|
||||||
|
|
||||||
|
deregisterFocusListener: function() {
|
||||||
|
return this._atspiListener.deregister(STATECHANGED + ':focused') &&
|
||||||
|
this._atspiListener.deregister(STATECHANGED + ':selected');
|
||||||
|
},
|
||||||
|
|
||||||
|
deregisterCaretListener: function() {
|
||||||
|
return this._atspiListener.deregister(CARETMOVED);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(FocusCaretTracker.prototype);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
@@ -10,15 +10,29 @@ const St = imports.gi.St;
|
|||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
function _navigateActor(actor) {
|
let _capturedEventId = 0;
|
||||||
if (!actor)
|
let _grabHelperStack = [];
|
||||||
return;
|
function _onCapturedEvent(actor, event) {
|
||||||
|
let grabHelper = _grabHelperStack[_grabHelperStack.length - 1];
|
||||||
|
return grabHelper.onCapturedEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
let needsGrab = true;
|
function _pushGrabHelper(grabHelper) {
|
||||||
if (actor instanceof St.Widget)
|
_grabHelperStack.push(grabHelper);
|
||||||
needsGrab = !actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
|
||||||
if (needsGrab)
|
if (_capturedEventId == 0)
|
||||||
actor.grab_key_focus();
|
_capturedEventId = global.stage.connect('captured-event', _onCapturedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _popGrabHelper(grabHelper) {
|
||||||
|
let poppedHelper = _grabHelperStack.pop();
|
||||||
|
if (poppedHelper != grabHelper)
|
||||||
|
throw new Error("incorrect grab helper pop");
|
||||||
|
|
||||||
|
if (_grabHelperStack.length == 0) {
|
||||||
|
global.stage.disconnect(_capturedEventId);
|
||||||
|
_capturedEventId = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GrabHelper:
|
// GrabHelper:
|
||||||
@@ -42,13 +56,9 @@ const GrabHelper = new Lang.Class({
|
|||||||
this._grabStack = [];
|
this._grabStack = [];
|
||||||
|
|
||||||
this._actors = [];
|
this._actors = [];
|
||||||
this._capturedEventId = 0;
|
|
||||||
this._keyFocusNotifyId = 0;
|
|
||||||
this._focusWindowChangedId = 0;
|
|
||||||
this._ignoreRelease = false;
|
this._ignoreRelease = false;
|
||||||
|
|
||||||
this._modalCount = 0;
|
this._modalCount = 0;
|
||||||
this._grabFocusCount = 0;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// addActor:
|
// addActor:
|
||||||
@@ -128,38 +138,36 @@ const GrabHelper = new Lang.Class({
|
|||||||
// grab:
|
// grab:
|
||||||
// @params: A bunch of parameters, see below
|
// @params: A bunch of parameters, see below
|
||||||
//
|
//
|
||||||
// Grabs the mouse and keyboard, according to the GrabHelper's
|
// The general effect of a "grab" is to ensure that the passed in actor
|
||||||
// parameters. If @newFocus is not %null, then the keyboard focus
|
// and all actors inside the grab get exclusive control of the mouse and
|
||||||
// is moved to the first #StWidget:can-focus widget inside it.
|
// keyboard, with the grab automatically being dropped if the user tries
|
||||||
|
// to dismiss it. The actor is passed in through @params.actor.
|
||||||
//
|
//
|
||||||
// The grab will automatically be dropped if:
|
// grab() can be called multiple times, with the scope of the grab being
|
||||||
// - The user clicks outside the grabbed actors
|
// changed to a different actor every time. A nested grab does not have
|
||||||
// - The user types Escape
|
// to have its grabbed actor inside the parent grab actors.
|
||||||
// - The keyboard focus is moved outside the grabbed actors
|
|
||||||
// - A window is focused
|
|
||||||
//
|
//
|
||||||
// If @params.actor is not null, then it will be focused as the
|
// Grabs can be automatically dropped if the user tries to dismiss it
|
||||||
// new actor. If you attempt to grab an already focused actor, the
|
// in one of two ways: the user clicking outside the currently grabbed
|
||||||
// request to be focused will be ignored. The actor will not be
|
// actor, or the user typing the Escape key.
|
||||||
// added to the grab stack, so do not call a paired ungrab().
|
|
||||||
//
|
//
|
||||||
// If @params contains { modal: true }, then grab() will push a modal
|
// If the user clicks outside the grabbed actors, and the clicked on
|
||||||
// on the owner of the GrabHelper. As long as there is at least one
|
// actor is part of a previous grab in the stack, grabs will be popped
|
||||||
// { modal: true } actor on the grab stack, the grab will be kept.
|
// until that grab is active. However, the click event will not be
|
||||||
// When the last { modal: true } actor is ungrabbed, then the modal
|
// replayed to the actor.
|
||||||
// will be dropped. A modal grab can fail if there is already a grab
|
|
||||||
// in effect from aother application; in this case the function returns
|
|
||||||
// false and nothing happens. Non-modal grabs can never fail.
|
|
||||||
//
|
//
|
||||||
// If @params contains { grabFocus: true }, then if you call grab()
|
// If the user types the Escape key, one grab from the grab stack will
|
||||||
// while the shell is outside the overview, it will set the stage
|
// be popped.
|
||||||
// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
|
//
|
||||||
// revert it back, and re-focus the previously-focused window (if
|
// When a grab is popped by user interacting as described above, if you
|
||||||
// another window hasn't been explicitly focused before then).
|
// pass a callback as @params.onUngrab, it will be called with %true.
|
||||||
|
//
|
||||||
|
// If @params.focus is not null, we'll set the key focus directly
|
||||||
|
// to that actor instead of navigating in @params.actor. This is for
|
||||||
|
// use cases like menus, where we want to grab the menu actor, but keep
|
||||||
|
// focus on the clicked on menu item.
|
||||||
grab: function(params) {
|
grab: function(params) {
|
||||||
params = Params.parse(params, { actor: null,
|
params = Params.parse(params, { actor: null,
|
||||||
modal: false,
|
|
||||||
grabFocus: false,
|
|
||||||
focus: null,
|
focus: null,
|
||||||
onUngrab: null });
|
onUngrab: null });
|
||||||
|
|
||||||
@@ -172,18 +180,18 @@ const GrabHelper = new Lang.Class({
|
|||||||
|
|
||||||
params.savedFocus = focus;
|
params.savedFocus = focus;
|
||||||
|
|
||||||
if (params.modal && !this._takeModalGrab())
|
if (!this._takeModalGrab())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (params.focus)
|
|
||||||
params.focus.grab_key_focus();
|
|
||||||
else if (hadFocus || params.grabFocus)
|
|
||||||
_navigateActor(newFocus);
|
|
||||||
|
|
||||||
this._grabStack.push(params);
|
this._grabStack.push(params);
|
||||||
|
|
||||||
|
if (params.focus) {
|
||||||
|
params.focus.grab_key_focus();
|
||||||
|
} else if (newFocus && hadFocus) {
|
||||||
|
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
|
||||||
|
newFocus.grab_key_focus();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -193,7 +201,7 @@ const GrabHelper = new Lang.Class({
|
|||||||
if (!Main.pushModal(this._owner, this._modalParams))
|
if (!Main.pushModal(this._owner, this._modalParams))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
_pushGrabHelper(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._modalCount++;
|
this._modalCount++;
|
||||||
@@ -205,63 +213,14 @@ const GrabHelper = new Lang.Class({
|
|||||||
if (this._modalCount > 0)
|
if (this._modalCount > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this._capturedEventId > 0) {
|
_popGrabHelper(this);
|
||||||
global.stage.disconnect(this._capturedEventId);
|
|
||||||
this._capturedEventId = 0;
|
this._ignoreRelease = false;
|
||||||
}
|
|
||||||
|
|
||||||
Main.popModal(this._owner);
|
Main.popModal(this._owner);
|
||||||
global.sync_pointer();
|
global.sync_pointer();
|
||||||
},
|
},
|
||||||
|
|
||||||
_takeFocusGrab: function(hadFocus) {
|
|
||||||
let firstGrab = (this._grabFocusCount == 0);
|
|
||||||
if (firstGrab) {
|
|
||||||
let metaDisplay = global.screen.get_display();
|
|
||||||
|
|
||||||
this._grabbedFromKeynav = hadFocus;
|
|
||||||
this._preGrabInputMode = global.stage_input_mode;
|
|
||||||
|
|
||||||
if (this._preGrabInputMode == Shell.StageInputMode.NONREACTIVE ||
|
|
||||||
this._preGrabInputMode == Shell.StageInputMode.NORMAL) {
|
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
|
||||||
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._grabFocusCount++;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_releaseFocusGrab: function() {
|
|
||||||
this._grabFocusCount--;
|
|
||||||
if (this._grabFocusCount > 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this._keyFocusNotifyId > 0) {
|
|
||||||
global.stage.disconnect(this._keyFocusNotifyId);
|
|
||||||
this._keyFocusNotifyId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._focusWindowChanged > 0) {
|
|
||||||
let metaDisplay = global.screen.get_display();
|
|
||||||
metaDisplay.disconnect(this._focusWindowChangedId);
|
|
||||||
this._focusWindowChangedId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let prePopInputMode = global.stage_input_mode;
|
|
||||||
|
|
||||||
if (this._grabbedFromKeynav) {
|
|
||||||
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
|
|
||||||
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
|
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
|
||||||
}
|
|
||||||
|
|
||||||
global.screen.focus_default_window(global.get_current_time());
|
|
||||||
},
|
|
||||||
|
|
||||||
// ignoreRelease:
|
// ignoreRelease:
|
||||||
//
|
//
|
||||||
// Make sure that the next button release event evaluated by the
|
// Make sure that the next button release event evaluated by the
|
||||||
@@ -275,10 +234,14 @@ const GrabHelper = new Lang.Class({
|
|||||||
// ungrab:
|
// ungrab:
|
||||||
// @params: The parameters for the grab; see below.
|
// @params: The parameters for the grab; see below.
|
||||||
//
|
//
|
||||||
// Pops an actor from the grab stack, potentially dropping the grab.
|
// Pops @params.actor from the grab stack, potentially dropping
|
||||||
|
// the grab. If the actor is not on the grab stack, this call is
|
||||||
|
// ignored with no ill effects.
|
||||||
//
|
//
|
||||||
// If the actor that was popped from the grab stack was not the actor
|
// If the actor is not at the top of the grab stack, grabs are
|
||||||
// That was passed in, this call is ignored.
|
// popped until the grabbed actor is at the top of the grab stack.
|
||||||
|
// The onUngrab callback for every grab is called for every popped
|
||||||
|
// grab with the parameter %false.
|
||||||
ungrab: function(params) {
|
ungrab: function(params) {
|
||||||
params = Params.parse(params, { actor: this.currentGrab.actor,
|
params = Params.parse(params, { actor: this.currentGrab.actor,
|
||||||
isUser: false });
|
isUser: false });
|
||||||
@@ -301,44 +264,39 @@ const GrabHelper = new Lang.Class({
|
|||||||
if (poppedGrab.onUngrab)
|
if (poppedGrab.onUngrab)
|
||||||
poppedGrab.onUngrab(params.isUser);
|
poppedGrab.onUngrab(params.isUser);
|
||||||
|
|
||||||
if (poppedGrab.modal)
|
|
||||||
this._releaseModalGrab();
|
this._releaseModalGrab();
|
||||||
|
|
||||||
if (poppedGrab.grabFocus)
|
|
||||||
this._releaseFocusGrab();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hadFocus) {
|
if (hadFocus) {
|
||||||
let poppedGrab = poppedGrabs[0];
|
let poppedGrab = poppedGrabs[0];
|
||||||
_navigateActor(poppedGrab.savedFocus);
|
if (poppedGrab.savedFocus)
|
||||||
|
poppedGrab.savedFocus.grab_key_focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCapturedEvent: function(actor, event) {
|
onCapturedEvent: function(event) {
|
||||||
let type = event.type();
|
let type = event.type();
|
||||||
|
|
||||||
|
if (type == Clutter.EventType.KEY_PRESS &&
|
||||||
|
event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||||
|
this.ungrab({ isUser: true });
|
||||||
|
return Clutter.EVENT_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
let press = type == Clutter.EventType.BUTTON_PRESS;
|
let press = type == Clutter.EventType.BUTTON_PRESS;
|
||||||
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
||||||
let button = press || release;
|
let button = press || release;
|
||||||
|
|
||||||
if (release && this._ignoreRelease) {
|
if (release && this._ignoreRelease) {
|
||||||
this._ignoreRelease = false;
|
this._ignoreRelease = false;
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
}
|
|
||||||
|
|
||||||
if (!button && this._modalCount == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (type == Clutter.EventType.KEY_PRESS &&
|
|
||||||
event.get_key_symbol() == Clutter.KEY_Escape) {
|
|
||||||
this.ungrab({ isUser: true });
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._isWithinGrabbedActor(event.get_source()))
|
if (this._isWithinGrabbedActor(event.get_source()))
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
if (Main.keyboard.shouldTakeEvent(event))
|
if (Main.keyboard.shouldTakeEvent(event))
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
if (button) {
|
if (button) {
|
||||||
// If we have a press event, ignore the next event,
|
// If we have a press event, ignore the next event,
|
||||||
@@ -347,20 +305,9 @@ const GrabHelper = new Lang.Class({
|
|||||||
this._ignoreRelease = true;
|
this._ignoreRelease = true;
|
||||||
let i = this._actorInGrabStack(event.get_source()) + 1;
|
let i = this._actorInGrabStack(event.get_source()) + 1;
|
||||||
this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
|
this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
|
||||||
|
return Clutter.EVENT_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._modalCount > 0;
|
return Clutter.EVENT_STOP;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onKeyFocusChanged: function() {
|
|
||||||
let focus = global.stage.key_focus;
|
|
||||||
if (!focus || !this._isWithinGrabbedActor(focus))
|
|
||||||
this.ungrab({ isUser: true });
|
|
||||||
},
|
|
||||||
|
|
||||||
_focusWindowChanged: function() {
|
|
||||||
let metaDisplay = global.screen.get_display();
|
|
||||||
if (metaDisplay.focus_window != null)
|
|
||||||
this.ungrab({ isUser: true });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,107 +3,147 @@
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const IBus = imports.gi.IBus;
|
const IBus = imports.gi.IBus;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const BoxPointer = imports.ui.boxpointer;
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
|
||||||
|
|
||||||
const MAX_CANDIDATES_PER_PAGE = 16;
|
const MAX_CANDIDATES_PER_PAGE = 16;
|
||||||
|
|
||||||
const CandidateArea = new Lang.Class({
|
const CandidateArea = new Lang.Class({
|
||||||
Name: 'CandidateArea',
|
Name: 'CandidateArea',
|
||||||
Extends: PopupMenu.PopupBaseMenuItem,
|
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.parent({ reactive: false });
|
this.actor = new St.BoxLayout({ vertical: true,
|
||||||
|
visible: false });
|
||||||
// St.Table exhibits some sizing problems so let's go with a
|
this._candidateBoxes = [];
|
||||||
// clutter layout manager for now.
|
|
||||||
this._table = new Clutter.Actor();
|
|
||||||
this.addActor(this._table);
|
|
||||||
|
|
||||||
this._tableLayout = new Clutter.TableLayout();
|
|
||||||
this._table.set_layout_manager(this._tableLayout);
|
|
||||||
|
|
||||||
this._indexLabels = [];
|
|
||||||
this._candidateLabels = [];
|
|
||||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||||
this._indexLabels.push(new St.Label({ style_class: 'candidate-index' }));
|
let box = new St.BoxLayout({ style_class: 'candidate-box',
|
||||||
this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' }));
|
reactive: true,
|
||||||
|
track_hover: true });
|
||||||
|
box._indexLabel = new St.Label({ style_class: 'candidate-index' });
|
||||||
|
box._candidateLabel = new St.Label({ style_class: 'candidate-label' });
|
||||||
|
box.add(box._indexLabel, { y_fill: false });
|
||||||
|
box.add(box._candidateLabel, { y_fill: false });
|
||||||
|
this._candidateBoxes.push(box);
|
||||||
|
this.actor.add(box);
|
||||||
|
|
||||||
|
let j = i;
|
||||||
|
box.connect('button-release-event', Lang.bind(this, function(actor, event) {
|
||||||
|
this.emit('candidate-clicked', j, event.get_button(), event.get_state());
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' });
|
||||||
|
|
||||||
|
this._previousButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-previous' });
|
||||||
|
this._previousButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
|
||||||
|
this._buttonBox.add(this._previousButton, { expand: true });
|
||||||
|
|
||||||
|
this._nextButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-next' });
|
||||||
|
this._nextButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
|
||||||
|
this._buttonBox.add(this._nextButton, { expand: true });
|
||||||
|
|
||||||
|
this.actor.add(this._buttonBox);
|
||||||
|
|
||||||
|
this._previousButton.connect('clicked', Lang.bind(this, function() {
|
||||||
|
this.emit('previous-page');
|
||||||
|
}));
|
||||||
|
this._nextButton.connect('clicked', Lang.bind(this, function() {
|
||||||
|
this.emit('next-page');
|
||||||
|
}));
|
||||||
|
|
||||||
this._orientation = -1;
|
this._orientation = -1;
|
||||||
this._cursorPosition = 0;
|
this._cursorPosition = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
_setOrientation: function(orientation) {
|
setOrientation: function(orientation) {
|
||||||
if (this._orientation == orientation)
|
if (this._orientation == orientation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._orientation = orientation;
|
this._orientation = orientation;
|
||||||
|
|
||||||
this._table.remove_all_children();
|
if (this._orientation == IBus.Orientation.HORIZONTAL) {
|
||||||
|
this.actor.vertical = false;
|
||||||
if (this._orientation == IBus.Orientation.HORIZONTAL)
|
this.actor.remove_style_class_name('vertical');
|
||||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
this.actor.add_style_class_name('horizontal');
|
||||||
this._tableLayout.pack(this._indexLabels[i], i*2, 0);
|
this._previousButton.child.icon_name = 'go-previous-symbolic';
|
||||||
this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0);
|
this._nextButton.child.icon_name = 'go-next-symbolic';
|
||||||
}
|
} else { // VERTICAL || SYSTEM
|
||||||
else // VERTICAL || SYSTEM
|
this.actor.vertical = true;
|
||||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
this.actor.add_style_class_name('vertical');
|
||||||
this._tableLayout.pack(this._indexLabels[i], 0, i);
|
this.actor.remove_style_class_name('horizontal');
|
||||||
this._tableLayout.pack(this._candidateLabels[i], 1, i);
|
this._previousButton.child.icon_name = 'go-up-symbolic';
|
||||||
|
this._nextButton.child.icon_name = 'go-down-symbolic';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) {
|
setCandidates: function(indexes, candidates, cursorPosition, cursorVisible) {
|
||||||
this._setOrientation(orientation);
|
|
||||||
|
|
||||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||||
let visible = i < candidates.length;
|
let visible = i < candidates.length;
|
||||||
this._indexLabels[i].visible = visible;
|
let box = this._candidateBoxes[i];
|
||||||
this._candidateLabels[i].visible = visible;
|
box.visible = visible;
|
||||||
|
|
||||||
if (!visible)
|
if (!visible)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1));
|
box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : '%x'.format(i + 1));
|
||||||
this._candidateLabels[i].text = candidates[i];
|
box._candidateLabel.text = candidates[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected');
|
this._candidateBoxes[this._cursorPosition].remove_style_pseudo_class('selected');
|
||||||
this._cursorPosition = cursorPosition;
|
this._cursorPosition = cursorPosition;
|
||||||
if (cursorVisible)
|
if (cursorVisible)
|
||||||
this._candidateLabels[cursorPosition].add_style_pseudo_class('selected');
|
this._candidateBoxes[cursorPosition].add_style_pseudo_class('selected');
|
||||||
|
},
|
||||||
|
|
||||||
|
updateButtons: function(wrapsAround, page, nPages) {
|
||||||
|
if (nPages < 2) {
|
||||||
|
this._buttonBox.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._buttonBox.show();
|
||||||
|
this._previousButton.reactive = wrapsAround || page > 0;
|
||||||
|
this._nextButton.reactive = wrapsAround || page < nPages - 1;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(CandidateArea.prototype);
|
||||||
|
|
||||||
const CandidatePopup = new Lang.Class({
|
const CandidatePopup = new Lang.Class({
|
||||||
Name: 'CandidatePopup',
|
Name: 'CandidatePopup',
|
||||||
Extends: PopupMenu.PopupMenu,
|
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._cursor = new St.Bin({ opacity: 0 });
|
this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
|
||||||
Main.uiGroup.add_actor(this._cursor);
|
this._boxPointer.actor.visible = false;
|
||||||
|
this._boxPointer.actor.style_class = 'candidate-popup-boxpointer';
|
||||||
|
Main.layoutManager.addChrome(this._boxPointer.actor);
|
||||||
|
|
||||||
this.parent(this._cursor, 0, St.Side.TOP);
|
let box = new St.BoxLayout({ style_class: 'candidate-popup-content',
|
||||||
this.actor.hide();
|
vertical: true });
|
||||||
Main.uiGroup.add_actor(this.actor);
|
this._boxPointer.bin.set_child(box);
|
||||||
|
|
||||||
this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
this._preeditText = new St.Label({ style_class: 'candidate-popup-text',
|
||||||
this._preeditTextItem.actor.hide();
|
visible: false });
|
||||||
this.addMenuItem(this._preeditTextItem);
|
box.add(this._preeditText);
|
||||||
|
|
||||||
this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
this._auxText = new St.Label({ style_class: 'candidate-popup-text',
|
||||||
this._auxTextItem.actor.hide();
|
visible: false });
|
||||||
this.addMenuItem(this._auxTextItem);
|
box.add(this._auxText);
|
||||||
|
|
||||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
this._candidateArea = new CandidateArea();
|
||||||
|
box.add(this._candidateArea.actor);
|
||||||
|
|
||||||
this._lookupTableItem = new CandidateArea();
|
this._candidateArea.connect('previous-page', Lang.bind(this, function() {
|
||||||
this._lookupTableItem.actor.hide();
|
this._panelService.page_up();
|
||||||
this.addMenuItem(this._lookupTableItem);
|
}));
|
||||||
|
this._candidateArea.connect('next-page', Lang.bind(this, function() {
|
||||||
|
this._panelService.page_down();
|
||||||
|
}));
|
||||||
|
this._candidateArea.connect('candidate-clicked', Lang.bind(this, function(ca, index, button, state) {
|
||||||
|
this._panelService.candidate_clicked(index, button, state);
|
||||||
|
}));
|
||||||
|
|
||||||
this._panelService = null;
|
this._panelService = null;
|
||||||
},
|
},
|
||||||
@@ -115,68 +155,62 @@ const CandidatePopup = new Lang.Class({
|
|||||||
|
|
||||||
panelService.connect('set-cursor-location',
|
panelService.connect('set-cursor-location',
|
||||||
Lang.bind(this, function(ps, x, y, w, h) {
|
Lang.bind(this, function(ps, x, y, w, h) {
|
||||||
this._cursor.set_position(x, y);
|
Main.layoutManager.setDummyCursorPosition(x, y);
|
||||||
this._cursor.set_size(w, h);
|
if (this._boxPointer.actor.visible)
|
||||||
|
this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
|
||||||
}));
|
}));
|
||||||
panelService.connect('update-preedit-text',
|
panelService.connect('update-preedit-text',
|
||||||
Lang.bind(this, function(ps, text, cursorPosition, visible) {
|
Lang.bind(this, function(ps, text, cursorPosition, visible) {
|
||||||
if (visible)
|
this._preeditText.visible = visible;
|
||||||
this._preeditTextItem.actor.show();
|
|
||||||
else
|
|
||||||
this._preeditTextItem.actor.hide();
|
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
|
|
||||||
this._preeditTextItem.actor.label_actor.text = text.get_text();
|
this._preeditText.text = text.get_text();
|
||||||
|
|
||||||
let attrs = text.get_attributes();
|
let attrs = text.get_attributes();
|
||||||
if (attrs)
|
if (attrs)
|
||||||
this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text,
|
this._setTextAttributes(this._preeditText.clutter_text,
|
||||||
attrs);
|
attrs);
|
||||||
}));
|
}));
|
||||||
panelService.connect('show-preedit-text',
|
panelService.connect('show-preedit-text',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._preeditTextItem.actor.show();
|
this._preeditText.show();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('hide-preedit-text',
|
panelService.connect('hide-preedit-text',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._preeditTextItem.actor.hide();
|
this._preeditText.hide();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('update-auxiliary-text',
|
panelService.connect('update-auxiliary-text',
|
||||||
Lang.bind(this, function(ps, text, visible) {
|
Lang.bind(this, function(ps, text, visible) {
|
||||||
if (visible)
|
this._auxText.visible = visible;
|
||||||
this._auxTextItem.actor.show();
|
|
||||||
else
|
|
||||||
this._auxTextItem.actor.hide();
|
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
|
|
||||||
this._auxTextItem.actor.label_actor.text = text.get_text();
|
this._auxText.text = text.get_text();
|
||||||
}));
|
}));
|
||||||
panelService.connect('show-auxiliary-text',
|
panelService.connect('show-auxiliary-text',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._auxTextItem.actor.show();
|
this._auxText.show();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('hide-auxiliary-text',
|
panelService.connect('hide-auxiliary-text',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._auxTextItem.actor.hide();
|
this._auxText.hide();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('update-lookup-table',
|
panelService.connect('update-lookup-table',
|
||||||
Lang.bind(this, function(ps, lookupTable, visible) {
|
Lang.bind(this, function(ps, lookupTable, visible) {
|
||||||
if (visible)
|
this._candidateArea.actor.visible = visible;
|
||||||
this._lookupTableItem.actor.show();
|
|
||||||
else
|
|
||||||
this._lookupTableItem.actor.hide();
|
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
|
|
||||||
|
let nCandidates = lookupTable.get_number_of_candidates();
|
||||||
let cursorPos = lookupTable.get_cursor_pos();
|
let cursorPos = lookupTable.get_cursor_pos();
|
||||||
let pageSize = lookupTable.get_page_size();
|
let pageSize = lookupTable.get_page_size();
|
||||||
|
let nPages = Math.ceil(nCandidates / pageSize);
|
||||||
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
|
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
|
||||||
let startIndex = page * pageSize;
|
let startIndex = page * pageSize;
|
||||||
let endIndex = Math.min((page + 1) * pageSize,
|
let endIndex = Math.min((page + 1) * pageSize, nCandidates);
|
||||||
lookupTable.get_number_of_candidates());
|
|
||||||
let indexes = [];
|
let indexes = [];
|
||||||
let indexLabel;
|
let indexLabel;
|
||||||
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
|
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
|
||||||
@@ -186,37 +220,41 @@ const CandidatePopup = new Lang.Class({
|
|||||||
for (let i = startIndex; i < endIndex; ++i)
|
for (let i = startIndex; i < endIndex; ++i)
|
||||||
candidates.push(lookupTable.get_candidate(i).get_text());
|
candidates.push(lookupTable.get_candidate(i).get_text());
|
||||||
|
|
||||||
this._lookupTableItem.setCandidates(indexes,
|
this._candidateArea.setCandidates(indexes,
|
||||||
candidates,
|
candidates,
|
||||||
lookupTable.get_orientation(),
|
|
||||||
cursorPos % pageSize,
|
cursorPos % pageSize,
|
||||||
lookupTable.is_cursor_visible());
|
lookupTable.is_cursor_visible());
|
||||||
|
this._candidateArea.setOrientation(lookupTable.get_orientation());
|
||||||
|
this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages);
|
||||||
}));
|
}));
|
||||||
panelService.connect('show-lookup-table',
|
panelService.connect('show-lookup-table',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._lookupTableItem.actor.show();
|
this._candidateArea.actor.show();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('hide-lookup-table',
|
panelService.connect('hide-lookup-table',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this._lookupTableItem.actor.hide();
|
this._candidateArea.actor.hide();
|
||||||
this._updateVisibility();
|
this._updateVisibility();
|
||||||
}));
|
}));
|
||||||
panelService.connect('focus-out',
|
panelService.connect('focus-out',
|
||||||
Lang.bind(this, function(ps) {
|
Lang.bind(this, function(ps) {
|
||||||
this.close(BoxPointer.PopupAnimation.NONE);
|
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateVisibility: function() {
|
_updateVisibility: function() {
|
||||||
let isVisible = (this._preeditTextItem.actor.visible ||
|
let isVisible = (this._preeditText.visible ||
|
||||||
this._auxTextItem.actor.visible ||
|
this._auxText.visible ||
|
||||||
this._lookupTableItem.actor.visible);
|
this._candidateArea.actor.visible);
|
||||||
|
|
||||||
if (isVisible)
|
if (isVisible) {
|
||||||
this.open(BoxPointer.PopupAnimation.NONE);
|
this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
|
||||||
else
|
this._boxPointer.show(BoxPointer.PopupAnimation.NONE);
|
||||||
this.close(BoxPointer.PopupAnimation.NONE);
|
this._boxPointer.actor.raise_top();
|
||||||
|
} else {
|
||||||
|
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_setTextAttributes: function(clutterText, ibusAttrList) {
|
_setTextAttributes: function(clutterText, ibusAttrList) {
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
const ICON_SIZE = 48;
|
const ICON_SIZE = 96;
|
||||||
|
const MIN_ICON_SIZE = 16;
|
||||||
|
|
||||||
|
const EXTRA_SPACE_ANIMATION_TIME = 0.25;
|
||||||
|
|
||||||
const BaseIcon = new Lang.Class({
|
const BaseIcon = new Lang.Class({
|
||||||
Name: 'BaseIcon',
|
Name: 'BaseIcon',
|
||||||
@@ -17,7 +23,12 @@ const BaseIcon = new Lang.Class({
|
|||||||
params = Params.parse(params, { createIcon: null,
|
params = Params.parse(params, { createIcon: null,
|
||||||
setSizeManually: false,
|
setSizeManually: false,
|
||||||
showLabel: true });
|
showLabel: true });
|
||||||
this.actor = new St.Bin({ style_class: 'overview-icon',
|
|
||||||
|
let styleClass = 'overview-icon';
|
||||||
|
if (params.showLabel)
|
||||||
|
styleClass += ' overview-icon-with-label';
|
||||||
|
|
||||||
|
this.actor = new St.Bin({ style_class: styleClass,
|
||||||
x_fill: true,
|
x_fill: true,
|
||||||
y_fill: true });
|
y_fill: true });
|
||||||
this.actor._delegate = this;
|
this.actor._delegate = this;
|
||||||
@@ -176,16 +187,31 @@ const IconGrid = new Lang.Class({
|
|||||||
_init: function(params) {
|
_init: function(params) {
|
||||||
params = Params.parse(params, { rowLimit: null,
|
params = Params.parse(params, { rowLimit: null,
|
||||||
columnLimit: null,
|
columnLimit: null,
|
||||||
xAlign: St.Align.MIDDLE });
|
minRows: 1,
|
||||||
|
minColumns: 1,
|
||||||
|
fillParent: false,
|
||||||
|
xAlign: St.Align.MIDDLE,
|
||||||
|
padWithSpacing: false });
|
||||||
this._rowLimit = params.rowLimit;
|
this._rowLimit = params.rowLimit;
|
||||||
this._colLimit = params.columnLimit;
|
this._colLimit = params.columnLimit;
|
||||||
|
this._minRows = params.minRows;
|
||||||
|
this._minColumns = params.minColumns;
|
||||||
this._xAlign = params.xAlign;
|
this._xAlign = params.xAlign;
|
||||||
|
this._fillParent = params.fillParent;
|
||||||
|
this._padWithSpacing = params.padWithSpacing;
|
||||||
|
|
||||||
|
this.topPadding = 0;
|
||||||
|
this.bottomPadding = 0;
|
||||||
|
this.rightPadding = 0;
|
||||||
|
this.leftPadding = 0;
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ style_class: 'icon-grid',
|
this.actor = new St.BoxLayout({ style_class: 'icon-grid',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
|
this._items = [];
|
||||||
// Pulled from CSS, but hardcode some defaults here
|
// Pulled from CSS, but hardcode some defaults here
|
||||||
this._spacing = 0;
|
this._spacing = 0;
|
||||||
this._hItemSize = this._vItemSize = ICON_SIZE;
|
this._hItemSize = this._vItemSize = ICON_SIZE;
|
||||||
|
this._fixedHItemSize = this._fixedVItemSize = undefined;
|
||||||
this._grid = new Shell.GenericContainer();
|
this._grid = new Shell.GenericContainer();
|
||||||
this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
|
this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
|
||||||
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||||
@@ -196,16 +222,21 @@ const IconGrid = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredWidth: function (grid, forHeight, alloc) {
|
_getPreferredWidth: function (grid, forHeight, alloc) {
|
||||||
let children = this._grid.get_children();
|
if (this._fillParent)
|
||||||
|
// Ignore all size requests of children and request a size of 0;
|
||||||
|
// later we'll allocate as many children as fit the parent
|
||||||
|
return;
|
||||||
|
|
||||||
|
let nChildren = this._grid.get_n_children();
|
||||||
let nColumns = this._colLimit ? Math.min(this._colLimit,
|
let nColumns = this._colLimit ? Math.min(this._colLimit,
|
||||||
children.length)
|
nChildren)
|
||||||
: children.length;
|
: nChildren;
|
||||||
let totalSpacing = Math.max(0, nColumns - 1) * this._spacing;
|
let totalSpacing = Math.max(0, nColumns - 1) * this._getSpacing();
|
||||||
// Kind of a lie, but not really an issue right now. If
|
// Kind of a lie, but not really an issue right now. If
|
||||||
// we wanted to support some sort of hidden/overflow that would
|
// we wanted to support some sort of hidden/overflow that would
|
||||||
// need higher level design
|
// need higher level design
|
||||||
alloc.min_size = this._hItemSize;
|
alloc.min_size = this._getHItemSize() + this.leftPadding + this.rightPadding;
|
||||||
alloc.natural_size = nColumns * this._hItemSize + totalSpacing;
|
alloc.natural_size = nColumns * this._getHItemSize() + totalSpacing + this.leftPadding + this.rightPadding;
|
||||||
},
|
},
|
||||||
|
|
||||||
_getVisibleChildren: function() {
|
_getVisibleChildren: function() {
|
||||||
@@ -217,12 +248,18 @@ const IconGrid = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||||
|
if (this._fillParent)
|
||||||
|
// Ignore all size requests of children and request a size of 0;
|
||||||
|
// later we'll allocate as many children as fit the parent
|
||||||
|
return;
|
||||||
|
|
||||||
let children = this._getVisibleChildren();
|
let children = this._getVisibleChildren();
|
||||||
let nColumns;
|
let nColumns;
|
||||||
if (forWidth < 0)
|
if (forWidth < 0)
|
||||||
nColumns = children.length;
|
nColumns = children.length;
|
||||||
else
|
else
|
||||||
nColumns = this._computeLayout(forWidth)[0];
|
[nColumns, ] = this._computeLayout(forWidth);
|
||||||
|
|
||||||
let nRows;
|
let nRows;
|
||||||
if (nColumns > 0)
|
if (nColumns > 0)
|
||||||
nRows = Math.ceil(children.length / nColumns);
|
nRows = Math.ceil(children.length / nColumns);
|
||||||
@@ -230,57 +267,47 @@ const IconGrid = new Lang.Class({
|
|||||||
nRows = 0;
|
nRows = 0;
|
||||||
if (this._rowLimit)
|
if (this._rowLimit)
|
||||||
nRows = Math.min(nRows, this._rowLimit);
|
nRows = Math.min(nRows, this._rowLimit);
|
||||||
let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
|
let totalSpacing = Math.max(0, nRows - 1) * this._getSpacing();
|
||||||
let height = nRows * this._vItemSize + totalSpacing;
|
let height = nRows * this._getVItemSize() + totalSpacing + this.topPadding + this.bottomPadding;
|
||||||
alloc.min_size = height;
|
alloc.min_size = height;
|
||||||
alloc.natural_size = height;
|
alloc.natural_size = height;
|
||||||
},
|
},
|
||||||
|
|
||||||
_allocate: function (grid, box, flags) {
|
_allocate: function (grid, box, flags) {
|
||||||
|
if (this._fillParent) {
|
||||||
|
// Reset the passed in box to fill the parent
|
||||||
|
let parentBox = this.actor.get_parent().allocation;
|
||||||
|
let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
|
||||||
|
box = this._grid.get_theme_node().get_content_box(gridBox);
|
||||||
|
}
|
||||||
|
|
||||||
let children = this._getVisibleChildren();
|
let children = this._getVisibleChildren();
|
||||||
let availWidth = box.x2 - box.x1;
|
let availWidth = box.x2 - box.x1;
|
||||||
let availHeight = box.y2 - box.y1;
|
let availHeight = box.y2 - box.y1;
|
||||||
|
let spacing = this._getSpacing();
|
||||||
let [nColumns, usedWidth] = this._computeLayout(availWidth);
|
let [nColumns, usedWidth] = this._computeLayout(availWidth);
|
||||||
|
|
||||||
let leftPadding;
|
let leftEmptySpace;
|
||||||
switch(this._xAlign) {
|
switch(this._xAlign) {
|
||||||
case St.Align.START:
|
case St.Align.START:
|
||||||
leftPadding = 0;
|
leftEmptySpace = 0;
|
||||||
break;
|
break;
|
||||||
case St.Align.MIDDLE:
|
case St.Align.MIDDLE:
|
||||||
leftPadding = Math.floor((availWidth - usedWidth) / 2);
|
leftEmptySpace = Math.floor((availWidth - usedWidth) / 2);
|
||||||
break;
|
break;
|
||||||
case St.Align.END:
|
case St.Align.END:
|
||||||
leftPadding = availWidth - usedWidth;
|
leftEmptySpace = availWidth - usedWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = box.x1 + leftPadding;
|
let x = box.x1 + leftEmptySpace + this.leftPadding;
|
||||||
let y = box.y1;
|
let y = box.y1 + this.topPadding;
|
||||||
let columnIndex = 0;
|
let columnIndex = 0;
|
||||||
let rowIndex = 0;
|
let rowIndex = 0;
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < children.length; i++) {
|
||||||
let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight]
|
let childBox = this._calculateChildBox(children[i], x, y, box);
|
||||||
= children[i].get_preferred_size();
|
|
||||||
|
|
||||||
/* Center the item in its allocation horizontally */
|
if (this._rowLimit && rowIndex >= this._rowLimit ||
|
||||||
let width = Math.min(this._hItemSize, childNaturalWidth);
|
this._fillParent && childBox.y2 > availHeight - this.bottomPadding) {
|
||||||
let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
|
|
||||||
let height = Math.min(this._vItemSize, childNaturalHeight);
|
|
||||||
let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
|
|
||||||
|
|
||||||
let childBox = new Clutter.ActorBox();
|
|
||||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
|
|
||||||
let _x = box.x2 - (x + width);
|
|
||||||
childBox.x1 = Math.floor(_x - childXSpacing);
|
|
||||||
} else {
|
|
||||||
childBox.x1 = Math.floor(x + childXSpacing);
|
|
||||||
}
|
|
||||||
childBox.y1 = Math.floor(y + childYSpacing);
|
|
||||||
childBox.x2 = childBox.x1 + width;
|
|
||||||
childBox.y2 = childBox.y1 + height;
|
|
||||||
|
|
||||||
if (this._rowLimit && rowIndex >= this._rowLimit) {
|
|
||||||
this._grid.set_skip_paint(children[i], true);
|
this._grid.set_skip_paint(children[i], true);
|
||||||
} else {
|
} else {
|
||||||
children[i].allocate(childBox, flags);
|
children[i].allocate(childBox, flags);
|
||||||
@@ -294,15 +321,38 @@ const IconGrid = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (columnIndex == 0) {
|
if (columnIndex == 0) {
|
||||||
y += this._vItemSize + this._spacing;
|
y += this._getVItemSize() + spacing;
|
||||||
x = box.x1 + leftPadding;
|
x = box.x1 + leftEmptySpace + this.leftPadding;
|
||||||
} else {
|
} else {
|
||||||
x += this._hItemSize + this._spacing;
|
x += this._getHItemSize() + spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
childrenInRow: function(rowWidth) {
|
_calculateChildBox: function(child, x, y, box) {
|
||||||
|
let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight] =
|
||||||
|
child.get_preferred_size();
|
||||||
|
|
||||||
|
/* Center the item in its allocation horizontally */
|
||||||
|
let width = Math.min(this._getHItemSize(), childNaturalWidth);
|
||||||
|
let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
|
||||||
|
let height = Math.min(this._getVItemSize(), childNaturalHeight);
|
||||||
|
let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
|
||||||
|
|
||||||
|
let childBox = new Clutter.ActorBox();
|
||||||
|
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
|
||||||
|
let _x = box.x2 - (x + width);
|
||||||
|
childBox.x1 = Math.floor(_x - childXSpacing);
|
||||||
|
} else {
|
||||||
|
childBox.x1 = Math.floor(x + childXSpacing);
|
||||||
|
}
|
||||||
|
childBox.y1 = Math.floor(y + childYSpacing);
|
||||||
|
childBox.x2 = childBox.x1 + width;
|
||||||
|
childBox.y2 = childBox.y1 + height;
|
||||||
|
return childBox;
|
||||||
|
},
|
||||||
|
|
||||||
|
columnsForWidth: function(rowWidth) {
|
||||||
return this._computeLayout(rowWidth)[0];
|
return this._computeLayout(rowWidth)[0];
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -312,15 +362,17 @@ const IconGrid = new Lang.Class({
|
|||||||
|
|
||||||
_computeLayout: function (forWidth) {
|
_computeLayout: function (forWidth) {
|
||||||
let nColumns = 0;
|
let nColumns = 0;
|
||||||
let usedWidth = 0;
|
let usedWidth = this.leftPadding + this.rightPadding;
|
||||||
|
let spacing = this._getSpacing();
|
||||||
|
|
||||||
while ((this._colLimit == null || nColumns < this._colLimit) &&
|
while ((this._colLimit == null || nColumns < this._colLimit) &&
|
||||||
(usedWidth + this._hItemSize <= forWidth)) {
|
(usedWidth + this._getHItemSize() <= forWidth)) {
|
||||||
usedWidth += this._hItemSize + this._spacing;
|
usedWidth += this._getHItemSize() + spacing;
|
||||||
nColumns += 1;
|
nColumns += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nColumns > 0)
|
if (nColumns > 0)
|
||||||
usedWidth -= this._spacing;
|
usedWidth -= spacing;
|
||||||
|
|
||||||
return [nColumns, usedWidth];
|
return [nColumns, usedWidth];
|
||||||
},
|
},
|
||||||
@@ -333,15 +385,56 @@ const IconGrid = new Lang.Class({
|
|||||||
this._grid.queue_relayout();
|
this._grid.queue_relayout();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
nRows: function(forWidth) {
|
||||||
|
let children = this._getVisibleChildren();
|
||||||
|
let nColumns = (forWidth < 0) ? children.length : this._computeLayout(forWidth)[0];
|
||||||
|
let nRows = (nColumns > 0) ? Math.ceil(children.length / nColumns) : 0;
|
||||||
|
if (this._rowLimit)
|
||||||
|
nRows = Math.min(nRows, this._rowLimit);
|
||||||
|
return nRows;
|
||||||
|
},
|
||||||
|
|
||||||
|
rowsForHeight: function(forHeight) {
|
||||||
|
return Math.floor((forHeight - (this.topPadding + this.bottomPadding) + this._getSpacing()) / (this._getVItemSize() + this._getSpacing()));
|
||||||
|
},
|
||||||
|
|
||||||
|
usedHeightForNRows: function(nRows) {
|
||||||
|
return (this._getVItemSize() + this._getSpacing()) * nRows - this._getSpacing() + this.topPadding + this.bottomPadding;
|
||||||
|
},
|
||||||
|
|
||||||
|
usedWidth: function(forWidth) {
|
||||||
|
return this.usedWidthForNColumns(this.columnsForWidth(forWidth));
|
||||||
|
},
|
||||||
|
|
||||||
|
usedWidthForNColumns: function(columns) {
|
||||||
|
let usedWidth = columns * (this._getHItemSize() + this._getSpacing());
|
||||||
|
usedWidth -= this._getSpacing();
|
||||||
|
return usedWidth + this.leftPadding + this.rightPadding;
|
||||||
|
},
|
||||||
|
|
||||||
removeAll: function() {
|
removeAll: function() {
|
||||||
|
this._items = [];
|
||||||
|
this._grid.remove_all_children();
|
||||||
|
},
|
||||||
|
|
||||||
|
destroyAll: function() {
|
||||||
|
this._items = [];
|
||||||
this._grid.destroy_all_children();
|
this._grid.destroy_all_children();
|
||||||
},
|
},
|
||||||
|
|
||||||
addItem: function(actor, index) {
|
addItem: function(item, index) {
|
||||||
|
if (!item.icon instanceof BaseIcon)
|
||||||
|
throw new Error('Only items with a BaseIcon icon property can be added to IconGrid');
|
||||||
|
|
||||||
|
this._items.push(item);
|
||||||
if (index !== undefined)
|
if (index !== undefined)
|
||||||
this._grid.insert_child_at_index(actor, index);
|
this._grid.insert_child_at_index(item.actor, index);
|
||||||
else
|
else
|
||||||
this._grid.add_actor(actor);
|
this._grid.add_actor(item.actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeItem: function(item) {
|
||||||
|
this._grid.remove_child(item.actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
getItemAtIndex: function(index) {
|
getItemAtIndex: function(index) {
|
||||||
@@ -350,5 +443,311 @@ const IconGrid = new Lang.Class({
|
|||||||
|
|
||||||
visibleItemsCount: function() {
|
visibleItemsCount: function() {
|
||||||
return this._grid.get_n_children() - this._grid.get_n_skip_paint();
|
return this._grid.get_n_children() - this._grid.get_n_skip_paint();
|
||||||
|
},
|
||||||
|
|
||||||
|
setSpacing: function(spacing) {
|
||||||
|
this._fixedSpacing = spacing;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getSpacing: function() {
|
||||||
|
return this._fixedSpacing ? this._fixedSpacing : this._spacing;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getHItemSize: function() {
|
||||||
|
return this._fixedHItemSize ? this._fixedHItemSize : this._hItemSize;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getVItemSize: function() {
|
||||||
|
return this._fixedVItemSize ? this._fixedVItemSize : this._vItemSize;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSpacingForSize: function(availWidth, availHeight) {
|
||||||
|
let maxEmptyVArea = availHeight - this._minRows * this._getVItemSize();
|
||||||
|
let maxEmptyHArea = availWidth - this._minColumns * this._getHItemSize();
|
||||||
|
let maxHSpacing, maxVSpacing;
|
||||||
|
|
||||||
|
if (this._padWithSpacing) {
|
||||||
|
// minRows + 1 because we want to put spacing before the first row, so it is like we have one more row
|
||||||
|
// to divide the empty space
|
||||||
|
maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows +1));
|
||||||
|
maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns +1));
|
||||||
|
} else {
|
||||||
|
if (this._minRows <= 1)
|
||||||
|
maxVSpacing = maxEmptyVArea;
|
||||||
|
else
|
||||||
|
maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows - 1));
|
||||||
|
|
||||||
|
if (this._minColumns <= 1)
|
||||||
|
maxHSpacing = maxEmptyHArea;
|
||||||
|
else
|
||||||
|
maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
let maxSpacing = Math.min(maxHSpacing, maxVSpacing);
|
||||||
|
// Limit spacing to the item size
|
||||||
|
maxSpacing = Math.min(maxSpacing, Math.min(this._getVItemSize(), this._getHItemSize()));
|
||||||
|
// The minimum spacing, regardless of whether it satisfies the row/columng minima,
|
||||||
|
// is the spacing we get from CSS.
|
||||||
|
let spacing = Math.max(this._spacing, maxSpacing);
|
||||||
|
this.setSpacing(spacing);
|
||||||
|
if (this._padWithSpacing)
|
||||||
|
this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = spacing;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function must to be called before iconGrid allocation,
|
||||||
|
* to know how much spacing can the grid has
|
||||||
|
*/
|
||||||
|
adaptToSize: function(availWidth, availHeight) {
|
||||||
|
this._fixedHItemSize = this._hItemSize;
|
||||||
|
this._fixedVItemSize = this._vItemSize;
|
||||||
|
this._updateSpacingForSize(availWidth, availHeight);
|
||||||
|
let spacing = this._getSpacing();
|
||||||
|
|
||||||
|
if (this.columnsForWidth(availWidth) < this._minColumns || this.rowsForHeight(availHeight) < this._minRows) {
|
||||||
|
let neededWidth = this.usedWidthForNColumns(this._minColumns) - availWidth ;
|
||||||
|
let neededHeight = this.usedHeightForNRows(this._minRows) - availHeight ;
|
||||||
|
|
||||||
|
let neededSpacePerItem = (neededWidth > neededHeight) ? Math.ceil(neededWidth / this._minColumns)
|
||||||
|
: Math.ceil(neededHeight / this._minRows);
|
||||||
|
this._fixedHItemSize = Math.max(this._hItemSize - neededSpacePerItem, MIN_ICON_SIZE);
|
||||||
|
this._fixedVItemSize = Math.max(this._vItemSize - neededSpacePerItem, MIN_ICON_SIZE);
|
||||||
|
|
||||||
|
if (this._fixedHItemSize < MIN_ICON_SIZE)
|
||||||
|
this._fixedHItemSize = MIN_ICON_SIZE;
|
||||||
|
if (this._fixedVItemSize < MIN_ICON_SIZE)
|
||||||
|
this._fixedVItemSize = MIN_ICON_SIZE;
|
||||||
|
|
||||||
|
this._updateSpacingForSize(availWidth, availHeight);
|
||||||
|
}
|
||||||
|
let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize, this._vItemSize);
|
||||||
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateChildrenScale(scale); }));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up
|
||||||
|
_updateChildrenScale: function(scale) {
|
||||||
|
for (let i in this._items) {
|
||||||
|
let newIconSize = Math.floor(ICON_SIZE * scale);
|
||||||
|
this._items[i].icon.setIconSize(newIconSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const PaginatedIconGrid = new Lang.Class({
|
||||||
|
Name: 'PaginatedIconGrid',
|
||||||
|
Extends: IconGrid,
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
this.parent(params);
|
||||||
|
this._nPages = 0;
|
||||||
|
this._rowsPerPage = 0;
|
||||||
|
this._spaceBetweenPages = 0;
|
||||||
|
this._childrenPerPage = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||||
|
alloc.min_size = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) * this._nPages + this._spaceBetweenPages * this._nPages;
|
||||||
|
alloc.natural_size = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) * this._nPages + this._spaceBetweenPages * this._nPages;
|
||||||
|
},
|
||||||
|
|
||||||
|
_allocate: function (grid, box, flags) {
|
||||||
|
if (this._childrenPerPage == 0)
|
||||||
|
log('computePages() must be called before allocate(); pagination will not work.');
|
||||||
|
|
||||||
|
if (this._fillParent) {
|
||||||
|
// Reset the passed in box to fill the parent
|
||||||
|
let parentBox = this.actor.get_parent().allocation;
|
||||||
|
let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
|
||||||
|
box = this._grid.get_theme_node().get_content_box(gridBox);
|
||||||
|
}
|
||||||
|
let children = this._getVisibleChildren();
|
||||||
|
let availWidth = box.x2 - box.x1;
|
||||||
|
let availHeight = box.y2 - box.y1;
|
||||||
|
let spacing = this._getSpacing();
|
||||||
|
let [nColumns, usedWidth] = this._computeLayout(availWidth);
|
||||||
|
|
||||||
|
let leftEmptySpace;
|
||||||
|
switch(this._xAlign) {
|
||||||
|
case St.Align.START:
|
||||||
|
leftEmptySpace = 0;
|
||||||
|
break;
|
||||||
|
case St.Align.MIDDLE:
|
||||||
|
leftEmptySpace = Math.floor((availWidth - usedWidth) / 2);
|
||||||
|
break;
|
||||||
|
case St.Align.END:
|
||||||
|
leftEmptySpace = availWidth - usedWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = box.x1 + leftEmptySpace + this.leftPadding;
|
||||||
|
let y = box.y1 + this.topPadding;
|
||||||
|
let columnIndex = 0;
|
||||||
|
let rowIndex = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
let childBox = this._calculateChildBox(children[i], x, y, box);
|
||||||
|
children[i].allocate(childBox, flags);
|
||||||
|
this._grid.set_skip_paint(children[i], false);
|
||||||
|
|
||||||
|
columnIndex++;
|
||||||
|
if (columnIndex == nColumns) {
|
||||||
|
columnIndex = 0;
|
||||||
|
rowIndex++;
|
||||||
|
}
|
||||||
|
if (columnIndex == 0) {
|
||||||
|
y += this._getVItemSize() + spacing;
|
||||||
|
if ((i + 1) % this._childrenPerPage == 0)
|
||||||
|
y += this._spaceBetweenPages - spacing + this.bottomPadding + this.topPadding;
|
||||||
|
x = box.x1 + leftEmptySpace + this.leftPadding;
|
||||||
|
} else
|
||||||
|
x += this._getHItemSize() + spacing;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_computePages: function (availWidthPerPage, availHeightPerPage) {
|
||||||
|
let [nColumns, usedWidth] = this._computeLayout(availWidthPerPage);
|
||||||
|
let nRows;
|
||||||
|
let children = this._getVisibleChildren();
|
||||||
|
if (nColumns > 0)
|
||||||
|
nRows = Math.ceil(children.length / nColumns);
|
||||||
|
else
|
||||||
|
nRows = 0;
|
||||||
|
if (this._rowLimit)
|
||||||
|
nRows = Math.min(nRows, this._rowLimit);
|
||||||
|
|
||||||
|
let spacing = this._getSpacing();
|
||||||
|
// We want to contain the grid inside the parent box with padding
|
||||||
|
this._rowsPerPage = this.rowsForHeight(availHeightPerPage);
|
||||||
|
this._nPages = Math.ceil(nRows / this._rowsPerPage);
|
||||||
|
this._spaceBetweenPages = availHeightPerPage - (this.topPadding + this.bottomPadding) - this._availableHeightPerPageForItems();
|
||||||
|
this._childrenPerPage = nColumns * this._rowsPerPage;
|
||||||
|
},
|
||||||
|
|
||||||
|
adaptToSize: function(availWidth, availHeight) {
|
||||||
|
this.parent(availWidth, availHeight);
|
||||||
|
this._computePages(availWidth, availHeight);
|
||||||
|
},
|
||||||
|
|
||||||
|
_availableHeightPerPageForItems: function() {
|
||||||
|
return this.usedHeightForNRows(this._rowsPerPage) - (this.topPadding + this.bottomPadding);
|
||||||
|
},
|
||||||
|
|
||||||
|
nPages: function() {
|
||||||
|
return this._nPages;
|
||||||
|
},
|
||||||
|
|
||||||
|
getPageY: function(pageNumber) {
|
||||||
|
if (!this._nPages)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
let firstPageItem = pageNumber * this._childrenPerPage
|
||||||
|
let childBox = this._getVisibleChildren()[firstPageItem].get_allocation_box();
|
||||||
|
return childBox.y1 - this.topPadding;
|
||||||
|
},
|
||||||
|
|
||||||
|
getItemPage: function(item) {
|
||||||
|
let children = this._getVisibleChildren();
|
||||||
|
let index = children.indexOf(item);
|
||||||
|
if (index == -1) {
|
||||||
|
throw new Error('Item not found.');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return Math.floor(index / this._childrenPerPage);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* openExtraSpace:
|
||||||
|
* @sourceItem: the item for which to create extra space
|
||||||
|
* @side: where @sourceItem should be located relative to the created space
|
||||||
|
* @nRows: the amount of space to create
|
||||||
|
*
|
||||||
|
* Pan view to create extra space for @nRows above or below @sourceItem.
|
||||||
|
*/
|
||||||
|
openExtraSpace: function(sourceItem, side, nRows) {
|
||||||
|
let children = this._getVisibleChildren();
|
||||||
|
let index = children.indexOf(sourceItem.actor);
|
||||||
|
if (index == -1) {
|
||||||
|
throw new Error('Item not found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let pageIndex = Math.floor(index / this._childrenPerPage);
|
||||||
|
let pageOffset = pageIndex * this._childrenPerPage;
|
||||||
|
|
||||||
|
let childrenPerRow = this._childrenPerPage / this._rowsPerPage;
|
||||||
|
let sourceRow = Math.floor((index - pageOffset) / childrenPerRow);
|
||||||
|
|
||||||
|
let nRowsAbove = (side == St.Side.TOP) ? sourceRow + 1
|
||||||
|
: sourceRow;
|
||||||
|
let nRowsBelow = this._rowsPerPage - nRowsAbove;
|
||||||
|
|
||||||
|
let nRowsUp, nRowsDown;
|
||||||
|
if (side == St.Side.TOP) {
|
||||||
|
nRowsDown = Math.min(nRowsBelow, nRows);
|
||||||
|
nRowsUp = nRows - nRowsDown;
|
||||||
|
} else {
|
||||||
|
nRowsUp = Math.min(nRowsAbove, nRows);
|
||||||
|
nRowsDown = nRows - nRowsUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
let childrenDown = children.splice(pageOffset +
|
||||||
|
nRowsAbove * childrenPerRow,
|
||||||
|
nRowsBelow * childrenPerRow);
|
||||||
|
let childrenUp = children.splice(pageOffset,
|
||||||
|
nRowsAbove * childrenPerRow);
|
||||||
|
|
||||||
|
// Special case: On the last row with no rows below the icon,
|
||||||
|
// there's no need to move any rows either up or down
|
||||||
|
if (childrenDown.length == 0 && nRowsUp == 0) {
|
||||||
|
this._translatedChildren = [];
|
||||||
|
this.emit('space-opened');
|
||||||
|
} else {
|
||||||
|
this._translateChildren(childrenUp, Gtk.DirectionType.UP, nRowsUp);
|
||||||
|
this._translateChildren(childrenDown, Gtk.DirectionType.DOWN, nRowsDown);
|
||||||
|
this._translatedChildren = childrenUp.concat(childrenDown);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_translateChildren: function(children, direction, nRows) {
|
||||||
|
let translationY = nRows * (this._getVItemSize() + this._getSpacing());
|
||||||
|
if (translationY == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (direction == Gtk.DirectionType.UP)
|
||||||
|
translationY *= -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
children[i].translation_y = 0;
|
||||||
|
let params = { translation_y: translationY,
|
||||||
|
time: EXTRA_SPACE_ANIMATION_TIME,
|
||||||
|
transition: 'easeInOutQuad'
|
||||||
|
};
|
||||||
|
if (i == (children.length - 1))
|
||||||
|
params.onComplete = Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this.emit('space-opened');
|
||||||
|
});
|
||||||
|
Tweener.addTween(children[i], params);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
closeExtraSpace: function() {
|
||||||
|
if (!this._translatedChildren || !this._translatedChildren.length) {
|
||||||
|
this.emit('space-closed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._translatedChildren.length; i++) {
|
||||||
|
if (!this._translatedChildren[i].translation_y)
|
||||||
|
continue;
|
||||||
|
Tweener.addTween(this._translatedChildren[i],
|
||||||
|
{ translation_y: 0,
|
||||||
|
time: EXTRA_SPACE_ANIMATION_TIME,
|
||||||
|
transition: 'easeInOutQuad',
|
||||||
|
onComplete: Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this.emit('space-closed');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(PaginatedIconGrid.prototype);
|
||||||
|
|||||||
@@ -2,45 +2,50 @@
|
|||||||
|
|
||||||
const Caribou = imports.gi.Caribou;
|
const Caribou = imports.gi.Caribou;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const DBus = imports.dbus;
|
|
||||||
const Gdk = imports.gi.Gdk;
|
const Gdk = imports.gi.Gdk;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
const BoxPointer = imports.ui.boxpointer;
|
const BoxPointer = imports.ui.boxpointer;
|
||||||
|
const Layout = imports.ui.layout;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
|
||||||
|
const KEYBOARD_REST_TIME = Layout.KEYBOARD_ANIMATION_TIME * 2 * 1000;
|
||||||
|
|
||||||
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
|
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
|
||||||
const KEYBOARD_TYPE = 'keyboard-type';
|
const KEYBOARD_TYPE = 'keyboard-type';
|
||||||
|
|
||||||
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||||
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
|
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
|
||||||
|
|
||||||
const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'>
|
const CaribouKeyboardIface = '<node> \
|
||||||
<method name='Show'>
|
<interface name="org.gnome.Caribou.Keyboard"> \
|
||||||
<arg type='u' direction='in' />
|
<method name="Show"> \
|
||||||
</method>
|
<arg type="u" direction="in" /> \
|
||||||
<method name='Hide'>
|
</method> \
|
||||||
<arg type='u' direction='in' />
|
<method name="Hide"> \
|
||||||
</method>
|
<arg type="u" direction="in" /> \
|
||||||
<method name='SetCursorLocation'>
|
</method> \
|
||||||
<arg type='i' direction='in' />
|
<method name="SetCursorLocation"> \
|
||||||
<arg type='i' direction='in' />
|
<arg type="i" direction="in" /> \
|
||||||
<arg type='i' direction='in' />
|
<arg type="i" direction="in" /> \
|
||||||
<arg type='i' direction='in' />
|
<arg type="i" direction="in" /> \
|
||||||
</method>
|
<arg type="i" direction="in" /> \
|
||||||
<method name='SetEntryLocation'>
|
</method> \
|
||||||
<arg type='i' direction='in' />
|
<method name="SetEntryLocation"> \
|
||||||
<arg type='i' direction='in' />
|
<arg type="i" direction="in" /> \
|
||||||
<arg type='i' direction='in' />
|
<arg type="i" direction="in" /> \
|
||||||
<arg type='i' direction='in' />
|
<arg type="i" direction="in" /> \
|
||||||
</method>
|
<arg type="i" direction="in" /> \
|
||||||
<property name='Name' access='read' type='s' />
|
</method> \
|
||||||
</interface>;
|
<property name="Name" access="read" type="s" /> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const Key = new Lang.Class({
|
const Key = new Lang.Class({
|
||||||
Name: 'Key',
|
Name: 'Key',
|
||||||
@@ -56,14 +61,7 @@ const Key = new Lang.Class({
|
|||||||
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
|
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
|
||||||
this._key.latch = true;
|
this._key.latch = true;
|
||||||
|
|
||||||
this._key.connect('key-pressed', Lang.bind(this, function ()
|
|
||||||
{ this.actor.checked = true }));
|
|
||||||
this._key.connect('key-released', Lang.bind(this, function ()
|
|
||||||
{ this.actor.checked = false; }));
|
|
||||||
|
|
||||||
if (this._extended_keys.length > 0) {
|
if (this._extended_keys.length > 0) {
|
||||||
this._grabbed = false;
|
|
||||||
this._eventCaptureId = 0;
|
|
||||||
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
|
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
|
||||||
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||||
{ x_fill: true,
|
{ x_fill: true,
|
||||||
@@ -84,8 +82,16 @@ const Key = new Lang.Class({
|
|||||||
style_class: 'keyboard-key' });
|
style_class: 'keyboard-key' });
|
||||||
|
|
||||||
button.key_width = this._key.width;
|
button.key_width = this._key.width;
|
||||||
button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
|
button.connect('button-press-event', Lang.bind(this,
|
||||||
button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
|
function () {
|
||||||
|
this._key.press();
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
}));
|
||||||
|
button.connect('button-release-event', Lang.bind(this,
|
||||||
|
function () {
|
||||||
|
this._key.release();
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
}));
|
||||||
|
|
||||||
return button;
|
return button;
|
||||||
},
|
},
|
||||||
@@ -108,59 +114,38 @@ const Key = new Lang.Class({
|
|||||||
let label = this._getUnichar(extended_key);
|
let label = this._getUnichar(extended_key);
|
||||||
let key = new St.Button({ label: label, style_class: 'keyboard-key' });
|
let key = new St.Button({ label: label, style_class: 'keyboard-key' });
|
||||||
key.extended_key = extended_key;
|
key.extended_key = extended_key;
|
||||||
key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
|
key.connect('button-press-event', Lang.bind(this,
|
||||||
key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
|
function () {
|
||||||
|
extended_key.press();
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
}));
|
||||||
|
key.connect('button-release-event', Lang.bind(this,
|
||||||
|
function () {
|
||||||
|
extended_key.release();
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
}));
|
||||||
this._extended_keyboard.add(key);
|
this._extended_keyboard.add(key);
|
||||||
}
|
}
|
||||||
this._boxPointer.bin.add_actor(this._extended_keyboard);
|
this._boxPointer.bin.add_actor(this._extended_keyboard);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onEventCapture: function (actor, event) {
|
get subkeys() {
|
||||||
let source = event.get_source();
|
return this._boxPointer;
|
||||||
let type = event.type();
|
|
||||||
|
|
||||||
if ((type == Clutter.EventType.BUTTON_PRESS ||
|
|
||||||
type == Clutter.EventType.BUTTON_RELEASE) &&
|
|
||||||
this._extended_keyboard.contains(source)) {
|
|
||||||
source.extended_key.press();
|
|
||||||
source.extended_key.release();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (type == Clutter.EventType.BUTTON_PRESS) {
|
|
||||||
this._boxPointer.actor.hide();
|
|
||||||
this._ungrab();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
_ungrab: function () {
|
|
||||||
global.stage.disconnect(this._eventCaptureId);
|
|
||||||
this._eventCaptureId = 0;
|
|
||||||
this._grabbed = false;
|
|
||||||
Main.popModal(this.actor);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onShowSubkeysChanged: function () {
|
_onShowSubkeysChanged: function () {
|
||||||
if (this._key.show_subkeys) {
|
if (this._key.show_subkeys) {
|
||||||
this.actor.fake_release();
|
|
||||||
this._boxPointer.actor.raise_top();
|
this._boxPointer.actor.raise_top();
|
||||||
this._boxPointer.setPosition(this.actor, 0.5);
|
this._boxPointer.setPosition(this.actor, 0.5);
|
||||||
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
|
this.emit('show-subkeys');
|
||||||
|
this.actor.fake_release();
|
||||||
this.actor.set_hover(false);
|
this.actor.set_hover(false);
|
||||||
if (!this._grabbed) {
|
|
||||||
Main.pushModal(this.actor);
|
|
||||||
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
|
|
||||||
this._grabbed = true;
|
|
||||||
}
|
|
||||||
this._key.release();
|
|
||||||
} else {
|
} else {
|
||||||
if (this._grabbed)
|
this.emit('hide-subkeys');
|
||||||
this._ungrab();
|
|
||||||
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(Key.prototype);
|
||||||
|
|
||||||
const Keyboard = new Lang.Class({
|
const Keyboard = new Lang.Class({
|
||||||
// HACK: we can't set Name, because it collides with Name dbus property
|
// HACK: we can't set Name, because it collides with Name dbus property
|
||||||
@@ -175,16 +160,26 @@ const Keyboard = new Lang.Class({
|
|||||||
this._focusInExtendedKeys = false;
|
this._focusInExtendedKeys = false;
|
||||||
|
|
||||||
this._timestamp = global.display.get_current_time_roundtrip();
|
this._timestamp = global.display.get_current_time_roundtrip();
|
||||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
|
||||||
|
|
||||||
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
||||||
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||||
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
|
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
|
||||||
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||||
this._settingsChanged();
|
this._settingsChanged();
|
||||||
},
|
|
||||||
|
|
||||||
init: function () {
|
this._showIdleId = 0;
|
||||||
|
this._subkeysBoxPointer = null;
|
||||||
|
this._capturedEventId = 0;
|
||||||
|
this._capturedPress = false;
|
||||||
|
|
||||||
|
this._keyboardVisible = false;
|
||||||
|
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, function(o, visible) {
|
||||||
|
this._keyboardVisible = visible;
|
||||||
|
}));
|
||||||
|
this._keyboardRequested = false;
|
||||||
|
this._keyboardRestingId = 0;
|
||||||
|
|
||||||
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
||||||
this._redraw();
|
this._redraw();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -199,25 +194,19 @@ const Keyboard = new Lang.Class({
|
|||||||
if (this._keyboard)
|
if (this._keyboard)
|
||||||
this._destroyKeyboard();
|
this._destroyKeyboard();
|
||||||
|
|
||||||
if (this._enableKeyboard) {
|
if (this._enableKeyboard)
|
||||||
// If we've been called because the setting actually just
|
this._setupKeyboard();
|
||||||
// changed to true (as opposed to being called from
|
else
|
||||||
// this._init()), then we want to pop up the keyboard.
|
|
||||||
let showKeyboard = (settings != null);
|
|
||||||
|
|
||||||
// However, caribou-gtk-module or this._onKeyFocusChanged
|
|
||||||
// will probably immediately tell us to hide it, so we
|
|
||||||
// have to fake things out so we'll ignore that request.
|
|
||||||
if (showKeyboard)
|
|
||||||
this._timestamp = global.display.get_current_time_roundtrip() + 1;
|
|
||||||
this._setupKeyboard(showKeyboard);
|
|
||||||
} else
|
|
||||||
Main.layoutManager.hideKeyboard(true);
|
Main.layoutManager.hideKeyboard(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
_destroyKeyboard: function() {
|
_destroyKeyboard: function() {
|
||||||
if (this._keyboardNotifyId)
|
if (this._keyboardNotifyId)
|
||||||
this._keyboard.disconnect(this._keyboardNotifyId);
|
this._keyboard.disconnect(this._keyboardNotifyId);
|
||||||
|
if (this._keyboardGroupAddedId)
|
||||||
|
this._keyboard.disconnect(this._keyboardGroupAddedId);
|
||||||
|
if (this._keyboardGroupRemovedId)
|
||||||
|
this._keyboard.disconnect(this._keyboardGroupRemovedId);
|
||||||
if (this._focusNotifyId)
|
if (this._focusNotifyId)
|
||||||
global.stage.disconnect(this._focusNotifyId);
|
global.stage.disconnect(this._focusNotifyId);
|
||||||
this._keyboard = null;
|
this._keyboard = null;
|
||||||
@@ -227,7 +216,7 @@ const Keyboard = new Lang.Class({
|
|||||||
this._destroySource();
|
this._destroySource();
|
||||||
},
|
},
|
||||||
|
|
||||||
_setupKeyboard: function(show) {
|
_setupKeyboard: function() {
|
||||||
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
|
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
|
||||||
Main.layoutManager.keyboardBox.add_actor(this.actor);
|
Main.layoutManager.keyboardBox.add_actor(this.actor);
|
||||||
Main.layoutManager.trackChrome(this.actor);
|
Main.layoutManager.trackChrome(this.actor);
|
||||||
@@ -248,11 +237,10 @@ const Keyboard = new Lang.Class({
|
|||||||
this.actor.text_direction = Clutter.TextDirection.LTR;
|
this.actor.text_direction = Clutter.TextDirection.LTR;
|
||||||
|
|
||||||
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
|
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
|
||||||
|
this._keyboardGroupAddedId = this._keyboard.connect('group-added', Lang.bind(this, this._onGroupAdded));
|
||||||
|
this._keyboardGroupRemovedId = this._keyboard.connect('group-removed', Lang.bind(this, this._onGroupRemoved));
|
||||||
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||||
|
|
||||||
if (show)
|
|
||||||
this.show(Main.layoutManager.focusIndex);
|
|
||||||
else
|
|
||||||
this._createSource();
|
this._createSource();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -273,16 +261,20 @@ const Keyboard = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
let time = global.get_current_time();
|
let time = global.get_current_time();
|
||||||
if (focus instanceof Clutter.Text)
|
if (!(focus instanceof Clutter.Text)) {
|
||||||
this.Show(time);
|
|
||||||
else
|
|
||||||
this.Hide(time);
|
this.Hide(time);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._showIdleId)
|
||||||
|
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this.Show(time);
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_addKeys: function () {
|
_createLayersForGroup: function (gname) {
|
||||||
let groups = this._keyboard.get_groups();
|
|
||||||
for (let i = 0; i < groups.length; ++i) {
|
|
||||||
let gname = groups[i];
|
|
||||||
let group = this._keyboard.get_group(gname);
|
let group = this._keyboard.get_group(gname);
|
||||||
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
|
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
|
||||||
let layers = {};
|
let layers = {};
|
||||||
@@ -298,63 +290,76 @@ const Keyboard = new Lang.Class({
|
|||||||
|
|
||||||
layout.hide();
|
layout.hide();
|
||||||
}
|
}
|
||||||
this._groups[gname] = layers;
|
return layers;
|
||||||
|
},
|
||||||
|
|
||||||
|
_addKeys: function () {
|
||||||
|
let groups = this._keyboard.get_groups();
|
||||||
|
for (let i = 0; i < groups.length; ++i) {
|
||||||
|
let gname = groups[i];
|
||||||
|
this._groups[gname] = this._createLayersForGroup(gname);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._setActiveLayer();
|
this._setActiveLayer();
|
||||||
},
|
},
|
||||||
|
|
||||||
_getTrayIcon: function () {
|
_onCapturedEvent: function(actor, event) {
|
||||||
let trayButton = new St.Button ({ label: _("tray"),
|
let type = event.type();
|
||||||
style_class: 'keyboard-key' });
|
let press = type == Clutter.EventType.BUTTON_PRESS;
|
||||||
trayButton.key_width = 1;
|
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
||||||
trayButton.connect('button-press-event', Lang.bind(this, function () {
|
|
||||||
Main.messageTray.toggle();
|
|
||||||
}));
|
|
||||||
|
|
||||||
Main.overview.connect('showing', Lang.bind(this, function () {
|
if (press)
|
||||||
trayButton.reactive = false;
|
this._capturedPress = true;
|
||||||
trayButton.add_style_pseudo_class('grayed');
|
else if (release && this._capturedPress)
|
||||||
}));
|
this._hideSubkeys();
|
||||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
|
||||||
trayButton.reactive = true;
|
|
||||||
trayButton.remove_style_pseudo_class('grayed');
|
|
||||||
}));
|
|
||||||
Main.sessionMode.connect('updated', Lang.bind(this, function() {
|
|
||||||
trayButton.reactive = !Main.sessionMode.isLocked;
|
|
||||||
if (Main.sessionMode.isLocked)
|
|
||||||
trayButton.add_style_pseudo_class('grayed');
|
|
||||||
else
|
|
||||||
trayButton.remove_style_pseudo_class('grayed');
|
|
||||||
}));
|
|
||||||
|
|
||||||
return trayButton;
|
return Clutter.EVENT_STOP;
|
||||||
},
|
},
|
||||||
|
|
||||||
_addRows : function (keys, layout) {
|
_addRows : function (keys, layout) {
|
||||||
let keyboard_row = new St.BoxLayout();
|
let keyboard_row = new St.BoxLayout();
|
||||||
for (let i = 0; i < keys.length; ++i) {
|
for (let i = 0; i < keys.length; ++i) {
|
||||||
let children = keys[i].get_children();
|
let children = keys[i].get_children();
|
||||||
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
|
||||||
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||||
|
let center_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||||
|
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||||
for (let j = 0; j < children.length; ++j) {
|
for (let j = 0; j < children.length; ++j) {
|
||||||
if (this._numOfHorizKeys == 0)
|
if (this._numOfHorizKeys == 0)
|
||||||
this._numOfHorizKeys = children.length;
|
this._numOfHorizKeys = children.length;
|
||||||
let key = children[j];
|
let key = children[j];
|
||||||
let button = new Key(key);
|
let button = new Key(key);
|
||||||
|
|
||||||
if (key.align == 'right')
|
switch (key.align) {
|
||||||
|
case 'right':
|
||||||
right_box.add(button.actor);
|
right_box.add(button.actor);
|
||||||
else
|
break;
|
||||||
|
case 'center':
|
||||||
|
center_box.add(button.actor);
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
default:
|
||||||
left_box.add(button.actor);
|
left_box.add(button.actor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (key.name == 'Caribou_Prefs') {
|
if (key.name == 'Caribou_Prefs') {
|
||||||
key.connect('key-released', Lang.bind(this, this.hide));
|
key.connect('key-released', Lang.bind(this, this.hide));
|
||||||
|
|
||||||
// Add new key for hiding message tray
|
|
||||||
right_box.add(this._getTrayIcon());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.connect('show-subkeys', Lang.bind(this, function() {
|
||||||
|
if (this._subkeysBoxPointer)
|
||||||
|
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||||
|
this._subkeysBoxPointer = button.subkeys;
|
||||||
|
this._subkeysBoxPointer.show(BoxPointer.PopupAnimation.FULL);
|
||||||
|
if (!this._capturedEventId)
|
||||||
|
this._capturedEventId = this.actor.connect('captured-event',
|
||||||
|
Lang.bind(this, this._onCapturedEvent));
|
||||||
|
}));
|
||||||
|
button.connect('hide-subkeys', Lang.bind(this, function() {
|
||||||
|
this._hideSubkeys();
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
|
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
|
||||||
|
keyboard_row.add(center_box, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||||
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
|
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
|
||||||
}
|
}
|
||||||
layout.add(keyboard_row);
|
layout.add(keyboard_row);
|
||||||
@@ -427,6 +432,14 @@ const Keyboard = new Lang.Class({
|
|||||||
this._redraw();
|
this._redraw();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onGroupAdded: function (keyboard, gname) {
|
||||||
|
this._groups[gname] = this._createLayersForGroup(gname);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onGroupRemoved: function (keyboard, gname) {
|
||||||
|
delete this._groups[gname];
|
||||||
|
},
|
||||||
|
|
||||||
_setActiveLayer: function () {
|
_setActiveLayer: function () {
|
||||||
let active_group_name = this._keyboard.active_group;
|
let active_group_name = this._keyboard.active_group;
|
||||||
let active_group = this._keyboard.get_group(active_group_name);
|
let active_group = this._keyboard.get_group(active_group_name);
|
||||||
@@ -444,7 +457,6 @@ const Keyboard = new Lang.Class({
|
|||||||
_createSource: function () {
|
_createSource: function () {
|
||||||
if (this._source == null) {
|
if (this._source == null) {
|
||||||
this._source = new KeyboardSource(this);
|
this._source = new KeyboardSource(this);
|
||||||
this._source.setTransient(true);
|
|
||||||
Main.messageTray.add(this._source);
|
Main.messageTray.add(this._source);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -462,7 +474,38 @@ const Keyboard = new Lang.Class({
|
|||||||
actor._extended_keys || actor.extended_key;
|
actor._extended_keys || actor.extended_key;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_clearKeyboardRestTimer: function() {
|
||||||
|
if (!this._keyboardRestingId)
|
||||||
|
return;
|
||||||
|
GLib.source_remove(this._keyboardRestingId);
|
||||||
|
this._keyboardRestingId = 0;
|
||||||
|
},
|
||||||
|
|
||||||
show: function (monitor) {
|
show: function (monitor) {
|
||||||
|
this._keyboardRequested = true;
|
||||||
|
|
||||||
|
if (this._keyboardVisible) {
|
||||||
|
if (monitor != Main.layoutManager.keyboardIndex) {
|
||||||
|
Main.layoutManager.keyboardIndex = monitor;
|
||||||
|
this._redraw();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._clearKeyboardRestTimer();
|
||||||
|
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||||
|
KEYBOARD_REST_TIME,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._clearKeyboardRestTimer();
|
||||||
|
this._show(monitor);
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_show: function(monitor) {
|
||||||
|
if (!this._keyboardRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
Main.layoutManager.keyboardIndex = monitor;
|
Main.layoutManager.keyboardIndex = monitor;
|
||||||
this._redraw();
|
this._redraw();
|
||||||
Main.layoutManager.showKeyboard();
|
Main.layoutManager.showKeyboard();
|
||||||
@@ -470,10 +513,42 @@ const Keyboard = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
hide: function () {
|
hide: function () {
|
||||||
|
this._keyboardRequested = false;
|
||||||
|
|
||||||
|
if (!this._keyboardVisible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._clearKeyboardRestTimer();
|
||||||
|
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||||
|
KEYBOARD_REST_TIME,
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
this._clearKeyboardRestTimer();
|
||||||
|
this._hide();
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_hide: function() {
|
||||||
|
if (this._keyboardRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._hideSubkeys();
|
||||||
Main.layoutManager.hideKeyboard();
|
Main.layoutManager.hideKeyboard();
|
||||||
this._createSource();
|
this._createSource();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_hideSubkeys: function() {
|
||||||
|
if (this._subkeysBoxPointer) {
|
||||||
|
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||||
|
this._subkeysBoxPointer = null;
|
||||||
|
}
|
||||||
|
if (this._capturedEventId) {
|
||||||
|
this.actor.disconnect(this._capturedEventId);
|
||||||
|
this._capturedEventId = 0;
|
||||||
|
}
|
||||||
|
this._capturedPress = false;
|
||||||
|
},
|
||||||
|
|
||||||
_moveTemporarily: function () {
|
_moveTemporarily: function () {
|
||||||
let currentWindow = global.screen.get_display().focus_window;
|
let currentWindow = global.screen.get_display().focus_window;
|
||||||
let rect = currentWindow.get_outer_rect();
|
let rect = currentWindow.get_outer_rect();
|
||||||
@@ -502,6 +577,13 @@ const Keyboard = new Lang.Class({
|
|||||||
return one - two;
|
return one - two;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_clearShowIdle: function() {
|
||||||
|
if (!this._showIdleId)
|
||||||
|
return;
|
||||||
|
GLib.source_remove(this._showIdleId);
|
||||||
|
this._showIdleId = 0;
|
||||||
|
},
|
||||||
|
|
||||||
// D-Bus methods
|
// D-Bus methods
|
||||||
Show: function(timestamp) {
|
Show: function(timestamp) {
|
||||||
if (!this._enableKeyboard)
|
if (!this._enableKeyboard)
|
||||||
@@ -510,6 +592,8 @@ const Keyboard = new Lang.Class({
|
|||||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this._clearShowIdle();
|
||||||
|
|
||||||
if (timestamp != Clutter.CURRENT_TIME)
|
if (timestamp != Clutter.CURRENT_TIME)
|
||||||
this._timestamp = timestamp;
|
this._timestamp = timestamp;
|
||||||
this.show(Main.layoutManager.focusIndex);
|
this.show(Main.layoutManager.focusIndex);
|
||||||
@@ -522,6 +606,8 @@ const Keyboard = new Lang.Class({
|
|||||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
this._clearShowIdle();
|
||||||
|
|
||||||
if (timestamp != Clutter.CURRENT_TIME)
|
if (timestamp != Clutter.CURRENT_TIME)
|
||||||
this._timestamp = timestamp;
|
this._timestamp = timestamp;
|
||||||
this.hide();
|
this.hide();
|
||||||
@@ -556,11 +642,7 @@ const KeyboardSource = new Lang.Class({
|
|||||||
this.keepTrayOnSummaryClick = true;
|
this.keepTrayOnSummaryClick = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSummaryClick: function() {
|
handleSummaryClick: function(button) {
|
||||||
let event = Clutter.get_current_event();
|
|
||||||
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
this.open();
|
this.open();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|||||||
1411
js/ui/layout.js
1411
js/ui/layout.js
File diff suppressed because it is too large
Load Diff
@@ -3,13 +3,40 @@
|
|||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
|
const Signals = imports.signals;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
const DEFAULT_FADE_FACTOR = 0.4;
|
const DEFAULT_FADE_FACTOR = 0.4;
|
||||||
|
|
||||||
|
const GLSL_DIM_EFFECT_DECLARATIONS = '\
|
||||||
|
float compute_dim_factor (const vec2 coords) {\
|
||||||
|
vec2 dist = coords - vec2(0.5, 0.5); \
|
||||||
|
float elipse_radius = 0.5; \
|
||||||
|
/* interpolate darkening value, based on distance from screen center */ \
|
||||||
|
float val = min(length(dist), elipse_radius); \
|
||||||
|
return mix(0.3, 1.0, val / elipse_radius) * 0.4; \
|
||||||
|
}';
|
||||||
|
const GLSL_DIM_EFFECT_CODE = '\
|
||||||
|
float a = compute_dim_factor (cogl_tex_coord0_in.xy);\
|
||||||
|
cogl_color_out = vec4(0, 0, 0, cogl_color_in.a * a);'
|
||||||
|
;
|
||||||
|
|
||||||
|
const RadialShaderQuad = new Lang.Class({
|
||||||
|
Name: 'RadialShaderQuad',
|
||||||
|
Extends: Shell.GLSLQuad,
|
||||||
|
|
||||||
|
vfunc_build_pipeline: function() {
|
||||||
|
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT,
|
||||||
|
GLSL_DIM_EFFECT_DECLARATIONS,
|
||||||
|
GLSL_DIM_EFFECT_CODE,
|
||||||
|
true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lightbox:
|
* Lightbox:
|
||||||
* @container: parent Clutter.Container
|
* @container: parent Clutter.Container
|
||||||
@@ -41,16 +68,18 @@ const Lightbox = new Lang.Class({
|
|||||||
params = Params.parse(params, { inhibitEvents: false,
|
params = Params.parse(params, { inhibitEvents: false,
|
||||||
width: null,
|
width: null,
|
||||||
height: null,
|
height: null,
|
||||||
fadeInTime: null,
|
fadeFactor: DEFAULT_FADE_FACTOR,
|
||||||
fadeOutTime: null,
|
radialEffect: false,
|
||||||
fadeFactor: DEFAULT_FADE_FACTOR
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this._container = container;
|
this._container = container;
|
||||||
this._children = container.get_children();
|
this._children = container.get_children();
|
||||||
this._fadeInTime = params.fadeInTime;
|
|
||||||
this._fadeOutTime = params.fadeOutTime;
|
|
||||||
this._fadeFactor = params.fadeFactor;
|
this._fadeFactor = params.fadeFactor;
|
||||||
|
if (params.radialEffect)
|
||||||
|
this.actor = new RadialShaderQuad({ x: 0,
|
||||||
|
y: 0,
|
||||||
|
reactive: params.inhibitEvents });
|
||||||
|
else
|
||||||
this.actor = new St.Bin({ x: 0,
|
this.actor = new St.Bin({ x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
style_class: 'lightbox',
|
style_class: 'lightbox',
|
||||||
@@ -100,31 +129,39 @@ const Lightbox = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
show: function() {
|
show: function(fadeInTime) {
|
||||||
if (this._fadeInTime) {
|
fadeInTime = fadeInTime || 0;
|
||||||
|
|
||||||
|
Tweener.removeTweens(this.actor);
|
||||||
|
if (fadeInTime != 0) {
|
||||||
this.shown = false;
|
this.shown = false;
|
||||||
this.actor.opacity = 0;
|
this.actor.opacity = 0;
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 255 * this._fadeFactor,
|
{ opacity: 255 * this._fadeFactor,
|
||||||
time: this._fadeInTime,
|
time: fadeInTime,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: Lang.bind(this, function() {
|
onComplete: Lang.bind(this, function() {
|
||||||
this.shown = true;
|
this.shown = true;
|
||||||
|
this.emit('shown');
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.actor.opacity = 255 * this._fadeFactor;
|
this.actor.opacity = 255 * this._fadeFactor;
|
||||||
this.shown = true;
|
this.shown = true;
|
||||||
|
this.emit('shown');
|
||||||
}
|
}
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
hide: function() {
|
hide: function(fadeOutTime) {
|
||||||
|
fadeOutTime = fadeOutTime || 0;
|
||||||
|
|
||||||
this.shown = false;
|
this.shown = false;
|
||||||
if (this._fadeOutTime) {
|
Tweener.removeTweens(this.actor);
|
||||||
|
if (fadeOutTime != 0) {
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: this._fadeOutTime,
|
time: fadeOutTime,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
onComplete: Lang.bind(this, function() {
|
onComplete: Lang.bind(this, function() {
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
@@ -197,3 +234,4 @@ const Lightbox = new Lang.Class({
|
|||||||
this.highlight(null);
|
this.highlight(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(Lightbox.prototype);
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
|||||||
* in the shell core code too. */
|
* in the shell core code too. */
|
||||||
'const stage = global.stage; ' +
|
'const stage = global.stage; ' +
|
||||||
/* Special lookingGlass functions */
|
/* Special lookingGlass functions */
|
||||||
|
'const inspect = Lang.bind(Main.lookingGlass, Main.lookingGlass.inspect); ' +
|
||||||
'const it = Main.lookingGlass.getIt(); ' +
|
'const it = Main.lookingGlass.getIt(); ' +
|
||||||
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
||||||
|
|
||||||
@@ -108,6 +109,7 @@ const AutoComplete = new Lang.Class({
|
|||||||
}
|
}
|
||||||
this._lastTabTime = currTime;
|
this._lastTabTime = currTime;
|
||||||
}
|
}
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Insert characters of text not already included in head at cursor position. i.e., if text="abc" and head="a",
|
// Insert characters of text not already included in head at cursor position. i.e., if text="abc" and head="a",
|
||||||
@@ -307,10 +309,6 @@ const Result = new Lang.Class({
|
|||||||
box.add(resultTxt);
|
box.add(resultTxt);
|
||||||
let objLink = new ObjLink(this._lookingGlass, o);
|
let objLink = new ObjLink(this._lookingGlass, o);
|
||||||
box.add(objLink.actor);
|
box.add(objLink.actor);
|
||||||
let line = new Clutter.Rectangle({ name: 'Separator' });
|
|
||||||
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
|
|
||||||
padBin.add_actor(line);
|
|
||||||
this.actor.add(padBin);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -505,7 +503,7 @@ const Inspector = new Lang.Class({
|
|||||||
let container = new Shell.GenericContainer({ width: 0,
|
let container = new Shell.GenericContainer({ width: 0,
|
||||||
height: 0 });
|
height: 0 });
|
||||||
container.connect('allocate', Lang.bind(this, this._allocate));
|
container.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
Main.uiGroup.add_actor(container);
|
Main.layoutManager.sessionGroup.add_actor(container);
|
||||||
|
|
||||||
let eventHandler = new St.BoxLayout({ name: 'LookingGlassDialog',
|
let eventHandler = new St.BoxLayout({ name: 'LookingGlassDialog',
|
||||||
vertical: false,
|
vertical: false,
|
||||||
@@ -561,7 +559,7 @@ const Inspector = new Lang.Class({
|
|||||||
_onKeyPressEvent: function (actor, event) {
|
_onKeyPressEvent: function (actor, event) {
|
||||||
if (event.get_key_symbol() == Clutter.Escape)
|
if (event.get_key_symbol() == Clutter.Escape)
|
||||||
this._close();
|
this._close();
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onButtonPressEvent: function (actor, event) {
|
_onButtonPressEvent: function (actor, event) {
|
||||||
@@ -570,7 +568,7 @@ const Inspector = new Lang.Class({
|
|||||||
this.emit('target', this._target, stageX, stageY);
|
this.emit('target', this._target, stageX, stageY);
|
||||||
}
|
}
|
||||||
this._close();
|
this._close();
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onScrollEvent: function (actor, event) {
|
_onScrollEvent: function (actor, event) {
|
||||||
@@ -604,12 +602,12 @@ const Inspector = new Lang.Class({
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMotionEvent: function (actor, event) {
|
_onMotionEvent: function (actor, event) {
|
||||||
this._update(event);
|
this._update(event);
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
},
|
},
|
||||||
|
|
||||||
_update: function(event) {
|
_update: function(event) {
|
||||||
@@ -632,59 +630,11 @@ const Inspector = new Lang.Class({
|
|||||||
|
|
||||||
Signals.addSignalMethods(Inspector.prototype);
|
Signals.addSignalMethods(Inspector.prototype);
|
||||||
|
|
||||||
const Memory = new Lang.Class({
|
|
||||||
Name: 'Memory',
|
|
||||||
|
|
||||||
_init: function() {
|
|
||||||
this.actor = new St.BoxLayout({ vertical: true });
|
|
||||||
this._glibc_uordblks = new St.Label();
|
|
||||||
this.actor.add(this._glibc_uordblks);
|
|
||||||
|
|
||||||
this._js_bytes = new St.Label();
|
|
||||||
this.actor.add(this._js_bytes);
|
|
||||||
|
|
||||||
this._gjs_boxed = new St.Label();
|
|
||||||
this.actor.add(this._gjs_boxed);
|
|
||||||
|
|
||||||
this._gjs_gobject = new St.Label();
|
|
||||||
this.actor.add(this._gjs_gobject);
|
|
||||||
|
|
||||||
this._gjs_function = new St.Label();
|
|
||||||
this.actor.add(this._gjs_function);
|
|
||||||
|
|
||||||
this._gjs_closure = new St.Label();
|
|
||||||
this.actor.add(this._gjs_closure);
|
|
||||||
|
|
||||||
this._last_gc_seconds_ago = new St.Label();
|
|
||||||
this.actor.add(this._last_gc_seconds_ago);
|
|
||||||
|
|
||||||
this._gcbutton = new St.Button({ label: 'Full GC',
|
|
||||||
style_class: 'lg-obj-inspector-button' });
|
|
||||||
this._gcbutton.connect('clicked', Lang.bind(this, function () { System.gc(); this._renderText(); }));
|
|
||||||
this.actor.add(this._gcbutton, { x_align: St.Align.START,
|
|
||||||
x_fill: false });
|
|
||||||
|
|
||||||
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
|
|
||||||
},
|
|
||||||
|
|
||||||
_renderText: function() {
|
|
||||||
if (!this.actor.mapped)
|
|
||||||
return;
|
|
||||||
let memInfo = global.get_memory_info();
|
|
||||||
this._glibc_uordblks.text = 'glibc_uordblks: ' + memInfo.glibc_uordblks;
|
|
||||||
this._js_bytes.text = 'js bytes: ' + memInfo.js_bytes;
|
|
||||||
this._gjs_boxed.text = 'gjs_boxed: ' + memInfo.gjs_boxed;
|
|
||||||
this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject;
|
|
||||||
this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function;
|
|
||||||
this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
|
|
||||||
this._last_gc_seconds_ago.text = 'last_gc_seconds_ago: ' + memInfo.last_gc_seconds_ago;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const Extensions = new Lang.Class({
|
const Extensions = new Lang.Class({
|
||||||
Name: 'Extensions',
|
Name: 'Extensions',
|
||||||
|
|
||||||
_init: function() {
|
_init: function(lookingGlass) {
|
||||||
|
this._lookingGlass = lookingGlass;
|
||||||
this.actor = new St.BoxLayout({ vertical: true,
|
this.actor = new St.BoxLayout({ vertical: true,
|
||||||
name: 'lookingGlassExtensions' });
|
name: 'lookingGlassExtensions' });
|
||||||
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
|
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
|
||||||
@@ -720,13 +670,13 @@ const Extensions = new Lang.Class({
|
|||||||
_onViewSource: function (actor) {
|
_onViewSource: function (actor) {
|
||||||
let extension = actor._extension;
|
let extension = actor._extension;
|
||||||
let uri = extension.dir.get_uri();
|
let uri = extension.dir.get_uri();
|
||||||
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
|
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context(0, -1));
|
||||||
this._lookingGlass.close();
|
this._lookingGlass.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onWebPage: function (actor) {
|
_onWebPage: function (actor) {
|
||||||
let extension = actor._extension;
|
let extension = actor._extension;
|
||||||
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
|
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context(0, -1));
|
||||||
this._lookingGlass.close();
|
this._lookingGlass.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -851,15 +801,14 @@ const LookingGlass = new Lang.Class({
|
|||||||
this._updateFont();
|
this._updateFont();
|
||||||
|
|
||||||
// We want it to appear to slide out from underneath the panel
|
// We want it to appear to slide out from underneath the panel
|
||||||
Main.layoutManager.panelBox.add_actor(this.actor);
|
Main.layoutManager.sessionGroup.add_actor(this.actor);
|
||||||
this.actor.lower_bottom();
|
Main.layoutManager.panelGroup.connect('allocation-changed',
|
||||||
Main.layoutManager.panelBox.connect('allocation-changed',
|
|
||||||
Lang.bind(this, this._queueResize));
|
Lang.bind(this, this._queueResize));
|
||||||
Main.layoutManager.keyboardBox.connect('allocation-changed',
|
Main.layoutManager.keyboardBox.connect('allocation-changed',
|
||||||
Lang.bind(this, this._queueResize));
|
Lang.bind(this, this._queueResize));
|
||||||
|
|
||||||
this._objInspector = new ObjInspector(this);
|
this._objInspector = new ObjInspector(this);
|
||||||
Main.uiGroup.add_actor(this._objInspector.actor);
|
Main.layoutManager.sessionGroup.add_actor(this._objInspector.actor);
|
||||||
this._objInspector.actor.hide();
|
this._objInspector.actor.hide();
|
||||||
|
|
||||||
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
|
let toolbar = new St.BoxLayout({ name: 'Toolbar' });
|
||||||
@@ -871,15 +820,29 @@ const LookingGlass = new Lang.Class({
|
|||||||
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||||
let inspector = new Inspector(this);
|
let inspector = new Inspector(this);
|
||||||
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
||||||
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
|
this._pushResult('inspect(' + Math.round(stageX) + ', ' + Math.round(stageY) + ')', target);
|
||||||
target);
|
|
||||||
}));
|
}));
|
||||||
inspector.connect('closed', Lang.bind(this, function() {
|
inspector.connect('closed', Lang.bind(this, function() {
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
global.stage.set_key_focus(this._entry);
|
global.stage.set_key_focus(this._entry);
|
||||||
}));
|
}));
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
|
}));
|
||||||
|
|
||||||
|
let gcIcon = new St.Icon({ icon_name: 'gnome-fs-trash-full',
|
||||||
|
icon_size: 24 });
|
||||||
|
toolbar.add_actor(gcIcon);
|
||||||
|
gcIcon.reactive = true;
|
||||||
|
gcIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||||
|
gcIcon.icon_name = 'gnome-fs-trash-empty';
|
||||||
|
System.gc();
|
||||||
|
this._timeoutId = Mainloop.timeout_add(500, Lang.bind(this, function () {
|
||||||
|
gcIcon.icon_name = 'gnome-fs-trash-full';
|
||||||
|
Mainloop.source_remove(this._timeoutId);
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
}));
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let notebook = new Notebook();
|
let notebook = new Notebook();
|
||||||
@@ -909,10 +872,7 @@ const LookingGlass = new Lang.Class({
|
|||||||
this._windowList = new WindowList(this);
|
this._windowList = new WindowList(this);
|
||||||
notebook.appendPage('Windows', this._windowList.actor);
|
notebook.appendPage('Windows', this._windowList.actor);
|
||||||
|
|
||||||
this._memory = new Memory();
|
this._extensions = new Extensions(this);
|
||||||
notebook.appendPage('Memory', this._memory.actor);
|
|
||||||
|
|
||||||
this._extensions = new Extensions();
|
|
||||||
notebook.appendPage('Extensions', this._extensions.actor);
|
notebook.appendPage('Extensions', this._extensions.actor);
|
||||||
|
|
||||||
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
|
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
|
||||||
@@ -922,7 +882,7 @@ const LookingGlass = new Lang.Class({
|
|||||||
let text = o.get_text();
|
let text = o.get_text();
|
||||||
// Ensure we don't get newlines in the command; the history file is
|
// Ensure we don't get newlines in the command; the history file is
|
||||||
// newline-separated.
|
// newline-separated.
|
||||||
text.replace('\n', ' ');
|
text = text.replace('\n', ' ');
|
||||||
// Strip leading and trailing whitespace
|
// Strip leading and trailing whitespace
|
||||||
text = text.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
text = text.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||||
if (text == '')
|
if (text == '')
|
||||||
@@ -950,9 +910,7 @@ const LookingGlass = new Lang.Class({
|
|||||||
|
|
||||||
_updateFont: function() {
|
_updateFont: function() {
|
||||||
let fontName = this._interfaceSettings.get_string('monospace-font-name');
|
let fontName = this._interfaceSettings.get_string('monospace-font-name');
|
||||||
// This is mishandled by the scanner - should by Pango.FontDescription_from_string(fontName);
|
let fontDesc = Pango.FontDescription.from_string(fontName);
|
||||||
// https://bugzilla.gnome.org/show_bug.cgi?id=595889
|
|
||||||
let fontDesc = Pango.font_description_from_string(fontName);
|
|
||||||
// We ignore everything but size and style; you'd be crazy to set your system-wide
|
// We ignore everything but size and style; you'd be crazy to set your system-wide
|
||||||
// monospace font to be bold/oblique/etc. Could easily be added here.
|
// monospace font to be bold/oblique/etc. Could easily be added here.
|
||||||
this.actor.style =
|
this.actor.style =
|
||||||
@@ -990,28 +948,18 @@ const LookingGlass = new Lang.Class({
|
|||||||
|
|
||||||
_showCompletions: function(completions) {
|
_showCompletions: function(completions) {
|
||||||
if (!this._completionActor) {
|
if (!this._completionActor) {
|
||||||
let actor = new St.BoxLayout({ vertical: true });
|
this._completionActor = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
|
||||||
|
this._completionActor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
this._completionText = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
|
this._completionActor.clutter_text.line_wrap = true;
|
||||||
this._completionText.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
|
||||||
this._completionText.clutter_text.line_wrap = true;
|
|
||||||
actor.add(this._completionText);
|
|
||||||
|
|
||||||
let line = new Clutter.Rectangle();
|
|
||||||
let padBin = new St.Bin({ x_fill: true, y_fill: true });
|
|
||||||
padBin.add_actor(line);
|
|
||||||
actor.add(padBin);
|
|
||||||
|
|
||||||
this._completionActor = actor;
|
|
||||||
this._evalBox.insert_child_below(this._completionActor, this._entryArea);
|
this._evalBox.insert_child_below(this._completionActor, this._entryArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._completionText.set_text(completions.join(', '));
|
this._completionActor.set_text(completions.join(', '));
|
||||||
|
|
||||||
// Setting the height to -1 allows us to get its actual preferred height rather than
|
// Setting the height to -1 allows us to get its actual preferred height rather than
|
||||||
// whatever was last given in set_height by Tweener.
|
// whatever was last given in set_height by Tweener.
|
||||||
this._completionActor.set_height(-1);
|
this._completionActor.set_height(-1);
|
||||||
let [minHeight, naturalHeight] = this._completionText.get_preferred_height(this._resultsArea.get_width());
|
let [minHeight, naturalHeight] = this._completionActor.get_preferred_height(this._resultsArea.get_width());
|
||||||
|
|
||||||
// Don't reanimate if we are already visible
|
// Don't reanimate if we are already visible
|
||||||
if (this._completionActor.visible) {
|
if (this._completionActor.visible) {
|
||||||
@@ -1057,6 +1005,10 @@ const LookingGlass = new Lang.Class({
|
|||||||
this._entry.text = '';
|
this._entry.text = '';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
inspect: function(x, y) {
|
||||||
|
return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||||
|
},
|
||||||
|
|
||||||
getIt: function () {
|
getIt: function () {
|
||||||
return this._it;
|
return this._it;
|
||||||
},
|
},
|
||||||
@@ -1082,15 +1034,15 @@ const LookingGlass = new Lang.Class({
|
|||||||
let myWidth = primary.width * 0.7;
|
let myWidth = primary.width * 0.7;
|
||||||
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
|
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
|
||||||
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
|
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
|
||||||
this.actor.x = (primary.width - myWidth) / 2;
|
this.actor.x = primary.x + (primary.width - myWidth) / 2;
|
||||||
this._hiddenY = this.actor.get_parent().height - myHeight - 4; // -4 to hide the top corners
|
this._hiddenY = primary.y + Main.layoutManager.panelGroup.height - myHeight - 4; // -4 to hide the top corners
|
||||||
this._targetY = this._hiddenY + myHeight;
|
this._targetY = this._hiddenY + myHeight;
|
||||||
this.actor.y = this._hiddenY;
|
this.actor.y = this._hiddenY;
|
||||||
this.actor.width = myWidth;
|
this.actor.width = myWidth;
|
||||||
this.actor.height = myHeight;
|
this.actor.height = myHeight;
|
||||||
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
|
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
|
||||||
this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
|
this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
|
||||||
primary.y + this._targetY + Math.floor(myHeight * 0.1));
|
this._targetY + Math.floor(myHeight * 0.1));
|
||||||
},
|
},
|
||||||
|
|
||||||
insertObject: function(obj) {
|
insertObject: function(obj) {
|
||||||
@@ -1112,7 +1064,7 @@ const LookingGlass = new Lang.Class({
|
|||||||
} else {
|
} else {
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
}
|
}
|
||||||
// Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
|
// Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
|
||||||
if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
|
if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
|
||||||
@@ -1122,14 +1074,14 @@ const LookingGlass = new Lang.Class({
|
|||||||
this._notebook.nextTab();
|
this._notebook.nextTab();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
open : function() {
|
open : function() {
|
||||||
if (this._open)
|
if (this._open)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!Main.pushModal(this._entry, { keybindingMode: Main.KeybindingMode.LOOKING_GLASS }))
|
if (!Main.pushModal(this._entry, { keybindingMode: Shell.KeyBindingMode.LOOKING_GLASS }))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._notebook.selectIndex(0);
|
this._notebook.selectIndex(0);
|
||||||
@@ -1160,7 +1112,7 @@ const LookingGlass = new Lang.Class({
|
|||||||
|
|
||||||
Main.popModal(this._entry);
|
Main.popModal(this._entry);
|
||||||
|
|
||||||
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
|
Tweener.addTween(this.actor, { time: Math.min(0.5 / St.get_slow_down_factor(), 0.5),
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
y: this._hiddenY,
|
y: this._hiddenY,
|
||||||
onComplete: Lang.bind(this, function () {
|
onComplete: Lang.bind(this, function () {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Atspi = imports.gi.Atspi;
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
const GDesktopEnums = imports.gi.GDesktopEnums;
|
const GDesktopEnums = imports.gi.GDesktopEnums;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
@@ -7,8 +8,10 @@ const Shell = imports.gi.Shell;
|
|||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const FocusCaretTracker = imports.ui.focusCaretTracker;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MagnifierDBus = imports.ui.magnifierDBus;
|
const MagnifierDBus = imports.ui.magnifierDBus;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
@@ -36,6 +39,8 @@ const CONTRAST_BLUE_KEY = 'contrast-blue';
|
|||||||
const LENS_MODE_KEY = 'lens-mode';
|
const LENS_MODE_KEY = 'lens-mode';
|
||||||
const CLAMP_MODE_KEY = 'scroll-at-edges';
|
const CLAMP_MODE_KEY = 'scroll-at-edges';
|
||||||
const MOUSE_TRACKING_KEY = 'mouse-tracking';
|
const MOUSE_TRACKING_KEY = 'mouse-tracking';
|
||||||
|
const FOCUS_TRACKING_KEY = 'focus-tracking';
|
||||||
|
const CARET_TRACKING_KEY = 'caret-tracking';
|
||||||
const SHOW_CROSS_HAIRS_KEY = 'show-cross-hairs';
|
const SHOW_CROSS_HAIRS_KEY = 'show-cross-hairs';
|
||||||
const CROSS_HAIRS_THICKNESS_KEY = 'cross-hairs-thickness';
|
const CROSS_HAIRS_THICKNESS_KEY = 'cross-hairs-thickness';
|
||||||
const CROSS_HAIRS_COLOR_KEY = 'cross-hairs-color';
|
const CROSS_HAIRS_COLOR_KEY = 'cross-hairs-color';
|
||||||
@@ -52,10 +57,24 @@ const Magnifier = new Lang.Class({
|
|||||||
// Magnifier is a manager of ZoomRegions.
|
// Magnifier is a manager of ZoomRegions.
|
||||||
this._zoomRegions = [];
|
this._zoomRegions = [];
|
||||||
|
|
||||||
|
// Export to dbus.
|
||||||
|
magDBusService = new MagnifierDBus.ShellMagnifier();
|
||||||
|
|
||||||
|
let showAtLaunch = this._settingsInit();
|
||||||
|
this.setActive(showAtLaunch);
|
||||||
|
},
|
||||||
|
|
||||||
|
_initialize: function() {
|
||||||
|
if (this._initialized)
|
||||||
|
return;
|
||||||
|
this._initialized = true;
|
||||||
|
|
||||||
|
this._settingsInitLate();
|
||||||
|
|
||||||
// Create small clutter tree for the magnified mouse.
|
// Create small clutter tree for the magnified mouse.
|
||||||
let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
|
let cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
|
||||||
this._mouseSprite = new Clutter.Texture();
|
this._mouseSprite = new Clutter.Texture();
|
||||||
xfixesCursor.update_texture_image(this._mouseSprite);
|
Shell.util_cursor_tracker_to_clutter(cursorTracker, this._mouseSprite);
|
||||||
this._cursorRoot = new Clutter.Actor();
|
this._cursorRoot = new Clutter.Actor();
|
||||||
this._cursorRoot.add_actor(this._mouseSprite);
|
this._cursorRoot.add_actor(this._mouseSprite);
|
||||||
|
|
||||||
@@ -67,15 +86,11 @@ const Magnifier = new Lang.Class({
|
|||||||
|
|
||||||
let aZoomRegion = new ZoomRegion(this, this._cursorRoot);
|
let aZoomRegion = new ZoomRegion(this, this._cursorRoot);
|
||||||
this._zoomRegions.push(aZoomRegion);
|
this._zoomRegions.push(aZoomRegion);
|
||||||
let showAtLaunch = this._settingsInit(aZoomRegion);
|
this._settingsInitRegion(aZoomRegion);
|
||||||
aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
|
aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
|
||||||
|
|
||||||
xfixesCursor.connect('cursor-change', Lang.bind(this, this._updateMouseSprite));
|
cursorTracker.connect('cursor-changed', Lang.bind(this, this._updateMouseSprite));
|
||||||
this._xfixesCursor = xfixesCursor;
|
this._cursorTracker = cursorTracker;
|
||||||
|
|
||||||
// Export to dbus.
|
|
||||||
magDBusService = new MagnifierDBus.ShellMagnifier();
|
|
||||||
this.setActive(showAtLaunch);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,7 +98,7 @@ const Magnifier = new Lang.Class({
|
|||||||
* Show the system mouse pointer.
|
* Show the system mouse pointer.
|
||||||
*/
|
*/
|
||||||
showSystemCursor: function() {
|
showSystemCursor: function() {
|
||||||
this._xfixesCursor.show();
|
this._cursorTracker.set_pointer_visible(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,7 +106,7 @@ const Magnifier = new Lang.Class({
|
|||||||
* Hide the system mouse pointer.
|
* Hide the system mouse pointer.
|
||||||
*/
|
*/
|
||||||
hideSystemCursor: function() {
|
hideSystemCursor: function() {
|
||||||
this._xfixesCursor.hide();
|
this._cursorTracker.set_pointer_visible(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,6 +115,12 @@ const Magnifier = new Lang.Class({
|
|||||||
* @activate: Boolean to activate or de-activate the magnifier.
|
* @activate: Boolean to activate or de-activate the magnifier.
|
||||||
*/
|
*/
|
||||||
setActive: function(activate) {
|
setActive: function(activate) {
|
||||||
|
if (activate == this.isActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (activate)
|
||||||
|
this._initialize();
|
||||||
|
|
||||||
this._zoomRegions.forEach (function(zoomRegion, index, array) {
|
this._zoomRegions.forEach (function(zoomRegion, index, array) {
|
||||||
zoomRegion.setActive(activate);
|
zoomRegion.setActive(activate);
|
||||||
});
|
});
|
||||||
@@ -112,7 +133,7 @@ const Magnifier = new Lang.Class({
|
|||||||
// Make sure system mouse pointer is shown when all zoom regions are
|
// Make sure system mouse pointer is shown when all zoom regions are
|
||||||
// invisible.
|
// invisible.
|
||||||
if (!activate)
|
if (!activate)
|
||||||
this._xfixesCursor.show();
|
this._cursorTracker.set_pointer_visible(true);
|
||||||
|
|
||||||
// Notify interested parties of this change
|
// Notify interested parties of this change
|
||||||
this.emit('active-changed', activate);
|
this.emit('active-changed', activate);
|
||||||
@@ -422,17 +443,12 @@ const Magnifier = new Lang.Class({
|
|||||||
//// Private methods ////
|
//// Private methods ////
|
||||||
|
|
||||||
_updateMouseSprite: function() {
|
_updateMouseSprite: function() {
|
||||||
this._xfixesCursor.update_texture_image(this._mouseSprite);
|
Shell.util_cursor_tracker_to_clutter(this._cursorTracker, this._mouseSprite);
|
||||||
let xHot = this._xfixesCursor.get_hot_x();
|
let [xHot, yHot] = this._cursorTracker.get_hot();
|
||||||
let yHot = this._xfixesCursor.get_hot_y();
|
|
||||||
this._mouseSprite.set_anchor_point(xHot, yHot);
|
this._mouseSprite.set_anchor_point(xHot, yHot);
|
||||||
},
|
},
|
||||||
|
|
||||||
_settingsInit: function(zoomRegion) {
|
_settingsInitRegion: function(zoomRegion) {
|
||||||
this._appSettings = new Gio.Settings({ schema: APPLICATIONS_SCHEMA });
|
|
||||||
this._settings = new Gio.Settings({ schema: MAGNIFIER_SCHEMA });
|
|
||||||
|
|
||||||
if (zoomRegion) {
|
|
||||||
// Mag factor is accurate to two decimal places.
|
// Mag factor is accurate to two decimal places.
|
||||||
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
|
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
|
||||||
if (aPref != 0.0)
|
if (aPref != 0.0)
|
||||||
@@ -449,6 +465,14 @@ const Magnifier = new Lang.Class({
|
|||||||
if (aPref)
|
if (aPref)
|
||||||
zoomRegion.setMouseTrackingMode(aPref);
|
zoomRegion.setMouseTrackingMode(aPref);
|
||||||
|
|
||||||
|
aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
|
||||||
|
if (aPref)
|
||||||
|
zoomRegion.setFocusTrackingMode(aPref);
|
||||||
|
|
||||||
|
aPref = this._settings.get_enum(CARET_TRACKING_KEY);
|
||||||
|
if (aPref)
|
||||||
|
zoomRegion.setCaretTrackingMode(aPref);
|
||||||
|
|
||||||
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
|
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
|
||||||
if (aPref)
|
if (aPref)
|
||||||
zoomRegion.setInvertLightness(aPref);
|
zoomRegion.setInvertLightness(aPref);
|
||||||
@@ -467,17 +491,25 @@ const Magnifier = new Lang.Class({
|
|||||||
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
||||||
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||||
zoomRegion.setContrast(bc);
|
zoomRegion.setContrast(bc);
|
||||||
}
|
},
|
||||||
|
|
||||||
|
_settingsInit: function() {
|
||||||
|
this._appSettings = new Gio.Settings({ schema: APPLICATIONS_SCHEMA });
|
||||||
|
this._settings = new Gio.Settings({ schema: MAGNIFIER_SCHEMA });
|
||||||
|
|
||||||
|
this._appSettings.connect('changed::' + SHOW_KEY, Lang.bind(this, function() {
|
||||||
|
let active = this._appSettings.get_boolean(SHOW_KEY);
|
||||||
|
this.setActive(active);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return this._appSettings.get_boolean(SHOW_KEY);
|
||||||
|
},
|
||||||
|
|
||||||
|
_settingsInitLate: function() {
|
||||||
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
|
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
|
||||||
this.addCrosshairs();
|
this.addCrosshairs();
|
||||||
this.setCrosshairsVisible(showCrosshairs);
|
this.setCrosshairsVisible(showCrosshairs);
|
||||||
|
|
||||||
this._appSettings.connect('changed::' + SHOW_KEY,
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
this.setActive(this._appSettings.get_boolean(SHOW_KEY));
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._settings.connect('changed::' + SCREEN_POSITION_KEY,
|
this._settings.connect('changed::' + SCREEN_POSITION_KEY,
|
||||||
Lang.bind(this, this._updateScreenPosition));
|
Lang.bind(this, this._updateScreenPosition));
|
||||||
this._settings.connect('changed::' + MAG_FACTOR_KEY,
|
this._settings.connect('changed::' + MAG_FACTOR_KEY,
|
||||||
@@ -488,6 +520,10 @@ const Magnifier = new Lang.Class({
|
|||||||
Lang.bind(this, this._updateClampMode));
|
Lang.bind(this, this._updateClampMode));
|
||||||
this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
|
this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
|
||||||
Lang.bind(this, this._updateMouseTrackingMode));
|
Lang.bind(this, this._updateMouseTrackingMode));
|
||||||
|
this._settings.connect('changed::' + FOCUS_TRACKING_KEY,
|
||||||
|
Lang.bind(this, this._updateFocusTrackingMode));
|
||||||
|
this._settings.connect('changed::' + CARET_TRACKING_KEY,
|
||||||
|
Lang.bind(this, this._updateCaretTrackingMode));
|
||||||
|
|
||||||
this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY,
|
this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY,
|
||||||
Lang.bind(this, this._updateInvertLightness));
|
Lang.bind(this, this._updateInvertLightness));
|
||||||
@@ -537,8 +573,6 @@ const Magnifier = new Lang.Class({
|
|||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY));
|
this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return this._appSettings.get_boolean(SHOW_KEY);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateScreenPosition: function() {
|
_updateScreenPosition: function() {
|
||||||
@@ -585,6 +619,24 @@ const Magnifier = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateFocusTrackingMode: function() {
|
||||||
|
// Applies only to the first zoom region.
|
||||||
|
if (this._zoomRegions.length) {
|
||||||
|
this._zoomRegions[0].setFocusTrackingMode(
|
||||||
|
this._settings.get_enum(FOCUS_TRACKING_KEY)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateCaretTrackingMode: function() {
|
||||||
|
// Applies only to the first zoom region.
|
||||||
|
if (this._zoomRegions.length) {
|
||||||
|
this._zoomRegions[0].setCaretTrackingMode(
|
||||||
|
this._settings.get_enum(CARET_TRACKING_KEY)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_updateInvertLightness: function() {
|
_updateInvertLightness: function() {
|
||||||
// Applies only to the first zoom region.
|
// Applies only to the first zoom region.
|
||||||
if (this._zoomRegions.length) {
|
if (this._zoomRegions.length) {
|
||||||
@@ -623,7 +675,7 @@ const Magnifier = new Lang.Class({
|
|||||||
contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||||
this._zoomRegions[0].setContrast(contrast);
|
this._zoomRegions[0].setContrast(contrast);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(Magnifier.prototype);
|
Signals.addSignalMethods(Magnifier.prototype);
|
||||||
|
|
||||||
@@ -632,8 +684,11 @@ const ZoomRegion = new Lang.Class({
|
|||||||
|
|
||||||
_init: function(magnifier, mouseSourceActor) {
|
_init: function(magnifier, mouseSourceActor) {
|
||||||
this._magnifier = magnifier;
|
this._magnifier = magnifier;
|
||||||
|
this._focusCaretTracker = new FocusCaretTracker.FocusCaretTracker();
|
||||||
|
|
||||||
this._mouseTrackingMode = GDesktopEnums.MagnifierMouseTrackingMode.NONE;
|
this._mouseTrackingMode = GDesktopEnums.MagnifierMouseTrackingMode.NONE;
|
||||||
|
this._focusTrackingMode = GDesktopEnums.MagnifierFocusTrackingMode.NONE;
|
||||||
|
this._caretTrackingMode = GDesktopEnums.MagnifierCaretTrackingMode.NONE;
|
||||||
this._clampScrollingAtEdges = false;
|
this._clampScrollingAtEdges = false;
|
||||||
this._lensMode = false;
|
this._lensMode = false;
|
||||||
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
|
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
|
||||||
@@ -659,9 +714,35 @@ const ZoomRegion = new Lang.Class({
|
|||||||
this._xMagFactor = 1;
|
this._xMagFactor = 1;
|
||||||
this._yMagFactor = 1;
|
this._yMagFactor = 1;
|
||||||
this._followingCursor = false;
|
this._followingCursor = false;
|
||||||
|
this._xFocus = 0;
|
||||||
|
this._yFocus = 0;
|
||||||
|
this._xCaret = 0;
|
||||||
|
this._yCaret = 0;
|
||||||
|
|
||||||
Main.layoutManager.connect('monitors-changed',
|
Main.layoutManager.connect('monitors-changed',
|
||||||
Lang.bind(this, this._monitorsChanged));
|
Lang.bind(this, this._monitorsChanged));
|
||||||
|
this._focusCaretTracker.connect('caret-moved',
|
||||||
|
Lang.bind(this, this._updateCaret));
|
||||||
|
this._focusCaretTracker.connect('focus-changed',
|
||||||
|
Lang.bind(this, this._updateFocus));
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateFocus: function(caller, event) {
|
||||||
|
let component = event.source.get_component_iface();
|
||||||
|
if (!component || event.detail1 != 1)
|
||||||
|
return;
|
||||||
|
let extents = component.get_extents(Atspi.CoordType.SCREEN);
|
||||||
|
[this._xFocus, this._yFocus] = [extents.x, extents.y]
|
||||||
|
this._centerFromFocusPosition();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateCaret: function(caller, event) {
|
||||||
|
let text = event.source.get_text_iface();
|
||||||
|
if (!text)
|
||||||
|
return;
|
||||||
|
let extents = text.get_character_extents(text.get_caret_offset(), 0);
|
||||||
|
[this._xCaret, this._yCaret] = [extents.x, extents.y];
|
||||||
|
this._centerFromCaretPosition();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -669,14 +750,17 @@ const ZoomRegion = new Lang.Class({
|
|||||||
* @activate: Boolean to show/hide the ZoomRegion.
|
* @activate: Boolean to show/hide the ZoomRegion.
|
||||||
*/
|
*/
|
||||||
setActive: function(activate) {
|
setActive: function(activate) {
|
||||||
if (activate && !this.isActive()) {
|
if (activate == this.isActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (activate) {
|
||||||
this._createActors();
|
this._createActors();
|
||||||
if (this._isMouseOverRegion())
|
if (this._isMouseOverRegion())
|
||||||
this._magnifier.hideSystemCursor();
|
this._magnifier.hideSystemCursor();
|
||||||
this._updateMagViewGeometry();
|
this._updateMagViewGeometry();
|
||||||
this._updateCloneGeometry();
|
this._updateCloneGeometry();
|
||||||
this._updateMousePosition();
|
this._updateMousePosition();
|
||||||
} else if (!activate && this.isActive()) {
|
} else {
|
||||||
this._destroyActors();
|
this._destroyActors();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -732,6 +816,30 @@ const ZoomRegion = new Lang.Class({
|
|||||||
return this._mouseTrackingMode;
|
return this._mouseTrackingMode;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setFocusTrackingMode
|
||||||
|
* @mode: One of the enum FocusTrackingMode values.
|
||||||
|
*/
|
||||||
|
setFocusTrackingMode: function(mode) {
|
||||||
|
this._focusTrackingMode = mode;
|
||||||
|
if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.NONE)
|
||||||
|
this._focusCaretTracker.deregisterFocusListener();
|
||||||
|
else
|
||||||
|
this._focusCaretTracker.registerFocusListener();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setCaretTrackingMode
|
||||||
|
* @mode: One of the enum CaretTrackingMode values.
|
||||||
|
*/
|
||||||
|
setCaretTrackingMode: function(mode) {
|
||||||
|
this._caretTrackingMode = mode;
|
||||||
|
if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.NONE)
|
||||||
|
this._focusCaretTracker.deregisterCaretListener();
|
||||||
|
else
|
||||||
|
this._focusCaretTracker.registerCaretListener();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setViewPort
|
* setViewPort
|
||||||
* Sets the position and size of the ZoomRegion on screen.
|
* Sets the position and size of the ZoomRegion on screen.
|
||||||
@@ -1023,20 +1131,6 @@ const ZoomRegion = new Lang.Class({
|
|||||||
this._magShaderEffects.setBrightness(this._brightness);
|
this._magShaderEffects.setBrightness(this._brightness);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* getBrightness:
|
|
||||||
* Retrive the current brightness of the Zoom Region.
|
|
||||||
* @return Object containing the brightness change for the red, green,
|
|
||||||
* and blue channels.
|
|
||||||
*/
|
|
||||||
getBrightness: function() {
|
|
||||||
let brightness = {};
|
|
||||||
brightness.r = this._brightness.r;
|
|
||||||
brightness.g = this._brightness.g;
|
|
||||||
brightness.b = this._brightness.b;
|
|
||||||
return brightness;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setContrast:
|
* setContrast:
|
||||||
* Alter the contrast of the magnified view.
|
* Alter the contrast of the magnified view.
|
||||||
@@ -1090,7 +1184,7 @@ const ZoomRegion = new Lang.Class({
|
|||||||
|
|
||||||
// Clone the group that contains all of UI on the screen. This is the
|
// Clone the group that contains all of UI on the screen. This is the
|
||||||
// chrome, the windows, etc.
|
// chrome, the windows, etc.
|
||||||
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
|
this._uiGroupClone = new Clutter.Clone({ source: Main.layoutManager.uiGroup });
|
||||||
mainGroup.add_actor(this._uiGroupClone);
|
mainGroup.add_actor(this._uiGroupClone);
|
||||||
|
|
||||||
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
|
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
|
||||||
@@ -1243,19 +1337,47 @@ const ZoomRegion = new Lang.Class({
|
|||||||
let yMouse = this._magnifier.yMouse;
|
let yMouse = this._magnifier.yMouse;
|
||||||
|
|
||||||
if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) {
|
if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) {
|
||||||
return this._centerFromMouseProportional(xMouse, yMouse);
|
return this._centerFromPointProportional(xMouse, yMouse);
|
||||||
}
|
}
|
||||||
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) {
|
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) {
|
||||||
return this._centerFromMousePush(xMouse, yMouse);
|
return this._centerFromPointPush(xMouse, yMouse);
|
||||||
}
|
}
|
||||||
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) {
|
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) {
|
||||||
return this._centerFromMouseCentered(xMouse, yMouse);
|
return this._centerFromPointCentered(xMouse, yMouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null; // Should never be hit
|
return null; // Should never be hit
|
||||||
},
|
},
|
||||||
|
|
||||||
_centerFromMousePush: function(xMouse, yMouse) {
|
_centerFromCaretPosition: function() {
|
||||||
|
let xCaret = this._xCaret;
|
||||||
|
let yCaret = this._yCaret;
|
||||||
|
|
||||||
|
if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PROPORTIONAL)
|
||||||
|
[xCaret, yCaret] = this._centerFromPointProportional(xCaret, yCaret);
|
||||||
|
else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PUSH)
|
||||||
|
[xCaret, yCaret] = this._centerFromPointPush(xCaret, yCaret);
|
||||||
|
else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.CENTERED)
|
||||||
|
[xCaret, yCaret] = this._centerFromPointCentered(xCaret, yCaret);
|
||||||
|
|
||||||
|
this.scrollContentsTo(xCaret, yCaret);
|
||||||
|
},
|
||||||
|
|
||||||
|
_centerFromFocusPosition: function() {
|
||||||
|
let xFocus = this._xFocus;
|
||||||
|
let yFocus = this._yFocus;
|
||||||
|
|
||||||
|
if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PROPORTIONAL)
|
||||||
|
[xFocus, yFocus] = this._centerFromPointProportional(xFocus, yFocus);
|
||||||
|
else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PUSH)
|
||||||
|
[xFocus, yFocus] = this._centerFromPointPush(xFocus, yFocus);
|
||||||
|
else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.CENTERED)
|
||||||
|
[xFocus, yFocus] = this._centerFromPointCentered(xFocus, yFocus);
|
||||||
|
|
||||||
|
this.scrollContentsTo(xFocus, yFocus);
|
||||||
|
},
|
||||||
|
|
||||||
|
_centerFromPointPush: function(xPoint, yPoint) {
|
||||||
let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
|
let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
|
||||||
let [cursorWidth, cursorHeight] = this._mouseSourceActor.get_size();
|
let [cursorWidth, cursorHeight] = this._mouseSourceActor.get_size();
|
||||||
let xPos = xRoi + widthRoi / 2;
|
let xPos = xRoi + widthRoi / 2;
|
||||||
@@ -1263,20 +1385,20 @@ const ZoomRegion = new Lang.Class({
|
|||||||
let xRoiRight = xRoi + widthRoi - cursorWidth;
|
let xRoiRight = xRoi + widthRoi - cursorWidth;
|
||||||
let yRoiBottom = yRoi + heightRoi - cursorHeight;
|
let yRoiBottom = yRoi + heightRoi - cursorHeight;
|
||||||
|
|
||||||
if (xMouse < xRoi)
|
if (xPoint < xRoi)
|
||||||
xPos -= (xRoi - xMouse);
|
xPos -= (xRoi - xPoint);
|
||||||
else if (xMouse > xRoiRight)
|
else if (xPoint > xRoiRight)
|
||||||
xPos += (xMouse - xRoiRight);
|
xPos += (xPoint - xRoiRight);
|
||||||
|
|
||||||
if (yMouse < yRoi)
|
if (yPoint < yRoi)
|
||||||
yPos -= (yRoi - yMouse);
|
yPos -= (yRoi - yPoint);
|
||||||
else if (yMouse > yRoiBottom)
|
else if (yPoint > yRoiBottom)
|
||||||
yPos += (yMouse - yRoiBottom);
|
yPos += (yPoint - yRoiBottom);
|
||||||
|
|
||||||
return [xPos, yPos];
|
return [xPos, yPos];
|
||||||
},
|
},
|
||||||
|
|
||||||
_centerFromMouseProportional: function(xMouse, yMouse) {
|
_centerFromPointProportional: function(xPoint, yPoint) {
|
||||||
let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
|
let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
|
||||||
let halfScreenWidth = global.screen_width / 2;
|
let halfScreenWidth = global.screen_width / 2;
|
||||||
let halfScreenHeight = global.screen_height / 2;
|
let halfScreenHeight = global.screen_height / 2;
|
||||||
@@ -1285,16 +1407,16 @@ const ZoomRegion = new Lang.Class({
|
|||||||
let unscaledPadding = Math.min(this._viewPortWidth, this._viewPortHeight) / 5;
|
let unscaledPadding = Math.min(this._viewPortWidth, this._viewPortHeight) / 5;
|
||||||
let xPadding = unscaledPadding / this._xMagFactor;
|
let xPadding = unscaledPadding / this._xMagFactor;
|
||||||
let yPadding = unscaledPadding / this._yMagFactor;
|
let yPadding = unscaledPadding / this._yMagFactor;
|
||||||
let xProportion = (xMouse - halfScreenWidth) / halfScreenWidth; // -1 ... 1
|
let xProportion = (xPoint - halfScreenWidth) / halfScreenWidth; // -1 ... 1
|
||||||
let yProportion = (yMouse - halfScreenHeight) / halfScreenHeight; // -1 ... 1
|
let yProportion = (yPoint - halfScreenHeight) / halfScreenHeight; // -1 ... 1
|
||||||
let xPos = xMouse - xProportion * (widthRoi / 2 - xPadding);
|
let xPos = xPoint - xProportion * (widthRoi / 2 - xPadding);
|
||||||
let yPos = yMouse - yProportion * (heightRoi /2 - yPadding);
|
let yPos = yPoint - yProportion * (heightRoi /2 - yPadding);
|
||||||
|
|
||||||
return [xPos, yPos];
|
return [xPos, yPos];
|
||||||
},
|
},
|
||||||
|
|
||||||
_centerFromMouseCentered: function(xMouse, yMouse) {
|
_centerFromPointCentered: function(xPoint, yPoint) {
|
||||||
return [xMouse, yMouse];
|
return [xPoint, yPoint];
|
||||||
},
|
},
|
||||||
|
|
||||||
_screenToViewPort: function(screenX, screenY) {
|
_screenToViewPort: function(screenX, screenY) {
|
||||||
@@ -1511,15 +1633,6 @@ const Crosshairs = new Lang.Class({
|
|||||||
this._vertBottomHair.set_opacity(opacity);
|
this._vertBottomHair.set_opacity(opacity);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* getOpacity:
|
|
||||||
* Retriev how opaque the crosshairs are.
|
|
||||||
* @return: A value between 0 (transparent) and 255 (opaque).
|
|
||||||
*/
|
|
||||||
getOpacity: function() {
|
|
||||||
return this._horizLeftHair.get_opacity();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setLength:
|
* setLength:
|
||||||
* Set the length of the vertical and horizontal lines in the crosshairs.
|
* Set the length of the vertical and horizontal lines in the crosshairs.
|
||||||
@@ -1563,15 +1676,6 @@ const Crosshairs = new Lang.Class({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* getClip:
|
|
||||||
* Get the dimensions of the clip rectangle.
|
|
||||||
* @return: An array of the form [width, height].
|
|
||||||
*/
|
|
||||||
getClip: function() {
|
|
||||||
return this._clipSize;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* show:
|
* show:
|
||||||
* Show the crosshairs.
|
* Show the crosshairs.
|
||||||
@@ -1667,23 +1771,10 @@ const MagShaderEffects = new Lang.Class({
|
|||||||
this._inverse.set_enabled(invertFlag);
|
this._inverse.set_enabled(invertFlag);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* getInvertLightness:
|
|
||||||
* Report whether the inversion effect is enabled.
|
|
||||||
* @return: Boolean.
|
|
||||||
*/
|
|
||||||
getInvertLightness: function() {
|
|
||||||
return this._inverse.get_enabled();
|
|
||||||
},
|
|
||||||
|
|
||||||
setColorSaturation: function(factor) {
|
setColorSaturation: function(factor) {
|
||||||
this._colorDesaturation.set_factor(1.0 - factor);
|
this._colorDesaturation.set_factor(1.0 - factor);
|
||||||
},
|
},
|
||||||
|
|
||||||
getColorSaturation: function() {
|
|
||||||
return 1.0 - this._colorDesaturation.get_factor();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setBrightness:
|
* setBrightness:
|
||||||
* Set the brightness of the magnified view.
|
* Set the brightness of the magnified view.
|
||||||
@@ -1708,24 +1799,6 @@ const MagShaderEffects = new Lang.Class({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* getBrightness:
|
|
||||||
* Retrieve current brightness of the magnified view.
|
|
||||||
* @return: Object containing the brightness for the red, green,
|
|
||||||
* and blue channels. Values of 0.0 represent "standard"
|
|
||||||
* brightness (no change), whereas values less or greater than
|
|
||||||
* 0.0 indicate decreased or incresaed brightness, respectively.
|
|
||||||
*/
|
|
||||||
getBrightness: function() {
|
|
||||||
let result = {};
|
|
||||||
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
|
|
||||||
result.r = bRed;
|
|
||||||
result.g = bGreen;
|
|
||||||
result.b = bBlue;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the contrast of the magnified view.
|
* Set the contrast of the magnified view.
|
||||||
* @contrast: Object containing the contrast for the red, green,
|
* @contrast: Object containing the contrast for the red, green,
|
||||||
@@ -1750,21 +1823,4 @@ const MagShaderEffects = new Lang.Class({
|
|||||||
bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
|
bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve current contrast of the magnified view.
|
|
||||||
* @return: Object containing the contrast for the red, green,
|
|
||||||
* and blue channels. Values of 0.0 represent "standard"
|
|
||||||
* contrast (no change), whereas values less or greater than
|
|
||||||
* 0.0 indicate decreased or incresaed contrast, respectively.
|
|
||||||
*/
|
|
||||||
getContrast: function() {
|
|
||||||
let resutl = {};
|
|
||||||
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
|
|
||||||
result.r = cRed;
|
|
||||||
result.g = cGreen;
|
|
||||||
result.b = cBlue;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,92 +4,94 @@ const Gio = imports.gi.Gio;
|
|||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
const MAG_SERVICE_NAME = 'org.gnome.Magnifier';
|
|
||||||
const MAG_SERVICE_PATH = '/org/gnome/Magnifier';
|
const MAG_SERVICE_PATH = '/org/gnome/Magnifier';
|
||||||
const ZOOM_SERVICE_NAME = 'org.gnome.Magnifier.ZoomRegion';
|
|
||||||
const ZOOM_SERVICE_PATH = '/org/gnome/Magnifier/ZoomRegion';
|
const ZOOM_SERVICE_PATH = '/org/gnome/Magnifier/ZoomRegion';
|
||||||
|
|
||||||
// Subset of gnome-mag's Magnifier dbus interface -- to be expanded. See:
|
// Subset of gnome-mag's Magnifier dbus interface -- to be expanded. See:
|
||||||
// http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml
|
// http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml
|
||||||
const MagnifierIface = <interface name={MAG_SERVICE_NAME}>
|
const MagnifierIface = '<node> \
|
||||||
<method name="setActive">
|
<interface name="org.gnome.Magnifier"> \
|
||||||
<arg type="b" direction="in" />
|
<method name="setActive"> \
|
||||||
</method>
|
<arg type="b" direction="in" /> \
|
||||||
<method name="isActive">
|
</method> \
|
||||||
<arg type="b" direction="out" />
|
<method name="isActive"> \
|
||||||
</method>
|
<arg type="b" direction="out" /> \
|
||||||
<method name="showCursor" />
|
</method> \
|
||||||
<method name="hideCursor" />
|
<method name="showCursor" /> \
|
||||||
<method name="createZoomRegion">
|
<method name="hideCursor" /> \
|
||||||
<arg type="d" direction="in" />
|
<method name="createZoomRegion"> \
|
||||||
<arg type="d" direction="in" />
|
<arg type="d" direction="in" /> \
|
||||||
<arg type="ai" direction="in" />
|
<arg type="d" direction="in" /> \
|
||||||
<arg type="ai" direction="in" />
|
<arg type="ai" direction="in" /> \
|
||||||
<arg type="o" direction="out" />
|
<arg type="ai" direction="in" /> \
|
||||||
</method>
|
<arg type="o" direction="out" /> \
|
||||||
<method name="addZoomRegion">
|
</method> \
|
||||||
<arg type="o" direction="in" />
|
<method name="addZoomRegion"> \
|
||||||
<arg type="b" direction="out" />
|
<arg type="o" direction="in" /> \
|
||||||
</method>
|
<arg type="b" direction="out" /> \
|
||||||
<method name="getZoomRegions">
|
</method> \
|
||||||
<arg type="ao" direction="out" />
|
<method name="getZoomRegions"> \
|
||||||
</method>
|
<arg type="ao" direction="out" /> \
|
||||||
<method name="clearAllZoomRegions" />
|
</method> \
|
||||||
<method name="fullScreenCapable">
|
<method name="clearAllZoomRegions" /> \
|
||||||
<arg type="b" direction="out" />
|
<method name="fullScreenCapable"> \
|
||||||
</method>
|
<arg type="b" direction="out" /> \
|
||||||
<method name="setCrosswireSize">
|
</method> \
|
||||||
<arg type="i" direction="in" />
|
<method name="setCrosswireSize"> \
|
||||||
</method>
|
<arg type="i" direction="in" /> \
|
||||||
<method name="getCrosswireSize">
|
</method> \
|
||||||
<arg type="i" direction="out" />
|
<method name="getCrosswireSize"> \
|
||||||
</method>
|
<arg type="i" direction="out" /> \
|
||||||
<method name="setCrosswireLength">
|
</method> \
|
||||||
<arg type="i" direction="in" />
|
<method name="setCrosswireLength"> \
|
||||||
</method>
|
<arg type="i" direction="in" /> \
|
||||||
<method name="getCrosswireLength">
|
</method> \
|
||||||
<arg type="i" direction="out" />
|
<method name="getCrosswireLength"> \
|
||||||
</method>
|
<arg type="i" direction="out" /> \
|
||||||
<method name="setCrosswireClip">
|
</method> \
|
||||||
<arg type="b" direction="in" />
|
<method name="setCrosswireClip"> \
|
||||||
</method>
|
<arg type="b" direction="in" /> \
|
||||||
<method name="getCrosswireClip">
|
</method> \
|
||||||
<arg type="b" direction="out" />
|
<method name="getCrosswireClip"> \
|
||||||
</method>
|
<arg type="b" direction="out" /> \
|
||||||
<method name="setCrosswireColor">
|
</method> \
|
||||||
<arg type="u" direction="in" />
|
<method name="setCrosswireColor"> \
|
||||||
</method>
|
<arg type="u" direction="in" /> \
|
||||||
<method name="getCrosswireColor">
|
</method> \
|
||||||
<arg type="u" direction="out" />
|
<method name="getCrosswireColor"> \
|
||||||
</method>
|
<arg type="u" direction="out" /> \
|
||||||
</interface>;
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
// Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded. See:
|
// Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded. See:
|
||||||
// http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml
|
// http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml
|
||||||
const ZoomRegionIface = <interface name={ZOOM_SERVICE_NAME}>
|
const ZoomRegionIface = '<node> \
|
||||||
<method name="setMagFactor">
|
<interface name="org.gnome.Magnifier.ZoomRegion"> \
|
||||||
<arg type="d" direction="in" />
|
<method name="setMagFactor"> \
|
||||||
<arg type="d" direction="in" />
|
<arg type="d" direction="in" /> \
|
||||||
</method>
|
<arg type="d" direction="in" /> \
|
||||||
<method name="getMagFactor">
|
</method> \
|
||||||
<arg type="d" direction="out" />
|
<method name="getMagFactor"> \
|
||||||
<arg type="d" direction="out" />
|
<arg type="d" direction="out" /> \
|
||||||
</method>
|
<arg type="d" direction="out" /> \
|
||||||
<method name="setRoi">
|
</method> \
|
||||||
<arg type="ai" direction="in" />
|
<method name="setRoi"> \
|
||||||
</method>
|
<arg type="ai" direction="in" /> \
|
||||||
<method name="getRoi">
|
</method> \
|
||||||
<arg type="ai" direction="out" />
|
<method name="getRoi"> \
|
||||||
</method>
|
<arg type="ai" direction="out" /> \
|
||||||
<method name="shiftContentsTo">
|
</method> \
|
||||||
<arg type="i" direction="in" />
|
<method name="shiftContentsTo"> \
|
||||||
<arg type="i" direction="in" />
|
<arg type="i" direction="in" /> \
|
||||||
<arg type="b" direction="out" />
|
<arg type="i" direction="in" /> \
|
||||||
</method>
|
<arg type="b" direction="out" /> \
|
||||||
<method name="moveResize">
|
</method> \
|
||||||
<arg type="ai" direction="in" />
|
<method name="moveResize"> \
|
||||||
</method>
|
<arg type="ai" direction="in" /> \
|
||||||
</interface>;
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
// For making unique ZoomRegion DBus proxy object paths of the form:
|
// For making unique ZoomRegion DBus proxy object paths of the form:
|
||||||
// '/org/gnome/Magnifier/ZoomRegion/zoomer0',
|
// '/org/gnome/Magnifier/ZoomRegion/zoomer0',
|
||||||
|
|||||||
422
js/ui/main.js
422
js/ui/main.js
@@ -18,40 +18,31 @@ const ExtensionSystem = imports.ui.extensionSystem;
|
|||||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||||
const Keyboard = imports.ui.keyboard;
|
const Keyboard = imports.ui.keyboard;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const OsdWindow = imports.ui.osdWindow;
|
||||||
const Overview = imports.ui.overview;
|
const Overview = imports.ui.overview;
|
||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const RunDialog = imports.ui.runDialog;
|
const RunDialog = imports.ui.runDialog;
|
||||||
const Layout = imports.ui.layout;
|
const Layout = imports.ui.layout;
|
||||||
|
const LoginManager = imports.misc.loginManager;
|
||||||
const LookingGlass = imports.ui.lookingGlass;
|
const LookingGlass = imports.ui.lookingGlass;
|
||||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||||
|
const Screencast = imports.ui.screencast;
|
||||||
const ScreenShield = imports.ui.screenShield;
|
const ScreenShield = imports.ui.screenShield;
|
||||||
const Scripting = imports.ui.scripting;
|
const Scripting = imports.ui.scripting;
|
||||||
const SessionMode = imports.ui.sessionMode;
|
const SessionMode = imports.ui.sessionMode;
|
||||||
const ShellDBus = imports.ui.shellDBus;
|
const ShellDBus = imports.ui.shellDBus;
|
||||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
const UnlockDialog = imports.ui.unlockDialog;
|
|
||||||
const WindowManager = imports.ui.windowManager;
|
const WindowManager = imports.ui.windowManager;
|
||||||
const Magnifier = imports.ui.magnifier;
|
const Magnifier = imports.ui.magnifier;
|
||||||
const XdndHandler = imports.ui.xdndHandler;
|
const XdndHandler = imports.ui.xdndHandler;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
|
|
||||||
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
||||||
|
|
||||||
const KeybindingMode = {
|
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||||
NONE: 0, // block all keybindings
|
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
|
||||||
NORMAL: 1 << 0, // window mode
|
|
||||||
OVERVIEW: 1 << 1,
|
|
||||||
LOCK_SCREEN: 1 << 2,
|
|
||||||
UNLOCK_SCREEN: 1 << 3,
|
|
||||||
LOGIN_SCREEN: 1 << 4,
|
|
||||||
MESSAGE_TRAY: 1 << 5,
|
|
||||||
SYSTEM_MODAL: 1 << 6,
|
|
||||||
LOOKING_GLASS: 1 << 7,
|
|
||||||
ALL: ~0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let componentManager = null;
|
let componentManager = null;
|
||||||
let panel = null;
|
let panel = null;
|
||||||
@@ -64,14 +55,15 @@ let screenShield = null;
|
|||||||
let notificationDaemon = null;
|
let notificationDaemon = null;
|
||||||
let windowAttentionHandler = null;
|
let windowAttentionHandler = null;
|
||||||
let ctrlAltTabManager = null;
|
let ctrlAltTabManager = null;
|
||||||
|
let osdWindow = null;
|
||||||
let sessionMode = null;
|
let sessionMode = null;
|
||||||
let shellDBusService = null;
|
let shellDBusService = null;
|
||||||
let shellMountOpDBusService = null;
|
let shellMountOpDBusService = null;
|
||||||
let screenSaverDBus = null;
|
let screenSaverDBus = null;
|
||||||
|
let screencastService = null;
|
||||||
let modalCount = 0;
|
let modalCount = 0;
|
||||||
let keybindingMode = KeybindingMode.NORMAL;
|
let keybindingMode = Shell.KeyBindingMode.NONE;
|
||||||
let modalActorFocusStack = [];
|
let modalActorFocusStack = [];
|
||||||
let uiGroup = null;
|
|
||||||
let magnifier = null;
|
let magnifier = null;
|
||||||
let xdndHandler = null;
|
let xdndHandler = null;
|
||||||
let keyboard = null;
|
let keyboard = null;
|
||||||
@@ -79,24 +71,30 @@ let layoutManager = null;
|
|||||||
let _startDate;
|
let _startDate;
|
||||||
let _defaultCssStylesheet = null;
|
let _defaultCssStylesheet = null;
|
||||||
let _cssStylesheet = null;
|
let _cssStylesheet = null;
|
||||||
let _overridesSettings = null;
|
let _a11ySettings = null;
|
||||||
|
let dynamicWorkspacesSchema = null;
|
||||||
let background = null;
|
|
||||||
|
|
||||||
function _sessionUpdated() {
|
function _sessionUpdated() {
|
||||||
|
_loadDefaultStylesheet();
|
||||||
|
|
||||||
wm.setCustomKeybindingHandler('panel-main-menu',
|
wm.setCustomKeybindingHandler('panel-main-menu',
|
||||||
KeybindingMode.NORMAL |
|
Shell.KeyBindingMode.NORMAL |
|
||||||
KeybindingMode.OVERVIEW,
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
|
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
|
||||||
wm.allowKeybinding('overlay-key', KeybindingMode.NORMAL |
|
wm.allowKeybinding('overlay-key', Shell.KeyBindingMode.NORMAL |
|
||||||
KeybindingMode.OVERVIEW);
|
Shell.KeyBindingMode.OVERVIEW);
|
||||||
|
|
||||||
wm.setCustomKeybindingHandler('panel-run-dialog',
|
wm.setCustomKeybindingHandler('panel-run-dialog',
|
||||||
KeybindingMode.NORMAL |
|
Shell.KeyBindingMode.NORMAL |
|
||||||
KeybindingMode.OVERVIEW,
|
Shell.KeyBindingMode.OVERVIEW,
|
||||||
sessionMode.hasRunDialog ? openRunDialog : null);
|
sessionMode.hasRunDialog ? openRunDialog : null);
|
||||||
if (sessionMode.isGreeter)
|
|
||||||
screenShield.showDialog();
|
if (!sessionMode.hasRunDialog) {
|
||||||
|
if (runDialog)
|
||||||
|
runDialog.close();
|
||||||
|
if (lookingGlass)
|
||||||
|
lookingGlass.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
@@ -104,15 +102,37 @@ function start() {
|
|||||||
global.logError = window.log;
|
global.logError = window.log;
|
||||||
global.log = window.log;
|
global.log = window.log;
|
||||||
|
|
||||||
|
if (!Meta.is_wayland_compositor)
|
||||||
|
Meta.is_wayland_compositor = function () { return false; };
|
||||||
|
|
||||||
// Chain up async errors reported from C
|
// Chain up async errors reported from C
|
||||||
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
|
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
|
||||||
|
|
||||||
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
||||||
|
|
||||||
sessionMode = new SessionMode.SessionMode();
|
sessionMode = new SessionMode.SessionMode();
|
||||||
|
sessionMode.connect('updated', _sessionUpdated);
|
||||||
|
_initializePrefs();
|
||||||
|
_initializeUI();
|
||||||
|
|
||||||
shellDBusService = new ShellDBus.GnomeShell();
|
shellDBusService = new ShellDBus.GnomeShell();
|
||||||
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
||||||
|
|
||||||
|
_sessionUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _initializePrefs() {
|
||||||
|
let keys = new Gio.Settings({ schema: sessionMode.overridesSchema }).list_keys();
|
||||||
|
for (let i = 0; i < keys.length; i++)
|
||||||
|
Meta.prefs_override_preference_schema(keys[i], sessionMode.overridesSchema);
|
||||||
|
|
||||||
|
if (keys.indexOf('dynamic-workspaces') > -1)
|
||||||
|
dynamicWorkspacesSchema = sessionMode.overridesSchema;
|
||||||
|
else
|
||||||
|
dynamicWorkspacesSchema = 'org.gnome.mutter';
|
||||||
|
}
|
||||||
|
|
||||||
|
function _initializeUI() {
|
||||||
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||||
// also initialize ShellAppSystem first. ShellAppSystem
|
// also initialize ShellAppSystem first. ShellAppSystem
|
||||||
// needs to load all the .desktop files, and ShellWindowTracker
|
// needs to load all the .desktop files, and ShellWindowTracker
|
||||||
@@ -121,52 +141,24 @@ function start() {
|
|||||||
// and recalculate application associations, so to avoid
|
// and recalculate application associations, so to avoid
|
||||||
// races for now we initialize it here. It's better to
|
// races for now we initialize it here. It's better to
|
||||||
// be predictable anyways.
|
// be predictable anyways.
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
Shell.WindowTracker.get_default();
|
||||||
Shell.AppUsage.get_default();
|
Shell.AppUsage.get_default();
|
||||||
|
|
||||||
tracker.connect('startup-sequence-changed', _queueCheckWorkspaces);
|
_loadDefaultStylesheet();
|
||||||
|
|
||||||
// The stage is always covered so Clutter doesn't need to clear it; however
|
|
||||||
// the color is used as the default contents for the Mutter root background
|
|
||||||
// actor so set it anyways.
|
|
||||||
global.stage.color = DEFAULT_BACKGROUND_COLOR;
|
|
||||||
global.stage.no_clear_hint = true;
|
|
||||||
|
|
||||||
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
|
||||||
loadTheme();
|
|
||||||
|
|
||||||
// Set up stage hierarchy to group all UI actors under one container.
|
|
||||||
uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
|
|
||||||
uiGroup.connect('allocate',
|
|
||||||
function (actor, box, flags) {
|
|
||||||
let children = uiGroup.get_children();
|
|
||||||
for (let i = 0; i < children.length; i++)
|
|
||||||
children[i].allocate_preferred_size(flags);
|
|
||||||
});
|
|
||||||
uiGroup.connect('get-preferred-width',
|
|
||||||
function(actor, forHeight, alloc) {
|
|
||||||
let width = global.stage.width;
|
|
||||||
[alloc.min_size, alloc.natural_size] = [width, width];
|
|
||||||
});
|
|
||||||
uiGroup.connect('get-preferred-height',
|
|
||||||
function(actor, forWidth, alloc) {
|
|
||||||
let height = global.stage.height;
|
|
||||||
[alloc.min_size, alloc.natural_size] = [height, height];
|
|
||||||
});
|
|
||||||
global.window_group.reparent(uiGroup);
|
|
||||||
global.overlay_group.reparent(uiGroup);
|
|
||||||
global.stage.add_actor(uiGroup);
|
|
||||||
|
|
||||||
|
// Setup the stage hierarchy early
|
||||||
layoutManager = new Layout.LayoutManager();
|
layoutManager = new Layout.LayoutManager();
|
||||||
|
|
||||||
|
screencastService = new Screencast.ScreencastService();
|
||||||
xdndHandler = new XdndHandler.XdndHandler();
|
xdndHandler = new XdndHandler.XdndHandler();
|
||||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||||
|
osdWindow = new OsdWindow.OsdWindow();
|
||||||
overview = new Overview.Overview();
|
overview = new Overview.Overview();
|
||||||
wm = new WindowManager.WindowManager();
|
wm = new WindowManager.WindowManager();
|
||||||
magnifier = new Magnifier.Magnifier();
|
magnifier = new Magnifier.Magnifier();
|
||||||
if (UnlockDialog.isSupported())
|
if (LoginManager.canLock())
|
||||||
screenShield = new ScreenShield.ScreenShield();
|
screenShield = new ScreenShield.ScreenShield();
|
||||||
else
|
|
||||||
screenShield = new ScreenShield.ScreenShieldFallback();
|
|
||||||
panel = new Panel.Panel();
|
panel = new Panel.Panel();
|
||||||
messageTray = new MessageTray.MessageTray();
|
messageTray = new MessageTray.MessageTray();
|
||||||
keyboard = new Keyboard.Keyboard();
|
keyboard = new Keyboard.Keyboard();
|
||||||
@@ -174,20 +166,36 @@ function start() {
|
|||||||
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
||||||
componentManager = new Components.ComponentManager();
|
componentManager = new Components.ComponentManager();
|
||||||
|
|
||||||
|
if (sessionMode.isGreeter && screenShield) {
|
||||||
|
layoutManager.connect('startup-prepared', function() {
|
||||||
|
screenShield.showDialog();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutManager.connect('startup-complete', function() {
|
||||||
|
if (keybindingMode == Shell.KeyBindingMode.NONE)
|
||||||
|
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||||
|
if (screenShield)
|
||||||
|
screenShield.lockIfWasLocked();
|
||||||
|
});
|
||||||
|
|
||||||
layoutManager.init();
|
layoutManager.init();
|
||||||
keyboard.init();
|
|
||||||
overview.init();
|
overview.init();
|
||||||
|
|
||||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
_a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
|
||||||
false, -1, 1);
|
|
||||||
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
global.display.connect('overlay-key', Lang.bind(overview, function () {
|
||||||
sessionMode.connect('updated', _sessionUpdated);
|
if (!_a11ySettings.get_boolean (STICKY_KEYS_ENABLE))
|
||||||
_sessionUpdated();
|
overview.toggle();
|
||||||
|
}));
|
||||||
|
|
||||||
// Provide the bus object for gnome-session to
|
// Provide the bus object for gnome-session to
|
||||||
// initiate logouts.
|
// initiate logouts.
|
||||||
EndSessionDialog.init();
|
EndSessionDialog.init();
|
||||||
|
|
||||||
|
// We're ready for the session manager to move to the next phase
|
||||||
|
Meta.register_with_session();
|
||||||
|
|
||||||
_startDate = new Date();
|
_startDate = new Date();
|
||||||
|
|
||||||
log('GNOME Shell started at ' + _startDate);
|
log('GNOME Shell started at ' + _startDate);
|
||||||
@@ -199,200 +207,20 @@ function start() {
|
|||||||
Scripting.runPerfScript(module, perfOutput);
|
Scripting.runPerfScript(module, perfOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
_overridesSettings = new Gio.Settings({ schema: OVERRIDES_SCHEMA });
|
|
||||||
_overridesSettings.connect('changed::dynamic-workspaces', _queueCheckWorkspaces);
|
|
||||||
|
|
||||||
global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
|
|
||||||
|
|
||||||
global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
|
|
||||||
global.screen.connect('window-left-monitor', _windowLeftMonitor);
|
|
||||||
global.screen.connect('restacked', _windowsRestacked);
|
|
||||||
|
|
||||||
_nWorkspacesChanged();
|
|
||||||
|
|
||||||
ExtensionDownloader.init();
|
ExtensionDownloader.init();
|
||||||
ExtensionSystem.init();
|
ExtensionSystem.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
let _workspaces = [];
|
function _loadDefaultStylesheet() {
|
||||||
let _checkWorkspacesId = 0;
|
if (!sessionMode.isPrimary)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
|
||||||
* When the last window closed on a workspace is a dialog or splash
|
if (_defaultCssStylesheet == stylesheet)
|
||||||
* screen, we assume that it might be an initial window shown before
|
return;
|
||||||
* the main window of an application, and give the app a grace period
|
|
||||||
* where it can map another window before we remove the workspace.
|
|
||||||
*/
|
|
||||||
const LAST_WINDOW_GRACE_TIME = 1000;
|
|
||||||
|
|
||||||
function _checkWorkspaces() {
|
_defaultCssStylesheet = stylesheet;
|
||||||
let i;
|
loadTheme();
|
||||||
let emptyWorkspaces = [];
|
|
||||||
|
|
||||||
if (!Meta.prefs_get_dynamic_workspaces()) {
|
|
||||||
_checkWorkspacesId = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < _workspaces.length; i++) {
|
|
||||||
let lastRemoved = _workspaces[i]._lastRemovedWindow;
|
|
||||||
if ((lastRemoved &&
|
|
||||||
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
|
|
||||||
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
|
|
||||||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
|
|
||||||
_workspaces[i]._keepAliveId)
|
|
||||||
emptyWorkspaces[i] = false;
|
|
||||||
else
|
|
||||||
emptyWorkspaces[i] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
|
|
||||||
for (i = 0; i < sequences.length; i++) {
|
|
||||||
let index = sequences[i].get_workspace();
|
|
||||||
if (index >= 0 && index <= global.screen.n_workspaces)
|
|
||||||
emptyWorkspaces[index] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let windows = global.get_window_actors();
|
|
||||||
for (i = 0; i < windows.length; i++) {
|
|
||||||
let win = windows[i];
|
|
||||||
|
|
||||||
if (win.get_meta_window().is_on_all_workspaces())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let workspaceIndex = win.get_workspace();
|
|
||||||
emptyWorkspaces[workspaceIndex] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't have an empty workspace at the end, add one
|
|
||||||
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
|
|
||||||
global.screen.append_new_workspace(false, global.get_current_time());
|
|
||||||
emptyWorkspaces.push(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
|
||||||
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
|
|
||||||
activeWorkspaceIndex < emptyWorkspaces.length - 1);
|
|
||||||
// Don't enter the overview when removing multiple empty workspaces at startup
|
|
||||||
let showOverview = (removingCurrentWorkspace &&
|
|
||||||
!emptyWorkspaces.every(function(x) { return x; }));
|
|
||||||
|
|
||||||
if (removingCurrentWorkspace) {
|
|
||||||
// "Merge" the empty workspace we are removing with the one at the end
|
|
||||||
wm.blockAnimations();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete other empty workspaces; do it from the end to avoid index changes
|
|
||||||
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
|
|
||||||
if (emptyWorkspaces[i])
|
|
||||||
global.screen.remove_workspace(_workspaces[i], global.get_current_time());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (removingCurrentWorkspace) {
|
|
||||||
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
|
|
||||||
wm.unblockAnimations();
|
|
||||||
|
|
||||||
if (!overview.visible && showOverview)
|
|
||||||
overview.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
_checkWorkspacesId = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function keepWorkspaceAlive(workspace, duration) {
|
|
||||||
if (workspace._keepAliveId)
|
|
||||||
Mainloop.source_remove(workspace._keepAliveId);
|
|
||||||
|
|
||||||
workspace._keepAliveId = Mainloop.timeout_add(duration, function() {
|
|
||||||
workspace._keepAliveId = 0;
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowRemoved(workspace, window) {
|
|
||||||
workspace._lastRemovedWindow = window;
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, function() {
|
|
||||||
if (workspace._lastRemovedWindow == window) {
|
|
||||||
workspace._lastRemovedWindow = null;
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
|
|
||||||
// If the window left the primary monitor, that
|
|
||||||
// might make that workspace empty
|
|
||||||
if (monitorIndex == layoutManager.primaryIndex)
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
|
|
||||||
// If the window entered the primary monitor, that
|
|
||||||
// might make that workspace non-empty
|
|
||||||
if (monitorIndex == layoutManager.primaryIndex)
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _windowsRestacked() {
|
|
||||||
// Figure out where the pointer is in case we lost track of
|
|
||||||
// it during a grab. (In particular, if a trayicon popup menu
|
|
||||||
// is dismissed, see if we need to close the message tray.)
|
|
||||||
global.sync_pointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _queueCheckWorkspaces() {
|
|
||||||
if (_checkWorkspacesId == 0)
|
|
||||||
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _nWorkspacesChanged() {
|
|
||||||
let oldNumWorkspaces = _workspaces.length;
|
|
||||||
let newNumWorkspaces = global.screen.n_workspaces;
|
|
||||||
|
|
||||||
if (oldNumWorkspaces == newNumWorkspaces)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
let lostWorkspaces = [];
|
|
||||||
if (newNumWorkspaces > oldNumWorkspaces) {
|
|
||||||
let w;
|
|
||||||
|
|
||||||
// Assume workspaces are only added at the end
|
|
||||||
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
|
||||||
_workspaces[w] = global.screen.get_workspace_by_index(w);
|
|
||||||
|
|
||||||
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
|
||||||
let workspace = _workspaces[w];
|
|
||||||
workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
|
|
||||||
workspace._windowRemovedId = workspace.connect('window-removed', _windowRemoved);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Assume workspaces are only removed sequentially
|
|
||||||
// (e.g. 2,3,4 - not 2,4,7)
|
|
||||||
let removedIndex;
|
|
||||||
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
|
||||||
for (let w = 0; w < oldNumWorkspaces; w++) {
|
|
||||||
let workspace = global.screen.get_workspace_by_index(w);
|
|
||||||
if (_workspaces[w] != workspace) {
|
|
||||||
removedIndex = w;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
|
|
||||||
lostWorkspaces.forEach(function(workspace) {
|
|
||||||
workspace.disconnect(workspace._windowAddedId);
|
|
||||||
workspace.disconnect(workspace._windowRemovedId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_queueCheckWorkspaces();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -429,11 +257,8 @@ function loadTheme() {
|
|||||||
let themeContext = St.ThemeContext.get_for_stage (global.stage);
|
let themeContext = St.ThemeContext.get_for_stage (global.stage);
|
||||||
let previousTheme = themeContext.get_theme();
|
let previousTheme = themeContext.get_theme();
|
||||||
|
|
||||||
let cssStylesheet = _defaultCssStylesheet;
|
let theme = new St.Theme ({ application_stylesheet: _cssStylesheet,
|
||||||
if (_cssStylesheet != null)
|
default_stylesheet: _defaultCssStylesheet });
|
||||||
cssStylesheet = _cssStylesheet;
|
|
||||||
|
|
||||||
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
|
||||||
|
|
||||||
if (previousTheme) {
|
if (previousTheme) {
|
||||||
let customStylesheets = previousTheme.get_custom_stylesheets();
|
let customStylesheets = previousTheme.get_custom_stylesheets();
|
||||||
@@ -475,17 +300,6 @@ function notifyError(msg, details) {
|
|||||||
notify(msg, details);
|
notify(msg, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
|
||||||
return win.get_workspace() == workspaceIndex ||
|
|
||||||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWindowActorsForWorkspace(workspaceIndex) {
|
|
||||||
return global.get_window_actors().filter(function (win) {
|
|
||||||
return isWindowActorDisplayedOnWorkspace(win, workspaceIndex);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _findModal(actor) {
|
function _findModal(actor) {
|
||||||
for (let i = 0; i < modalActorFocusStack.length; i++) {
|
for (let i = 0; i < modalActorFocusStack.length; i++) {
|
||||||
if (modalActorFocusStack[i].actor == actor)
|
if (modalActorFocusStack[i].actor == actor)
|
||||||
@@ -494,10 +308,6 @@ function _findModal(actor) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isInModalStack(actor) {
|
|
||||||
return _findModal(actor) != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pushModal:
|
* pushModal:
|
||||||
* @actor: #ClutterActor which will be given keyboard focus
|
* @actor: #ClutterActor which will be given keyboard focus
|
||||||
@@ -520,7 +330,7 @@ function isInModalStack(actor) {
|
|||||||
* - options: Meta.ModalOptions flags to indicate that the pointer is
|
* - options: Meta.ModalOptions flags to indicate that the pointer is
|
||||||
* already grabbed
|
* already grabbed
|
||||||
*
|
*
|
||||||
* - keybindingMode: used to set the current Main.KeybindingMode to filter
|
* - keybindingMode: used to set the current Shell.KeyBindingMode to filter
|
||||||
* global keybindings; the default of NONE will filter
|
* global keybindings; the default of NONE will filter
|
||||||
* out all keybindings
|
* out all keybindings
|
||||||
*
|
*
|
||||||
@@ -529,7 +339,7 @@ function isInModalStack(actor) {
|
|||||||
function pushModal(actor, params) {
|
function pushModal(actor, params) {
|
||||||
params = Params.parse(params, { timestamp: global.get_current_time(),
|
params = Params.parse(params, { timestamp: global.get_current_time(),
|
||||||
options: 0,
|
options: 0,
|
||||||
keybindingMode: KeybindingMode.NONE });
|
keybindingMode: Shell.KeyBindingMode.NONE });
|
||||||
|
|
||||||
if (modalCount == 0) {
|
if (modalCount == 0) {
|
||||||
if (!global.begin_modal(params.timestamp, params.options)) {
|
if (!global.begin_modal(params.timestamp, params.options)) {
|
||||||
@@ -539,27 +349,26 @@ function pushModal(actor, params) {
|
|||||||
Meta.disable_unredirect_for_screen(global.screen);
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
|
|
||||||
|
|
||||||
modalCount += 1;
|
modalCount += 1;
|
||||||
let actorDestroyId = actor.connect('destroy', function() {
|
let actorDestroyId = actor.connect('destroy', function() {
|
||||||
let index = _findModal(actor);
|
let index = _findModal(actor);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
popModal(actor);
|
popModal(actor);
|
||||||
});
|
});
|
||||||
let curFocus = global.stage.get_key_focus();
|
|
||||||
let curFocusDestroyId;
|
let prevFocus = global.stage.get_key_focus();
|
||||||
if (curFocus != null) {
|
let prevFocusDestroyId;
|
||||||
curFocusDestroyId = curFocus.connect('destroy', function() {
|
if (prevFocus != null) {
|
||||||
|
prevFocusDestroyId = prevFocus.connect('destroy', function() {
|
||||||
let index = _findModal(actor);
|
let index = _findModal(actor);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
modalActorFocusStack[index].actor = null;
|
modalActorFocusStack[index].prevFocus = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
modalActorFocusStack.push({ actor: actor,
|
modalActorFocusStack.push({ actor: actor,
|
||||||
focus: curFocus,
|
|
||||||
destroyId: actorDestroyId,
|
destroyId: actorDestroyId,
|
||||||
focusDestroyId: curFocusDestroyId,
|
prevFocus: prevFocus,
|
||||||
|
prevFocusDestroyId: prevFocusDestroyId,
|
||||||
keybindingMode: keybindingMode });
|
keybindingMode: keybindingMode });
|
||||||
|
|
||||||
keybindingMode = params.keybindingMode;
|
keybindingMode = params.keybindingMode;
|
||||||
@@ -588,8 +397,7 @@ function popModal(actor, timestamp) {
|
|||||||
if (focusIndex < 0) {
|
if (focusIndex < 0) {
|
||||||
global.stage.set_key_focus(null);
|
global.stage.set_key_focus(null);
|
||||||
global.end_modal(timestamp);
|
global.end_modal(timestamp);
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||||
keybindingMode = KeybindingMode.NORMAL;
|
|
||||||
|
|
||||||
throw new Error('incorrect pop');
|
throw new Error('incorrect pop');
|
||||||
}
|
}
|
||||||
@@ -600,18 +408,33 @@ function popModal(actor, timestamp) {
|
|||||||
record.actor.disconnect(record.destroyId);
|
record.actor.disconnect(record.destroyId);
|
||||||
|
|
||||||
if (focusIndex == modalActorFocusStack.length - 1) {
|
if (focusIndex == modalActorFocusStack.length - 1) {
|
||||||
if (record.focus)
|
if (record.prevFocus)
|
||||||
record.focus.disconnect(record.focusDestroyId);
|
record.prevFocus.disconnect(record.prevFocusDestroyId);
|
||||||
keybindingMode = record.keybindingMode;
|
keybindingMode = record.keybindingMode;
|
||||||
global.stage.set_key_focus(record.focus);
|
global.stage.set_key_focus(record.prevFocus);
|
||||||
} else {
|
} else {
|
||||||
|
// If we have:
|
||||||
|
// global.stage.set_focus(a);
|
||||||
|
// Main.pushModal(b);
|
||||||
|
// Main.pushModal(c);
|
||||||
|
// Main.pushModal(d);
|
||||||
|
//
|
||||||
|
// then we have the stack:
|
||||||
|
// [{ prevFocus: a, actor: b },
|
||||||
|
// { prevFocus: b, actor: c },
|
||||||
|
// { prevFocus: c, actor: d }]
|
||||||
|
//
|
||||||
|
// When actor c is destroyed/popped, if we only simply remove the
|
||||||
|
// record, then the focus stack will be [a, c], rather than the correct
|
||||||
|
// [a, b]. Shift the focus stack up before removing the record to ensure
|
||||||
|
// that we get the correct result.
|
||||||
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
|
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
|
||||||
if (t.focus)
|
if (t.prevFocus)
|
||||||
t.focus.disconnect(t.focusDestroyId);
|
t.prevFocus.disconnect(t.prevFocusDestroyId);
|
||||||
// Remove from the middle, shift the focus chain up
|
// Remove from the middle, shift the focus chain up
|
||||||
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
|
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
|
||||||
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
|
modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
|
||||||
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
|
modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
|
||||||
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
|
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -621,9 +444,8 @@ function popModal(actor, timestamp) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
global.end_modal(timestamp);
|
global.end_modal(timestamp);
|
||||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
|
||||||
Meta.enable_unredirect_for_screen(global.screen);
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
keybindingMode = KeybindingMode.NORMAL;
|
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLookingGlass() {
|
function createLookingGlass() {
|
||||||
@@ -779,7 +601,7 @@ function queueDeferredWork(workId) {
|
|||||||
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
|
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
|
||||||
_runAllDeferredWork();
|
_runAllDeferredWork();
|
||||||
_deferredTimeoutId = 0;
|
_deferredTimeoutId = 0;
|
||||||
return false;
|
return GLib.SOURCE_REMOVE;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1602
js/ui/messageTray.js
1602
js/ui/messageTray.js
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@ const Atk = imports.gi.Atk;
|
|||||||
|
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
|
const Animation = imports.ui.animation;
|
||||||
const Layout = imports.ui.layout;
|
const Layout = imports.ui.layout;
|
||||||
const Lightbox = imports.ui.lightbox;
|
const Lightbox = imports.ui.lightbox;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
@@ -22,6 +23,10 @@ const Tweener = imports.ui.tweener;
|
|||||||
const OPEN_AND_CLOSE_TIME = 0.1;
|
const OPEN_AND_CLOSE_TIME = 0.1;
|
||||||
const FADE_OUT_DIALOG_TIME = 1.0;
|
const FADE_OUT_DIALOG_TIME = 1.0;
|
||||||
|
|
||||||
|
const WORK_SPINNER_ICON_SIZE = 24;
|
||||||
|
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
|
||||||
|
const WORK_SPINNER_ANIMATION_TIME = 0.3;
|
||||||
|
|
||||||
const State = {
|
const State = {
|
||||||
OPENED: 0,
|
OPENED: 0,
|
||||||
CLOSED: 1,
|
CLOSED: 1,
|
||||||
@@ -36,15 +41,17 @@ const ModalDialog = new Lang.Class({
|
|||||||
_init: function(params) {
|
_init: function(params) {
|
||||||
params = Params.parse(params, { shellReactive: false,
|
params = Params.parse(params, { shellReactive: false,
|
||||||
styleClass: null,
|
styleClass: null,
|
||||||
parentActor: Main.uiGroup,
|
parentActor: Main.layoutManager.dialogGroup,
|
||||||
keybindingMode: Main.KeybindingMode.SYSTEM_MODAL,
|
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
|
||||||
shouldFadeIn: true });
|
shouldFadeIn: true,
|
||||||
|
destroyOnClose: true });
|
||||||
|
|
||||||
this.state = State.CLOSED;
|
this.state = State.CLOSED;
|
||||||
this._hasModal = false;
|
this._hasModal = false;
|
||||||
this._keybindingMode = params.keybindingMode;
|
this._keybindingMode = params.keybindingMode;
|
||||||
this._shellReactive = params.shellReactive;
|
this._shellReactive = params.shellReactive;
|
||||||
this._shouldFadeIn = params.shouldFadeIn;
|
this._shouldFadeIn = params.shouldFadeIn;
|
||||||
|
this._destroyOnClose = params.destroyOnClose;
|
||||||
|
|
||||||
this._group = new St.Widget({ visible: false,
|
this._group = new St.Widget({ visible: false,
|
||||||
x: 0,
|
x: 0,
|
||||||
@@ -58,55 +65,60 @@ const ModalDialog = new Lang.Class({
|
|||||||
|
|
||||||
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
||||||
|
|
||||||
|
this._pressedKey = null;
|
||||||
this._buttonKeys = {};
|
this._buttonKeys = {};
|
||||||
|
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||||
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
|
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
|
||||||
|
|
||||||
this._backgroundBin = new St.Bin();
|
this.backgroundStack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||||
|
this._backgroundBin = new St.Bin({ child: this.backgroundStack,
|
||||||
|
x_fill: true, y_fill: true });
|
||||||
this._monitorConstraint = new Layout.MonitorConstraint();
|
this._monitorConstraint = new Layout.MonitorConstraint();
|
||||||
this._backgroundBin.add_constraint(this._monitorConstraint);
|
this._backgroundBin.add_constraint(this._monitorConstraint);
|
||||||
this._group.add_actor(this._backgroundBin);
|
this._group.add_actor(this._backgroundBin);
|
||||||
|
|
||||||
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
|
||||||
vertical: true });
|
vertical: true });
|
||||||
if (params.styleClass != null) {
|
// modal dialogs are fixed width and grow vertically; set the request
|
||||||
|
// mode accordingly so wrapped labels are handled correctly during
|
||||||
|
// size requests.
|
||||||
|
this.dialogLayout.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
|
||||||
|
|
||||||
|
if (params.styleClass != null)
|
||||||
this.dialogLayout.add_style_class_name(params.styleClass);
|
this.dialogLayout.add_style_class_name(params.styleClass);
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._shellReactive) {
|
if (!this._shellReactive) {
|
||||||
this._lightbox = new Lightbox.Lightbox(this._group,
|
this._lightbox = new Lightbox.Lightbox(this._group,
|
||||||
{ inhibitEvents: true });
|
{ inhibitEvents: true,
|
||||||
|
radialEffect: true });
|
||||||
this._lightbox.highlight(this._backgroundBin);
|
this._lightbox.highlight(this._backgroundBin);
|
||||||
|
|
||||||
let stack = new Shell.Stack();
|
this._eventBlocker = new Clutter.Actor({ reactive: true });
|
||||||
this._backgroundBin.child = stack;
|
this.backgroundStack.add_actor(this._eventBlocker);
|
||||||
|
|
||||||
this._eventBlocker = new Clutter.Group({ reactive: true });
|
|
||||||
stack.add_actor(this._eventBlocker);
|
|
||||||
stack.add_actor(this.dialogLayout);
|
|
||||||
} else {
|
|
||||||
this._backgroundBin.child = this.dialogLayout;
|
|
||||||
}
|
}
|
||||||
|
this.backgroundStack.add_actor(this.dialogLayout);
|
||||||
|
|
||||||
|
|
||||||
this.contentLayout = new St.BoxLayout({ vertical: true });
|
this.contentLayout = new St.BoxLayout({ vertical: true });
|
||||||
this.dialogLayout.add(this.contentLayout,
|
this.dialogLayout.add(this.contentLayout,
|
||||||
{ x_fill: true,
|
{ expand: true,
|
||||||
|
x_fill: true,
|
||||||
y_fill: true,
|
y_fill: true,
|
||||||
x_align: St.Align.MIDDLE,
|
x_align: St.Align.MIDDLE,
|
||||||
y_align: St.Align.START });
|
y_align: St.Align.START });
|
||||||
|
|
||||||
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||||
visible: false,
|
|
||||||
vertical: false });
|
vertical: false });
|
||||||
this.dialogLayout.add(this.buttonLayout,
|
this.dialogLayout.add(this.buttonLayout,
|
||||||
{ expand: true,
|
{ x_align: St.Align.MIDDLE,
|
||||||
x_align: St.Align.MIDDLE,
|
|
||||||
y_align: St.Align.END });
|
y_align: St.Align.END });
|
||||||
|
|
||||||
global.focus_manager.add_group(this.dialogLayout);
|
global.focus_manager.add_group(this.dialogLayout);
|
||||||
this._initialKeyFocus = this.dialogLayout;
|
this._initialKeyFocus = this.dialogLayout;
|
||||||
this._initialKeyFocusDestroyId = 0;
|
this._initialKeyFocusDestroyId = 0;
|
||||||
this._savedKeyFocus = null;
|
this._savedKeyFocus = null;
|
||||||
|
|
||||||
|
this._workSpinner = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
@@ -120,7 +132,6 @@ const ModalDialog = new Lang.Class({
|
|||||||
|
|
||||||
setButtons: function(buttons) {
|
setButtons: function(buttons) {
|
||||||
this.clearButtons();
|
this.clearButtons();
|
||||||
this.buttonLayout.visible = (buttons.length > 0);
|
|
||||||
|
|
||||||
for (let i = 0; i < buttons.length; i++) {
|
for (let i = 0; i < buttons.length; i++) {
|
||||||
let buttonInfo = buttons[i];
|
let buttonInfo = buttons[i];
|
||||||
@@ -159,6 +170,7 @@ const ModalDialog = new Lang.Class({
|
|||||||
keys = [];
|
keys = [];
|
||||||
|
|
||||||
let button = new St.Button({ style_class: 'modal-dialog-button',
|
let button = new St.Button({ style_class: 'modal-dialog-button',
|
||||||
|
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||||
reactive: true,
|
reactive: true,
|
||||||
can_focus: true,
|
can_focus: true,
|
||||||
label: label });
|
label: label });
|
||||||
@@ -180,22 +192,68 @@ const ModalDialog = new Lang.Class({
|
|||||||
return button;
|
return button;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onKeyReleaseEvent: function(object, event) {
|
placeSpinner: function(layoutInfo) {
|
||||||
let symbol = event.get_key_symbol();
|
let spinnerIcon = global.datadir + '/theme/process-working.svg';
|
||||||
let buttonInfo = this._buttonKeys[symbol];
|
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
|
||||||
|
this._workSpinner.actor.opacity = 0;
|
||||||
|
this._workSpinner.actor.show();
|
||||||
|
|
||||||
|
this.buttonLayout.add(this._workSpinner.actor, layoutInfo);
|
||||||
|
},
|
||||||
|
|
||||||
|
setWorking: function(working) {
|
||||||
|
if (!this._workSpinner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Tweener.removeTweens(this._workSpinner.actor);
|
||||||
|
if (working) {
|
||||||
|
this._workSpinner.play();
|
||||||
|
Tweener.addTween(this._workSpinner.actor,
|
||||||
|
{ opacity: 255,
|
||||||
|
delay: WORK_SPINNER_ANIMATION_DELAY,
|
||||||
|
time: WORK_SPINNER_ANIMATION_TIME,
|
||||||
|
transition: 'linear'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Tweener.addTween(this._workSpinner.actor,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: WORK_SPINNER_ANIMATION_TIME,
|
||||||
|
transition: 'linear',
|
||||||
|
onCompleteScope: this,
|
||||||
|
onComplete: function() {
|
||||||
|
if (this._workSpinner)
|
||||||
|
this._workSpinner.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyPressEvent: function(object, event) {
|
||||||
|
this._pressedKey = event.get_key_symbol();
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyReleaseEvent: function(object, event) {
|
||||||
|
let pressedKey = this._pressedKey;
|
||||||
|
this._pressedKey = null;
|
||||||
|
|
||||||
|
let symbol = event.get_key_symbol();
|
||||||
|
if (symbol != pressedKey)
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
|
let buttonInfo = this._buttonKeys[symbol];
|
||||||
if (!buttonInfo)
|
if (!buttonInfo)
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
let button = buttonInfo['button'];
|
let button = buttonInfo['button'];
|
||||||
let action = buttonInfo['action'];
|
let action = buttonInfo['action'];
|
||||||
|
|
||||||
if (action && button.reactive) {
|
if (action && button.reactive) {
|
||||||
action();
|
action();
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onGroupDestroy: function() {
|
_onGroupDestroy: function() {
|
||||||
@@ -266,6 +324,10 @@ const ModalDialog = new Lang.Class({
|
|||||||
function() {
|
function() {
|
||||||
this.state = State.CLOSED;
|
this.state = State.CLOSED;
|
||||||
this._group.hide();
|
this._group.hide();
|
||||||
|
this.emit('closed');
|
||||||
|
|
||||||
|
if (this._destroyOnClose)
|
||||||
|
this.destroy();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -301,8 +363,9 @@ const ModalDialog = new Lang.Class({
|
|||||||
if (this._savedKeyFocus) {
|
if (this._savedKeyFocus) {
|
||||||
this._savedKeyFocus.grab_key_focus();
|
this._savedKeyFocus.grab_key_focus();
|
||||||
this._savedKeyFocus = null;
|
this._savedKeyFocus = null;
|
||||||
} else
|
} else {
|
||||||
this._initialKeyFocus.grab_key_focus();
|
this._initialKeyFocus.grab_key_focus();
|
||||||
|
}
|
||||||
|
|
||||||
if (!this._shellReactive)
|
if (!this._shellReactive)
|
||||||
this._eventBlocker.lower_bottom();
|
this._eventBlocker.lower_bottom();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter;
|
|||||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
@@ -15,54 +16,56 @@ const MessageTray = imports.ui.messageTray;
|
|||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const Util = imports.misc.util;
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
let nextNotificationId = 1;
|
|
||||||
|
|
||||||
// Should really be defined in Gio.js
|
// Should really be defined in Gio.js
|
||||||
const BusIface = <interface name="org.freedesktop.DBus">
|
const BusIface = '<node> \
|
||||||
<method name="GetConnectionUnixProcessID">
|
<interface name="org.freedesktop.DBus"> \
|
||||||
<arg type="s" direction="in" />
|
<method name="GetConnectionUnixProcessID"> \
|
||||||
<arg type="u" direction="out" />
|
<arg type="s" direction="in" /> \
|
||||||
</method>
|
<arg type="u" direction="out" /> \
|
||||||
</interface>;
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface);
|
var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface);
|
||||||
function Bus() {
|
function Bus() {
|
||||||
return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus');
|
return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus');
|
||||||
}
|
}
|
||||||
|
|
||||||
const NotificationDaemonIface = <interface name="org.freedesktop.Notifications">
|
const FdoNotificationsIface = '<node> \
|
||||||
<method name="Notify">
|
<interface name="org.freedesktop.Notifications"> \
|
||||||
<arg type="s" direction="in"/>
|
<method name="Notify"> \
|
||||||
<arg type="u" direction="in"/>
|
<arg type="s" direction="in"/> \
|
||||||
<arg type="s" direction="in"/>
|
<arg type="u" direction="in"/> \
|
||||||
<arg type="s" direction="in"/>
|
<arg type="s" direction="in"/> \
|
||||||
<arg type="s" direction="in"/>
|
<arg type="s" direction="in"/> \
|
||||||
<arg type="as" direction="in"/>
|
<arg type="s" direction="in"/> \
|
||||||
<arg type="a{sv}" direction="in"/>
|
<arg type="as" direction="in"/> \
|
||||||
<arg type="i" direction="in"/>
|
<arg type="a{sv}" direction="in"/> \
|
||||||
<arg type="u" direction="out"/>
|
<arg type="i" direction="in"/> \
|
||||||
</method>
|
<arg type="u" direction="out"/> \
|
||||||
<method name="CloseNotification">
|
</method> \
|
||||||
<arg type="u" direction="in"/>
|
<method name="CloseNotification"> \
|
||||||
</method>
|
<arg type="u" direction="in"/> \
|
||||||
<method name="GetCapabilities">
|
</method> \
|
||||||
<arg type="as" direction="out"/>
|
<method name="GetCapabilities"> \
|
||||||
</method>
|
<arg type="as" direction="out"/> \
|
||||||
<method name="GetServerInformation">
|
</method> \
|
||||||
<arg type="s" direction="out"/>
|
<method name="GetServerInformation"> \
|
||||||
<arg type="s" direction="out"/>
|
<arg type="s" direction="out"/> \
|
||||||
<arg type="s" direction="out"/>
|
<arg type="s" direction="out"/> \
|
||||||
<arg type="s" direction="out"/>
|
<arg type="s" direction="out"/> \
|
||||||
</method>
|
<arg type="s" direction="out"/> \
|
||||||
<signal name="NotificationClosed">
|
</method> \
|
||||||
<arg type="u"/>
|
<signal name="NotificationClosed"> \
|
||||||
<arg type="u"/>
|
<arg type="u"/> \
|
||||||
</signal>
|
<arg type="u"/> \
|
||||||
<signal name="ActionInvoked">
|
</signal> \
|
||||||
<arg type="u"/>
|
<signal name="ActionInvoked"> \
|
||||||
<arg type="s"/>
|
<arg type="u"/> \
|
||||||
</signal>
|
<arg type="s"/> \
|
||||||
</interface>;
|
</signal> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const NotificationClosedReason = {
|
const NotificationClosedReason = {
|
||||||
EXPIRED: 1,
|
EXPIRED: 1,
|
||||||
@@ -103,11 +106,11 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
|||||||
'ibus-ui-gtk': 'keyboard'
|
'ibus-ui-gtk': 'keyboard'
|
||||||
};
|
};
|
||||||
|
|
||||||
const NotificationDaemon = new Lang.Class({
|
const FdoNotificationDaemon = new Lang.Class({
|
||||||
Name: 'NotificationDaemon',
|
Name: 'FdoNotificationDaemon',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(NotificationDaemonIface, this);
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FdoNotificationsIface, this);
|
||||||
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
|
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
|
||||||
|
|
||||||
this._sources = [];
|
this._sources = [];
|
||||||
@@ -115,6 +118,8 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
this._notifications = {};
|
this._notifications = {};
|
||||||
this._busProxy = new Bus();
|
this._busProxy = new Bus();
|
||||||
|
|
||||||
|
this._nextNotificationId = 1;
|
||||||
|
|
||||||
this._trayManager = new Shell.TrayManager();
|
this._trayManager = new Shell.TrayManager();
|
||||||
this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
|
||||||
this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
|
||||||
@@ -124,33 +129,22 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
Main.overview.connect('hidden',
|
Main.overview.connect('hidden',
|
||||||
Lang.bind(this, this._onFocusAppChanged));
|
Lang.bind(this, this._onFocusAppChanged));
|
||||||
|
|
||||||
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
|
this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
|
||||||
},
|
},
|
||||||
|
|
||||||
_iconForNotificationData: function(icon, hints) {
|
_imageForNotificationData: function(hints) {
|
||||||
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
if (hints['image-data']) {
|
||||||
// and don't show a large image. There are currently many applications that use
|
|
||||||
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
|
||||||
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
|
|
||||||
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
|
|
||||||
// So the logic here does the right thing for this case. If both an icon and either
|
|
||||||
// one of 'image-data' or 'image-path' are specified, we show both an icon and
|
|
||||||
// a large image.
|
|
||||||
if (icon) {
|
|
||||||
if (icon.substr(0, 7) == 'file://')
|
|
||||||
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
|
||||||
else if (icon[0] == '/') {
|
|
||||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
|
||||||
} else
|
|
||||||
return new Gio.ThemedIcon({ name: icon });
|
|
||||||
} else if (hints['image-data']) {
|
|
||||||
let [width, height, rowStride, hasAlpha,
|
let [width, height, rowStride, hasAlpha,
|
||||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||||
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||||
bitsPerSample, width, height, rowStride);
|
bitsPerSample, width, height, rowStride);
|
||||||
} else if (hints['image-path']) {
|
} else if (hints['image-path']) {
|
||||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
|
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
|
||||||
} else {
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_fallbackIconForNotificationData: function(hints) {
|
||||||
let stockIcon;
|
let stockIcon;
|
||||||
switch (hints.urgency) {
|
switch (hints.urgency) {
|
||||||
case Urgency.LOW:
|
case Urgency.LOW:
|
||||||
@@ -162,7 +156,18 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return new Gio.ThemedIcon({ name: stockIcon });
|
return new Gio.ThemedIcon({ name: stockIcon });
|
||||||
|
},
|
||||||
|
|
||||||
|
_iconForNotificationData: function(icon) {
|
||||||
|
if (icon) {
|
||||||
|
if (icon.substr(0, 7) == 'file://')
|
||||||
|
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
||||||
|
else if (icon[0] == '/')
|
||||||
|
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
||||||
|
else
|
||||||
|
return new Gio.ThemedIcon({ name: icon });
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_lookupSource: function(title, pid, trayIcon) {
|
_lookupSource: function(title, pid, trayIcon) {
|
||||||
@@ -176,12 +181,10 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Returns the source associated with ndata.notification if it is set.
|
// Returns the source associated with ndata.notification if it is set.
|
||||||
// Otherwise, returns the source associated with the title and pid if
|
// If the existing or requested source is associated with a tray icon
|
||||||
// such source is stored in this._sources and the notification is not
|
// and passed in pid matches a pid of an existing source, the title
|
||||||
// transient. If the existing or requested source is associated with
|
// match is ignored to enable representing a tray icon and notifications
|
||||||
// a tray icon and passed in pid matches a pid of an existing source,
|
// from the same application with a single source.
|
||||||
// the title match is ignored to enable representing a tray icon and
|
|
||||||
// notifications from the same application with a single source.
|
|
||||||
//
|
//
|
||||||
// If no existing source is found, a new source is created as long as
|
// If no existing source is found, a new source is created as long as
|
||||||
// pid is provided.
|
// pid is provided.
|
||||||
@@ -199,32 +202,20 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
if (ndata && ndata.notification)
|
if (ndata && ndata.notification)
|
||||||
return ndata.notification.source;
|
return ndata.notification.source;
|
||||||
|
|
||||||
let isForTransientNotification = (ndata && ndata.hints['transient'] == true);
|
|
||||||
|
|
||||||
// We don't want to override a persistent notification
|
|
||||||
// with a transient one from the same sender, so we
|
|
||||||
// always create a new source object for new transient notifications
|
|
||||||
// and never add it to this._sources .
|
|
||||||
if (!isForTransientNotification) {
|
|
||||||
let source = this._lookupSource(title, pid, trayIcon);
|
let source = this._lookupSource(title, pid, trayIcon);
|
||||||
if (source) {
|
if (source) {
|
||||||
source.setTitle(title);
|
source.setTitle(title);
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let source = new Source(title, pid, sender, trayIcon);
|
let source = new FdoNotificationDaemonSource(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
|
||||||
source.setTransient(isForTransientNotification);
|
|
||||||
|
|
||||||
if (!isForTransientNotification) {
|
|
||||||
this._sources.push(source);
|
this._sources.push(source);
|
||||||
source.connect('destroy', Lang.bind(this,
|
source.connect('destroy', Lang.bind(this, function() {
|
||||||
function() {
|
|
||||||
let index = this._sources.indexOf(source);
|
let index = this._sources.indexOf(source);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
this._sources.splice(index, 1);
|
this._sources.splice(index, 1);
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
|
|
||||||
Main.messageTray.add(source);
|
Main.messageTray.add(source);
|
||||||
return source;
|
return source;
|
||||||
@@ -252,11 +243,11 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
hints['category'] == 'presence.offline')) {
|
hints['category'] == 'presence.offline')) {
|
||||||
// Ignore replacesId since we already sent back a
|
// Ignore replacesId since we already sent back a
|
||||||
// NotificationClosed for that id.
|
// NotificationClosed for that id.
|
||||||
id = nextNotificationId++;
|
id = this._nextNotificationId++;
|
||||||
Mainloop.idle_add(Lang.bind(this,
|
Mainloop.idle_add(Lang.bind(this,
|
||||||
function () {
|
function () {
|
||||||
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
||||||
return false;
|
return GLib.SOURCE_REMOVE;
|
||||||
}));
|
}));
|
||||||
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
||||||
}
|
}
|
||||||
@@ -276,12 +267,13 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
if (!hints['image-path'] && hints['image_path'])
|
if (!hints['image-path'] && hints['image_path'])
|
||||||
hints['image-path'] = hints['image_path']; // version 1.1 of the spec
|
hints['image-path'] = hints['image_path']; // version 1.1 of the spec
|
||||||
|
|
||||||
if (!hints['image-data'])
|
if (!hints['image-data']) {
|
||||||
if (hints['image_data'])
|
if (hints['image_data'])
|
||||||
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
|
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
|
||||||
else if (hints['icon_data'] && !hints['image-path'])
|
else if (hints['icon_data'] && !hints['image-path'])
|
||||||
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
|
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
|
||||||
hints['image-data'] = hints['icon_data'];
|
hints['image-data'] = hints['icon_data'];
|
||||||
|
}
|
||||||
|
|
||||||
let ndata = { appName: appName,
|
let ndata = { appName: appName,
|
||||||
icon: icon,
|
icon: icon,
|
||||||
@@ -295,7 +287,7 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
ndata.notification = this._notifications[replacesId].notification;
|
ndata.notification = this._notifications[replacesId].notification;
|
||||||
} else {
|
} else {
|
||||||
replacesId = 0;
|
replacesId = 0;
|
||||||
ndata.id = id = nextNotificationId++;
|
ndata.id = id = this._nextNotificationId++;
|
||||||
}
|
}
|
||||||
this._notifications[id] = ndata;
|
this._notifications[id] = ndata;
|
||||||
|
|
||||||
@@ -330,37 +322,36 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
let [pid] = result;
|
let [pid] = result;
|
||||||
source = this._getSource(appName, pid, ndata, sender, null);
|
source = this._getSource(appName, pid, ndata, sender, null);
|
||||||
|
|
||||||
// We only store sender-pid entries for persistent sources.
|
|
||||||
// Removing the entries once the source is destroyed
|
|
||||||
// would result in the entries associated with transient
|
|
||||||
// sources removed once the notification is shown anyway.
|
|
||||||
// However, keeping these pairs would mean that we would
|
|
||||||
// possibly remove an entry associated with a persistent
|
|
||||||
// source when a transient source for the same sender is
|
|
||||||
// distroyed.
|
|
||||||
if (!source.isTransient) {
|
|
||||||
this._senderToPid[sender] = pid;
|
this._senderToPid[sender] = pid;
|
||||||
source.connect('destroy', Lang.bind(this, function() {
|
source.connect('destroy', Lang.bind(this, function() {
|
||||||
delete this._senderToPid[sender];
|
delete this._senderToPid[sender];
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
this._notifyForSource(source, ndata);
|
this._notifyForSource(source, ndata);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_makeButton: function(id, label, useActionIcons) {
|
||||||
|
let button = new St.Button({ can_focus: true });
|
||||||
|
let iconName = id.endsWith('-symbolic') ? id : id + '-symbolic';
|
||||||
|
if (useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
|
||||||
|
button.add_style_class_name('notification-icon-button');
|
||||||
|
button.child = new St.Icon({ icon_name: iconName });
|
||||||
|
} else {
|
||||||
|
button.add_style_class_name('notification-button');
|
||||||
|
button.label = label;
|
||||||
|
}
|
||||||
|
return button;
|
||||||
|
},
|
||||||
|
|
||||||
_notifyForSource: function(source, ndata) {
|
_notifyForSource: function(source, ndata) {
|
||||||
let [id, icon, summary, body, actions, hints, notification] =
|
let [id, icon, summary, body, actions, hints, notification] =
|
||||||
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
||||||
ndata.actions, ndata.hints, ndata.notification];
|
ndata.actions, ndata.hints, ndata.notification];
|
||||||
|
|
||||||
let gicon = this._iconForNotificationData(icon, hints);
|
|
||||||
|
|
||||||
if (notification == null) {
|
if (notification == null) {
|
||||||
notification = new MessageTray.Notification(source, summary, body,
|
notification = new MessageTray.Notification(source);
|
||||||
{ gicon: gicon,
|
|
||||||
bannerMarkup: true });
|
|
||||||
ndata.notification = notification;
|
ndata.notification = notification;
|
||||||
notification.connect('destroy', Lang.bind(this,
|
notification.connect('destroy', Lang.bind(this,
|
||||||
function(n, reason) {
|
function(n, reason) {
|
||||||
@@ -379,46 +370,66 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
}
|
}
|
||||||
this._emitNotificationClosed(ndata.id, notificationClosedReason);
|
this._emitNotificationClosed(ndata.id, notificationClosedReason);
|
||||||
}));
|
}));
|
||||||
notification.connect('action-invoked', Lang.bind(this,
|
}
|
||||||
function(n, actionId) {
|
|
||||||
this._emitActionInvoked(ndata.id, actionId);
|
// Mark music notifications so they can be shown in the screen shield
|
||||||
}));
|
notification.isMusic = (ndata.hints['category'] == 'x-gnome.music');
|
||||||
} else {
|
|
||||||
|
let gicon = this._iconForNotificationData(icon, hints);
|
||||||
|
let gimage = this._imageForNotificationData(hints);
|
||||||
|
|
||||||
|
let image = null;
|
||||||
|
|
||||||
|
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
||||||
|
// and don't show a large image. There are currently many applications that use
|
||||||
|
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
||||||
|
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
|
||||||
|
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
|
||||||
|
// So the logic here does the right thing for this case. If both an icon and either
|
||||||
|
// one of 'image-data' or 'image-path' are specified, we show both an icon and
|
||||||
|
// a large image.
|
||||||
|
if (gicon && gimage)
|
||||||
|
image = new St.Icon({ gicon: gimage,
|
||||||
|
icon_size: notification.IMAGE_SIZE });
|
||||||
|
else if (!gicon && gimage)
|
||||||
|
gicon = gimage;
|
||||||
|
else if (!gicon)
|
||||||
|
gicon = this._fallbackIconForNotificationData(hints);
|
||||||
|
|
||||||
notification.update(summary, body, { gicon: gicon,
|
notification.update(summary, body, { gicon: gicon,
|
||||||
bannerMarkup: true,
|
bannerMarkup: true,
|
||||||
clear: true });
|
clear: true,
|
||||||
}
|
soundFile: hints['sound-file'],
|
||||||
|
soundName: hints['sound-name'] });
|
||||||
// We only display a large image if an icon is also specified.
|
|
||||||
if (icon && (hints['image-data'] || hints['image-path'])) {
|
|
||||||
let image = null;
|
|
||||||
if (hints['image-data']) {
|
|
||||||
let [width, height, rowStride, hasAlpha,
|
|
||||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
|
||||||
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
|
|
||||||
width, height, rowStride, notification.IMAGE_SIZE);
|
|
||||||
} else if (hints['image-path']) {
|
|
||||||
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
|
|
||||||
notification.IMAGE_SIZE,
|
|
||||||
notification.IMAGE_SIZE);
|
|
||||||
}
|
|
||||||
notification.setImage(image);
|
notification.setImage(image);
|
||||||
} else {
|
|
||||||
notification.unsetImage();
|
let hasDefaultAction = false;
|
||||||
}
|
|
||||||
|
|
||||||
if (actions.length) {
|
if (actions.length) {
|
||||||
notification.setUseActionIcons(hints['action-icons'] == true);
|
let useActionIcons = (hints['action-icons'] == true);
|
||||||
|
|
||||||
for (let i = 0; i < actions.length - 1; i += 2) {
|
for (let i = 0; i < actions.length - 1; i += 2) {
|
||||||
if (actions[i] == 'default')
|
let [actionId, label] = [actions[i], actions[i+1]];
|
||||||
notification.connect('clicked', Lang.bind(this,
|
if (actionId == 'default') {
|
||||||
function() {
|
hasDefaultAction = true;
|
||||||
this._emitActionInvoked(ndata.id, "default");
|
} else {
|
||||||
|
notification.addButton(this._makeButton(actionId, label, useActionIcons), Lang.bind(this, function() {
|
||||||
|
this._emitActionInvoked(ndata.id, actionId);
|
||||||
}));
|
}));
|
||||||
else
|
|
||||||
notification.addButton(actions[i], actions[i + 1]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDefaultAction) {
|
||||||
|
notification.connect('clicked', Lang.bind(this, function() {
|
||||||
|
this._emitActionInvoked(ndata.id, 'default');
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
notification.connect('clicked', Lang.bind(this, function() {
|
||||||
|
source.open();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
switch (hints.urgency) {
|
switch (hints.urgency) {
|
||||||
case Urgency.LOW:
|
case Urgency.LOW:
|
||||||
notification.setUrgency(MessageTray.Urgency.LOW);
|
notification.setUrgency(MessageTray.Urgency.LOW);
|
||||||
@@ -435,7 +446,7 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
||||||
notification.setTransient(hints['transient'] == true);
|
notification.setTransient(hints['transient'] == true);
|
||||||
|
|
||||||
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
|
let sourceGIcon = source.useNotificationIcon ? gicon : null;
|
||||||
source.processNotification(notification, sourceGIcon);
|
source.processNotification(notification, sourceGIcon);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -459,7 +470,7 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
// 'icon-multi',
|
// 'icon-multi',
|
||||||
'icon-static',
|
'icon-static',
|
||||||
'persistence',
|
'persistence',
|
||||||
// 'sound',
|
'sound',
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -511,16 +522,26 @@ const NotificationDaemon = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const Source = new Lang.Class({
|
const FdoNotificationDaemonSource = new Lang.Class({
|
||||||
Name: 'NotificationDaemonSource',
|
Name: 'FdoNotificationDaemonSource',
|
||||||
Extends: MessageTray.Source,
|
Extends: MessageTray.Source,
|
||||||
|
|
||||||
_init: function(title, pid, sender, trayIcon) {
|
_init: function(title, pid, sender, trayIcon, appId) {
|
||||||
|
// Need to set the app before chaining up, so
|
||||||
|
// methods called from the parent constructor can find it
|
||||||
|
this.trayIcon = trayIcon;
|
||||||
|
this.pid = pid;
|
||||||
|
this.app = this._getApp(appId);
|
||||||
|
|
||||||
this.parent(title);
|
this.parent(title);
|
||||||
|
|
||||||
this.initialTitle = title;
|
this.initialTitle = title;
|
||||||
|
|
||||||
this.pid = pid;
|
if (this.app)
|
||||||
|
this.title = this.app.get_name();
|
||||||
|
else
|
||||||
|
this.useNotificationIcon = true;
|
||||||
|
|
||||||
if (sender)
|
if (sender)
|
||||||
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
|
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
|
||||||
Gio.BusNameWatcherFlags.NONE,
|
Gio.BusNameWatcherFlags.NONE,
|
||||||
@@ -529,19 +550,22 @@ const Source = new Lang.Class({
|
|||||||
else
|
else
|
||||||
this._nameWatcherId = 0;
|
this._nameWatcherId = 0;
|
||||||
|
|
||||||
this._setApp();
|
|
||||||
if (this.app)
|
|
||||||
this.title = this.app.get_name();
|
|
||||||
else
|
|
||||||
this.useNotificationIcon = true;
|
|
||||||
|
|
||||||
this.trayIcon = trayIcon;
|
|
||||||
if (this.trayIcon) {
|
if (this.trayIcon) {
|
||||||
|
// Try again finding the app, using the WM_CLASS from the tray icon
|
||||||
this._setSummaryIcon(this.trayIcon);
|
this._setSummaryIcon(this.trayIcon);
|
||||||
this.useNotificationIcon = false;
|
this.useNotificationIcon = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_createPolicy: function() {
|
||||||
|
if (this.app && this.app.get_app_info()) {
|
||||||
|
let id = this.app.get_id().replace(/\.desktop$/,'');
|
||||||
|
return new MessageTray.NotificationApplicationPolicy(id);
|
||||||
|
} else {
|
||||||
|
return new MessageTray.NotificationGenericPolicy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_onNameVanished: function() {
|
_onNameVanished: function() {
|
||||||
// Destroy the notification source when its sender is removed from DBus.
|
// Destroy the notification source when its sender is removed from DBus.
|
||||||
// Only do so if this.app is set to avoid removing "notify-send" sources, senders
|
// Only do so if this.app is set to avoid removing "notify-send" sources, senders
|
||||||
@@ -565,24 +589,22 @@ const Source = new Lang.Class({
|
|||||||
this.notify(notification);
|
this.notify(notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSummaryClick: function() {
|
handleSummaryClick: function(button) {
|
||||||
if (!this.trayIcon)
|
if (!this.trayIcon)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let event = Clutter.get_current_event();
|
let event = Clutter.get_current_event();
|
||||||
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Left clicks are passed through only where there aren't unacknowledged
|
// Left clicks are passed through only where there aren't unacknowledged
|
||||||
// notifications, so it possible to open them in summary mode; right
|
// notifications, so it possible to open them in summary mode; right
|
||||||
// clicks are always forwarded, as the right click menu is not useful for
|
// clicks are always forwarded, as the right click menu is not useful for
|
||||||
// tray icons
|
// tray icons
|
||||||
if (event.get_button() == 1 &&
|
if (button == 1 &&
|
||||||
this.notifications.length > 0)
|
this.notifications.length > 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
|
let id = global.stage.connect('deactivate', Lang.bind(this, function () {
|
||||||
global.disconnect(id);
|
global.stage.disconnect(id);
|
||||||
this.trayIcon.click(event);
|
this.trayIcon.click(event);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -590,7 +612,7 @@ const Source = new Lang.Class({
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_getApp: function() {
|
_getApp: function(appId) {
|
||||||
let app;
|
let app;
|
||||||
|
|
||||||
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
||||||
@@ -598,7 +620,17 @@ const Source = new Lang.Class({
|
|||||||
return app;
|
return app;
|
||||||
|
|
||||||
if (this.trayIcon) {
|
if (this.trayIcon) {
|
||||||
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
|
app = Shell.AppSystem.get_default().lookup_startup_wmclass(this.trayIcon.wm_class);
|
||||||
|
if (app != null)
|
||||||
|
return app;
|
||||||
|
|
||||||
|
app = Shell.AppSystem.get_default().lookup_desktop_wmclass(this.trayIcon.wm_class);
|
||||||
|
if (app != null)
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appId) {
|
||||||
|
app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
|
||||||
if (app != null)
|
if (app != null)
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
@@ -606,22 +638,6 @@ const Source = new Lang.Class({
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_setApp: function() {
|
|
||||||
if (this.app)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.app = this._getApp();
|
|
||||||
if (!this.app)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Only override the icon if we were previously using
|
|
||||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
|
||||||
if (!this.trayIcon) {
|
|
||||||
this.useNotificationIcon = false;
|
|
||||||
this.iconUpdated();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setTitle: function(title) {
|
setTitle: function(title) {
|
||||||
// Do nothing if .app is set, we don't want to override the
|
// Do nothing if .app is set, we don't want to override the
|
||||||
// app name with whatever is provided through libnotify (usually
|
// app name with whatever is provided through libnotify (usually
|
||||||
@@ -632,9 +648,9 @@ const Source = new Lang.Class({
|
|||||||
this.parent(title);
|
this.parent(title);
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function(notification) {
|
open: function() {
|
||||||
this.destroyNonResidentNotifications();
|
|
||||||
this.openApp();
|
this.openApp();
|
||||||
|
this.destroyNonResidentNotifications();
|
||||||
},
|
},
|
||||||
|
|
||||||
_lastNotificationRemoved: function() {
|
_lastNotificationRemoved: function() {
|
||||||
@@ -646,11 +662,8 @@ const Source = new Lang.Class({
|
|||||||
if (this.app == null)
|
if (this.app == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let windows = this.app.get_windows();
|
this.app.activate();
|
||||||
if (windows.length > 0) {
|
Main.overview.hide();
|
||||||
let mostRecentWindow = windows[0];
|
|
||||||
Main.activateWindow(mostRecentWindow);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
@@ -677,3 +690,276 @@ const Source = new Lang.Class({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const GtkNotificationDaemonNotification = new Lang.Class({
|
||||||
|
Name: 'GtkNotificationDaemonNotification',
|
||||||
|
Extends: MessageTray.Notification,
|
||||||
|
|
||||||
|
_init: function(source, notification) {
|
||||||
|
this.parent(source);
|
||||||
|
this._serialized = GLib.Variant.new('a{sv}', notification);
|
||||||
|
|
||||||
|
let { "title": title,
|
||||||
|
"body": body,
|
||||||
|
"icon": gicon,
|
||||||
|
"urgent": urgent,
|
||||||
|
"buttons": buttons,
|
||||||
|
"default-action": defaultAction,
|
||||||
|
"default-action-target": defaultActionTarget } = notification;
|
||||||
|
|
||||||
|
this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL
|
||||||
|
: MessageTray.Urgency.NORMAL);
|
||||||
|
|
||||||
|
if (buttons) {
|
||||||
|
buttons.deep_unpack().forEach(Lang.bind(this, function(button) {
|
||||||
|
this.addAction(button.label.unpack(),
|
||||||
|
Lang.bind(this, this._onButtonClicked, button));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._defaultAction = defaultAction ? defaultAction.unpack() : null;
|
||||||
|
this._defaultActionTarget = defaultActionTarget;
|
||||||
|
|
||||||
|
this.update(title.unpack(), body ? body.unpack() : null,
|
||||||
|
{ gicon: gicon ? Gio.icon_deserialize(gicon) : null });
|
||||||
|
},
|
||||||
|
|
||||||
|
_activateAction: function(namespacedActionId, target) {
|
||||||
|
if (namespacedActionId) {
|
||||||
|
if (namespacedActionId.startsWith('app.')) {
|
||||||
|
let actionId = namespacedActionId.slice('app.'.length);
|
||||||
|
this.source.activateAction(actionId, target);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.source.open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onButtonClicked: function(button) {
|
||||||
|
let { 'action': action, 'target': actionTarget } = button;
|
||||||
|
this._activateAction(action.unpack(), actionTarget);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onClicked: function() {
|
||||||
|
this._activateAction(this._defaultAction, this._defaultActionTarget);
|
||||||
|
this.parent();
|
||||||
|
},
|
||||||
|
|
||||||
|
serialize: function() {
|
||||||
|
return this._serialized;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const FdoApplicationIface = '<node> \
|
||||||
|
<interface name="org.freedesktop.Application"> \
|
||||||
|
<method name="ActivateAction"> \
|
||||||
|
<arg type="s" direction="in" /> \
|
||||||
|
<arg type="av" direction="in" /> \
|
||||||
|
<arg type="a{sv}" direction="in" /> \
|
||||||
|
</method> \
|
||||||
|
<method name="Activate"> \
|
||||||
|
<arg type="a{sv}" direction="in" /> \
|
||||||
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface);
|
||||||
|
|
||||||
|
function objectPathFromAppId(appId) {
|
||||||
|
return '/' + appId.replace(/\./g, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlatformData() {
|
||||||
|
let startupId = GLib.Variant.new('s', '_TIME' + global.get_current_time());
|
||||||
|
return { "desktop-startup-id": startupId };
|
||||||
|
}
|
||||||
|
|
||||||
|
function InvalidAppError() {}
|
||||||
|
|
||||||
|
const GtkNotificationDaemonAppSource = new Lang.Class({
|
||||||
|
Name: 'GtkNotificationDaemonAppSource',
|
||||||
|
Extends: MessageTray.Source,
|
||||||
|
|
||||||
|
_init: function(appId) {
|
||||||
|
this._appId = appId;
|
||||||
|
this._objectPath = objectPathFromAppId(appId);
|
||||||
|
|
||||||
|
this._app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
|
||||||
|
if (!this._app)
|
||||||
|
throw new InvalidAppError();
|
||||||
|
|
||||||
|
this._notifications = {};
|
||||||
|
|
||||||
|
this.parent(this._app.get_name());
|
||||||
|
},
|
||||||
|
|
||||||
|
createIcon: function(size) {
|
||||||
|
return this._app.create_icon_texture(size);
|
||||||
|
},
|
||||||
|
|
||||||
|
_createPolicy: function() {
|
||||||
|
return new MessageTray.NotificationApplicationPolicy(this._appId);
|
||||||
|
},
|
||||||
|
|
||||||
|
_createApp: function() {
|
||||||
|
return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath);
|
||||||
|
},
|
||||||
|
|
||||||
|
activateAction: function(actionId, target) {
|
||||||
|
let app = this._createApp();
|
||||||
|
app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
|
||||||
|
},
|
||||||
|
|
||||||
|
open: function() {
|
||||||
|
let app = this._createApp();
|
||||||
|
app.ActivateRemote(getPlatformData());
|
||||||
|
},
|
||||||
|
|
||||||
|
addNotification: function(notificationId, notificationParams, showBanner) {
|
||||||
|
if (this._notifications[notificationId])
|
||||||
|
this._notifications[notificationId].destroy();
|
||||||
|
|
||||||
|
let notification = new GtkNotificationDaemonNotification(this, notificationParams);
|
||||||
|
notification.connect('destroy', Lang.bind(this, function() {
|
||||||
|
delete this._notifications[notificationId];
|
||||||
|
}));
|
||||||
|
this._notifications[notificationId] = notification;
|
||||||
|
|
||||||
|
if (showBanner)
|
||||||
|
this.notify(notification);
|
||||||
|
else
|
||||||
|
this.pushNotification(notification);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeNotification: function(notificationId) {
|
||||||
|
if (this._notifications[notificationId])
|
||||||
|
this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
|
||||||
|
},
|
||||||
|
|
||||||
|
serialize: function() {
|
||||||
|
let notifications = [];
|
||||||
|
for (let notificationId in this._notifications) {
|
||||||
|
let notification = this._notifications[notificationId];
|
||||||
|
notifications.push([notificationId, notification.serialize()]);
|
||||||
|
}
|
||||||
|
return [this._appId, notifications];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const GtkNotificationsIface = '<node> \
|
||||||
|
<interface name="org.gtk.Notifications"> \
|
||||||
|
<method name="AddNotification"> \
|
||||||
|
<arg type="s" direction="in" /> \
|
||||||
|
<arg type="s" direction="in" /> \
|
||||||
|
<arg type="a{sv}" direction="in" /> \
|
||||||
|
</method> \
|
||||||
|
<method name="RemoveNotification"> \
|
||||||
|
<arg type="s" direction="in" /> \
|
||||||
|
<arg type="s" direction="in" /> \
|
||||||
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
|
const GtkNotificationDaemon = new Lang.Class({
|
||||||
|
Name: 'GtkNotificationDaemon',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._sources = {};
|
||||||
|
|
||||||
|
this._loadNotifications();
|
||||||
|
|
||||||
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GtkNotificationsIface, this);
|
||||||
|
this._dbusImpl.export(Gio.DBus.session, '/org/gtk/Notifications');
|
||||||
|
|
||||||
|
Gio.DBus.session.own_name('org.gtk.Notifications', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureAppSource: function(appId) {
|
||||||
|
if (this._sources[appId])
|
||||||
|
return this._sources[appId];
|
||||||
|
|
||||||
|
let source = new GtkNotificationDaemonAppSource(appId);
|
||||||
|
|
||||||
|
source.connect('destroy', Lang.bind(this, function() {
|
||||||
|
delete this._sources[appId];
|
||||||
|
this._saveNotifications();
|
||||||
|
}));
|
||||||
|
source.connect('count-updated', Lang.bind(this, this._saveNotifications));
|
||||||
|
Main.messageTray.add(source);
|
||||||
|
this._sources[appId] = source;
|
||||||
|
return source;
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadNotifications: function() {
|
||||||
|
this._isLoading = true;
|
||||||
|
|
||||||
|
let value = global.get_persistent_state('a(sa(sv))', 'notifications');
|
||||||
|
if (value) {
|
||||||
|
let sources = value.deep_unpack();
|
||||||
|
sources.forEach(Lang.bind(this, function([appId, notifications]) {
|
||||||
|
if (notifications.length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let source;
|
||||||
|
try {
|
||||||
|
source = this._ensureAppSource(appId);
|
||||||
|
} catch(e if e instanceof InvalidAppError) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifications.forEach(function([notificationId, notification]) {
|
||||||
|
source.addNotification(notificationId, notification.deep_unpack(), false);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isLoading = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_saveNotifications: function() {
|
||||||
|
if (this._isLoading)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let sources = [];
|
||||||
|
for (let appId in this._sources) {
|
||||||
|
let source = this._sources[appId];
|
||||||
|
sources.push(source.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
global.set_persistent_state('notifications', new GLib.Variant('a(sa(sv))', sources));
|
||||||
|
},
|
||||||
|
|
||||||
|
AddNotificationAsync: function(params, invocation) {
|
||||||
|
let [appId, notificationId, notification] = params;
|
||||||
|
|
||||||
|
let source;
|
||||||
|
try {
|
||||||
|
source = this._ensureAppSource(appId);
|
||||||
|
} catch(e if e instanceof InvalidAppError) {
|
||||||
|
invocation.return_dbus_error('org.gtk.Notifications.InvalidApp', 'The app by ID "%s" could not be found'.format(appId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
source.addNotification(notificationId, notification, true);
|
||||||
|
|
||||||
|
invocation.return_value(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
RemoveNotificationAsync: function(params, invocation) {
|
||||||
|
let [appId, notificationId] = params;
|
||||||
|
let source = this._sources[appId];
|
||||||
|
if (source)
|
||||||
|
source.removeNotification(notificationId);
|
||||||
|
|
||||||
|
invocation.return_value(null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const NotificationDaemon = new Lang.Class({
|
||||||
|
Name: 'NotificationDaemon',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._fdoNotificationDaemon = new FdoNotificationDaemon();
|
||||||
|
this._gtkNotificationDaemon = new GtkNotificationDaemon();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
240
js/ui/osdWindow.js
Normal file
240
js/ui/osdWindow.js
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Layout = imports.ui.layout;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Mainloop = imports.mainloop;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
|
||||||
|
const HIDE_TIMEOUT = 1500;
|
||||||
|
const FADE_TIME = 0.1;
|
||||||
|
const LEVEL_ANIMATION_TIME = 0.1;
|
||||||
|
|
||||||
|
const LevelBar = new Lang.Class({
|
||||||
|
Name: 'LevelBar',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._level = 0;
|
||||||
|
|
||||||
|
this.actor = new St.Bin({ style_class: 'level',
|
||||||
|
x_fill: true, y_fill: true });
|
||||||
|
this._bar = new St.DrawingArea();
|
||||||
|
this._bar.connect('repaint', Lang.bind(this, this._repaint));
|
||||||
|
|
||||||
|
this.actor.set_child(this._bar);
|
||||||
|
},
|
||||||
|
|
||||||
|
get level() {
|
||||||
|
return this._level;
|
||||||
|
},
|
||||||
|
|
||||||
|
set level(value) {
|
||||||
|
let newValue = Math.max(0, Math.min(value, 100));
|
||||||
|
if (newValue == this._level)
|
||||||
|
return;
|
||||||
|
this._level = newValue;
|
||||||
|
this._bar.queue_repaint();
|
||||||
|
},
|
||||||
|
|
||||||
|
_repaint: function() {
|
||||||
|
let cr = this._bar.get_context();
|
||||||
|
|
||||||
|
let node = this.actor.get_theme_node();
|
||||||
|
let radius = node.get_border_radius(0); // assume same radius for all corners
|
||||||
|
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
|
||||||
|
|
||||||
|
let [w, h] = this._bar.get_surface_size();
|
||||||
|
w *= (this._level / 100.);
|
||||||
|
|
||||||
|
if (w == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cr.moveTo(radius, 0);
|
||||||
|
if (w >= radius)
|
||||||
|
cr.arc(w - radius, radius, radius, 1.5 * Math.PI, 2. * Math.PI);
|
||||||
|
else
|
||||||
|
cr.lineTo(w, 0);
|
||||||
|
if (w >= radius)
|
||||||
|
cr.arc(w - radius, h - radius, radius, 0, 0.5 * Math.PI);
|
||||||
|
else
|
||||||
|
cr.lineTo(w, h);
|
||||||
|
cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI);
|
||||||
|
cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI);
|
||||||
|
cr.fill();
|
||||||
|
cr.$dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const OsdWindow = new Lang.Class({
|
||||||
|
Name: 'OsdWindow',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._popupSize = 0;
|
||||||
|
this.actor = new St.Widget({ x_expand: true,
|
||||||
|
y_expand: true,
|
||||||
|
x_align: Clutter.ActorAlign.CENTER,
|
||||||
|
y_align: Clutter.ActorAlign.CENTER });
|
||||||
|
this._currentMonitor = undefined;
|
||||||
|
this.setMonitor (-1);
|
||||||
|
this._box = new St.BoxLayout({ style_class: 'osd-window',
|
||||||
|
vertical: true });
|
||||||
|
this.actor.add_actor(this._box);
|
||||||
|
|
||||||
|
this._box.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||||
|
this._box.connect('notify::height', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this._box.width = this._box.height;
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._icon = new St.Icon();
|
||||||
|
this._box.add(this._icon, { expand: true });
|
||||||
|
|
||||||
|
this._label = new St.Label();
|
||||||
|
this._box.add(this._label);
|
||||||
|
|
||||||
|
this._level = new LevelBar();
|
||||||
|
this._box.add(this._level.actor);
|
||||||
|
|
||||||
|
this._hideTimeoutId = 0;
|
||||||
|
this._reset();
|
||||||
|
|
||||||
|
Main.layoutManager.connect('monitors-changed',
|
||||||
|
Lang.bind(this, this._monitorsChanged));
|
||||||
|
this._monitorsChanged();
|
||||||
|
|
||||||
|
Main.layoutManager.osdGroup.add_child(this.actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
setIcon: function(icon) {
|
||||||
|
this._icon.gicon = icon;
|
||||||
|
},
|
||||||
|
|
||||||
|
setLabel: function(label) {
|
||||||
|
this._label.visible = (label != undefined);
|
||||||
|
if (label)
|
||||||
|
this._label.text = label;
|
||||||
|
},
|
||||||
|
|
||||||
|
setLevel: function(level) {
|
||||||
|
this._level.actor.visible = (level != undefined);
|
||||||
|
if (level) {
|
||||||
|
if (this.actor.visible)
|
||||||
|
Tweener.addTween(this._level,
|
||||||
|
{ level: level,
|
||||||
|
time: LEVEL_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
else
|
||||||
|
this._level.level = level;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function() {
|
||||||
|
if (!this._icon.gicon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this.actor.visible) {
|
||||||
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
|
this.actor.show();
|
||||||
|
this.actor.opacity = 0;
|
||||||
|
this.actor.get_parent().set_child_above_sibling(this.actor, null);
|
||||||
|
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ opacity: 255,
|
||||||
|
time: FADE_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._hideTimeoutId)
|
||||||
|
Mainloop.source_remove(this._hideTimeoutId);
|
||||||
|
this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
|
||||||
|
Lang.bind(this, this._hide));
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: function() {
|
||||||
|
if (!this._hideTimeoutId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Mainloop.source_remove(this._hideTimeoutId);
|
||||||
|
this._hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_hide: function() {
|
||||||
|
this._hideTimeoutId = 0;
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: FADE_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function() {
|
||||||
|
this._reset();
|
||||||
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
|
},
|
||||||
|
|
||||||
|
_reset: function() {
|
||||||
|
this.actor.hide();
|
||||||
|
this.setLabel(null);
|
||||||
|
this.setLevel(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
_monitorsChanged: function() {
|
||||||
|
/* assume 110x110 on a 640x480 display and scale from there */
|
||||||
|
let monitor;
|
||||||
|
|
||||||
|
if (this._currentMonitor >= 0)
|
||||||
|
monitor = Main.layoutManager.monitors[this._currentMonitor];
|
||||||
|
else
|
||||||
|
monitor = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
|
let scalew = monitor.width / 640.0;
|
||||||
|
let scaleh = monitor.height / 480.0;
|
||||||
|
let scale = Math.min(scalew, scaleh);
|
||||||
|
this._popupSize = 110 * Math.max(1, scale);
|
||||||
|
|
||||||
|
this._box.translation_y = monitor.height / 4;
|
||||||
|
this._icon.icon_size = this._popupSize / 2;
|
||||||
|
this._box.style_changed();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onStyleChanged: function() {
|
||||||
|
let themeNode = this._box.get_theme_node();
|
||||||
|
let horizontalPadding = themeNode.get_horizontal_padding();
|
||||||
|
let verticalPadding = themeNode.get_vertical_padding();
|
||||||
|
let topBorder = themeNode.get_border_width(St.Side.TOP);
|
||||||
|
let bottomBorder = themeNode.get_border_width(St.Side.BOTTOM);
|
||||||
|
let leftBorder = themeNode.get_border_width(St.Side.LEFT);
|
||||||
|
let rightBorder = themeNode.get_border_width(St.Side.RIGHT);
|
||||||
|
|
||||||
|
let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder;
|
||||||
|
let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
|
||||||
|
|
||||||
|
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight));
|
||||||
|
},
|
||||||
|
|
||||||
|
setMonitor: function(index) {
|
||||||
|
let constraint;
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
index = -1;
|
||||||
|
if (this._currentMonitor == index)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
constraint = new Layout.MonitorConstraint({ primary: true });
|
||||||
|
else
|
||||||
|
constraint = new Layout.MonitorConstraint({ index: index });
|
||||||
|
|
||||||
|
this.actor.clear_constraints();
|
||||||
|
this.actor.add_constraint(constraint);
|
||||||
|
this._currentMonitor = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
@@ -10,38 +11,28 @@ const St = imports.gi.St;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const Gdk = imports.gi.Gdk;
|
const Gdk = imports.gi.Gdk;
|
||||||
|
|
||||||
const Dash = imports.ui.dash;
|
const Background = imports.ui.background;
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
|
const LayoutManager = imports.ui.layout;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
|
const OverviewControls = imports.ui.overviewControls;
|
||||||
const Panel = imports.ui.panel;
|
const Panel = imports.ui.panel;
|
||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
const ViewSelector = imports.ui.viewSelector;
|
|
||||||
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||||
|
|
||||||
// Time for initial animation going into Overview mode
|
// Time for initial animation going into Overview mode
|
||||||
const ANIMATION_TIME = 0.25;
|
const ANIMATION_TIME = 0.25;
|
||||||
|
|
||||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
|
// Must be less than ANIMATION_TIME, since we switch to
|
||||||
|
// or from the overview completely after ANIMATION_TIME,
|
||||||
|
// and don't want the shading animation to get cut off
|
||||||
|
const SHADE_ANIMATION_TIME = .20;
|
||||||
|
|
||||||
const GLSL_DIM_EFFECT_DECLARATIONS = '';
|
const DND_WINDOW_SWITCH_TIMEOUT = 750;
|
||||||
const GLSL_DIM_EFFECT_CODE = '\
|
|
||||||
vec2 dist = cogl_tex_coord_in[0].xy - vec2(0.5, 0.5); \
|
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
|
||||||
float elipse_radius = 0.5; \
|
|
||||||
/* from https://bugzilla.gnome.org/show_bug.cgi?id=669798: \
|
|
||||||
the alpha on the gradient goes from 250 at its darkest to 180 at its most transparent. */ \
|
|
||||||
float y = 250.0 / 255.0; \
|
|
||||||
float x = 180.0 / 255.0; \
|
|
||||||
/* interpolate darkening value, based on distance from screen center */ \
|
|
||||||
float val = min(length(dist), elipse_radius); \
|
|
||||||
float a = mix(x, y, val / elipse_radius); \
|
|
||||||
/* dim_factor varies from [1.0 -> 0.5] when overview is showing \
|
|
||||||
We use it to smooth value, then we clamp it to valid color interval */ \
|
|
||||||
a = clamp(a - cogl_color_in.r + 0.5, 0.0, 1.0); \
|
|
||||||
/* We\'re blending between: color and black color (obviously omitted in the equation) */ \
|
|
||||||
cogl_color_out.xyz = cogl_color_out.xyz * (1.0 - a); \
|
|
||||||
cogl_color_out.a = 1.0;';
|
|
||||||
|
|
||||||
const ShellInfo = new Lang.Class({
|
const ShellInfo = new Lang.Class({
|
||||||
Name: 'ShellInfo',
|
Name: 'ShellInfo',
|
||||||
@@ -88,10 +79,8 @@ const ShellInfo = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._undoCallback = undoCallback;
|
this._undoCallback = undoCallback;
|
||||||
if (undoCallback) {
|
if (undoCallback)
|
||||||
notification.addButton('system-undo', _("Undo"));
|
notification.addAction(_("Undo"), Lang.bind(this, this._onUndoClicked));
|
||||||
notification.connect('action-invoked', Lang.bind(this, this._onUndoClicked));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._source.notify(notification);
|
this._source.notify(notification);
|
||||||
}
|
}
|
||||||
@@ -117,52 +106,52 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
this._overviewCreated = true;
|
this._overviewCreated = true;
|
||||||
|
|
||||||
// The main BackgroundActor is inside global.window_group which is
|
// The main Background actors are inside global.window_group which are
|
||||||
// hidden when displaying the overview, so we create a new
|
// hidden when displaying the overview, so we create a new
|
||||||
// one. Instances of this class share a single CoglTexture behind the
|
// one. Instances of this class share a single CoglTexture behind the
|
||||||
// scenes which allows us to show the background with different
|
// scenes which allows us to show the background with different
|
||||||
// rendering options without duplicating the texture data.
|
// rendering options without duplicating the texture data.
|
||||||
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
let monitor = Main.layoutManager.primaryMonitor;
|
||||||
this._background.add_glsl_snippet(Meta.SnippetHook.FRAGMENT,
|
|
||||||
GLSL_DIM_EFFECT_DECLARATIONS,
|
|
||||||
GLSL_DIM_EFFECT_CODE,
|
|
||||||
false);
|
|
||||||
this._background.hide();
|
|
||||||
global.overlay_group.add_actor(this._background);
|
|
||||||
|
|
||||||
this._desktopFade = new St.Bin();
|
let layout = new Clutter.BinLayout();
|
||||||
global.overlay_group.add_actor(this._desktopFade);
|
this._stack = new Clutter.Actor({ layout_manager: layout });
|
||||||
|
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
|
||||||
|
|
||||||
/* Translators: This is the main view to select
|
/* Translators: This is the main view to select
|
||||||
activities. See also note for "Activities" string. */
|
activities. See also note for "Activities" string. */
|
||||||
this._overview = new St.BoxLayout({ name: 'overview',
|
this._overview = new St.BoxLayout({ name: 'overview',
|
||||||
accessible_name: _("Overview"),
|
accessible_name: _("Overview"),
|
||||||
reactive: true,
|
reactive: true,
|
||||||
vertical: true });
|
vertical: true,
|
||||||
|
x_expand: true,
|
||||||
|
y_expand: true });
|
||||||
this._overview._delegate = this;
|
this._overview._delegate = this;
|
||||||
|
|
||||||
this._group = new St.BoxLayout({ name: 'overview-group' });
|
this._backgroundGroup = new Meta.BackgroundGroup();
|
||||||
|
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
|
||||||
|
this._bgManagers = [];
|
||||||
|
|
||||||
this._capturedEventId = 0;
|
this._desktopFade = new St.Widget();
|
||||||
this._buttonPressId = 0;
|
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
|
||||||
|
|
||||||
|
this._activationTime = 0;
|
||||||
|
|
||||||
this.visible = false; // animating to overview, in overview, animating out
|
this.visible = false; // animating to overview, in overview, animating out
|
||||||
this._shown = false; // show() and not hide()
|
this._shown = false; // show() and not hide()
|
||||||
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
|
|
||||||
this._modal = false; // have a modal grab
|
this._modal = false; // have a modal grab
|
||||||
this.animationInProgress = false;
|
this.animationInProgress = false;
|
||||||
this._hideInProgress = false;
|
this.visibleTarget = false;
|
||||||
|
|
||||||
// During transitions, we raise this to the top to avoid having the overview
|
// During transitions, we raise this to the top to avoid having the overview
|
||||||
// area be reactive; it causes too many issues such as double clicks on
|
// area be reactive; it causes too many issues such as double clicks on
|
||||||
// Dash elements, or mouseover handlers in the workspaces.
|
// Dash elements, or mouseover handlers in the workspaces.
|
||||||
this._coverPane = new Clutter.Rectangle({ opacity: 0,
|
this._coverPane = new Clutter.Actor({ opacity: 0,
|
||||||
reactive: true });
|
reactive: true });
|
||||||
this._overview.add_actor(this._coverPane);
|
Main.layoutManager.overviewGroup.add_child(this._coverPane);
|
||||||
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
|
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return Clutter.EVENT_STOP; }));
|
||||||
|
|
||||||
this._overview.hide();
|
this._stack.add_actor(this._overview);
|
||||||
global.overlay_group.add_actor(this._overview);
|
Main.layoutManager.overviewGroup.add_child(this._stack);
|
||||||
|
|
||||||
this._coverPane.hide();
|
this._coverPane.hide();
|
||||||
|
|
||||||
@@ -186,6 +175,56 @@ const Overview = new Lang.Class({
|
|||||||
this.init();
|
this.init();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_updateBackgrounds: function() {
|
||||||
|
for (let i = 0; i < this._bgManagers.length; i++)
|
||||||
|
this._bgManagers[i].destroy();
|
||||||
|
|
||||||
|
this._bgManagers = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
|
||||||
|
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
|
||||||
|
monitorIndex: i,
|
||||||
|
effects: Meta.BackgroundEffects.VIGNETTE });
|
||||||
|
this._bgManagers.push(bgManager);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_unshadeBackgrounds: function() {
|
||||||
|
let backgrounds = this._backgroundGroup.get_children();
|
||||||
|
for (let i = 0; i < backgrounds.length; i++) {
|
||||||
|
let background = backgrounds[i]._delegate;
|
||||||
|
|
||||||
|
Tweener.addTween(background,
|
||||||
|
{ brightness: 1.0,
|
||||||
|
time: SHADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
Tweener.addTween(background,
|
||||||
|
{ vignetteSharpness: 0.0,
|
||||||
|
time: SHADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_shadeBackgrounds: function() {
|
||||||
|
let backgrounds = this._backgroundGroup.get_children();
|
||||||
|
for (let i = 0; i < backgrounds.length; i++) {
|
||||||
|
let background = backgrounds[i]._delegate;
|
||||||
|
|
||||||
|
Tweener.addTween(background,
|
||||||
|
{ brightness: 0.8,
|
||||||
|
time: SHADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
Tweener.addTween(background,
|
||||||
|
{ vignetteSharpness: 0.7,
|
||||||
|
time: SHADE_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_sessionUpdated: function() {
|
_sessionUpdated: function() {
|
||||||
this.isDummy = !Main.sessionMode.hasOverview;
|
this.isDummy = !Main.sessionMode.hasOverview;
|
||||||
this._createOverview();
|
this._createOverview();
|
||||||
@@ -215,53 +254,41 @@ const Overview = new Lang.Class({
|
|||||||
in the search entry when no search is
|
in the search entry when no search is
|
||||||
active; it should not exceed ~30
|
active; it should not exceed ~30
|
||||||
characters. */
|
characters. */
|
||||||
hint_text: _("Type to search..."),
|
hint_text: _("Type to search…"),
|
||||||
track_hover: true,
|
track_hover: true,
|
||||||
can_focus: true });
|
can_focus: true });
|
||||||
this._searchEntryBin = new St.Bin({ child: this._searchEntry,
|
this._searchEntryBin = new St.Bin({ child: this._searchEntry,
|
||||||
x_align: St.Align.MIDDLE });
|
x_align: St.Align.MIDDLE });
|
||||||
this._overview.add_actor(this._searchEntryBin);
|
this._overview.add_actor(this._searchEntryBin);
|
||||||
|
|
||||||
|
// Create controls
|
||||||
|
this._controls = new OverviewControls.ControlsManager(this._searchEntry);
|
||||||
|
this._dash = this._controls.dash;
|
||||||
|
this.viewSelector = this._controls.viewSelector;
|
||||||
|
|
||||||
|
// Add our same-line elements after the search entry
|
||||||
|
this._overview.add(this._controls.actor, { y_fill: true, expand: true });
|
||||||
|
this._controls.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||||
|
|
||||||
|
this._stack.add_actor(this._controls.indicatorActor);
|
||||||
|
|
||||||
// TODO - recalculate everything when desktop size changes
|
// TODO - recalculate everything when desktop size changes
|
||||||
this._dash = new Dash.Dash();
|
|
||||||
this._group.add_actor(this._dash.actor);
|
|
||||||
this.dashIconSize = this._dash.iconSize;
|
this.dashIconSize = this._dash.iconSize;
|
||||||
this._dash.connect('icon-size-changed',
|
this._dash.connect('icon-size-changed',
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
this.dashIconSize = this._dash.iconSize;
|
this.dashIconSize = this._dash.iconSize;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Translators: this is the name of the dock/favorites area on
|
|
||||||
// the left of the overview
|
|
||||||
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
|
|
||||||
|
|
||||||
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
|
|
||||||
this._dash.showAppsButton);
|
|
||||||
this._group.add(this._viewSelector.actor, { x_fill: true,
|
|
||||||
expand: true });
|
|
||||||
|
|
||||||
// Add our same-line elements after the search entry
|
|
||||||
this._overview.add(this._group, { y_fill: true,
|
|
||||||
expand: true });
|
|
||||||
|
|
||||||
// Then account for message tray
|
|
||||||
this._messageTrayGhost = new St.Bin({ style_class: 'message-tray-summary',
|
|
||||||
reactive: false,
|
|
||||||
opacity: 0,
|
|
||||||
x_fill: true,
|
|
||||||
y_fill: true });
|
|
||||||
this._overview.add_actor(this._messageTrayGhost);
|
|
||||||
|
|
||||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
||||||
this._relayout();
|
this._relayout();
|
||||||
},
|
},
|
||||||
|
|
||||||
addSearchProvider: function(provider) {
|
addSearchProvider: function(provider) {
|
||||||
this._viewSelector.addSearchProvider(provider);
|
this.viewSelector.addSearchProvider(provider);
|
||||||
},
|
},
|
||||||
|
|
||||||
removeSearchProvider: function(provider) {
|
removeSearchProvider: function(provider) {
|
||||||
this._viewSelector.removeSearchProvider(provider);
|
this.viewSelector.removeSearchProvider(provider);
|
||||||
},
|
},
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -277,18 +304,22 @@ const Overview = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onDragBegin: function() {
|
_onDragBegin: function() {
|
||||||
|
this._inXdndDrag = true;
|
||||||
|
|
||||||
DND.addDragMonitor(this._dragMonitor);
|
DND.addDragMonitor(this._dragMonitor);
|
||||||
// Remember the workspace we started from
|
// Remember the workspace we started from
|
||||||
this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
|
this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragEnd: function(time) {
|
_onDragEnd: function(time) {
|
||||||
|
this._inXdndDrag = false;
|
||||||
|
|
||||||
// In case the drag was canceled while in the overview
|
// In case the drag was canceled while in the overview
|
||||||
// we have to go back to where we started and hide
|
// we have to go back to where we started and hide
|
||||||
// the overview
|
// the overview
|
||||||
if (this._shownTemporarily) {
|
if (this._shown) {
|
||||||
global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
|
global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
|
||||||
this.hideTemporarily();
|
this.hide();
|
||||||
}
|
}
|
||||||
this._resetWindowSwitchTimeout();
|
this._resetWindowSwitchTimeout();
|
||||||
this._lastHoveredWindow = null;
|
this._lastHoveredWindow = null;
|
||||||
@@ -333,17 +364,24 @@ const Overview = new Lang.Class({
|
|||||||
this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
|
this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
|
||||||
this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
|
this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
|
||||||
Lang.bind(this, function() {
|
Lang.bind(this, function() {
|
||||||
|
this._windowSwitchTimeoutId = 0;
|
||||||
this._needsFakePointerEvent = true;
|
this._needsFakePointerEvent = true;
|
||||||
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
|
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
|
||||||
this._windowSwitchTimestamp);
|
this._windowSwitchTimestamp);
|
||||||
this.hideTemporarily();
|
this.hide();
|
||||||
this._lastHoveredWindow = null;
|
this._lastHoveredWindow = null;
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onScrollEvent: function(actor, event) {
|
||||||
|
this.emit('scroll-event', event);
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
},
|
||||||
|
|
||||||
addAction: function(action) {
|
addAction: function(action) {
|
||||||
if (this.isDummy)
|
if (this.isDummy)
|
||||||
return;
|
return;
|
||||||
@@ -373,16 +411,12 @@ const Overview = new Lang.Class({
|
|||||||
// when it is next shown.
|
// when it is next shown.
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|
||||||
let primary = Main.layoutManager.primaryMonitor;
|
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||||
|
|
||||||
let contentY = Main.panel.actor.height;
|
this._coverPane.set_position(0, workArea.y);
|
||||||
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
|
this._coverPane.set_size(workArea.width, workArea.height);
|
||||||
|
|
||||||
this._overview.set_position(primary.x, primary.y);
|
this._updateBackgrounds();
|
||||||
this._overview.set_size(primary.width, primary.height);
|
|
||||||
|
|
||||||
this._coverPane.set_position(0, contentY);
|
|
||||||
this._coverPane.set_size(primary.width, contentHeight);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onRestacked: function() {
|
_onRestacked: function() {
|
||||||
@@ -401,6 +435,7 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
beginItemDrag: function(source) {
|
beginItemDrag: function(source) {
|
||||||
this.emit('item-drag-begin');
|
this.emit('item-drag-begin');
|
||||||
|
this._inDrag = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelledItemDrag: function(source) {
|
cancelledItemDrag: function(source) {
|
||||||
@@ -409,18 +444,21 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
endItemDrag: function(source) {
|
endItemDrag: function(source) {
|
||||||
this.emit('item-drag-end');
|
this.emit('item-drag-end');
|
||||||
|
this._inDrag = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
beginWindowDrag: function(source) {
|
beginWindowDrag: function(clone) {
|
||||||
this.emit('window-drag-begin');
|
this.emit('window-drag-begin', clone);
|
||||||
|
this._inDrag = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelledWindowDrag: function(source) {
|
cancelledWindowDrag: function(clone) {
|
||||||
this.emit('window-drag-cancelled');
|
this.emit('window-drag-cancelled', clone);
|
||||||
},
|
},
|
||||||
|
|
||||||
endWindowDrag: function(source) {
|
endWindowDrag: function(clone) {
|
||||||
this.emit('window-drag-end');
|
this.emit('window-drag-end', clone);
|
||||||
|
this._inDrag = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
// show:
|
// show:
|
||||||
@@ -432,12 +470,19 @@ const Overview = new Lang.Class({
|
|||||||
if (this._shown)
|
if (this._shown)
|
||||||
return;
|
return;
|
||||||
this._shown = true;
|
this._shown = true;
|
||||||
this._syncInputMode();
|
|
||||||
if (!this._modal)
|
if (!this._syncGrab())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Main.layoutManager.showOverview();
|
||||||
this._animateVisible();
|
this._animateVisible();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
focusSearch: function() {
|
||||||
|
this.show();
|
||||||
|
this._searchEntry.grab_key_focus();
|
||||||
|
},
|
||||||
|
|
||||||
fadeInDesktop: function() {
|
fadeInDesktop: function() {
|
||||||
this._desktopFade.opacity = 0;
|
this._desktopFade.opacity = 0;
|
||||||
this._desktopFade.show();
|
this._desktopFade.show();
|
||||||
@@ -448,8 +493,8 @@ const Overview = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
fadeOutDesktop: function() {
|
fadeOutDesktop: function() {
|
||||||
if (!this._desktopFade.child)
|
if (!this._desktopFade.get_n_children())
|
||||||
this._desktopFade.child = this._getDesktopClone();
|
this._desktopFade.add_child(this._getDesktopClone());
|
||||||
|
|
||||||
this._desktopFade.opacity = 255;
|
this._desktopFade.opacity = 255;
|
||||||
this._desktopFade.show();
|
this._desktopFade.show();
|
||||||
@@ -466,60 +511,27 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.animationInProgress = true;
|
this.animationInProgress = true;
|
||||||
|
this.visibleTarget = true;
|
||||||
|
this._activationTime = Date.now() / 1000;
|
||||||
|
|
||||||
// All the the actors in the window group are completely obscured,
|
|
||||||
// hiding the group holding them while the Overview is displayed greatly
|
|
||||||
// increases performance of the Overview especially when there are many
|
|
||||||
// windows visible.
|
|
||||||
//
|
|
||||||
// If we switched to displaying the actors in the Overview rather than
|
|
||||||
// clones of them, this would obviously no longer be necessary.
|
|
||||||
//
|
|
||||||
// Disable unredirection while in the overview
|
|
||||||
Meta.disable_unredirect_for_screen(global.screen);
|
Meta.disable_unredirect_for_screen(global.screen);
|
||||||
global.window_group.hide();
|
this.viewSelector.show();
|
||||||
this._overview.show();
|
|
||||||
this._background.show();
|
|
||||||
this._viewSelector.show();
|
|
||||||
|
|
||||||
this._overview.opacity = 0;
|
this._stack.opacity = 0;
|
||||||
Tweener.addTween(this._overview,
|
Tweener.addTween(this._stack,
|
||||||
{ opacity: 255,
|
{ opacity: 255,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
time: ANIMATION_TIME,
|
time: ANIMATION_TIME,
|
||||||
onComplete: this._showDone,
|
onComplete: this._showDone,
|
||||||
onCompleteScope: this
|
onCompleteScope: this
|
||||||
});
|
});
|
||||||
|
this._shadeBackgrounds();
|
||||||
Tweener.addTween(this._background,
|
|
||||||
{ dim_factor: 0.8,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad'
|
|
||||||
});
|
|
||||||
|
|
||||||
this._coverPane.raise_top();
|
this._coverPane.raise_top();
|
||||||
this._coverPane.show();
|
this._coverPane.show();
|
||||||
this.emit('showing');
|
this.emit('showing');
|
||||||
},
|
},
|
||||||
|
|
||||||
// showTemporarily:
|
|
||||||
//
|
|
||||||
// Animates the overview visible without grabbing mouse and keyboard input;
|
|
||||||
// if show() has already been called, this has no immediate effect, but
|
|
||||||
// will result in the overview not being hidden until hideTemporarily() is
|
|
||||||
// called.
|
|
||||||
showTemporarily: function() {
|
|
||||||
if (this.isDummy)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this._shownTemporarily)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._syncInputMode();
|
|
||||||
this._animateVisible();
|
|
||||||
this._shownTemporarily = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
// hide:
|
// hide:
|
||||||
//
|
//
|
||||||
// Reverses the effect of show()
|
// Reverses the effect of show()
|
||||||
@@ -530,28 +542,20 @@ const Overview = new Lang.Class({
|
|||||||
if (!this._shown)
|
if (!this._shown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!this._shownTemporarily)
|
let event = Clutter.get_current_event();
|
||||||
|
if (event) {
|
||||||
|
let type = event.type();
|
||||||
|
let button = (type == Clutter.EventType.BUTTON_PRESS ||
|
||||||
|
type == Clutter.EventType.BUTTON_RELEASE);
|
||||||
|
let ctrl = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
|
||||||
|
if (button && ctrl)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._animateNotVisible();
|
this._animateNotVisible();
|
||||||
|
|
||||||
this._shown = false;
|
this._shown = false;
|
||||||
this._syncInputMode();
|
this._syncGrab();
|
||||||
},
|
|
||||||
|
|
||||||
// hideTemporarily:
|
|
||||||
//
|
|
||||||
// Reverses the effect of showTemporarily()
|
|
||||||
hideTemporarily: function() {
|
|
||||||
if (this.isDummy)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!this._shownTemporarily)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!this._shown)
|
|
||||||
this._animateNotVisible();
|
|
||||||
|
|
||||||
this._shownTemporarily = false;
|
|
||||||
this._syncInputMode();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
toggle: function() {
|
toggle: function() {
|
||||||
@@ -564,37 +568,51 @@ const Overview = new Lang.Class({
|
|||||||
this.show();
|
this.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Checks if the Activities button is currently sensitive to
|
||||||
|
// clicks. The first call to this function within the
|
||||||
|
// OVERVIEW_ACTIVATION_TIMEOUT time of the hot corner being
|
||||||
|
// triggered will return false. This avoids opening and closing
|
||||||
|
// the overview if the user both triggered the hot corner and
|
||||||
|
// clicked the Activities button.
|
||||||
|
shouldToggleByCornerOrButton: function() {
|
||||||
|
if (this.animationInProgress)
|
||||||
|
return false;
|
||||||
|
if (this._inDrag)
|
||||||
|
return false;
|
||||||
|
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
//// Private methods ////
|
//// Private methods ////
|
||||||
|
|
||||||
_syncInputMode: function() {
|
_syncGrab: function() {
|
||||||
// We delay input mode changes during animation so that when removing the
|
// We delay grab changes during animation so that when removing the
|
||||||
// overview we don't have a problem with the release of a press/release
|
// overview we don't have a problem with the release of a press/release
|
||||||
// going to an application.
|
// going to an application.
|
||||||
if (this.animationInProgress)
|
if (this.animationInProgress)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
if (this._shown) {
|
if (this._shown) {
|
||||||
|
let shouldBeModal = !this._inXdndDrag;
|
||||||
|
if (shouldBeModal) {
|
||||||
if (!this._modal) {
|
if (!this._modal) {
|
||||||
if (Main.pushModal(this._overview,
|
if (Main.pushModal(this._overview,
|
||||||
{ keybindingMode: Main.KeybindingMode.OVERVIEW }))
|
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW })) {
|
||||||
this._modal = true;
|
this._modal = true;
|
||||||
else
|
} else {
|
||||||
this.hide();
|
this.hide();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (this._shownTemporarily) {
|
|
||||||
if (this._modal) {
|
|
||||||
Main.popModal(this._overview);
|
|
||||||
this._modal = false;
|
|
||||||
}
|
}
|
||||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
|
||||||
} else {
|
} else {
|
||||||
if (this._modal) {
|
if (this._modal) {
|
||||||
Main.popModal(this._overview);
|
Main.popModal(this._overview);
|
||||||
this._modal = false;
|
this._modal = false;
|
||||||
}
|
}
|
||||||
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
|
|
||||||
global.stage_input_mode = Shell.StageInputMode.NORMAL;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_animateNotVisible: function() {
|
_animateNotVisible: function() {
|
||||||
@@ -602,24 +620,19 @@ const Overview = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this.animationInProgress = true;
|
this.animationInProgress = true;
|
||||||
this._hideInProgress = true;
|
this.visibleTarget = false;
|
||||||
|
|
||||||
this._viewSelector.zoomFromOverview();
|
this.viewSelector.leaveOverview();
|
||||||
|
|
||||||
// Make other elements fade out.
|
// Make other elements fade out.
|
||||||
Tweener.addTween(this._overview,
|
Tweener.addTween(this._stack,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
transition: 'easeOutQuad',
|
transition: 'easeOutQuad',
|
||||||
time: ANIMATION_TIME,
|
time: ANIMATION_TIME,
|
||||||
onComplete: this._hideDone,
|
onComplete: this._hideDone,
|
||||||
onCompleteScope: this
|
onCompleteScope: this
|
||||||
});
|
});
|
||||||
|
this._unshadeBackgrounds();
|
||||||
Tweener.addTween(this._background,
|
|
||||||
{ dim_factor: 1.0,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad'
|
|
||||||
});
|
|
||||||
|
|
||||||
this._coverPane.raise_top();
|
this._coverPane.raise_top();
|
||||||
this._coverPane.show();
|
this._coverPane.show();
|
||||||
@@ -633,10 +646,10 @@ const Overview = new Lang.Class({
|
|||||||
|
|
||||||
this.emit('shown');
|
this.emit('shown');
|
||||||
// Handle any calls to hide* while we were showing
|
// Handle any calls to hide* while we were showing
|
||||||
if (!this._shown && !this._shownTemporarily)
|
if (!this._shown)
|
||||||
this._animateNotVisible();
|
this._animateNotVisible();
|
||||||
|
|
||||||
this._syncInputMode();
|
this._syncGrab();
|
||||||
global.sync_pointer();
|
global.sync_pointer();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -644,25 +657,21 @@ const Overview = new Lang.Class({
|
|||||||
// Re-enable unredirection
|
// Re-enable unredirection
|
||||||
Meta.enable_unredirect_for_screen(global.screen);
|
Meta.enable_unredirect_for_screen(global.screen);
|
||||||
|
|
||||||
global.window_group.show();
|
this.viewSelector.hide();
|
||||||
|
|
||||||
this._viewSelector.hide();
|
|
||||||
this._desktopFade.hide();
|
this._desktopFade.hide();
|
||||||
this._background.hide();
|
this._coverPane.hide();
|
||||||
this._overview.hide();
|
|
||||||
|
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
this.animationInProgress = false;
|
this.animationInProgress = false;
|
||||||
this._hideInProgress = false;
|
|
||||||
|
|
||||||
this._coverPane.hide();
|
|
||||||
|
|
||||||
this.emit('hidden');
|
this.emit('hidden');
|
||||||
// Handle any calls to show* while we were hiding
|
// Handle any calls to show* while we were hiding
|
||||||
if (this._shown || this._shownTemporarily)
|
if (this._shown)
|
||||||
this._animateVisible();
|
this._animateVisible();
|
||||||
|
else
|
||||||
|
Main.layoutManager.hideOverview();
|
||||||
|
|
||||||
this._syncInputMode();
|
this._syncGrab();
|
||||||
|
|
||||||
// Fake a pointer event if requested
|
// Fake a pointer event if requested
|
||||||
if (this._needsFakePointerEvent) {
|
if (this._needsFakePointerEvent) {
|
||||||
|
|||||||
624
js/ui/overviewControls.js
Normal file
624
js/ui/overviewControls.js
Normal file
@@ -0,0 +1,624 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const GObject = imports.gi.GObject;
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const Dash = imports.ui.dash;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Params = imports.misc.params;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
const ViewSelector = imports.ui.viewSelector;
|
||||||
|
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||||
|
|
||||||
|
const SIDE_CONTROLS_ANIMATION_TIME = 0.16;
|
||||||
|
|
||||||
|
function getRtlSlideDirection(direction, actor) {
|
||||||
|
let rtl = (actor.text_direction == Clutter.TextDirection.RTL);
|
||||||
|
if (rtl)
|
||||||
|
direction = (direction == SlideDirection.LEFT) ?
|
||||||
|
SlideDirection.RIGHT : SlideDirection.LEFT;
|
||||||
|
|
||||||
|
return direction;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SlideDirection = {
|
||||||
|
LEFT: 0,
|
||||||
|
RIGHT: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const SlideLayout = new Lang.Class({
|
||||||
|
Name: 'SlideLayout',
|
||||||
|
Extends: Clutter.FixedLayout,
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
this._slideX = 1;
|
||||||
|
this._translationX = 0;
|
||||||
|
this._direction = SlideDirection.LEFT;
|
||||||
|
|
||||||
|
this.parent(params);
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_get_preferred_width: function(container, forHeight) {
|
||||||
|
let child = container.get_first_child();
|
||||||
|
|
||||||
|
let [minWidth, natWidth] = child.get_preferred_width(forHeight);
|
||||||
|
|
||||||
|
minWidth *= this._slideX;
|
||||||
|
natWidth *= this._slideX;
|
||||||
|
|
||||||
|
return [minWidth, natWidth];
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_allocate: function(container, box, flags) {
|
||||||
|
let child = container.get_first_child();
|
||||||
|
|
||||||
|
let availWidth = Math.round(box.x2 - box.x1);
|
||||||
|
let availHeight = Math.round(box.y2 - box.y1);
|
||||||
|
let [, natWidth] = child.get_preferred_width(availHeight);
|
||||||
|
|
||||||
|
// Align the actor inside the clipped box, as the actor's alignment
|
||||||
|
// flags only determine what to do if the allocated box is bigger
|
||||||
|
// than the actor's box.
|
||||||
|
let realDirection = getRtlSlideDirection(this._direction, child);
|
||||||
|
let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0;
|
||||||
|
|
||||||
|
let actorBox = new Clutter.ActorBox();
|
||||||
|
actorBox.x1 = box.x1 + alignX + this._translationX;
|
||||||
|
actorBox.x2 = actorBox.x1 + (child.x_expand ? availWidth : natWidth);
|
||||||
|
actorBox.y1 = box.y1;
|
||||||
|
actorBox.y2 = actorBox.y1 + availHeight;
|
||||||
|
|
||||||
|
child.allocate(actorBox, flags);
|
||||||
|
},
|
||||||
|
|
||||||
|
set slideX(value) {
|
||||||
|
this._slideX = value;
|
||||||
|
this.layout_changed();
|
||||||
|
},
|
||||||
|
|
||||||
|
get slideX() {
|
||||||
|
return this._slideX;
|
||||||
|
},
|
||||||
|
|
||||||
|
set slideDirection(direction) {
|
||||||
|
this._direction = direction;
|
||||||
|
this.layout_changed();
|
||||||
|
},
|
||||||
|
|
||||||
|
get slideDirection() {
|
||||||
|
return this._direction;
|
||||||
|
},
|
||||||
|
|
||||||
|
set translationX(value) {
|
||||||
|
this._translationX = value;
|
||||||
|
this.layout_changed();
|
||||||
|
},
|
||||||
|
|
||||||
|
get translationX() {
|
||||||
|
return this._translationX;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const SlidingControl = new Lang.Class({
|
||||||
|
Name: 'SlidingControl',
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
|
||||||
|
|
||||||
|
this._visible = true;
|
||||||
|
this._inDrag = false;
|
||||||
|
|
||||||
|
this.layout = new SlideLayout();
|
||||||
|
this.layout.slideDirection = params.slideDirection;
|
||||||
|
this.actor = new St.Widget({ layout_manager: this.layout,
|
||||||
|
style_class: 'overview-controls',
|
||||||
|
clip_to_allocation: true });
|
||||||
|
|
||||||
|
Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing));
|
||||||
|
Main.overview.connect('hiding', Lang.bind(this, this._onOverviewHiding));
|
||||||
|
|
||||||
|
Main.overview.connect('item-drag-begin', Lang.bind(this, this._onDragBegin));
|
||||||
|
Main.overview.connect('item-drag-end', Lang.bind(this, this._onDragEnd));
|
||||||
|
Main.overview.connect('item-drag-cancelled', Lang.bind(this, this._onDragEnd));
|
||||||
|
|
||||||
|
Main.overview.connect('window-drag-begin', Lang.bind(this, this._onWindowDragBegin));
|
||||||
|
Main.overview.connect('window-drag-cancelled', Lang.bind(this, this._onWindowDragEnd));
|
||||||
|
Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
|
||||||
|
},
|
||||||
|
|
||||||
|
_getSlide: function() {
|
||||||
|
throw new Error('getSlide() must be overridden');
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSlide: function() {
|
||||||
|
Tweener.addTween(this.layout, { slideX: this._getSlide(),
|
||||||
|
time: SIDE_CONTROLS_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
},
|
||||||
|
|
||||||
|
getVisibleWidth: function() {
|
||||||
|
let child = this.actor.get_first_child();
|
||||||
|
let [, , natWidth, ] = child.get_preferred_size();
|
||||||
|
return natWidth;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getTranslation: function() {
|
||||||
|
let child = this.actor.get_first_child();
|
||||||
|
let direction = getRtlSlideDirection(this.layout.slideDirection, child);
|
||||||
|
let visibleWidth = this.getVisibleWidth();
|
||||||
|
|
||||||
|
if (direction == SlideDirection.LEFT)
|
||||||
|
return - visibleWidth;
|
||||||
|
else
|
||||||
|
return visibleWidth;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateTranslation: function() {
|
||||||
|
let translationStart = 0;
|
||||||
|
let translationEnd = 0;
|
||||||
|
let translation = this._getTranslation();
|
||||||
|
|
||||||
|
if (this._visible) {
|
||||||
|
translationStart = translation;
|
||||||
|
} else {
|
||||||
|
translationEnd = translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.layout.translationX == translationEnd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.layout.translationX = translationStart;
|
||||||
|
Tweener.addTween(this.layout, { translationX: translationEnd,
|
||||||
|
time: SIDE_CONTROLS_ANIMATION_TIME,
|
||||||
|
transition: 'easeOutQuad' });
|
||||||
|
},
|
||||||
|
|
||||||
|
_onOverviewShowing: function() {
|
||||||
|
this._visible = true;
|
||||||
|
this.layout.slideX = this._getSlide();
|
||||||
|
this.layout.translationX = this._getTranslation();
|
||||||
|
this.slideIn();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onOverviewHiding: function() {
|
||||||
|
this.slideOut();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onWindowDragBegin: function() {
|
||||||
|
this._onDragBegin();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onWindowDragEnd: function() {
|
||||||
|
this._onDragEnd();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDragBegin: function() {
|
||||||
|
this._inDrag = true;
|
||||||
|
this.layout.translationX = 0;
|
||||||
|
this._updateSlide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDragEnd: function() {
|
||||||
|
this._inDrag = false;
|
||||||
|
this._updateSlide();
|
||||||
|
},
|
||||||
|
|
||||||
|
fadeIn: function() {
|
||||||
|
Tweener.addTween(this.actor, { opacity: 255,
|
||||||
|
time: SIDE_CONTROLS_ANIMATION_TIME / 2,
|
||||||
|
transition: 'easeInQuad'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
fadeHalf: function() {
|
||||||
|
Tweener.addTween(this.actor, { opacity: 128,
|
||||||
|
time: SIDE_CONTROLS_ANIMATION_TIME / 2,
|
||||||
|
transition: 'easeOutQuad'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
slideIn: function() {
|
||||||
|
this._visible = true;
|
||||||
|
this._updateTranslation();
|
||||||
|
// we will update slideX and the translation from pageEmpty
|
||||||
|
},
|
||||||
|
|
||||||
|
slideOut: function() {
|
||||||
|
this._visible = false;
|
||||||
|
this._updateTranslation();
|
||||||
|
// we will update slideX from pageEmpty
|
||||||
|
},
|
||||||
|
|
||||||
|
pageEmpty: function() {
|
||||||
|
// When pageEmpty is received, there's no visible view in the
|
||||||
|
// selector; this means we can now safely set the full slide for
|
||||||
|
// the next page, since slideIn or slideOut might have been called,
|
||||||
|
// changing the visiblity
|
||||||
|
this.layout.slideX = this._getSlide();
|
||||||
|
this._updateTranslation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ThumbnailsSlider = new Lang.Class({
|
||||||
|
Name: 'ThumbnailsSlider',
|
||||||
|
Extends: SlidingControl,
|
||||||
|
|
||||||
|
_init: function(thumbnailsBox) {
|
||||||
|
this.parent({ slideDirection: SlideDirection.RIGHT });
|
||||||
|
|
||||||
|
this._thumbnailsBox = thumbnailsBox;
|
||||||
|
|
||||||
|
this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
|
||||||
|
this.actor.reactive = true;
|
||||||
|
this.actor.track_hover = true;
|
||||||
|
this.actor.add_actor(this._thumbnailsBox.actor);
|
||||||
|
|
||||||
|
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateSlide));
|
||||||
|
this.actor.connect('notify::hover', Lang.bind(this, this._updateSlide));
|
||||||
|
this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getAlwaysZoomOut: function() {
|
||||||
|
// Always show the pager when hover, during a drag, or if workspaces are
|
||||||
|
// actually used, e.g. there are windows on more than one
|
||||||
|
let alwaysZoomOut = this.actor.hover || this._inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
|
||||||
|
|
||||||
|
if (!alwaysZoomOut) {
|
||||||
|
let monitors = Main.layoutManager.monitors;
|
||||||
|
let primary = Main.layoutManager.primaryMonitor;
|
||||||
|
|
||||||
|
/* Look for any monitor to the right of the primary, if there is
|
||||||
|
* one, we always keep zoom out, otherwise its hard to reach
|
||||||
|
* the thumbnail area without passing into the next monitor. */
|
||||||
|
for (let i = 0; i < monitors.length; i++) {
|
||||||
|
if (monitors[i].x >= primary.x + primary.width) {
|
||||||
|
alwaysZoomOut = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return alwaysZoomOut;
|
||||||
|
},
|
||||||
|
|
||||||
|
getNonExpandedWidth: function() {
|
||||||
|
let child = this.actor.get_first_child();
|
||||||
|
return child.get_theme_node().get_length('visible-width');
|
||||||
|
},
|
||||||
|
|
||||||
|
_getSlide: function() {
|
||||||
|
if (!this._visible)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
let alwaysZoomOut = this._getAlwaysZoomOut();
|
||||||
|
if (alwaysZoomOut)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
let child = this.actor.get_first_child();
|
||||||
|
let preferredHeight = child.get_preferred_height(-1)[1];
|
||||||
|
let expandedWidth = child.get_preferred_width(preferredHeight)[1];
|
||||||
|
|
||||||
|
return this.getNonExpandedWidth() / expandedWidth;
|
||||||
|
},
|
||||||
|
|
||||||
|
getVisibleWidth: function() {
|
||||||
|
let alwaysZoomOut = this._getAlwaysZoomOut();
|
||||||
|
if (alwaysZoomOut)
|
||||||
|
return this.parent();
|
||||||
|
else
|
||||||
|
return this.getNonExpandedWidth();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const DashSlider = new Lang.Class({
|
||||||
|
Name: 'DashSlider',
|
||||||
|
Extends: SlidingControl,
|
||||||
|
|
||||||
|
_init: function(dash) {
|
||||||
|
this.parent({ slideDirection: SlideDirection.LEFT });
|
||||||
|
|
||||||
|
this._dash = dash;
|
||||||
|
|
||||||
|
// SlideLayout reads the actor's expand flags to decide
|
||||||
|
// whether to allocate the natural size to its child, or the whole
|
||||||
|
// available allocation
|
||||||
|
this._dash.actor.x_expand = true;
|
||||||
|
|
||||||
|
this.actor.x_expand = true;
|
||||||
|
this.actor.x_align = Clutter.ActorAlign.START;
|
||||||
|
this.actor.y_expand = true;
|
||||||
|
|
||||||
|
this.actor.add_actor(this._dash.actor);
|
||||||
|
|
||||||
|
this._dash.connect('icon-size-changed', Lang.bind(this, this._updateSlide));
|
||||||
|
},
|
||||||
|
|
||||||
|
_getSlide: function() {
|
||||||
|
if (this._visible || this._inDrag)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onWindowDragBegin: function() {
|
||||||
|
this.fadeHalf();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onWindowDragEnd: function() {
|
||||||
|
this.fadeIn();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const DashSpacer = new Lang.Class({
|
||||||
|
Name: 'DashSpacer',
|
||||||
|
Extends: St.Widget,
|
||||||
|
|
||||||
|
_init: function(params) {
|
||||||
|
this.parent(params);
|
||||||
|
|
||||||
|
this._bindConstraint = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
setDashActor: function(dashActor) {
|
||||||
|
if (this._bindConstraint) {
|
||||||
|
this.remove_constraint(this._bindConstraint);
|
||||||
|
this._bindConstraint = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dashActor) {
|
||||||
|
this._bindConstraint = new Clutter.BindConstraint({ source: dashActor,
|
||||||
|
coordinate: Clutter.BindCoordinate.SIZE });
|
||||||
|
this.add_constraint(this._bindConstraint);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_get_preferred_width: function(forHeight) {
|
||||||
|
let box = this.get_allocation_box();
|
||||||
|
let minWidth = this.parent(forHeight)[0];
|
||||||
|
let natWidth = box.x2 - box.x1;
|
||||||
|
return [minWidth, natWidth];
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_get_preferred_height: function(forWidth) {
|
||||||
|
let box = this.get_allocation_box();
|
||||||
|
let minHeight = this.parent(forWidth)[0];
|
||||||
|
let natHeight = box.y2 - box.y1;
|
||||||
|
return [minHeight, natHeight];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const MessagesIndicator = new Lang.Class({
|
||||||
|
Name: 'MessagesIndicator',
|
||||||
|
|
||||||
|
_init: function(viewSelector) {
|
||||||
|
this._count = 0;
|
||||||
|
this._sources = [];
|
||||||
|
this._viewSelector = viewSelector;
|
||||||
|
|
||||||
|
this._container = new St.BoxLayout({ style_class: 'messages-indicator-contents',
|
||||||
|
reactive: true,
|
||||||
|
track_hover: true,
|
||||||
|
x_expand: true,
|
||||||
|
y_expand: true,
|
||||||
|
x_align: Clutter.ActorAlign.CENTER });
|
||||||
|
|
||||||
|
this._icon = new St.Icon({ icon_name: 'user-idle-symbolic',
|
||||||
|
icon_size: 16 });
|
||||||
|
this._container.add_actor(this._icon);
|
||||||
|
|
||||||
|
this._label = new St.Label();
|
||||||
|
this._container.add_actor(this._label);
|
||||||
|
|
||||||
|
this._highlight = new St.Widget({ style_class: 'messages-indicator-highlight',
|
||||||
|
x_expand: true,
|
||||||
|
y_expand: true,
|
||||||
|
y_align: Clutter.ActorAlign.END,
|
||||||
|
visible: false });
|
||||||
|
|
||||||
|
this._container.connect('notify::hover', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this._highlight.visible = this._container.hover;
|
||||||
|
}));
|
||||||
|
|
||||||
|
let clickAction = new Clutter.ClickAction();
|
||||||
|
this._container.add_action(clickAction);
|
||||||
|
clickAction.connect('clicked', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
Main.messageTray.openTray();
|
||||||
|
}));
|
||||||
|
|
||||||
|
Main.messageTray.connect('showing', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this._highlight.visible = false;
|
||||||
|
this._container.hover = false;
|
||||||
|
}));
|
||||||
|
|
||||||
|
let layout = new Clutter.BinLayout();
|
||||||
|
this.actor = new St.Widget({ layout_manager: layout,
|
||||||
|
style_class: 'messages-indicator',
|
||||||
|
y_expand: true,
|
||||||
|
y_align: Clutter.ActorAlign.END,
|
||||||
|
visible: false });
|
||||||
|
this.actor.add_actor(this._container);
|
||||||
|
this.actor.add_actor(this._highlight);
|
||||||
|
|
||||||
|
Main.messageTray.connect('source-added', Lang.bind(this, this._onSourceAdded));
|
||||||
|
Main.messageTray.connect('source-removed', Lang.bind(this, this._onSourceRemoved));
|
||||||
|
|
||||||
|
let sources = Main.messageTray.getSources();
|
||||||
|
sources.forEach(Lang.bind(this, function(source) { this._onSourceAdded(null, source); }));
|
||||||
|
|
||||||
|
this._viewSelector.connect('page-changed', Lang.bind(this, this._updateVisibility));
|
||||||
|
Main.overview.connect('showing', Lang.bind(this, this._updateVisibility));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSourceAdded: function(tray, source) {
|
||||||
|
if (source.trayIcon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
source.connect('count-updated', Lang.bind(this, this._updateCount));
|
||||||
|
this._sources.push(source);
|
||||||
|
this._updateCount();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSourceRemoved: function(tray, source) {
|
||||||
|
this._sources.splice(this._sources.indexOf(source), 1);
|
||||||
|
this._updateCount();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateCount: function() {
|
||||||
|
let count = 0;
|
||||||
|
let hasChats = false;
|
||||||
|
this._sources.forEach(Lang.bind(this,
|
||||||
|
function(source) {
|
||||||
|
count += source.indicatorCount;
|
||||||
|
hasChats |= source.isChat;
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._count = count;
|
||||||
|
this._label.text = ngettext("%d new message",
|
||||||
|
"%d new messages",
|
||||||
|
count).format(count);
|
||||||
|
|
||||||
|
this._icon.visible = hasChats;
|
||||||
|
this._updateVisibility();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateVisibility: function() {
|
||||||
|
let activePage = this._viewSelector.getActivePage();
|
||||||
|
let visible = ((this._count > 0) && (activePage == ViewSelector.ViewPage.WINDOWS));
|
||||||
|
|
||||||
|
this.actor.visible = visible;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ControlsLayout = new Lang.Class({
|
||||||
|
Name: 'ControlsLayout',
|
||||||
|
Extends: Clutter.BinLayout,
|
||||||
|
Signals: { 'allocation-changed': { flags: GObject.SignalFlags.RUN_LAST } },
|
||||||
|
|
||||||
|
vfunc_allocate: function(container, box, flags) {
|
||||||
|
this.parent(container, box, flags);
|
||||||
|
this.emit('allocation-changed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ControlsManager = new Lang.Class({
|
||||||
|
Name: 'ControlsManager',
|
||||||
|
|
||||||
|
_init: function(searchEntry) {
|
||||||
|
this.dash = new Dash.Dash();
|
||||||
|
this._dashSlider = new DashSlider(this.dash);
|
||||||
|
this._dashSpacer = new DashSpacer();
|
||||||
|
this._dashSpacer.setDashActor(this._dashSlider.actor);
|
||||||
|
|
||||||
|
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
||||||
|
this._thumbnailsSlider = new ThumbnailsSlider(this._thumbnailsBox);
|
||||||
|
|
||||||
|
this.viewSelector = new ViewSelector.ViewSelector(searchEntry,
|
||||||
|
this.dash.showAppsButton);
|
||||||
|
this.viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
|
||||||
|
this.viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
|
||||||
|
|
||||||
|
this._indicator = new MessagesIndicator(this.viewSelector);
|
||||||
|
this.indicatorActor = this._indicator.actor;
|
||||||
|
|
||||||
|
let layout = new ControlsLayout();
|
||||||
|
this.actor = new St.Widget({ layout_manager: layout,
|
||||||
|
reactive: true,
|
||||||
|
x_expand: true, y_expand: true,
|
||||||
|
clip_to_allocation: true });
|
||||||
|
this._group = new St.BoxLayout({ name: 'overview-group',
|
||||||
|
x_expand: true, y_expand: true });
|
||||||
|
this.actor.add_actor(this._group);
|
||||||
|
|
||||||
|
this.actor.add_actor(this._dashSlider.actor);
|
||||||
|
|
||||||
|
this._group.add_actor(this._dashSpacer);
|
||||||
|
this._group.add(this.viewSelector.actor, { x_fill: true,
|
||||||
|
expand: true });
|
||||||
|
this._group.add_actor(this._thumbnailsSlider.actor);
|
||||||
|
|
||||||
|
layout.connect('allocation-changed', Lang.bind(this, this._updateWorkspacesGeometry));
|
||||||
|
|
||||||
|
Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
|
||||||
|
Main.overview.connect('item-drag-begin', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
let activePage = this.viewSelector.getActivePage();
|
||||||
|
if (activePage != ViewSelector.ViewPage.WINDOWS)
|
||||||
|
this.viewSelector.fadeHalf();
|
||||||
|
}));
|
||||||
|
Main.overview.connect('item-drag-end', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this.viewSelector.fadeIn();
|
||||||
|
}));
|
||||||
|
Main.overview.connect('item-drag-cancelled', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
this.viewSelector.fadeIn();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateWorkspacesGeometry: function() {
|
||||||
|
let [x, y] = this.actor.get_transformed_position();
|
||||||
|
let [width, height] = this.actor.get_transformed_size();
|
||||||
|
let geometry = { x: x, y: y, width: width, height: height };
|
||||||
|
|
||||||
|
let spacing = this.actor.get_theme_node().get_length('spacing');
|
||||||
|
let dashWidth = this._dashSlider.getVisibleWidth() + spacing;
|
||||||
|
let thumbnailsWidth = this._thumbnailsSlider.getNonExpandedWidth() + spacing;
|
||||||
|
|
||||||
|
geometry.width -= dashWidth;
|
||||||
|
geometry.width -= thumbnailsWidth;
|
||||||
|
|
||||||
|
if (this.actor.get_text_direction() == Clutter.TextDirection.LTR)
|
||||||
|
geometry.x += dashWidth;
|
||||||
|
else
|
||||||
|
geometry.x += thumbnailsWidth;
|
||||||
|
|
||||||
|
this.viewSelector.setWorkspacesFullGeometry(geometry);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setVisibility: function() {
|
||||||
|
// Ignore the case when we're leaving the overview, since
|
||||||
|
// actors will be made visible again when entering the overview
|
||||||
|
// next time, and animating them while doing so is just
|
||||||
|
// unnecessary noise
|
||||||
|
if (!Main.overview.visible ||
|
||||||
|
(Main.overview.animationInProgress && !Main.overview.visibleTarget))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let activePage = this.viewSelector.getActivePage();
|
||||||
|
let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
|
||||||
|
activePage == ViewSelector.ViewPage.APPS);
|
||||||
|
let thumbnailsVisible = (activePage == ViewSelector.ViewPage.WINDOWS);
|
||||||
|
|
||||||
|
if (dashVisible)
|
||||||
|
this._dashSlider.slideIn();
|
||||||
|
else
|
||||||
|
this._dashSlider.slideOut();
|
||||||
|
|
||||||
|
if (thumbnailsVisible)
|
||||||
|
this._thumbnailsSlider.slideIn();
|
||||||
|
else
|
||||||
|
this._thumbnailsSlider.slideOut();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSpacerVisibility: function() {
|
||||||
|
if (Main.overview.animationInProgress && !Main.overview.visibleTarget)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let activePage = this.viewSelector.getActivePage();
|
||||||
|
this._dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onPageEmpty: function() {
|
||||||
|
this._dashSlider.pageEmpty();
|
||||||
|
this._thumbnailsSlider.pageEmpty();
|
||||||
|
|
||||||
|
this._updateSpacerVisibility();
|
||||||
|
}
|
||||||
|
});
|
||||||
555
js/ui/panel.js
555
js/ui/panel.js
@@ -15,13 +15,14 @@ const Signals = imports.signals;
|
|||||||
const Atk = imports.gi.Atk;
|
const Atk = imports.gi.Atk;
|
||||||
|
|
||||||
|
|
||||||
|
const Animation = imports.ui.animation;
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
const DND = imports.ui.dnd;
|
const DND = imports.ui.dnd;
|
||||||
const Layout = imports.ui.layout;
|
|
||||||
const Overview = imports.ui.overview;
|
const Overview = imports.ui.overview;
|
||||||
const PopupMenu = imports.ui.popupMenu;
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
const PanelMenu = imports.ui.panelMenu;
|
const PanelMenu = imports.ui.panelMenu;
|
||||||
|
const RemoteMenu = imports.ui.remoteMenu;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
const Tweener = imports.ui.tweener;
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
@@ -29,7 +30,6 @@ const PANEL_ICON_SIZE = 24;
|
|||||||
|
|
||||||
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
||||||
|
|
||||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
|
||||||
const SPINNER_ANIMATION_TIME = 0.2;
|
const SPINNER_ANIMATION_TIME = 0.2;
|
||||||
|
|
||||||
// To make sure the panel corners blend nicely with the panel,
|
// To make sure the panel corners blend nicely with the panel,
|
||||||
@@ -75,81 +75,6 @@ function _unpremultiply(color) {
|
|||||||
blue: blue, alpha: color.alpha });
|
blue: blue, alpha: color.alpha });
|
||||||
};
|
};
|
||||||
|
|
||||||
const Animation = new Lang.Class({
|
|
||||||
Name: 'Animation',
|
|
||||||
|
|
||||||
_init: function(filename, width, height, speed) {
|
|
||||||
this.actor = new St.Bin();
|
|
||||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
|
||||||
this._speed = speed;
|
|
||||||
|
|
||||||
this._isLoaded = false;
|
|
||||||
this._isPlaying = false;
|
|
||||||
this._timeoutId = 0;
|
|
||||||
this._frame = 0;
|
|
||||||
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
|
|
||||||
Lang.bind(this, this._animationsLoaded));
|
|
||||||
this.actor.set_child(this._animations);
|
|
||||||
},
|
|
||||||
|
|
||||||
play: function() {
|
|
||||||
if (this._isLoaded && this._timeoutId == 0) {
|
|
||||||
if (this._frame == 0)
|
|
||||||
this._showFrame(0);
|
|
||||||
|
|
||||||
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
|
|
||||||
}
|
|
||||||
|
|
||||||
this._isPlaying = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
stop: function() {
|
|
||||||
if (this._timeoutId > 0) {
|
|
||||||
Mainloop.source_remove(this._timeoutId);
|
|
||||||
this._timeoutId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._isPlaying = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
_showFrame: function(frame) {
|
|
||||||
let oldFrameActor = this._animations.get_child_at_index(this._frame);
|
|
||||||
if (oldFrameActor)
|
|
||||||
oldFrameActor.hide();
|
|
||||||
|
|
||||||
this._frame = (frame % this._animations.get_n_children());
|
|
||||||
|
|
||||||
let newFrameActor = this._animations.get_child_at_index(this._frame);
|
|
||||||
if (newFrameActor)
|
|
||||||
newFrameActor.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
_update: function() {
|
|
||||||
this._showFrame(this._frame + 1);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
_animationsLoaded: function() {
|
|
||||||
this._isLoaded = true;
|
|
||||||
|
|
||||||
if (this._isPlaying)
|
|
||||||
this.play();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onDestroy: function() {
|
|
||||||
this.stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const AnimatedIcon = new Lang.Class({
|
|
||||||
Name: 'AnimatedIcon',
|
|
||||||
Extends: Animation,
|
|
||||||
|
|
||||||
_init: function(name, size) {
|
|
||||||
this.parent(global.datadir + '/theme/' + name, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const TextShadower = new Lang.Class({
|
const TextShadower = new Lang.Class({
|
||||||
Name: 'TextShadower',
|
Name: 'TextShadower',
|
||||||
|
|
||||||
@@ -203,7 +128,7 @@ const TextShadower = new Lang.Class({
|
|||||||
let child = children[i];
|
let child = children[i];
|
||||||
let childBox = new Clutter.ActorBox();
|
let childBox = new Clutter.ActorBox();
|
||||||
// The order of the labels here is arbitrary, except
|
// The order of the labels here is arbitrary, except
|
||||||
// we know the "real" label is at the end because Clutter.Group
|
// we know the "real" label is at the end because Clutter.Actor
|
||||||
// sorts by Z order
|
// sorts by Z order
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 0: // top
|
case 0: // top
|
||||||
@@ -259,11 +184,11 @@ const AppMenuButton = new Lang.Class({
|
|||||||
this._actionGroupNotifyId = 0;
|
this._actionGroupNotifyId = 0;
|
||||||
|
|
||||||
let bin = new St.Bin({ name: 'appMenu' });
|
let bin = new St.Bin({ name: 'appMenu' });
|
||||||
|
bin.connect('style-changed', Lang.bind(this, this._onStyleChanged));
|
||||||
this.actor.add_actor(bin);
|
this.actor.add_actor(bin);
|
||||||
|
|
||||||
this.actor.bind_property("reactive", this.actor, "can-focus", 0);
|
this.actor.bind_property("reactive", this.actor, "can-focus", 0);
|
||||||
this.actor.reactive = false;
|
this.actor.reactive = false;
|
||||||
this._targetIsCurrent = false;
|
|
||||||
|
|
||||||
this._container = new Shell.GenericContainer();
|
this._container = new Shell.GenericContainer();
|
||||||
bin.set_child(this._container);
|
bin.set_child(this._container);
|
||||||
@@ -281,51 +206,47 @@ const AppMenuButton = new Lang.Class({
|
|||||||
this._iconBox.connect('notify::allocation',
|
this._iconBox.connect('notify::allocation',
|
||||||
Lang.bind(this, this._updateIconBoxClip));
|
Lang.bind(this, this._updateIconBoxClip));
|
||||||
this._container.add_actor(this._iconBox);
|
this._container.add_actor(this._iconBox);
|
||||||
|
|
||||||
|
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
|
||||||
|
this._container.add_actor(this._hbox);
|
||||||
|
|
||||||
this._label = new TextShadower();
|
this._label = new TextShadower();
|
||||||
this._container.add_actor(this._label.actor);
|
this._label.actor.y_align = Clutter.ActorAlign.CENTER;
|
||||||
|
this._hbox.add_actor(this._label.actor);
|
||||||
|
this._arrow = PopupMenu.unicodeArrow(St.Side.BOTTOM);
|
||||||
|
this._hbox.add_actor(this._arrow);
|
||||||
|
|
||||||
this._iconBottomClip = 0;
|
this._iconBottomClip = 0;
|
||||||
|
|
||||||
this._visible = !Main.overview.visible;
|
this._visible = !Main.overview.visible;
|
||||||
if (!this._visible)
|
if (!this._visible)
|
||||||
this.actor.hide();
|
this.actor.hide();
|
||||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, this._sync));
|
||||||
this.show();
|
this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, this._sync));
|
||||||
}));
|
|
||||||
Main.overview.connect('showing', Lang.bind(this, function () {
|
|
||||||
this.hide();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._stop = true;
|
this._stop = true;
|
||||||
|
|
||||||
this._spinner = new AnimatedIcon('process-working.svg',
|
this._spinner = null;
|
||||||
PANEL_ICON_SIZE);
|
|
||||||
this._container.add_actor(this._spinner.actor);
|
|
||||||
this._spinner.actor.hide();
|
|
||||||
this._spinner.actor.lower_bottom();
|
|
||||||
|
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
let appSys = Shell.AppSystem.get_default();
|
let appSys = Shell.AppSystem.get_default();
|
||||||
|
this._focusAppNotifyId =
|
||||||
tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
|
tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
|
||||||
|
this._appStateChangedSignalId =
|
||||||
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
|
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
|
||||||
|
this._switchWorkspaceNotifyId =
|
||||||
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
|
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
|
||||||
|
|
||||||
this._sync();
|
this._sync();
|
||||||
},
|
},
|
||||||
|
|
||||||
show: function() {
|
show: function() {
|
||||||
if (this._visible || Main.screenShield.locked)
|
if (this._visible)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._visible = true;
|
this._visible = true;
|
||||||
this.actor.show();
|
|
||||||
|
|
||||||
if (!this._targetIsCurrent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.actor.reactive = true;
|
this.actor.reactive = true;
|
||||||
|
this.actor.show();
|
||||||
Tweener.removeTweens(this.actor);
|
Tweener.removeTweens(this.actor);
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 255,
|
{ opacity: 255,
|
||||||
@@ -339,11 +260,6 @@ const AppMenuButton = new Lang.Class({
|
|||||||
|
|
||||||
this._visible = false;
|
this._visible = false;
|
||||||
this.actor.reactive = false;
|
this.actor.reactive = false;
|
||||||
if (!this._targetIsCurrent) {
|
|
||||||
this.actor.hide();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Tweener.removeTweens(this.actor);
|
Tweener.removeTweens(this.actor);
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
@@ -355,19 +271,36 @@ const AppMenuButton = new Lang.Class({
|
|||||||
onCompleteScope: this });
|
onCompleteScope: this });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onStyleChanged: function(actor) {
|
||||||
|
let node = actor.get_theme_node();
|
||||||
|
let [success, icon] = node.lookup_url('spinner-image', false);
|
||||||
|
if (!success || this._spinnerIcon == icon)
|
||||||
|
return;
|
||||||
|
this._spinnerIcon = icon;
|
||||||
|
this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
|
||||||
|
this._hbox.add_actor(this._spinner.actor);
|
||||||
|
this._spinner.actor.hide();
|
||||||
|
},
|
||||||
|
|
||||||
_onIconBoxStyleChanged: function() {
|
_onIconBoxStyleChanged: function() {
|
||||||
let node = this._iconBox.get_theme_node();
|
let node = this._iconBox.get_theme_node();
|
||||||
this._iconBottomClip = node.get_length('app-icon-bottom-clip');
|
this._iconBottomClip = node.get_length('app-icon-bottom-clip');
|
||||||
this._updateIconBoxClip();
|
this._updateIconBoxClip();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_syncIcon: function() {
|
||||||
|
if (!this._targetApp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE, this._iconBox.text_direction);
|
||||||
|
this._iconBox.set_child(icon);
|
||||||
|
},
|
||||||
|
|
||||||
_onIconThemeChanged: function() {
|
_onIconThemeChanged: function() {
|
||||||
if (this._iconBox.child == null)
|
if (this._iconBox.child == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._iconBox.child.destroy();
|
this._syncIcon();
|
||||||
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
|
||||||
this._iconBox.set_child(icon);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateIconBoxClip: function() {
|
_updateIconBoxClip: function() {
|
||||||
@@ -385,7 +318,10 @@ const AppMenuButton = new Lang.Class({
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this._stop = true;
|
this._stop = true;
|
||||||
this.actor.reactive = true;
|
|
||||||
|
if (this._spinner == null)
|
||||||
|
return;
|
||||||
|
|
||||||
Tweener.addTween(this._spinner.actor,
|
Tweener.addTween(this._spinner.actor,
|
||||||
{ opacity: 0,
|
{ opacity: 0,
|
||||||
time: SPINNER_ANIMATION_TIME,
|
time: SPINNER_ANIMATION_TIME,
|
||||||
@@ -401,7 +337,10 @@ const AppMenuButton = new Lang.Class({
|
|||||||
|
|
||||||
startAnimation: function() {
|
startAnimation: function() {
|
||||||
this._stop = false;
|
this._stop = false;
|
||||||
this.actor.reactive = false;
|
|
||||||
|
if (this._spinner == null)
|
||||||
|
return;
|
||||||
|
|
||||||
this._spinner.play();
|
this._spinner.play();
|
||||||
this._spinner.actor.show();
|
this._spinner.actor.show();
|
||||||
},
|
},
|
||||||
@@ -410,7 +349,7 @@ const AppMenuButton = new Lang.Class({
|
|||||||
let [minSize, naturalSize] = this._iconBox.get_preferred_width(forHeight);
|
let [minSize, naturalSize] = this._iconBox.get_preferred_width(forHeight);
|
||||||
alloc.min_size = minSize;
|
alloc.min_size = minSize;
|
||||||
alloc.natural_size = naturalSize;
|
alloc.natural_size = naturalSize;
|
||||||
[minSize, naturalSize] = this._label.actor.get_preferred_width(forHeight);
|
[minSize, naturalSize] = this._hbox.get_preferred_width(forHeight);
|
||||||
alloc.min_size = alloc.min_size + Math.max(0, minSize - Math.floor(alloc.min_size / 2));
|
alloc.min_size = alloc.min_size + Math.max(0, minSize - Math.floor(alloc.min_size / 2));
|
||||||
alloc.natural_size = alloc.natural_size + Math.max(0, naturalSize - Math.floor(alloc.natural_size / 2));
|
alloc.natural_size = alloc.natural_size + Math.max(0, naturalSize - Math.floor(alloc.natural_size / 2));
|
||||||
},
|
},
|
||||||
@@ -419,7 +358,7 @@ const AppMenuButton = new Lang.Class({
|
|||||||
let [minSize, naturalSize] = this._iconBox.get_preferred_height(forWidth);
|
let [minSize, naturalSize] = this._iconBox.get_preferred_height(forWidth);
|
||||||
alloc.min_size = minSize;
|
alloc.min_size = minSize;
|
||||||
alloc.natural_size = naturalSize;
|
alloc.natural_size = naturalSize;
|
||||||
[minSize, naturalSize] = this._label.actor.get_preferred_height(forWidth);
|
[minSize, naturalSize] = this._hbox.get_preferred_height(forWidth);
|
||||||
if (minSize > alloc.min_size)
|
if (minSize > alloc.min_size)
|
||||||
alloc.min_size = minSize;
|
alloc.min_size = minSize;
|
||||||
if (naturalSize > alloc.natural_size)
|
if (naturalSize > alloc.natural_size)
|
||||||
@@ -449,11 +388,10 @@ const AppMenuButton = new Lang.Class({
|
|||||||
|
|
||||||
let iconWidth = childBox.x2 - childBox.x1;
|
let iconWidth = childBox.x2 - childBox.x1;
|
||||||
|
|
||||||
[minWidth, minHeight, naturalWidth, naturalHeight] = this._label.actor.get_preferred_size();
|
[minWidth, naturalWidth] = this._hbox.get_preferred_width(-1);
|
||||||
|
|
||||||
yPadding = Math.floor(Math.max(0, allocHeight - naturalHeight) / 2);
|
childBox.y1 = 0;
|
||||||
childBox.y1 = yPadding;
|
childBox.y2 = allocHeight;
|
||||||
childBox.y2 = childBox.y1 + Math.min(naturalHeight, allocHeight);
|
|
||||||
|
|
||||||
if (direction == Clutter.TextDirection.LTR) {
|
if (direction == Clutter.TextDirection.LTR) {
|
||||||
childBox.x1 = Math.floor(iconWidth / 2);
|
childBox.x1 = Math.floor(iconWidth / 2);
|
||||||
@@ -462,21 +400,7 @@ const AppMenuButton = new Lang.Class({
|
|||||||
childBox.x2 = allocWidth - Math.floor(iconWidth / 2);
|
childBox.x2 = allocWidth - Math.floor(iconWidth / 2);
|
||||||
childBox.x1 = Math.max(0, childBox.x2 - naturalWidth);
|
childBox.x1 = Math.max(0, childBox.x2 - naturalWidth);
|
||||||
}
|
}
|
||||||
this._label.actor.allocate(childBox, flags);
|
this._hbox.allocate(childBox, flags);
|
||||||
|
|
||||||
if (direction == Clutter.TextDirection.LTR) {
|
|
||||||
childBox.x1 = Math.floor(iconWidth / 2) + this._label.actor.width;
|
|
||||||
childBox.x2 = childBox.x1 + this._spinner.actor.width;
|
|
||||||
childBox.y1 = box.y1;
|
|
||||||
childBox.y2 = box.y2 - 1;
|
|
||||||
this._spinner.actor.allocate(childBox, flags);
|
|
||||||
} else {
|
|
||||||
childBox.x1 = -this._spinner.actor.width;
|
|
||||||
childBox.x2 = childBox.x1 + this._spinner.actor.width;
|
|
||||||
childBox.y1 = box.y1;
|
|
||||||
childBox.y2 = box.y2 - 1;
|
|
||||||
this._spinner.actor.allocate(childBox, flags);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onAppStateChanged: function(appSys, app) {
|
_onAppStateChanged: function(appSys, app) {
|
||||||
@@ -502,104 +426,88 @@ const AppMenuButton = new Lang.Class({
|
|||||||
// If the app has just lost focus to the panel, pretend
|
// If the app has just lost focus to the panel, pretend
|
||||||
// nothing happened; otherwise you can't keynav to the
|
// nothing happened; otherwise you can't keynav to the
|
||||||
// app menu.
|
// app menu.
|
||||||
if (global.stage_input_mode == Shell.StageInputMode.FOCUSED)
|
if (global.stage.key_focus != null)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._sync();
|
this._sync();
|
||||||
},
|
},
|
||||||
|
|
||||||
_sync: function() {
|
_findTargetApp: function() {
|
||||||
|
let workspace = global.screen.get_active_workspace();
|
||||||
let tracker = Shell.WindowTracker.get_default();
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
let focusedApp = tracker.focus_app;
|
let focusedApp = tracker.focus_app;
|
||||||
let lastStartedApp = null;
|
if (focusedApp && focusedApp.is_on_workspace(workspace))
|
||||||
let workspace = global.screen.get_active_workspace();
|
return focusedApp;
|
||||||
|
|
||||||
for (let i = 0; i < this._startingApps.length; i++)
|
for (let i = 0; i < this._startingApps.length; i++)
|
||||||
if (this._startingApps[i].is_on_workspace(workspace))
|
if (this._startingApps[i].is_on_workspace(workspace))
|
||||||
lastStartedApp = this._startingApps[i];
|
return this._startingApps[i];
|
||||||
|
|
||||||
let targetApp = focusedApp != null ? focusedApp : lastStartedApp;
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
if (targetApp == null) {
|
_sync: function() {
|
||||||
if (!this._targetIsCurrent)
|
let targetApp = this._findTargetApp();
|
||||||
return;
|
|
||||||
|
|
||||||
this.actor.reactive = false;
|
if (this._targetApp != targetApp) {
|
||||||
this._targetIsCurrent = false;
|
if (this._appMenuNotifyId) {
|
||||||
|
|
||||||
Tweener.removeTweens(this.actor);
|
|
||||||
Tweener.addTween(this.actor, { opacity: 0,
|
|
||||||
time: Overview.ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetApp.is_on_workspace(workspace))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!this._targetIsCurrent) {
|
|
||||||
this.actor.reactive = true;
|
|
||||||
this._targetIsCurrent = true;
|
|
||||||
|
|
||||||
Tweener.removeTweens(this.actor);
|
|
||||||
Tweener.addTween(this.actor, { opacity: 255,
|
|
||||||
time: Overview.ANIMATION_TIME,
|
|
||||||
transition: 'easeOutQuad' });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetApp == this._targetApp) {
|
|
||||||
if (targetApp && targetApp.get_state() != Shell.AppState.STARTING) {
|
|
||||||
this.stopAnimation();
|
|
||||||
this._maybeSetMenu();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._spinner.actor.hide();
|
|
||||||
if (this._iconBox.child != null)
|
|
||||||
this._iconBox.child.destroy();
|
|
||||||
this._iconBox.hide();
|
|
||||||
this._label.setText('');
|
|
||||||
|
|
||||||
if (this._appMenuNotifyId)
|
|
||||||
this._targetApp.disconnect(this._appMenuNotifyId);
|
this._targetApp.disconnect(this._appMenuNotifyId);
|
||||||
if (this._actionGroupNotifyId)
|
|
||||||
this._targetApp.disconnect(this._actionGroupNotifyId);
|
|
||||||
if (targetApp) {
|
|
||||||
this._appMenuNotifyId = targetApp.connect('notify::menu', Lang.bind(this, this._sync));
|
|
||||||
this._actionGroupNotifyId = targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
|
|
||||||
} else {
|
|
||||||
this._appMenuNotifyId = 0;
|
this._appMenuNotifyId = 0;
|
||||||
|
}
|
||||||
|
if (this._actionGroupNotifyId) {
|
||||||
|
this._targetApp.disconnect(this._actionGroupNotifyId);
|
||||||
this._actionGroupNotifyId = 0;
|
this._actionGroupNotifyId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._targetApp = targetApp;
|
this._targetApp = targetApp;
|
||||||
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
|
|
||||||
|
|
||||||
this._label.setText(targetApp.get_name());
|
if (this._targetApp) {
|
||||||
this.setName(targetApp.get_name());
|
this._appMenuNotifyId = this._targetApp.connect('notify::menu', Lang.bind(this, this._sync));
|
||||||
|
this._actionGroupNotifyId = this._targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
|
||||||
|
this._label.setText(this._targetApp.get_name());
|
||||||
|
this.actor.set_accessible_name(this._targetApp.get_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._iconBox.set_child(icon);
|
let visible = (this._targetApp != null && !Main.overview.visibleTarget);
|
||||||
this._iconBox.show();
|
if (visible)
|
||||||
|
this.show();
|
||||||
|
else
|
||||||
|
this.hide();
|
||||||
|
|
||||||
if (targetApp.get_state() == Shell.AppState.STARTING)
|
let isBusy = (this._targetApp != null &&
|
||||||
|
(this._targetApp.get_state() == Shell.AppState.STARTING ||
|
||||||
|
this._targetApp.get_state() == Shell.AppState.BUSY));
|
||||||
|
if (isBusy)
|
||||||
this.startAnimation();
|
this.startAnimation();
|
||||||
else
|
else
|
||||||
this._maybeSetMenu();
|
this.stopAnimation();
|
||||||
|
|
||||||
|
this.actor.reactive = (visible && !isBusy);
|
||||||
|
|
||||||
|
this._syncIcon();
|
||||||
|
this._maybeSetMenu();
|
||||||
this.emit('changed');
|
this.emit('changed');
|
||||||
},
|
},
|
||||||
|
|
||||||
_maybeSetMenu: function() {
|
_maybeSetMenu: function() {
|
||||||
let menu;
|
let menu;
|
||||||
|
|
||||||
if (this._targetApp.action_group && this._targetApp.menu) {
|
if (this._targetApp == null) {
|
||||||
if (this.menu instanceof PopupMenu.RemoteMenu &&
|
menu = null;
|
||||||
|
} else if (this._targetApp.action_group && this._targetApp.menu) {
|
||||||
|
if (this.menu instanceof RemoteMenu.RemoteMenu &&
|
||||||
this.menu.actionGroup == this._targetApp.action_group)
|
this.menu.actionGroup == this._targetApp.action_group)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
|
menu = new RemoteMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
|
||||||
|
menu.connect('activate', Lang.bind(this, function() {
|
||||||
|
let win = this._targetApp.get_windows()[0];
|
||||||
|
win.check_alive(global.get_current_time());
|
||||||
|
}));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (this.menu.isDummyQuitMenu)
|
if (this.menu && this.menu.isDummyQuitMenu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// fallback to older menu
|
// fallback to older menu
|
||||||
@@ -611,7 +519,35 @@ const AppMenuButton = new Lang.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setMenu(menu);
|
this.setMenu(menu);
|
||||||
|
if (menu)
|
||||||
this._menuManager.addMenu(menu);
|
this._menuManager.addMenu(menu);
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
if (this._appStateChangedSignalId > 0) {
|
||||||
|
let appSys = Shell.AppSystem.get_default();
|
||||||
|
appSys.disconnect(this._appStateChangedSignalId);
|
||||||
|
this._appStateChangedSignalId = 0;
|
||||||
|
}
|
||||||
|
if (this._focusAppNotifyId > 0) {
|
||||||
|
let tracker = Shell.WindowTracker.get_default();
|
||||||
|
tracker.disconnect(this._focusAppNotifyId);
|
||||||
|
this._focusAppNotifyId = 0;
|
||||||
|
}
|
||||||
|
if (this._overviewHidingId > 0) {
|
||||||
|
Main.overview.disconnect(this._overviewHidingId);
|
||||||
|
this._overviewHidingId = 0;
|
||||||
|
}
|
||||||
|
if (this._overviewShowingId > 0) {
|
||||||
|
Main.overview.disconnect(this._overviewShowingId);
|
||||||
|
this._overviewShowingId = 0;
|
||||||
|
}
|
||||||
|
if (this._switchWorkspaceNotifyId > 0) {
|
||||||
|
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||||
|
this._switchWorkspaceNotifyId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -625,23 +561,16 @@ const ActivitiesButton = new Lang.Class({
|
|||||||
this.parent(0.0, null, true);
|
this.parent(0.0, null, true);
|
||||||
this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;
|
this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;
|
||||||
|
|
||||||
let container = new Shell.GenericContainer();
|
|
||||||
container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
|
|
||||||
container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
|
|
||||||
container.connect('allocate', Lang.bind(this, this._containerAllocate));
|
|
||||||
this.actor.add_actor(container);
|
|
||||||
this.actor.name = 'panelActivities';
|
this.actor.name = 'panelActivities';
|
||||||
|
|
||||||
/* Translators: If there is no suitable word for "Activities"
|
/* Translators: If there is no suitable word for "Activities"
|
||||||
in your language, you can use the word for "Overview". */
|
in your language, you can use the word for "Overview". */
|
||||||
this._label = new St.Label({ text: _("Activities") });
|
this._label = new St.Label({ text: _("Activities"),
|
||||||
container.add_actor(this._label);
|
y_align: Clutter.ActorAlign.CENTER });
|
||||||
|
this.actor.add_actor(this._label);
|
||||||
|
|
||||||
this.actor.label_actor = this._label;
|
this.actor.label_actor = this._label;
|
||||||
|
|
||||||
this.hotCorner = new Layout.HotCorner();
|
|
||||||
container.add_actor(this.hotCorner.actor);
|
|
||||||
|
|
||||||
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||||
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
|
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
|
||||||
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
|
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
|
||||||
@@ -656,44 +585,6 @@ const ActivitiesButton = new Lang.Class({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
this._xdndTimeOut = 0;
|
this._xdndTimeOut = 0;
|
||||||
|
|
||||||
// Since the hot corner uses stage coordinates, Clutter won't
|
|
||||||
// queue relayouts for us when the panel moves. Queue a relayout
|
|
||||||
// when that happens.
|
|
||||||
Main.layoutManager.connect('panel-box-changed', Lang.bind(this, function() {
|
|
||||||
container.queue_relayout();
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
_containerGetPreferredWidth: function(actor, forHeight, alloc) {
|
|
||||||
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight);
|
|
||||||
},
|
|
||||||
|
|
||||||
_containerGetPreferredHeight: function(actor, forWidth, alloc) {
|
|
||||||
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth);
|
|
||||||
},
|
|
||||||
|
|
||||||
_containerAllocate: function(actor, box, flags) {
|
|
||||||
this._label.allocate(box, flags);
|
|
||||||
|
|
||||||
// The hot corner needs to be outside any padding/alignment
|
|
||||||
// that has been imposed on us
|
|
||||||
let primary = Main.layoutManager.primaryMonitor;
|
|
||||||
let hotBox = new Clutter.ActorBox();
|
|
||||||
let ok, x, y;
|
|
||||||
if (actor.get_text_direction() == Clutter.TextDirection.LTR) {
|
|
||||||
[ok, x, y] = actor.transform_stage_point(primary.x, primary.y)
|
|
||||||
} else {
|
|
||||||
[ok, x, y] = actor.transform_stage_point(primary.x + primary.width, primary.y);
|
|
||||||
// hotCorner.actor has northeast gravity, so we don't need
|
|
||||||
// to adjust x for its width
|
|
||||||
}
|
|
||||||
|
|
||||||
hotBox.x1 = Math.round(x);
|
|
||||||
hotBox.x2 = hotBox.x1 + this.hotCorner.actor.width;
|
|
||||||
hotBox.y1 = Math.round(y);
|
|
||||||
hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height;
|
|
||||||
this.hotCorner.actor.allocate(hotBox, flags);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDragOver: function(source, actor, x, y, time) {
|
handleDragOver: function(source, actor, x, y, time) {
|
||||||
@@ -703,21 +594,23 @@ const ActivitiesButton = new Lang.Class({
|
|||||||
if (this._xdndTimeOut != 0)
|
if (this._xdndTimeOut != 0)
|
||||||
Mainloop.source_remove(this._xdndTimeOut);
|
Mainloop.source_remove(this._xdndTimeOut);
|
||||||
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
|
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
|
||||||
Lang.bind(this, this._xdndShowOverview, actor));
|
Lang.bind(this, this._xdndToggleOverview, actor));
|
||||||
|
|
||||||
return DND.DragMotionResult.CONTINUE;
|
return DND.DragMotionResult.CONTINUE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCapturedEvent: function(actor, event) {
|
_onCapturedEvent: function(actor, event) {
|
||||||
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||||
if (!this.hotCorner.shouldToggleOverviewOnClick())
|
if (!Main.overview.shouldToggleByCornerOrButton())
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
}
|
}
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onButtonRelease: function() {
|
_onButtonRelease: function() {
|
||||||
Main.overview.toggle();
|
Main.overview.toggle();
|
||||||
|
this.menu.close();
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onKeyRelease: function(actor, event) {
|
_onKeyRelease: function(actor, event) {
|
||||||
@@ -725,33 +618,28 @@ const ActivitiesButton = new Lang.Class({
|
|||||||
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
|
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
|
||||||
Main.overview.toggle();
|
Main.overview.toggle();
|
||||||
}
|
}
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_xdndShowOverview: function(actor) {
|
_xdndToggleOverview: function(actor) {
|
||||||
let [x, y, mask] = global.get_pointer();
|
let [x, y, mask] = global.get_pointer();
|
||||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||||
|
|
||||||
if (pickedActor == this.actor) {
|
if (pickedActor == this.actor && Main.overview.shouldToggleByCornerOrButton())
|
||||||
if (!Main.overview.visible && !Main.overview.animationInProgress) {
|
Main.overview.toggle();
|
||||||
Main.overview.showTemporarily();
|
|
||||||
Main.overview.beginItemDrag(actor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Mainloop.source_remove(this._xdndTimeOut);
|
Mainloop.source_remove(this._xdndTimeOut);
|
||||||
this._xdndTimeOut = 0;
|
this._xdndTimeOut = 0;
|
||||||
|
return GLib.SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const PanelCorner = new Lang.Class({
|
const PanelCorner = new Lang.Class({
|
||||||
Name: 'PanelCorner',
|
Name: 'PanelCorner',
|
||||||
|
|
||||||
_init: function(box, side) {
|
_init: function(side) {
|
||||||
this._side = side;
|
this._side = side;
|
||||||
|
|
||||||
this._box = box;
|
|
||||||
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
|
|
||||||
|
|
||||||
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
|
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
|
||||||
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
|
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
|
||||||
this.actor.connect('repaint', Lang.bind(this, this._repaint));
|
this.actor.connect('repaint', Lang.bind(this, this._repaint));
|
||||||
@@ -807,12 +695,12 @@ const PanelCorner = new Lang.Class({
|
|||||||
return children[index];
|
return children[index];
|
||||||
},
|
},
|
||||||
|
|
||||||
_boxStyleChanged: function() {
|
setStyleParent: function(box) {
|
||||||
let side = this._side;
|
let side = this._side;
|
||||||
|
|
||||||
let rtlAwareContainer = this._box instanceof St.BoxLayout;
|
let rtlAwareContainer = box instanceof St.BoxLayout;
|
||||||
if (rtlAwareContainer &&
|
if (rtlAwareContainer &&
|
||||||
this._box.get_text_direction() == Clutter.TextDirection.RTL) {
|
box.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||||
if (this._side == St.Side.LEFT)
|
if (this._side == St.Side.LEFT)
|
||||||
side = St.Side.RIGHT;
|
side = St.Side.RIGHT;
|
||||||
else if (this._side == St.Side.RIGHT)
|
else if (this._side == St.Side.RIGHT)
|
||||||
@@ -821,9 +709,9 @@ const PanelCorner = new Lang.Class({
|
|||||||
|
|
||||||
let button;
|
let button;
|
||||||
if (side == St.Side.LEFT)
|
if (side == St.Side.LEFT)
|
||||||
button = this._findLeftmostButton(this._box);
|
button = this._findLeftmostButton(box);
|
||||||
else if (side == St.Side.RIGHT)
|
else if (side == St.Side.RIGHT)
|
||||||
button = this._findRightmostButton(this._box);
|
button = this._findRightmostButton(box);
|
||||||
|
|
||||||
if (button) {
|
if (button) {
|
||||||
if (this._button && this._buttonStyleChangedSignalId) {
|
if (this._button && this._buttonStyleChangedSignalId) {
|
||||||
@@ -850,7 +738,7 @@ const PanelCorner = new Lang.Class({
|
|||||||
|
|
||||||
// The corner doesn't support theme transitions, so override
|
// The corner doesn't support theme transitions, so override
|
||||||
// the .panel-button default
|
// the .panel-button default
|
||||||
button.style = 'transition-duration: 0';
|
button.style = 'transition-duration: 0ms';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -863,8 +751,8 @@ const PanelCorner = new Lang.Class({
|
|||||||
let backgroundColor = node.get_color('-panel-corner-background-color');
|
let backgroundColor = node.get_color('-panel-corner-background-color');
|
||||||
let borderColor = node.get_color('-panel-corner-border-color');
|
let borderColor = node.get_color('-panel-corner-border-color');
|
||||||
|
|
||||||
let noOverlap = borderColor.alpha == 0;
|
let overlap = borderColor.alpha != 0;
|
||||||
let offsetY = noOverlap ? borderWidth : 0;
|
let offsetY = overlap ? 0 : borderWidth;
|
||||||
|
|
||||||
let cr = this.actor.get_context();
|
let cr = this.actor.get_context();
|
||||||
cr.setOperator(Cairo.Operator.SOURCE);
|
cr.setOperator(Cairo.Operator.SOURCE);
|
||||||
@@ -888,9 +776,7 @@ const PanelCorner = new Lang.Class({
|
|||||||
Clutter.cairo_set_source_color(cr, over);
|
Clutter.cairo_set_source_color(cr, over);
|
||||||
cr.fill();
|
cr.fill();
|
||||||
|
|
||||||
if (noOverlap)
|
if (overlap) {
|
||||||
return;
|
|
||||||
|
|
||||||
let offset = borderWidth;
|
let offset = borderWidth;
|
||||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||||
|
|
||||||
@@ -899,6 +785,9 @@ const PanelCorner = new Lang.Class({
|
|||||||
cr.appendPath(savedPath);
|
cr.appendPath(savedPath);
|
||||||
cr.fill();
|
cr.fill();
|
||||||
cr.restore();
|
cr.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
cr.$dispose();
|
||||||
},
|
},
|
||||||
|
|
||||||
_styleChanged: function() {
|
_styleChanged: function() {
|
||||||
@@ -912,31 +801,67 @@ const PanelCorner = new Lang.Class({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const AggregateMenu = new Lang.Class({
|
||||||
|
Name: 'AggregateMenu',
|
||||||
|
Extends: PanelMenu.Button,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent(0.0, _("Settings"), false);
|
||||||
|
this.menu.actor.add_style_class_name('aggregate-menu');
|
||||||
|
|
||||||
|
this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
|
||||||
|
this.actor.add_child(this._indicators);
|
||||||
|
|
||||||
|
this._network = new imports.ui.status.network.NMApplet();
|
||||||
|
if (Config.HAVE_BLUETOOTH) {
|
||||||
|
this._bluetooth = new imports.ui.status.bluetooth.Indicator();
|
||||||
|
} else {
|
||||||
|
this._bluetooth = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._power = new imports.ui.status.power.Indicator();
|
||||||
|
this._rfkill = new imports.ui.status.rfkill.Indicator();
|
||||||
|
this._volume = new imports.ui.status.volume.Indicator();
|
||||||
|
this._brightness = new imports.ui.status.brightness.Indicator();
|
||||||
|
this._system = new imports.ui.status.system.Indicator();
|
||||||
|
this._screencast = new imports.ui.status.screencast.Indicator();
|
||||||
|
this._location = new imports.ui.status.location.Indicator();
|
||||||
|
|
||||||
|
this._indicators.add_child(this._screencast.indicators);
|
||||||
|
this._indicators.add_child(this._location.indicators);
|
||||||
|
this._indicators.add_child(this._network.indicators);
|
||||||
|
if (this._bluetooth) {
|
||||||
|
this._indicators.add_child(this._bluetooth.indicators);
|
||||||
|
}
|
||||||
|
this._indicators.add_child(this._rfkill.indicators);
|
||||||
|
this._indicators.add_child(this._volume.indicators);
|
||||||
|
this._indicators.add_child(this._power.indicators);
|
||||||
|
this._indicators.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
|
||||||
|
|
||||||
|
this.menu.addMenuItem(this._volume.menu);
|
||||||
|
this.menu.addMenuItem(this._brightness.menu);
|
||||||
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
this.menu.addMenuItem(this._network.menu);
|
||||||
|
if (this._bluetooth) {
|
||||||
|
this.menu.addMenuItem(this._bluetooth.menu);
|
||||||
|
}
|
||||||
|
this.menu.addMenuItem(this._rfkill.menu);
|
||||||
|
this.menu.addMenuItem(this._power.menu);
|
||||||
|
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||||
|
this.menu.addMenuItem(this._system.menu);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const PANEL_ITEM_IMPLEMENTATIONS = {
|
const PANEL_ITEM_IMPLEMENTATIONS = {
|
||||||
'activities': ActivitiesButton,
|
'activities': ActivitiesButton,
|
||||||
|
'aggregateMenu': AggregateMenu,
|
||||||
'appMenu': AppMenuButton,
|
'appMenu': AppMenuButton,
|
||||||
'dateMenu': imports.ui.dateMenu.DateMenuButton,
|
'dateMenu': imports.ui.dateMenu.DateMenuButton,
|
||||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||||
'volume': imports.ui.status.volume.Indicator,
|
'a11yGreeter': imports.ui.status.accessibility.ATGreeterIndicator,
|
||||||
'battery': imports.ui.status.power.Indicator,
|
|
||||||
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
|
|
||||||
'logo': imports.gdm.loginDialog.LogoMenuButton,
|
|
||||||
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
|
||||||
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
|
|
||||||
'userMenu': imports.ui.userMenu.UserMenuButton
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Config.HAVE_BLUETOOTH)
|
|
||||||
PANEL_ITEM_IMPLEMENTATIONS['bluetooth'] =
|
|
||||||
imports.ui.status.bluetooth.Indicator;
|
|
||||||
|
|
||||||
try {
|
|
||||||
PANEL_ITEM_IMPLEMENTATIONS['network'] =
|
|
||||||
imports.ui.status.network.NMApplet;
|
|
||||||
} catch(e) {
|
|
||||||
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
|
|
||||||
}
|
|
||||||
|
|
||||||
const Panel = new Lang.Class({
|
const Panel = new Lang.Class({
|
||||||
Name: 'Panel',
|
Name: 'Panel',
|
||||||
|
|
||||||
@@ -949,7 +874,7 @@ const Panel = new Lang.Class({
|
|||||||
|
|
||||||
this.statusArea = {};
|
this.statusArea = {};
|
||||||
|
|
||||||
this.menuManager = new PopupMenu.PopupMenuManager(this);
|
this.menuManager = new PopupMenu.PopupMenuManager(this, { keybindingMode: Shell.KeyBindingMode.TOPBAR_POPUP });
|
||||||
|
|
||||||
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
|
||||||
this.actor.add_actor(this._leftBox);
|
this.actor.add_actor(this._leftBox);
|
||||||
@@ -958,17 +883,10 @@ const Panel = new Lang.Class({
|
|||||||
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
||||||
this.actor.add_actor(this._rightBox);
|
this.actor.add_actor(this._rightBox);
|
||||||
|
|
||||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
this._leftCorner = new PanelCorner(St.Side.LEFT);
|
||||||
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
|
|
||||||
else
|
|
||||||
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
|
|
||||||
|
|
||||||
this.actor.add_actor(this._leftCorner.actor);
|
this.actor.add_actor(this._leftCorner.actor);
|
||||||
|
|
||||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
this._rightCorner = new PanelCorner(St.Side.RIGHT);
|
||||||
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
|
|
||||||
else
|
|
||||||
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
|
|
||||||
this.actor.add_actor(this._rightCorner.actor);
|
this.actor.add_actor(this._rightCorner.actor);
|
||||||
|
|
||||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||||
@@ -976,7 +894,14 @@ const Panel = new Lang.Class({
|
|||||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||||
|
|
||||||
Main.layoutManager.panelBox.add(this.actor);
|
Main.overview.connect('showing', Lang.bind(this, function () {
|
||||||
|
this.actor.add_style_pseudo_class('overview');
|
||||||
|
}));
|
||||||
|
Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||||
|
this.actor.remove_style_pseudo_class('overview');
|
||||||
|
}));
|
||||||
|
|
||||||
|
Main.layoutManager.panelGroup.add_child(this.actor);
|
||||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
|
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
|
||||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||||
|
|
||||||
@@ -1062,21 +987,24 @@ const Panel = new Lang.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onButtonPress: function(actor, event) {
|
_onButtonPress: function(actor, event) {
|
||||||
|
if (Main.modalCount > 0)
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
if (event.get_source() != actor)
|
if (event.get_source() != actor)
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
let button = event.get_button();
|
let button = event.get_button();
|
||||||
if (button != 1)
|
if (button != 1)
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
let focusWindow = global.display.focus_window;
|
let focusWindow = global.display.focus_window;
|
||||||
if (!focusWindow)
|
if (!focusWindow)
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for()
|
let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for()
|
||||||
: focusWindow;
|
: focusWindow;
|
||||||
if (!dragWindow)
|
if (!dragWindow)
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
let rect = dragWindow.get_outer_rect();
|
let rect = dragWindow.get_outer_rect();
|
||||||
let [stageX, stageY] = event.get_coords();
|
let [stageX, stageY] = event.get_coords();
|
||||||
@@ -1085,7 +1013,7 @@ const Panel = new Lang.Class({
|
|||||||
stageX > rect.x && stageX < rect.x + rect.width;
|
stageX > rect.x && stageX < rect.x + rect.width;
|
||||||
|
|
||||||
if (!allowDrag)
|
if (!allowDrag)
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
global.display.begin_grab_op(global.screen,
|
global.display.begin_grab_op(global.screen,
|
||||||
dragWindow,
|
dragWindow,
|
||||||
@@ -1097,19 +1025,20 @@ const Panel = new Lang.Class({
|
|||||||
event.get_time(),
|
event.get_time(),
|
||||||
stageX, stageY);
|
stageX, stageY);
|
||||||
|
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
},
|
},
|
||||||
|
|
||||||
openAppMenu: function() {
|
toggleAppMenu: function() {
|
||||||
let indicator = this.statusArea.appMenu;
|
let indicator = this.statusArea.appMenu;
|
||||||
if (!indicator) // appMenu not supported by current session mode
|
if (!indicator) // appMenu not supported by current session mode
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let menu = indicator.menu;
|
let menu = indicator.menu;
|
||||||
if (!indicator.actor.reactive || menu.isOpen)
|
if (!indicator.actor.reactive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
menu.open();
|
menu.toggle();
|
||||||
|
if (menu.isOpen)
|
||||||
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1141,17 +1070,13 @@ const Panel = new Lang.Class({
|
|||||||
this._sessionStyle = Main.sessionMode.panelStyle;
|
this._sessionStyle = Main.sessionMode.panelStyle;
|
||||||
if (this._sessionStyle)
|
if (this._sessionStyle)
|
||||||
this._addStyleClassName(this._sessionStyle);
|
this._addStyleClassName(this._sessionStyle);
|
||||||
},
|
|
||||||
|
|
||||||
_initBox: function(elements, box) {
|
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||||
for (let i = 0; i < elements.length; i++) {
|
this._leftCorner.setStyleParent(this._rightBox);
|
||||||
let role = elements[i];
|
this._rightCorner.setStyleParent(this._leftBox);
|
||||||
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
|
} else {
|
||||||
if (!constructor) {
|
this._leftCorner.setStyleParent(this._leftBox);
|
||||||
// panel icon is not supported (can happen for
|
this._rightCorner.setStyleParent(this._rightBox);
|
||||||
// bluetooth or network)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -86,13 +86,8 @@ const ButtonBox = new Lang.Class({
|
|||||||
childBox.x2 = availWidth - this._minHPadding;
|
childBox.x2 = availWidth - this._minHPadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (natHeight <= availHeight) {
|
|
||||||
childBox.y1 = Math.floor((availHeight - natHeight) / 2);
|
|
||||||
childBox.y2 = childBox.y1 + natHeight;
|
|
||||||
} else {
|
|
||||||
childBox.y1 = 0;
|
childBox.y1 = 0;
|
||||||
childBox.y2 = availHeight;
|
childBox.y2 = availHeight;
|
||||||
}
|
|
||||||
|
|
||||||
child.allocate(childBox, flags);
|
child.allocate(childBox, flags);
|
||||||
},
|
},
|
||||||
@@ -106,17 +101,17 @@ const Button = new Lang.Class({
|
|||||||
this.parent({ reactive: true,
|
this.parent({ reactive: true,
|
||||||
can_focus: true,
|
can_focus: true,
|
||||||
track_hover: true,
|
track_hover: true,
|
||||||
|
accessible_name: nameText ? nameText : "",
|
||||||
accessible_role: Atk.Role.MENU });
|
accessible_role: Atk.Role.MENU });
|
||||||
|
|
||||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||||
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
|
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
|
||||||
|
this.actor.connect('notify::visible', Lang.bind(this, this._onVisibilityChanged));
|
||||||
|
|
||||||
if (dontCreateMenu)
|
if (dontCreateMenu)
|
||||||
this.menu = new PopupMenu.PopupDummyMenu(this.actor);
|
this.menu = new PopupMenu.PopupDummyMenu(this.actor);
|
||||||
else
|
else
|
||||||
this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
|
this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
|
||||||
|
|
||||||
this.setName(nameText);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setSensitive: function(sensitive) {
|
setSensitive: function(sensitive) {
|
||||||
@@ -125,22 +120,6 @@ const Button = new Lang.Class({
|
|||||||
this.actor.track_hover = sensitive;
|
this.actor.track_hover = sensitive;
|
||||||
},
|
},
|
||||||
|
|
||||||
setName: function(text) {
|
|
||||||
if (text != null) {
|
|
||||||
// This is the easiest way to provide a accessible name to
|
|
||||||
// this widget. The label could be also used for other
|
|
||||||
// purposes in the future.
|
|
||||||
if (!this.label) {
|
|
||||||
this.label = new St.Label({ text: text });
|
|
||||||
this.actor.label_actor = this.label;
|
|
||||||
} else
|
|
||||||
this.label.text = text;
|
|
||||||
} else {
|
|
||||||
this.label = null;
|
|
||||||
this.actor.label_actor = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setMenu: function(menu) {
|
setMenu: function(menu) {
|
||||||
if (this.menu)
|
if (this.menu)
|
||||||
this.menu.destroy();
|
this.menu.destroy();
|
||||||
@@ -151,49 +130,61 @@ const Button = new Lang.Class({
|
|||||||
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
|
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
|
||||||
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
|
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
|
||||||
|
|
||||||
Main.uiGroup.add_actor(this.menu.actor);
|
Main.layoutManager.menuGroup.add_child(this.menu.actor);
|
||||||
this.menu.actor.hide();
|
this.menu.actor.hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onButtonPress: function(actor, event) {
|
_onButtonPress: function(actor, event) {
|
||||||
if (!this.menu)
|
if (!this.menu)
|
||||||
return;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
this.menu.toggle();
|
this.menu.toggle();
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSourceKeyPress: function(actor, event) {
|
_onSourceKeyPress: function(actor, event) {
|
||||||
if (!this.menu)
|
if (!this.menu)
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
|
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
|
||||||
this.menu.toggle();
|
this.menu.toggle();
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
|
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
|
||||||
this.menu.close();
|
this.menu.close();
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
} else if (symbol == Clutter.KEY_Down) {
|
} else if (symbol == Clutter.KEY_Down) {
|
||||||
if (!this.menu.isOpen)
|
if (!this.menu.isOpen)
|
||||||
this.menu.toggle();
|
this.menu.toggle();
|
||||||
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
|
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
} else
|
} else
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onVisibilityChanged: function() {
|
||||||
|
if (!this.menu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this.actor.visible)
|
||||||
|
this.menu.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMenuKeyPress: function(actor, event) {
|
_onMenuKeyPress: function(actor, event) {
|
||||||
|
if (global.focus_manager.navigate_from_event(event))
|
||||||
|
return Clutter.EVENT_STOP;
|
||||||
|
|
||||||
let symbol = event.get_key_symbol();
|
let symbol = event.get_key_symbol();
|
||||||
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
||||||
let group = global.focus_manager.get_group(this.actor);
|
let group = global.focus_manager.get_group(this.actor);
|
||||||
if (group) {
|
if (group) {
|
||||||
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
|
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
|
||||||
group.navigate_focus(this.actor, direction, false);
|
group.navigate_focus(this.actor, direction, false);
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onOpenStateChanged: function(menu, open) {
|
_onOpenStateChanged: function(menu, open) {
|
||||||
@@ -205,10 +196,8 @@ const Button = new Lang.Class({
|
|||||||
// Setting the max-height won't do any good if the minimum height of the
|
// Setting the max-height won't do any good if the minimum height of the
|
||||||
// menu is higher then the screen; it's useful if part of the menu is
|
// menu is higher then the screen; it's useful if part of the menu is
|
||||||
// scrollable so the minimum height is smaller than the natural height
|
// scrollable so the minimum height is smaller than the natural height
|
||||||
let monitor = Main.layoutManager.primaryMonitor;
|
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||||
this.menu.actor.style = ('max-height: ' +
|
this.menu.actor.style = ('max-height: ' + Math.round(workArea.height) + 'px;');
|
||||||
Math.round(monitor.height - Main.panel.actor.height) +
|
|
||||||
'px;');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
@@ -223,48 +212,35 @@ const Button = new Lang.Class({
|
|||||||
});
|
});
|
||||||
Signals.addSignalMethods(Button.prototype);
|
Signals.addSignalMethods(Button.prototype);
|
||||||
|
|
||||||
/* SystemStatusButton:
|
/* SystemIndicator:
|
||||||
*
|
*
|
||||||
* This class manages one System Status indicator (network, keyboard,
|
* This class manages one system indicator, which are the icons
|
||||||
* volume, bluetooth...), which is just a PanelMenuButton with an
|
* that you see at the top right. A system indicator is composed
|
||||||
* icon.
|
* of an icon and a menu section, which will be composed into the
|
||||||
|
* aggregate menu.
|
||||||
*/
|
*/
|
||||||
const SystemStatusButton = new Lang.Class({
|
const SystemIndicator = new Lang.Class({
|
||||||
Name: 'SystemStatusButton',
|
Name: 'SystemIndicator',
|
||||||
Extends: Button,
|
|
||||||
|
|
||||||
_init: function(iconName, nameText) {
|
_init: function() {
|
||||||
this.parent(0.0, nameText);
|
this.indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box',
|
||||||
this.actor.add_style_class_name('panel-status-button');
|
reactive: true });
|
||||||
|
this.indicators.hide();
|
||||||
this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' });
|
this.menu = new PopupMenu.PopupMenuSection();
|
||||||
this.actor.add_actor(this._box);
|
|
||||||
|
|
||||||
if (iconName)
|
|
||||||
this.setIcon(iconName);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
addIcon: function(gicon) {
|
_syncIndicatorsVisible: function() {
|
||||||
let icon = new St.Icon({ gicon: gicon,
|
this.indicators.visible = this.indicators.get_children().some(function(actor) {
|
||||||
style_class: 'system-status-icon' });
|
return actor.visible;
|
||||||
this._box.add_actor(icon);
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_addIndicator: function() {
|
||||||
|
let icon = new St.Icon({ style_class: 'system-status-icon' });
|
||||||
|
this.indicators.add_actor(icon);
|
||||||
|
icon.connect('notify::visible', Lang.bind(this, this._syncIndicatorsVisible));
|
||||||
|
this._syncIndicatorsVisible();
|
||||||
return icon;
|
return icon;
|
||||||
},
|
|
||||||
|
|
||||||
setIcon: function(iconName) {
|
|
||||||
// Need to first add a NULL GIcon and then set icon_name, to ensure
|
|
||||||
// compatibility with -symbolic fallbacks
|
|
||||||
|
|
||||||
if (!this.mainIcon)
|
|
||||||
this.mainIcon = this.addIcon(null);
|
|
||||||
this.mainIcon.icon_name = iconName;
|
|
||||||
},
|
|
||||||
|
|
||||||
setGIcon: function(gicon) {
|
|
||||||
if (this.mainIcon)
|
|
||||||
this.mainIcon.gicon = gicon;
|
|
||||||
else
|
|
||||||
this.mainIcon = this.addIcon(gicon);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Signals.addSignalMethods(SystemIndicator.prototype);
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
@@ -41,10 +43,9 @@ const PointerWatcher = new Lang.Class({
|
|||||||
Name: 'PointerWatcher',
|
Name: 'PointerWatcher',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
let idleMonitor = new GnomeDesktop.IdleMonitor();
|
this._idleMonitor = Meta.IdleMonitor.get_core();
|
||||||
idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
|
this._idleMonitor.add_idle_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
|
||||||
idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
|
this._idle = this._idleMonitor.get_idletime() > IDLE_TIME;
|
||||||
this._idle = idleMonitor.get_idletime() > IDLE_TIME;
|
|
||||||
this._watches = [];
|
this._watches = [];
|
||||||
this.pointerX = null;
|
this.pointerX = null;
|
||||||
this.pointerY = null;
|
this.pointerY = null;
|
||||||
@@ -87,6 +88,7 @@ const PointerWatcher = new Lang.Class({
|
|||||||
|
|
||||||
_onIdleMonitorBecameIdle: function(monitor) {
|
_onIdleMonitorBecameIdle: function(monitor) {
|
||||||
this._idle = true;
|
this._idle = true;
|
||||||
|
this._idleMonitor.add_user_active_watch(Lang.bind(this, this._onIdleMonitorBecameActive));
|
||||||
this._updateTimeout();
|
this._updateTimeout();
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -109,7 +111,7 @@ const PointerWatcher = new Lang.Class({
|
|||||||
|
|
||||||
_onTimeout: function() {
|
_onTimeout: function() {
|
||||||
this._updatePointer();
|
this._updatePointer();
|
||||||
return true;
|
return GLib.SOURCE_CONTINUE;
|
||||||
},
|
},
|
||||||
|
|
||||||
_updatePointer: function() {
|
_updatePointer: function() {
|
||||||
|
|||||||
1555
js/ui/popupMenu.js
1555
js/ui/popupMenu.js
File diff suppressed because it is too large
Load Diff
199
js/ui/remoteMenu.js
Normal file
199
js/ui/remoteMenu.js
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Atk = imports.gi.Atk;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const GObject = imports.gi.GObject;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const ShellMenu = imports.gi.ShellMenu;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const PopupMenu = imports.ui.popupMenu;
|
||||||
|
|
||||||
|
function stripMnemonics(label) {
|
||||||
|
if (!label)
|
||||||
|
return '';
|
||||||
|
|
||||||
|
// remove all underscores that are not followed by another underscore
|
||||||
|
return label.replace(/_([^_])/, '$1');
|
||||||
|
}
|
||||||
|
|
||||||
|
function _insertItem(menu, trackerItem, position) {
|
||||||
|
let mapper;
|
||||||
|
|
||||||
|
if (trackerItem.get_is_separator())
|
||||||
|
mapper = new RemoteMenuSeparatorItemMapper(trackerItem);
|
||||||
|
else if (trackerItem.get_has_submenu())
|
||||||
|
mapper = new RemoteMenuSubmenuItemMapper(trackerItem);
|
||||||
|
else
|
||||||
|
mapper = new RemoteMenuItemMapper(trackerItem);
|
||||||
|
|
||||||
|
let item = mapper.menuItem;
|
||||||
|
menu.addMenuItem(item, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _removeItem(menu, position) {
|
||||||
|
let items = menu._getMenuItems();
|
||||||
|
items[position].destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
const RemoteMenuSeparatorItemMapper = new Lang.Class({
|
||||||
|
Name: 'RemoteMenuSeparatorItemMapper',
|
||||||
|
|
||||||
|
_init: function(trackerItem) {
|
||||||
|
this._trackerItem = trackerItem;
|
||||||
|
this.menuItem = new PopupMenu.PopupSeparatorMenuItem();
|
||||||
|
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
|
||||||
|
this._updateLabel();
|
||||||
|
|
||||||
|
this.menuItem.connect('destroy', function() {
|
||||||
|
trackerItem.run_dispose();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLabel: function() {
|
||||||
|
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RequestSubMenu = new Lang.Class({
|
||||||
|
Name: 'RequestSubMenu',
|
||||||
|
Extends: PopupMenu.PopupSubMenuMenuItem,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent('');
|
||||||
|
this._requestOpen = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_setOpenState: function(open) {
|
||||||
|
this.emit('request-open', open);
|
||||||
|
this._requestOpen = open;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getOpenState: function() {
|
||||||
|
return this._requestOpen;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RemoteMenuSubmenuItemMapper = new Lang.Class({
|
||||||
|
Name: 'RemoteMenuSubmenuItemMapper',
|
||||||
|
|
||||||
|
_init: function(trackerItem) {
|
||||||
|
this._trackerItem = trackerItem;
|
||||||
|
this.menuItem = new RequestSubMenu();
|
||||||
|
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
|
||||||
|
this._updateLabel();
|
||||||
|
|
||||||
|
this._tracker = Shell.MenuTracker.new_for_item_submenu(this._trackerItem,
|
||||||
|
_insertItem.bind(null, this.menuItem.menu),
|
||||||
|
_removeItem.bind(null, this.menuItem.menu));
|
||||||
|
|
||||||
|
this.menuItem.connect('request-open', Lang.bind(this, function(menu, open) {
|
||||||
|
this._trackerItem.request_submenu_shown(open);
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._trackerItem.connect('notify::submenu-shown', Lang.bind(this, function() {
|
||||||
|
this.menuItem.setSubmenuShown(this._trackerItem.get_submenu_shown());
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.menuItem.connect('destroy', function() {
|
||||||
|
trackerItem.run_dispose();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._tracker.destroy();
|
||||||
|
this.parent();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLabel: function() {
|
||||||
|
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RemoteMenuItemMapper = new Lang.Class({
|
||||||
|
Name: 'RemoteMenuItemMapper',
|
||||||
|
|
||||||
|
_init: function(trackerItem) {
|
||||||
|
this._trackerItem = trackerItem;
|
||||||
|
|
||||||
|
this.menuItem = new PopupMenu.PopupBaseMenuItem();
|
||||||
|
this._label = new St.Label();
|
||||||
|
this.menuItem.actor.add_child(this._label);
|
||||||
|
this.menuItem.actor.label_actor = this._label;
|
||||||
|
|
||||||
|
this.menuItem.connect('activate', Lang.bind(this, function() {
|
||||||
|
this._trackerItem.activated();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._trackerItem.bind_property('visible', this.menuItem.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||||
|
|
||||||
|
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
|
||||||
|
this._trackerItem.connect('notify::sensitive', Lang.bind(this, this._updateSensitivity));
|
||||||
|
this._trackerItem.connect('notify::role', Lang.bind(this, this._updateRole));
|
||||||
|
this._trackerItem.connect('notify::toggled', Lang.bind(this, this._updateDecoration));
|
||||||
|
|
||||||
|
this._updateLabel();
|
||||||
|
this._updateSensitivity();
|
||||||
|
this._updateRole();
|
||||||
|
|
||||||
|
this.menuItem.connect('destroy', function() {
|
||||||
|
trackerItem.run_dispose();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateLabel: function() {
|
||||||
|
this._label.text = stripMnemonics(this._trackerItem.label);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateSensitivity: function() {
|
||||||
|
this.menuItem.setSensitive(this._trackerItem.sensitive);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateDecoration: function() {
|
||||||
|
let ornamentForRole = {};
|
||||||
|
ornamentForRole[ShellMenu.MenuTrackerItemRole.RADIO] = PopupMenu.Ornament.DOT;
|
||||||
|
ornamentForRole[ShellMenu.MenuTrackerItemRole.CHECK] = PopupMenu.Ornament.CHECK;
|
||||||
|
|
||||||
|
let ornament = PopupMenu.Ornament.NONE;
|
||||||
|
if (this._trackerItem.toggled)
|
||||||
|
ornament = ornamentForRole[this._trackerItem.role];
|
||||||
|
|
||||||
|
this.menuItem.setOrnament(ornament);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateRole: function() {
|
||||||
|
let a11yRoles = {};
|
||||||
|
a11yRoles[ShellMenu.MenuTrackerItemRole.NORMAL] = Atk.Role.MENU_ITEM;
|
||||||
|
a11yRoles[ShellMenu.MenuTrackerItemRole.RADIO] = Atk.Role.RADIO_MENU_ITEM;
|
||||||
|
a11yRoles[ShellMenu.MenuTrackerItemRole.CHECK] = Atk.Role.CHECK_MENU_ITEM;
|
||||||
|
|
||||||
|
let a11yRole = a11yRoles[this._trackerItem.role];
|
||||||
|
this.menuItem.actor.accessible_role = a11yRole;
|
||||||
|
|
||||||
|
this._updateDecoration();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const RemoteMenu = new Lang.Class({
|
||||||
|
Name: 'RemoteMenu',
|
||||||
|
Extends: PopupMenu.PopupMenu,
|
||||||
|
|
||||||
|
_init: function(sourceActor, model, actionGroup) {
|
||||||
|
this.parent(sourceActor, 0.0, St.Side.TOP);
|
||||||
|
|
||||||
|
this._model = model;
|
||||||
|
this._actionGroup = actionGroup;
|
||||||
|
this._tracker = Shell.MenuTracker.new(this._actionGroup,
|
||||||
|
this._model,
|
||||||
|
null, /* action namespace */
|
||||||
|
_insertItem.bind(null, this),
|
||||||
|
_removeItem.bind(null, this));
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this._tracker.destroy();
|
||||||
|
this.parent();
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,74 +1,73 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
const FileUtils = imports.misc.fileUtils;
|
const FileUtils = imports.misc.fileUtils;
|
||||||
const Search = imports.ui.search;
|
const Search = imports.ui.search;
|
||||||
|
|
||||||
const KEY_FILE_GROUP = 'Shell Search Provider';
|
const KEY_FILE_GROUP = 'Shell Search Provider';
|
||||||
|
|
||||||
const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider">
|
const SearchProviderIface = '<node> \
|
||||||
<method name="GetInitialResultSet">
|
<interface name="org.gnome.Shell.SearchProvider"> \
|
||||||
<arg type="as" direction="in" />
|
<method name="GetInitialResultSet"> \
|
||||||
<arg type="as" direction="out" />
|
<arg type="as" direction="in" /> \
|
||||||
</method>
|
<arg type="as" direction="out" /> \
|
||||||
<method name="GetSubsearchResultSet">
|
</method> \
|
||||||
<arg type="as" direction="in" />
|
<method name="GetSubsearchResultSet"> \
|
||||||
<arg type="as" direction="in" />
|
<arg type="as" direction="in" /> \
|
||||||
<arg type="as" direction="out" />
|
<arg type="as" direction="in" /> \
|
||||||
</method>
|
<arg type="as" direction="out" /> \
|
||||||
<method name="GetResultMetas">
|
</method> \
|
||||||
<arg type="as" direction="in" />
|
<method name="GetResultMetas"> \
|
||||||
<arg type="aa{sv}" direction="out" />
|
<arg type="as" direction="in" /> \
|
||||||
</method>
|
<arg type="aa{sv}" direction="out" /> \
|
||||||
<method name="ActivateResult">
|
</method> \
|
||||||
<arg type="s" direction="in" />
|
<method name="ActivateResult"> \
|
||||||
</method>
|
<arg type="s" direction="in" /> \
|
||||||
</interface>;
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const SearchProvider2Iface = <interface name="org.gnome.Shell.SearchProvider2">
|
const SearchProvider2Iface = '<node> \
|
||||||
<method name="GetInitialResultSet">
|
<interface name="org.gnome.Shell.SearchProvider2"> \
|
||||||
<arg type="as" direction="in" />
|
<method name="GetInitialResultSet"> \
|
||||||
<arg type="as" direction="out" />
|
<arg type="as" direction="in" /> \
|
||||||
</method>
|
<arg type="as" direction="out" /> \
|
||||||
<method name="GetSubsearchResultSet">
|
</method> \
|
||||||
<arg type="as" direction="in" />
|
<method name="GetSubsearchResultSet"> \
|
||||||
<arg type="as" direction="in" />
|
<arg type="as" direction="in" /> \
|
||||||
<arg type="as" direction="out" />
|
<arg type="as" direction="in" /> \
|
||||||
</method>
|
<arg type="as" direction="out" /> \
|
||||||
<method name="GetResultMetas">
|
</method> \
|
||||||
<arg type="as" direction="in" />
|
<method name="GetResultMetas"> \
|
||||||
<arg type="aa{sv}" direction="out" />
|
<arg type="as" direction="in" /> \
|
||||||
</method>
|
<arg type="aa{sv}" direction="out" /> \
|
||||||
<method name="ActivateResult">
|
</method> \
|
||||||
<arg type="s" direction="in" />
|
<method name="ActivateResult"> \
|
||||||
<arg type="as" direction="in" />
|
<arg type="s" direction="in" /> \
|
||||||
<arg type="u" direction="in" />
|
<arg type="as" direction="in" /> \
|
||||||
</method>
|
<arg type="u" direction="in" /> \
|
||||||
<method name="LaunchSearch">
|
</method> \
|
||||||
<arg type="as" direction="in" />
|
<method name="LaunchSearch"> \
|
||||||
<arg type="u" direction="in" />
|
<arg type="as" direction="in" /> \
|
||||||
</method>
|
<arg type="u" direction="in" /> \
|
||||||
</interface>;
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
|
var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface);
|
||||||
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
|
var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface);
|
||||||
|
|
||||||
function loadRemoteSearchProviders(addProviderCallback) {
|
function loadRemoteSearchProviders(callback) {
|
||||||
let data = { loadedProviders: [],
|
let objectPaths = {};
|
||||||
objectPaths: {},
|
let loadedProviders = [];
|
||||||
addProviderCallback: addProviderCallback };
|
|
||||||
FileUtils.collectFromDatadirsAsync('search-providers',
|
|
||||||
{ loadedCallback: remoteProvidersLoaded,
|
|
||||||
processFile: loadRemoteSearchProvider,
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadRemoteSearchProvider(file, info, data) {
|
function loadRemoteSearchProvider(file) {
|
||||||
let keyfile = new GLib.KeyFile();
|
let keyfile = new GLib.KeyFile();
|
||||||
let path = file.get_path();
|
let path = file.get_path();
|
||||||
|
|
||||||
@@ -87,7 +86,7 @@ function loadRemoteSearchProvider(file, info, data) {
|
|||||||
let busName = keyfile.get_string(group, 'BusName');
|
let busName = keyfile.get_string(group, 'BusName');
|
||||||
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
let objectPath = keyfile.get_string(group, 'ObjectPath');
|
||||||
|
|
||||||
if (data.objectPaths[objectPath])
|
if (objectPaths[objectPath])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let appInfo = null;
|
let appInfo = null;
|
||||||
@@ -111,20 +110,33 @@ function loadRemoteSearchProvider(file, info, data) {
|
|||||||
else
|
else
|
||||||
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
|
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
|
||||||
|
|
||||||
data.objectPaths[objectPath] = remoteProvider;
|
objectPaths[objectPath] = remoteProvider;
|
||||||
data.loadedProviders.push(remoteProvider);
|
loadedProviders.push(remoteProvider);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
log('Failed to add search provider %s: %s'.format(path, e.toString()));
|
log('Failed to add search provider %s: %s'.format(path, e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function remoteProvidersLoaded(loadState) {
|
|
||||||
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
|
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
|
||||||
let sortOrder = searchSettings.get_strv('sort-order');
|
if (searchSettings.get_boolean('disable-external')) {
|
||||||
let numSorted = sortOrder.length;
|
callback([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
loadState.loadedProviders.sort(
|
FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider);
|
||||||
function(providerA, providerB) {
|
|
||||||
|
let sortOrder = searchSettings.get_strv('sort-order');
|
||||||
|
|
||||||
|
// Special case gnome-control-center to be always active and always first
|
||||||
|
sortOrder.unshift('gnome-control-center.desktop');
|
||||||
|
|
||||||
|
loadedProviders = loadedProviders.filter(function(provider) {
|
||||||
|
let appId = provider.appInfo.get_id();
|
||||||
|
let disabled = searchSettings.get_strv('disabled');
|
||||||
|
return disabled.indexOf(appId) == -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
loadedProviders.sort(function(providerA, providerB) {
|
||||||
let idxA, idxB;
|
let idxA, idxB;
|
||||||
let appIdA, appIdB;
|
let appIdA, appIdB;
|
||||||
|
|
||||||
@@ -135,16 +147,11 @@ function remoteProvidersLoaded(loadState) {
|
|||||||
idxB = sortOrder.indexOf(appIdB);
|
idxB = sortOrder.indexOf(appIdB);
|
||||||
|
|
||||||
// if no provider is found in the order, use alphabetical order
|
// if no provider is found in the order, use alphabetical order
|
||||||
if ((idxA == -1) && (idxB == -1))
|
if ((idxA == -1) && (idxB == -1)) {
|
||||||
return GLib.utf8_collate(providerA.title, providerB.title);
|
let nameA = providerA.appInfo.get_name();
|
||||||
|
let nameB = providerB.appInfo.get_name();
|
||||||
|
|
||||||
if (numSorted > 1) {
|
return GLib.utf8_collate(nameA, nameB);
|
||||||
// if providerA is the last, it goes after everything
|
|
||||||
if ((idxA + 1) == numSorted)
|
|
||||||
return 1;
|
|
||||||
// if providerB is the last, it goes after everything
|
|
||||||
else if ((idxB + 1) == numSorted)
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if providerA isn't found, it's sorted after providerB
|
// if providerA isn't found, it's sorted after providerB
|
||||||
@@ -159,109 +166,113 @@ function remoteProvidersLoaded(loadState) {
|
|||||||
return (idxA - idxB);
|
return (idxA - idxB);
|
||||||
});
|
});
|
||||||
|
|
||||||
loadState.loadedProviders.forEach(
|
callback(loadedProviders);
|
||||||
function(provider) {
|
|
||||||
loadState.addProviderCallback(provider);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const RemoteSearchProvider = new Lang.Class({
|
const RemoteSearchProvider = new Lang.Class({
|
||||||
Name: 'RemoteSearchProvider',
|
Name: 'RemoteSearchProvider',
|
||||||
|
|
||||||
_init: function(appInfo, dbusName, dbusPath, proxyType) {
|
_init: function(appInfo, dbusName, dbusPath, proxyInfo) {
|
||||||
if (!proxyType)
|
if (!proxyInfo)
|
||||||
proxyType = SearchProviderProxy;
|
proxyInfo = SearchProviderProxyInfo;
|
||||||
|
|
||||||
this.proxy = new proxyType(Gio.DBus.session,
|
this.proxy = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SESSION,
|
||||||
dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed));
|
g_name: dbusName,
|
||||||
|
g_object_path: dbusPath,
|
||||||
|
g_interface_info: proxyInfo,
|
||||||
|
g_interface_name: proxyInfo.name,
|
||||||
|
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION |
|
||||||
|
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
|
||||||
|
this.proxy.init_async(GLib.PRIORITY_DEFAULT, null, null);
|
||||||
|
|
||||||
this.appInfo = appInfo;
|
this.appInfo = appInfo;
|
||||||
this.id = appInfo.get_id();
|
this.id = appInfo.get_id();
|
||||||
this.isRemoteProvider = true;
|
this.isRemoteProvider = true;
|
||||||
|
|
||||||
this._cancellable = new Gio.Cancellable();
|
|
||||||
},
|
|
||||||
|
|
||||||
_onProxyConstructed: function(proxy) {
|
|
||||||
// Do nothing
|
|
||||||
},
|
},
|
||||||
|
|
||||||
createIcon: function(size, meta) {
|
createIcon: function(size, meta) {
|
||||||
if (meta['gicon']) {
|
let gicon = null;
|
||||||
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
|
let icon = null;
|
||||||
icon_size: size });
|
|
||||||
|
if (meta['icon']) {
|
||||||
|
gicon = Gio.icon_deserialize(meta['icon']);
|
||||||
|
} else if (meta['gicon']) {
|
||||||
|
gicon = Gio.icon_new_for_string(meta['gicon']);
|
||||||
} else if (meta['icon-data']) {
|
} else if (meta['icon-data']) {
|
||||||
let [width, height, rowStride, hasAlpha,
|
let [width, height, rowStride, hasAlpha,
|
||||||
bitsPerSample, nChannels, data] = meta['icon-data'];
|
bitsPerSample, nChannels, data] = meta['icon-data'];
|
||||||
let textureCache = St.TextureCache.get_default();
|
gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||||
return textureCache.load_from_raw(data, hasAlpha,
|
bitsPerSample, width, height, rowStride);
|
||||||
width, height, rowStride, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
if (gicon)
|
||||||
|
icon = new St.Icon({ gicon: gicon,
|
||||||
|
icon_size: size });
|
||||||
|
return icon;
|
||||||
},
|
},
|
||||||
|
|
||||||
_getResultsFinished: function(results, error) {
|
filterResults: function(results, maxNumber) {
|
||||||
if (error)
|
if (results.length <= maxNumber)
|
||||||
|
return results;
|
||||||
|
|
||||||
|
let regularResults = results.filter(function(r) { return !r.startsWith('special:'); });
|
||||||
|
let specialResults = results.filter(function(r) { return r.startsWith('special:'); });
|
||||||
|
|
||||||
|
return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
|
||||||
|
},
|
||||||
|
|
||||||
|
_getResultsFinished: function(results, error, callback) {
|
||||||
|
if (error) {
|
||||||
|
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
||||||
|
log('Received error from DBus search provider %s: %s'.format(this.id, String(error)));
|
||||||
|
callback([]);
|
||||||
return;
|
return;
|
||||||
this.searchSystem.pushResults(this, results[0]);
|
}
|
||||||
|
|
||||||
|
callback(results[0]);
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialResultSet: function(terms) {
|
getInitialResultSet: function(terms, callback, cancellable) {
|
||||||
this._cancellable.cancel();
|
|
||||||
this._cancellable.reset();
|
|
||||||
try {
|
|
||||||
this.proxy.GetInitialResultSetRemote(terms,
|
this.proxy.GetInitialResultSetRemote(terms,
|
||||||
Lang.bind(this, this._getResultsFinished),
|
Lang.bind(this, this._getResultsFinished, callback),
|
||||||
this._cancellable);
|
cancellable);
|
||||||
} catch(e) {
|
|
||||||
log('Error calling GetInitialResultSet for provider %s: %s'.format( this.title, e.toString()));
|
|
||||||
this.searchSystem.pushResults(this, []);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getSubsearchResultSet: function(previousResults, newTerms) {
|
getSubsearchResultSet: function(previousResults, newTerms, callback, cancellable) {
|
||||||
this._cancellable.cancel();
|
|
||||||
this._cancellable.reset();
|
|
||||||
try {
|
|
||||||
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
|
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
|
||||||
Lang.bind(this, this._getResultsFinished),
|
Lang.bind(this, this._getResultsFinished, callback),
|
||||||
this._cancellable);
|
cancellable);
|
||||||
} catch(e) {
|
|
||||||
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.title, e.toString()));
|
|
||||||
this.searchSystem.pushResults(this, []);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_getResultMetasFinished: function(results, error, callback) {
|
_getResultMetasFinished: function(results, error, callback) {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
||||||
|
log('Received error from DBus search provider %s during GetResultMetas: %s'.format(this.id, String(error)));
|
||||||
callback([]);
|
callback([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let metas = results[0];
|
let metas = results[0];
|
||||||
let resultMetas = [];
|
let resultMetas = [];
|
||||||
for (let i = 0; i < metas.length; i++) {
|
for (let i = 0; i < metas.length; i++) {
|
||||||
for (let prop in metas[i])
|
for (let prop in metas[i]) {
|
||||||
|
// we can use the serialized icon variant directly
|
||||||
|
if (prop != 'icon')
|
||||||
metas[i][prop] = metas[i][prop].deep_unpack();
|
metas[i][prop] = metas[i][prop].deep_unpack();
|
||||||
|
}
|
||||||
|
|
||||||
resultMetas.push({ id: metas[i]['id'],
|
resultMetas.push({ id: metas[i]['id'],
|
||||||
name: metas[i]['name'],
|
name: metas[i]['name'],
|
||||||
|
description: metas[i]['description'],
|
||||||
createIcon: Lang.bind(this,
|
createIcon: Lang.bind(this,
|
||||||
this.createIcon, metas[i]) });
|
this.createIcon, metas[i]) });
|
||||||
}
|
}
|
||||||
callback(resultMetas);
|
callback(resultMetas);
|
||||||
},
|
},
|
||||||
|
|
||||||
getResultMetas: function(ids, callback) {
|
getResultMetas: function(ids, callback, cancellable) {
|
||||||
this._cancellable.cancel();
|
|
||||||
this._cancellable.reset();
|
|
||||||
try {
|
|
||||||
this.proxy.GetResultMetasRemote(ids,
|
this.proxy.GetResultMetasRemote(ids,
|
||||||
Lang.bind(this, this._getResultMetasFinished, callback),
|
Lang.bind(this, this._getResultMetasFinished, callback),
|
||||||
this._cancellable);
|
cancellable);
|
||||||
} catch(e) {
|
|
||||||
log('Error calling GetResultMetas for provider %s: %s'.format(this.title, e.toString()));
|
|
||||||
callback([]);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
activateResult: function(id) {
|
activateResult: function(id) {
|
||||||
@@ -272,7 +283,7 @@ const RemoteSearchProvider = new Lang.Class({
|
|||||||
// the provider is not compatible with the new version of the interface, launch
|
// the provider is not compatible with the new version of the interface, launch
|
||||||
// the app itself but warn so we can catch the error in logs
|
// the app itself but warn so we can catch the error in logs
|
||||||
log('Search provider ' + this.appInfo.get_id() + ' does not implement LaunchSearch');
|
log('Search provider ' + this.appInfo.get_id() + ' does not implement LaunchSearch');
|
||||||
this.appInfo.launch([], global.create_app_launch_context());
|
this.appInfo.launch([], global.create_app_launch_context(0, -1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -281,7 +292,7 @@ const RemoteSearchProvider2 = new Lang.Class({
|
|||||||
Extends: RemoteSearchProvider,
|
Extends: RemoteSearchProvider,
|
||||||
|
|
||||||
_init: function(appInfo, dbusName, dbusPath) {
|
_init: function(appInfo, dbusName, dbusPath) {
|
||||||
this.parent(appInfo, dbusName, dbusPath, SearchProvider2Proxy);
|
this.parent(appInfo, dbusName, dbusPath, SearchProvider2ProxyInfo);
|
||||||
|
|
||||||
this.canLaunchSearch = true;
|
this.canLaunchSearch = true;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -30,144 +30,13 @@ const EXEC_ARG_KEY = 'exec-arg';
|
|||||||
|
|
||||||
const DIALOG_GROW_TIME = 0.1;
|
const DIALOG_GROW_TIME = 0.1;
|
||||||
|
|
||||||
const CommandCompleter = new Lang.Class({
|
|
||||||
Name: 'CommandCompleter',
|
|
||||||
|
|
||||||
_init : function() {
|
|
||||||
this._changedCount = 0;
|
|
||||||
this._paths = GLib.getenv('PATH').split(':');
|
|
||||||
this._paths.push(GLib.get_home_dir());
|
|
||||||
this._valid = false;
|
|
||||||
this._updateInProgress = false;
|
|
||||||
this._childs = new Array(this._paths.length);
|
|
||||||
this._monitors = new Array(this._paths.length);
|
|
||||||
for (let i = 0; i < this._paths.length; i++) {
|
|
||||||
this._childs[i] = [];
|
|
||||||
let file = Gio.file_new_for_path(this._paths[i]);
|
|
||||||
let info;
|
|
||||||
try {
|
|
||||||
info = file.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, null);
|
|
||||||
} catch (e) {
|
|
||||||
// FIXME catchall
|
|
||||||
this._paths[i] = null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.get_attribute_uint32(Gio.FILE_ATTRIBUTE_STANDARD_TYPE) != Gio.FileType.DIRECTORY)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
this._paths[i] = file.get_path();
|
|
||||||
this._monitors[i] = file.monitor_directory(Gio.FileMonitorFlags.NONE, null);
|
|
||||||
if (this._monitors[i] != null) {
|
|
||||||
this._monitors[i].connect('changed', Lang.bind(this, this._onChanged));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._paths = this._paths.filter(function(a) {
|
|
||||||
return a != null;
|
|
||||||
});
|
|
||||||
this._update(0);
|
|
||||||
},
|
|
||||||
|
|
||||||
update : function() {
|
|
||||||
if (this._valid)
|
|
||||||
return;
|
|
||||||
this._update(0);
|
|
||||||
},
|
|
||||||
|
|
||||||
_update : function(i) {
|
|
||||||
if (i == 0 && this._updateInProgress)
|
|
||||||
return;
|
|
||||||
this._updateInProgress = true;
|
|
||||||
this._changedCount = 0;
|
|
||||||
this._i = i;
|
|
||||||
if (i >= this._paths.length) {
|
|
||||||
this._valid = true;
|
|
||||||
this._updateInProgress = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let file = Gio.file_new_for_path(this._paths[i]);
|
|
||||||
this._childs[this._i] = [];
|
|
||||||
FileUtils.listDirAsync(file, Lang.bind(this, function (files) {
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
this._childs[this._i].push(files[i].get_name());
|
|
||||||
}
|
|
||||||
this._update(this._i + 1);
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
_onChanged : function(m, f, of, type) {
|
|
||||||
if (!this._valid)
|
|
||||||
return;
|
|
||||||
let path = f.get_parent().get_path();
|
|
||||||
let k = undefined;
|
|
||||||
for (let i = 0; i < this._paths.length; i++) {
|
|
||||||
if (this._paths[i] == path)
|
|
||||||
k = i;
|
|
||||||
}
|
|
||||||
if (k === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (type == Gio.FileMonitorEvent.CREATED) {
|
|
||||||
this._childs[k].push(f.get_basename());
|
|
||||||
}
|
|
||||||
if (type == Gio.FileMonitorEvent.DELETED) {
|
|
||||||
this._changedCount++;
|
|
||||||
if (this._changedCount > MAX_FILE_DELETED_BEFORE_INVALID) {
|
|
||||||
this._valid = false;
|
|
||||||
}
|
|
||||||
let name = f.get_basename();
|
|
||||||
this._childs[k] = this._childs[k].filter(function(e) {
|
|
||||||
return e != name;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (type == Gio.FileMonitorEvent.UNMOUNTED) {
|
|
||||||
this._childs[k] = [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getCompletion: function(text) {
|
|
||||||
let common = '';
|
|
||||||
let notInit = true;
|
|
||||||
if (!this._valid) {
|
|
||||||
this._update(0);
|
|
||||||
return common;
|
|
||||||
}
|
|
||||||
function _getCommon(s1, s2) {
|
|
||||||
let k = 0;
|
|
||||||
for (; k < s1.length && k < s2.length; k++) {
|
|
||||||
if (s1[k] != s2[k])
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (k == 0)
|
|
||||||
return '';
|
|
||||||
return s1.substr(0, k);
|
|
||||||
}
|
|
||||||
function _hasPrefix(s1, prefix) {
|
|
||||||
return s1.indexOf(prefix) == 0;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < this._childs.length; i++) {
|
|
||||||
for (let k = 0; k < this._childs[i].length; k++) {
|
|
||||||
if (!_hasPrefix(this._childs[i][k], text))
|
|
||||||
continue;
|
|
||||||
if (notInit) {
|
|
||||||
common = this._childs[i][k];
|
|
||||||
notInit = false;
|
|
||||||
}
|
|
||||||
common = _getCommon(common, this._childs[i][k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (common.length)
|
|
||||||
return common.substr(text.length);
|
|
||||||
return common;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const RunDialog = new Lang.Class({
|
const RunDialog = new Lang.Class({
|
||||||
Name: 'RunDialog',
|
Name: 'RunDialog',
|
||||||
Extends: ModalDialog.ModalDialog,
|
Extends: ModalDialog.ModalDialog,
|
||||||
|
|
||||||
_init : function() {
|
_init : function() {
|
||||||
this.parent({ styleClass: 'run-dialog' });
|
this.parent({ styleClass: 'run-dialog',
|
||||||
|
destroyOnClose: false });
|
||||||
|
|
||||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||||
this._terminalSettings = new Gio.Settings({ schema: TERMINAL_SCHEMA });
|
this._terminalSettings = new Gio.Settings({ schema: TERMINAL_SCHEMA });
|
||||||
@@ -204,7 +73,9 @@ const RunDialog = new Lang.Class({
|
|||||||
let label = new St.Label({ style_class: 'run-dialog-label',
|
let label = new St.Label({ style_class: 'run-dialog-label',
|
||||||
text: _("Enter a Command") });
|
text: _("Enter a Command") });
|
||||||
|
|
||||||
this.contentLayout.add(label, { y_align: St.Align.START });
|
this.contentLayout.add(label, { x_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
let entry = new St.Entry({ style_class: 'run-dialog-entry',
|
let entry = new St.Entry({ style_class: 'run-dialog-entry',
|
||||||
can_focus: true });
|
can_focus: true });
|
||||||
@@ -232,6 +103,8 @@ const RunDialog = new Lang.Class({
|
|||||||
this._errorMessage.clutter_text.line_wrap = true;
|
this._errorMessage.clutter_text.line_wrap = true;
|
||||||
|
|
||||||
this._errorBox.add(this._errorMessage, { expand: true,
|
this._errorBox.add(this._errorMessage, { expand: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
x_fill: false,
|
||||||
y_align: St.Align.MIDDLE,
|
y_align: St.Align.MIDDLE,
|
||||||
y_fill: false });
|
y_fill: false });
|
||||||
|
|
||||||
@@ -242,8 +115,6 @@ const RunDialog = new Lang.Class({
|
|||||||
key: Clutter.Escape }]);
|
key: Clutter.Escape }]);
|
||||||
|
|
||||||
this._pathCompleter = new Gio.FilenameCompleter();
|
this._pathCompleter = new Gio.FilenameCompleter();
|
||||||
this._commandCompleter = new CommandCompleter();
|
|
||||||
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
|
|
||||||
|
|
||||||
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
|
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
|
||||||
entry: this._entryText });
|
entry: this._entryText });
|
||||||
@@ -257,19 +128,7 @@ const RunDialog = new Lang.Class({
|
|||||||
!this.pushModal())
|
!this.pushModal())
|
||||||
this.close();
|
this.close();
|
||||||
|
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
}
|
|
||||||
if (symbol == Clutter.slash) {
|
|
||||||
// Need preload data before get completion. GFilenameCompleter load content of parent directory.
|
|
||||||
// Parent directory for /usr/include/ is /usr/. So need to add fake name('a').
|
|
||||||
let text = o.get_text().concat('/a');
|
|
||||||
let prefix;
|
|
||||||
if (text.lastIndexOf(' ') == -1)
|
|
||||||
prefix = text;
|
|
||||||
else
|
|
||||||
prefix = text.substr(text.lastIndexOf(' ') + 1);
|
|
||||||
this._getCompletion(prefix);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
if (symbol == Clutter.Tab) {
|
if (symbol == Clutter.Tab) {
|
||||||
let text = o.get_text();
|
let text = o.get_text();
|
||||||
@@ -282,20 +141,60 @@ const RunDialog = new Lang.Class({
|
|||||||
if (postfix != null && postfix.length > 0) {
|
if (postfix != null && postfix.length > 0) {
|
||||||
o.insert_text(postfix, -1);
|
o.insert_text(postfix, -1);
|
||||||
o.set_cursor_position(text.length + postfix.length);
|
o.set_cursor_position(text.length + postfix.length);
|
||||||
if (postfix[postfix.length - 1] == '/')
|
|
||||||
this._getCompletion(text + postfix + 'a');
|
|
||||||
}
|
}
|
||||||
return true;
|
return Clutter.EVENT_STOP;
|
||||||
}
|
}
|
||||||
return false;
|
return Clutter.EVENT_PROPAGATE;
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getCommandCompletion: function(text) {
|
||||||
|
function _getCommon(s1, s2) {
|
||||||
|
if (s1 == null)
|
||||||
|
return s2;
|
||||||
|
|
||||||
|
let k = 0;
|
||||||
|
for (; k < s1.length && k < s2.length; k++) {
|
||||||
|
if (s1[k] != s2[k])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (k == 0)
|
||||||
|
return '';
|
||||||
|
return s1.substr(0, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
let paths = GLib.getenv('PATH').split(':');
|
||||||
|
paths.push(GLib.get_home_dir());
|
||||||
|
let someResults = paths.map(function(path) {
|
||||||
|
let results = [];
|
||||||
|
try {
|
||||||
|
let file = Gio.File.new_for_path(path);
|
||||||
|
let fileEnum = file.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
|
||||||
|
let info;
|
||||||
|
while ((info = fileEnum.next_file(null))) {
|
||||||
|
let name = info.get_name();
|
||||||
|
if (name.slice(0, text.length) == text)
|
||||||
|
results.push(name);
|
||||||
|
}
|
||||||
|
} catch (e if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND) &&
|
||||||
|
!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_DIRECTORY))) {
|
||||||
|
log(e);
|
||||||
|
} finally {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let results = someResults.reduce(function(a, b) {
|
||||||
|
return a.concat(b);
|
||||||
|
}, []);
|
||||||
|
let common = results.reduce(_getCommon, null);
|
||||||
|
return common.substr(text.length);
|
||||||
|
},
|
||||||
|
|
||||||
_getCompletion : function(text) {
|
_getCompletion : function(text) {
|
||||||
if (text.indexOf('/') != -1) {
|
if (text.indexOf('/') != -1) {
|
||||||
return this._pathCompleter.get_completion_suffix(text);
|
return this._pathCompleter.get_completion_suffix(text);
|
||||||
} else {
|
} else {
|
||||||
return this._commandCompleter.getCompletion(text);
|
return this._getCommandCompletion(text);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -334,7 +233,7 @@ const RunDialog = new Lang.Class({
|
|||||||
let file = Gio.file_new_for_path(path);
|
let file = Gio.file_new_for_path(path);
|
||||||
try {
|
try {
|
||||||
Gio.app_info_launch_default_for_uri(file.get_uri(),
|
Gio.app_info_launch_default_for_uri(file.get_uri(),
|
||||||
global.create_app_launch_context());
|
global.create_app_launch_context(0, -1));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// The exception from gjs contains an error string like:
|
// The exception from gjs contains an error string like:
|
||||||
// Error invoking Gio.app_info_launch_default_for_uri: No application
|
// Error invoking Gio.app_info_launch_default_for_uri: No application
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
163
js/ui/screencast.js
Normal file
163
js/ui/screencast.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
|
const ScreencastIface = '<node> \
|
||||||
|
<interface name="org.gnome.Shell.Screencast"> \
|
||||||
|
<method name="Screencast"> \
|
||||||
|
<arg type="s" direction="in" name="file_template"/> \
|
||||||
|
<arg type="a{sv}" direction="in" name="options"/> \
|
||||||
|
<arg type="b" direction="out" name="success"/> \
|
||||||
|
<arg type="s" direction="out" name="filename_used"/> \
|
||||||
|
</method> \
|
||||||
|
<method name="ScreencastArea"> \
|
||||||
|
<arg type="i" direction="in" name="x"/> \
|
||||||
|
<arg type="i" direction="in" name="y"/> \
|
||||||
|
<arg type="i" direction="in" name="width"/> \
|
||||||
|
<arg type="i" direction="in" name="height"/> \
|
||||||
|
<arg type="s" direction="in" name="file_template"/> \
|
||||||
|
<arg type="a{sv}" direction="in" name="options"/> \
|
||||||
|
<arg type="b" direction="out" name="success"/> \
|
||||||
|
<arg type="s" direction="out" name="filename_used"/> \
|
||||||
|
</method> \
|
||||||
|
<method name="StopScreencast"> \
|
||||||
|
<arg type="b" direction="out" name="success"/> \
|
||||||
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
|
const ScreencastService = new Lang.Class({
|
||||||
|
Name: 'ScreencastService',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreencastIface, this);
|
||||||
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screencast');
|
||||||
|
|
||||||
|
Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||||
|
|
||||||
|
this._recorders = new Map();
|
||||||
|
|
||||||
|
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||||
|
},
|
||||||
|
|
||||||
|
get isRecording() {
|
||||||
|
return this._recorders.size > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureRecorderForSender: function(sender) {
|
||||||
|
let recorder = this._recorders.get(sender);
|
||||||
|
if (!recorder) {
|
||||||
|
recorder = new Shell.Recorder({ stage: global.stage,
|
||||||
|
screen: global.screen });
|
||||||
|
recorder._watchNameId =
|
||||||
|
Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
|
||||||
|
Lang.bind(this, this._onNameVanished));
|
||||||
|
this._recorders.set(sender, recorder);
|
||||||
|
this.emit('updated');
|
||||||
|
}
|
||||||
|
return recorder;
|
||||||
|
},
|
||||||
|
|
||||||
|
_sessionUpdated: function() {
|
||||||
|
if (Main.sessionMode.allowScreencast)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._recorders.clear();
|
||||||
|
this.emit('updated');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onNameVanished: function(connection, name) {
|
||||||
|
this._stopRecordingForSender(name);
|
||||||
|
},
|
||||||
|
|
||||||
|
_stopRecordingForSender: function(sender) {
|
||||||
|
let recorder = this._recorders.get(sender);
|
||||||
|
if (!recorder)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Gio.bus_unwatch_name(recorder._watchNameId);
|
||||||
|
recorder.close();
|
||||||
|
this._recorders.delete(sender);
|
||||||
|
this.emit('updated');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_applyOptionalParameters: function(recorder, options) {
|
||||||
|
for (let option in options)
|
||||||
|
options[option] = options[option].deep_unpack();
|
||||||
|
|
||||||
|
if (options['pipeline'])
|
||||||
|
recorder.set_pipeline(options['pipeline']);
|
||||||
|
if (options['framerate'])
|
||||||
|
recorder.set_framerate(options['framerate']);
|
||||||
|
if (options['draw-cursor'])
|
||||||
|
recorder.set_draw_cursor(options['draw-cursor']);
|
||||||
|
},
|
||||||
|
|
||||||
|
ScreencastAsync: function(params, invocation) {
|
||||||
|
let returnValue = [false, ''];
|
||||||
|
if (!Main.sessionMode.allowScreencast) {
|
||||||
|
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sender = invocation.get_sender();
|
||||||
|
let recorder = this._ensureRecorderForSender(sender);
|
||||||
|
if (!recorder.is_recording()) {
|
||||||
|
let [fileTemplate, options] = params;
|
||||||
|
|
||||||
|
recorder.set_file_template(fileTemplate);
|
||||||
|
this._applyOptionalParameters(recorder, options);
|
||||||
|
let [success, fileName] = recorder.record();
|
||||||
|
returnValue = [success, fileName ? fileName : ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||||
|
},
|
||||||
|
|
||||||
|
ScreencastAreaAsync: function(params, invocation) {
|
||||||
|
let returnValue = [false, ''];
|
||||||
|
if (!Main.sessionMode.allowScreencast) {
|
||||||
|
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sender = invocation.get_sender();
|
||||||
|
let recorder = this._ensureRecorderForSender(sender);
|
||||||
|
|
||||||
|
if (!recorder.is_recording()) {
|
||||||
|
let [x, y, width, height, fileTemplate, options] = params;
|
||||||
|
|
||||||
|
if (x < 0 || y < 0 ||
|
||||||
|
width <= 0 || height <= 0 ||
|
||||||
|
x + width > global.screen_width ||
|
||||||
|
y + height > global.screen_height) {
|
||||||
|
invocation.return_error_literal(Gio.IOErrorEnum,
|
||||||
|
Gio.IOErrorEnum.CANCELLED,
|
||||||
|
"Invalid params");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
recorder.set_file_template(fileTemplate);
|
||||||
|
recorder.set_area(x, y, width, height);
|
||||||
|
this._applyOptionalParameters(recorder, options);
|
||||||
|
let [success, fileName] = recorder.record();
|
||||||
|
returnValue = [success, fileName ? fileName : ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||||
|
},
|
||||||
|
|
||||||
|
StopScreencastAsync: function(params, invocation) {
|
||||||
|
let success = this._stopRecordingForSender(invocation.get_sender());
|
||||||
|
invocation.return_value(GLib.Variant.new('(b)', [success]));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(ScreencastService.prototype);
|
||||||
282
js/ui/screenshot.js
Normal file
282
js/ui/screenshot.js
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Gdk = imports.gi.Gdk;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const Lightbox = imports.ui.lightbox;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Tweener = imports.ui.tweener;
|
||||||
|
|
||||||
|
const ScreenshotIface = '<node> \
|
||||||
|
<interface name="org.gnome.Shell.Screenshot"> \
|
||||||
|
<method name="ScreenshotArea"> \
|
||||||
|
<arg type="i" direction="in" name="x"/> \
|
||||||
|
<arg type="i" direction="in" name="y"/> \
|
||||||
|
<arg type="i" direction="in" name="width"/> \
|
||||||
|
<arg type="i" direction="in" name="height"/> \
|
||||||
|
<arg type="b" direction="in" name="flash"/> \
|
||||||
|
<arg type="s" direction="in" name="filename"/> \
|
||||||
|
<arg type="b" direction="out" name="success"/> \
|
||||||
|
<arg type="s" direction="out" name="filename_used"/> \
|
||||||
|
</method> \
|
||||||
|
<method name="ScreenshotWindow"> \
|
||||||
|
<arg type="b" direction="in" name="include_frame"/> \
|
||||||
|
<arg type="b" direction="in" name="include_cursor"/> \
|
||||||
|
<arg type="b" direction="in" name="flash"/> \
|
||||||
|
<arg type="s" direction="in" name="filename"/> \
|
||||||
|
<arg type="b" direction="out" name="success"/> \
|
||||||
|
<arg type="s" direction="out" name="filename_used"/> \
|
||||||
|
</method> \
|
||||||
|
<method name="Screenshot"> \
|
||||||
|
<arg type="b" direction="in" name="include_cursor"/> \
|
||||||
|
<arg type="b" direction="in" name="flash"/> \
|
||||||
|
<arg type="s" direction="in" name="filename"/> \
|
||||||
|
<arg type="b" direction="out" name="success"/> \
|
||||||
|
<arg type="s" direction="out" name="filename_used"/> \
|
||||||
|
</method> \
|
||||||
|
<method name="SelectArea"> \
|
||||||
|
<arg type="i" direction="out" name="x"/> \
|
||||||
|
<arg type="i" direction="out" name="y"/> \
|
||||||
|
<arg type="i" direction="out" name="width"/> \
|
||||||
|
<arg type="i" direction="out" name="height"/> \
|
||||||
|
</method> \
|
||||||
|
<method name="FlashArea"> \
|
||||||
|
<arg type="i" direction="in" name="x"/> \
|
||||||
|
<arg type="i" direction="in" name="y"/> \
|
||||||
|
<arg type="i" direction="in" name="width"/> \
|
||||||
|
<arg type="i" direction="in" name="height"/> \
|
||||||
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
|
const ScreenshotService = new Lang.Class({
|
||||||
|
Name: 'ScreenshotService',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenshotIface, this);
|
||||||
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
|
||||||
|
|
||||||
|
Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
|
||||||
|
if (flash && result) {
|
||||||
|
let flashspot = new Flashspot(area);
|
||||||
|
flashspot.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
|
||||||
|
invocation.return_value(retval);
|
||||||
|
},
|
||||||
|
|
||||||
|
ScreenshotAreaAsync : function (params, invocation) {
|
||||||
|
let [x, y, width, height, flash, filename, callback] = params;
|
||||||
|
if (x < 0 || y < 0 ||
|
||||||
|
width <= 0 || height <= 0 ||
|
||||||
|
x + width > global.screen_width || y + height > global.screen_height) {
|
||||||
|
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
|
||||||
|
"Invalid params");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let screenshot = new Shell.Screenshot();
|
||||||
|
screenshot.screenshot_area (x, y, width, height, filename,
|
||||||
|
Lang.bind(this, this._onScreenshotComplete,
|
||||||
|
flash, invocation));
|
||||||
|
},
|
||||||
|
|
||||||
|
ScreenshotWindowAsync : function (params, invocation) {
|
||||||
|
let [include_frame, include_cursor, flash, filename] = params;
|
||||||
|
let screenshot = new Shell.Screenshot();
|
||||||
|
screenshot.screenshot_window (include_frame, include_cursor, filename,
|
||||||
|
Lang.bind(this, this._onScreenshotComplete,
|
||||||
|
flash, invocation));
|
||||||
|
},
|
||||||
|
|
||||||
|
ScreenshotAsync : function (params, invocation) {
|
||||||
|
let [include_cursor, flash, filename] = params;
|
||||||
|
let screenshot = new Shell.Screenshot();
|
||||||
|
screenshot.screenshot(include_cursor, filename,
|
||||||
|
Lang.bind(this, this._onScreenshotComplete,
|
||||||
|
flash, invocation));
|
||||||
|
},
|
||||||
|
|
||||||
|
SelectAreaAsync: function (params, invocation) {
|
||||||
|
let selectArea = new SelectArea();
|
||||||
|
selectArea.show();
|
||||||
|
selectArea.connect('finished', Lang.bind(this,
|
||||||
|
function(selectArea, areaRectangle) {
|
||||||
|
if (areaRectangle) {
|
||||||
|
let retval = GLib.Variant.new('(iiii)',
|
||||||
|
[areaRectangle.x, areaRectangle.y,
|
||||||
|
areaRectangle.width, areaRectangle.height]);
|
||||||
|
invocation.return_value(retval);
|
||||||
|
} else {
|
||||||
|
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
|
||||||
|
"Operation was cancelled");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
FlashArea: function(x, y, width, height) {
|
||||||
|
let flashspot = new Flashspot({ x : x, y : y, width: width, height: height});
|
||||||
|
flashspot.fire();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const SelectArea = new Lang.Class({
|
||||||
|
Name: 'SelectArea',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this._startX = -1;
|
||||||
|
this._startY = -1;
|
||||||
|
this._lastX = 0;
|
||||||
|
this._lastY = 0;
|
||||||
|
|
||||||
|
this._initRubberbandColors();
|
||||||
|
|
||||||
|
this._group = new St.Widget({ visible: false,
|
||||||
|
reactive: true,
|
||||||
|
x: 0,
|
||||||
|
y: 0 });
|
||||||
|
Main.layoutManager.osdGroup.add_actor(this._group);
|
||||||
|
|
||||||
|
this._group.connect('button-press-event',
|
||||||
|
Lang.bind(this, this._onButtonPress));
|
||||||
|
this._group.connect('button-release-event',
|
||||||
|
Lang.bind(this, this._onButtonRelease));
|
||||||
|
this._group.connect('key-press-event',
|
||||||
|
Lang.bind(this, this._onKeyPress));
|
||||||
|
this._group.connect('motion-event',
|
||||||
|
Lang.bind(this, this._onMotionEvent));
|
||||||
|
|
||||||
|
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||||
|
coordinate: Clutter.BindCoordinate.ALL });
|
||||||
|
this._group.add_constraint(constraint);
|
||||||
|
|
||||||
|
this._rubberband = new Clutter.Rectangle({ color: this._background,
|
||||||
|
has_border: true,
|
||||||
|
border_width: 1,
|
||||||
|
border_color: this._border });
|
||||||
|
this._group.add_actor(this._rubberband);
|
||||||
|
},
|
||||||
|
|
||||||
|
show: function() {
|
||||||
|
if (!Main.pushModal(this._group) || this._group.visible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
global.screen.set_cursor(Meta.Cursor.CROSSHAIR);
|
||||||
|
this._group.visible = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_initRubberbandColors: function() {
|
||||||
|
function colorFromRGBA(rgba) {
|
||||||
|
return new Clutter.Color({ red: rgba.red * 255,
|
||||||
|
green: rgba.green * 255,
|
||||||
|
blue: rgba.blue * 255,
|
||||||
|
alpha: rgba.alpha * 255 });
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = new Gtk.WidgetPath();
|
||||||
|
path.append_type(Gtk.IconView);
|
||||||
|
|
||||||
|
let context = new Gtk.StyleContext();
|
||||||
|
context.set_path(path);
|
||||||
|
context.add_class('rubberband');
|
||||||
|
|
||||||
|
this._background = colorFromRGBA(context.get_background_color(Gtk.StateFlags.NORMAL));
|
||||||
|
this._border = colorFromRGBA(context.get_border_color(Gtk.StateFlags.NORMAL));
|
||||||
|
},
|
||||||
|
|
||||||
|
_getGeometry: function() {
|
||||||
|
return { x: Math.min(this._startX, this._lastX),
|
||||||
|
y: Math.min(this._startY, this._lastY),
|
||||||
|
width: Math.abs(this._startX - this._lastX),
|
||||||
|
height: Math.abs(this._startY - this._lastY) };
|
||||||
|
},
|
||||||
|
|
||||||
|
_onKeyPress: function(actor, event) {
|
||||||
|
if (event.get_key_symbol() == Clutter.Escape)
|
||||||
|
this._destroy(null, false);
|
||||||
|
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMotionEvent: function(actor, event) {
|
||||||
|
if (this._startX == -1 || this._startY == -1)
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
|
||||||
|
[this._lastX, this._lastY] = event.get_coords();
|
||||||
|
let geometry = this._getGeometry();
|
||||||
|
|
||||||
|
this._rubberband.set_position(geometry.x, geometry.y);
|
||||||
|
this._rubberband.set_size(geometry.width, geometry.height);
|
||||||
|
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onButtonPress: function(actor, event) {
|
||||||
|
[this._startX, this._startY] = event.get_coords();
|
||||||
|
this._rubberband.set_position(this._startX, this._startY);
|
||||||
|
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onButtonRelease: function(actor, event) {
|
||||||
|
this._destroy(this._getGeometry(), true);
|
||||||
|
return Clutter.EVENT_PROPAGATE;
|
||||||
|
},
|
||||||
|
|
||||||
|
_destroy: function(geometry, fade) {
|
||||||
|
Tweener.addTween(this._group,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: fade ? 0.2 : 0,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
Main.popModal(this._group);
|
||||||
|
this._group.destroy();
|
||||||
|
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||||
|
|
||||||
|
this.emit('finished', geometry);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(SelectArea.prototype);
|
||||||
|
|
||||||
|
const FLASHSPOT_ANIMATION_OUT_TIME = 0.5; // seconds
|
||||||
|
|
||||||
|
const Flashspot = new Lang.Class({
|
||||||
|
Name: 'Flashspot',
|
||||||
|
Extends: Lightbox.Lightbox,
|
||||||
|
|
||||||
|
_init: function(area) {
|
||||||
|
this.parent(Main.layoutManager.osdGroup, { inhibitEvents: true,
|
||||||
|
width: area.width,
|
||||||
|
height: area.height });
|
||||||
|
|
||||||
|
this.actor.style_class = 'flashspot';
|
||||||
|
this.actor.set_position(area.x, area.y);
|
||||||
|
},
|
||||||
|
|
||||||
|
fire: function() {
|
||||||
|
this.actor.show();
|
||||||
|
this.actor.opacity = 255;
|
||||||
|
Tweener.addTween(this.actor,
|
||||||
|
{ opacity: 0,
|
||||||
|
time: FLASHSPOT_ANIMATION_OUT_TIME,
|
||||||
|
transition: 'easeOutQuad',
|
||||||
|
onComplete: Lang.bind(this, function() {
|
||||||
|
this.destroy();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
|
const GLib = imports.gi.GLib;
|
||||||
const Mainloop = imports.mainloop;
|
const Mainloop = imports.mainloop;
|
||||||
const Meta = imports.gi.Meta;
|
const Meta = imports.gi.Meta;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
@@ -41,7 +42,7 @@ function sleep(milliseconds) {
|
|||||||
Mainloop.timeout_add(milliseconds, function() {
|
Mainloop.timeout_add(milliseconds, function() {
|
||||||
if (cb)
|
if (cb)
|
||||||
cb();
|
cb();
|
||||||
return false;
|
return GLib.SOURCE_REMOVE;
|
||||||
});
|
});
|
||||||
|
|
||||||
return function(callback) {
|
return function(callback) {
|
||||||
@@ -69,16 +70,18 @@ function waitLeisure() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const PerfHelperIface = <interface name="org.gnome.Shell.PerfHelper">
|
const PerfHelperIface = '<node> \
|
||||||
<method name="CreateWindow">
|
<interface name="org.gnome.Shell.PerfHelper"> \
|
||||||
<arg type="i" direction="in" />
|
<method name="CreateWindow"> \
|
||||||
<arg type="i" direction="in" />
|
<arg type="i" direction="in" /> \
|
||||||
<arg type="b" direction="in" />
|
<arg type="i" direction="in" /> \
|
||||||
<arg type="b" direction="in" />
|
<arg type="b" direction="in" /> \
|
||||||
</method>
|
<arg type="b" direction="in" /> \
|
||||||
<method name="WaitWindows" />
|
</method> \
|
||||||
<method name="DestroyWindows" />
|
<method name="WaitWindows" /> \
|
||||||
</interface>;
|
<method name="DestroyWindows" /> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface);
|
var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface);
|
||||||
function PerfHelper() {
|
function PerfHelper() {
|
||||||
|
|||||||
735
js/ui/search.js
735
js/ui/search.js
@@ -1,115 +1,706 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
|
const Gio = imports.gi.Gio;
|
||||||
|
const Gtk = imports.gi.Gtk;
|
||||||
|
const Meta = imports.gi.Meta;
|
||||||
const Signals = imports.signals;
|
const Signals = imports.signals;
|
||||||
|
const St = imports.gi.St;
|
||||||
|
const Atk = imports.gi.Atk;
|
||||||
|
|
||||||
|
const AppDisplay = imports.ui.appDisplay;
|
||||||
|
const DND = imports.ui.dnd;
|
||||||
|
const IconGrid = imports.ui.iconGrid;
|
||||||
|
const Main = imports.ui.main;
|
||||||
|
const Overview = imports.ui.overview;
|
||||||
|
const RemoteSearch = imports.ui.remoteSearch;
|
||||||
|
const Separator = imports.ui.separator;
|
||||||
|
const Util = imports.misc.util;
|
||||||
|
|
||||||
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
|
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
|
||||||
|
|
||||||
|
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
|
||||||
|
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
|
||||||
|
|
||||||
const SearchSystem = new Lang.Class({
|
const SearchSystem = new Lang.Class({
|
||||||
Name: 'SearchSystem',
|
Name: 'SearchSystem',
|
||||||
|
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this._providers = [];
|
this._providers = [];
|
||||||
this._remoteProviders = [];
|
|
||||||
this.reset();
|
this._registerProvider(new AppDisplay.AppSearchProvider());
|
||||||
|
|
||||||
|
this._searchSettings = new Gio.Settings({ schema: SEARCH_PROVIDERS_SCHEMA });
|
||||||
|
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
|
||||||
|
this._searchSettings.connect('changed::disable-external', Lang.bind(this, this._reloadRemoteProviders));
|
||||||
|
this._searchSettings.connect('changed::sort-order', Lang.bind(this, this._reloadRemoteProviders));
|
||||||
|
|
||||||
|
this._reloadRemoteProviders();
|
||||||
|
|
||||||
|
this._cancellable = new Gio.Cancellable();
|
||||||
},
|
},
|
||||||
|
|
||||||
registerProvider: function (provider) {
|
addProvider: function(provider) {
|
||||||
provider.searchSystem = this;
|
|
||||||
this._providers.push(provider);
|
this._providers.push(provider);
|
||||||
|
this.emit('providers-changed');
|
||||||
if (provider.isRemoteProvider)
|
|
||||||
this._remoteProviders.push(provider);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
unregisterProvider: function (provider) {
|
_reloadRemoteProviders: function() {
|
||||||
let index = this._providers.indexOf(provider);
|
let remoteProviders = this._providers.filter(function(provider) {
|
||||||
if (index == -1)
|
return provider.isRemoteProvider;
|
||||||
return;
|
});
|
||||||
provider.searchSystem = null;
|
remoteProviders.forEach(Lang.bind(this, function(provider) {
|
||||||
this._providers.splice(index, 1);
|
this._unregisterProvider(provider);
|
||||||
|
}));
|
||||||
|
|
||||||
let remoteIndex = this._remoteProviders.indexOf(provider);
|
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, function(providers) {
|
||||||
if (remoteIndex != -1)
|
providers.forEach(Lang.bind(this, this._registerProvider));
|
||||||
this._remoteProviders.splice(index, 1);
|
}));
|
||||||
|
|
||||||
|
this.emit('providers-changed');
|
||||||
|
},
|
||||||
|
|
||||||
|
_registerProvider: function (provider) {
|
||||||
|
this._providers.push(provider);
|
||||||
|
},
|
||||||
|
|
||||||
|
_unregisterProvider: function (provider) {
|
||||||
|
let index = this._providers.indexOf(provider);
|
||||||
|
this._providers.splice(index, 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
getProviders: function() {
|
getProviders: function() {
|
||||||
return this._providers;
|
return this._providers;
|
||||||
},
|
},
|
||||||
|
|
||||||
getRemoteProviders: function() {
|
|
||||||
return this._remoteProviders;
|
|
||||||
},
|
|
||||||
|
|
||||||
getTerms: function() {
|
getTerms: function() {
|
||||||
return this._previousTerms;
|
return this._terms;
|
||||||
},
|
},
|
||||||
|
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this._previousTerms = [];
|
this._terms = [];
|
||||||
this._previousResults = [];
|
this._results = {};
|
||||||
},
|
},
|
||||||
|
|
||||||
pushResults: function(provider, results) {
|
_gotResults: function(results, provider) {
|
||||||
let i = this._providers.indexOf(provider);
|
this._results[provider.id] = results;
|
||||||
if (i == -1)
|
this.emit('search-updated', provider, results);
|
||||||
return;
|
|
||||||
|
|
||||||
this._previousResults[i] = [provider, results];
|
|
||||||
this.emit('search-updated', this._previousResults[i]);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
updateSearch: function(searchString) {
|
setTerms: function(terms) {
|
||||||
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
this._cancellable.cancel();
|
||||||
if (searchString == '')
|
this._cancellable.reset();
|
||||||
return;
|
|
||||||
|
|
||||||
let terms = searchString.split(/\s+/);
|
let previousResults = this._results;
|
||||||
this.updateSearchResults(terms);
|
let previousTerms = this._terms;
|
||||||
},
|
this.reset();
|
||||||
|
|
||||||
updateSearchResults: function(terms) {
|
|
||||||
if (!terms)
|
if (!terms)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let isSubSearch = terms.length == this._previousTerms.length;
|
let searchString = terms.join(' ');
|
||||||
if (isSubSearch) {
|
let previousSearchString = previousTerms.join(' ');
|
||||||
for (let i = 0; i < terms.length; i++) {
|
if (searchString == previousSearchString)
|
||||||
if (terms[i].indexOf(this._previousTerms[i]) != 0) {
|
return;
|
||||||
isSubSearch = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let previousResultsArr = this._previousResults;
|
let isSubSearch = false;
|
||||||
|
if (previousTerms.length > 0)
|
||||||
|
isSubSearch = searchString.indexOf(previousSearchString) == 0;
|
||||||
|
|
||||||
let results = [];
|
this._terms = terms;
|
||||||
this._previousTerms = terms;
|
|
||||||
this._previousResults = results;
|
|
||||||
|
|
||||||
if (isSubSearch) {
|
this._providers.forEach(Lang.bind(this, function(provider) {
|
||||||
for (let i = 0; i < this._providers.length; i++) {
|
let previousProviderResults = previousResults[provider.id];
|
||||||
let [provider, previousResults] = previousResultsArr[i];
|
if (isSubSearch && previousProviderResults)
|
||||||
try {
|
provider.getSubsearchResultSet(previousProviderResults, terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
|
||||||
results.push([provider, []]);
|
else
|
||||||
provider.getSubsearchResultSet(previousResults, terms);
|
provider.getInitialResultSet(terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
|
||||||
} catch (error) {
|
}));
|
||||||
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (let i = 0; i < this._providers.length; i++) {
|
|
||||||
let provider = this._providers[i];
|
|
||||||
try {
|
|
||||||
results.push([provider, []]);
|
|
||||||
provider.getInitialResultSet(terms);
|
|
||||||
} catch (error) {
|
|
||||||
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
Signals.addSignalMethods(SearchSystem.prototype);
|
Signals.addSignalMethods(SearchSystem.prototype);
|
||||||
|
|
||||||
|
const MaxWidthBin = new Lang.Class({
|
||||||
|
Name: 'MaxWidthBin',
|
||||||
|
Extends: St.Bin,
|
||||||
|
|
||||||
|
vfunc_allocate: function(box, flags) {
|
||||||
|
let themeNode = this.get_theme_node();
|
||||||
|
let maxWidth = themeNode.get_max_width();
|
||||||
|
let availWidth = box.x2 - box.x1;
|
||||||
|
let adjustedBox = box;
|
||||||
|
|
||||||
|
if (availWidth > maxWidth) {
|
||||||
|
let excessWidth = availWidth - maxWidth;
|
||||||
|
adjustedBox.x1 += Math.floor(excessWidth / 2);
|
||||||
|
adjustedBox.x2 -= Math.floor(excessWidth / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parent(adjustedBox, flags);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const SearchResult = new Lang.Class({
|
||||||
|
Name: 'SearchResult',
|
||||||
|
|
||||||
|
_init: function(provider, metaInfo) {
|
||||||
|
this.provider = provider;
|
||||||
|
this.metaInfo = metaInfo;
|
||||||
|
|
||||||
|
this.actor = new St.Button({ reactive: true,
|
||||||
|
can_focus: true,
|
||||||
|
track_hover: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_fill: true });
|
||||||
|
|
||||||
|
this.actor._delegate = this;
|
||||||
|
this.actor.connect('clicked', Lang.bind(this, this.activate));
|
||||||
|
},
|
||||||
|
|
||||||
|
activate: function() {
|
||||||
|
this.emit('activate', this.metaInfo.id);
|
||||||
|
},
|
||||||
|
|
||||||
|
setSelected: function(selected) {
|
||||||
|
if (selected)
|
||||||
|
this.actor.add_style_pseudo_class('selected');
|
||||||
|
else
|
||||||
|
this.actor.remove_style_pseudo_class('selected');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(SearchResult.prototype);
|
||||||
|
|
||||||
|
const ListSearchResult = new Lang.Class({
|
||||||
|
Name: 'ListSearchResult',
|
||||||
|
Extends: SearchResult,
|
||||||
|
|
||||||
|
ICON_SIZE: 64,
|
||||||
|
|
||||||
|
_init: function(provider, metaInfo) {
|
||||||
|
this.parent(provider, metaInfo);
|
||||||
|
|
||||||
|
this.actor.style_class = 'list-search-result';
|
||||||
|
this.actor.x_fill = true;
|
||||||
|
|
||||||
|
let content = new St.BoxLayout({ style_class: 'list-search-result-content',
|
||||||
|
vertical: false });
|
||||||
|
this.actor.set_child(content);
|
||||||
|
|
||||||
|
// An icon for, or thumbnail of, content
|
||||||
|
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
|
||||||
|
if (icon) {
|
||||||
|
content.add(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
let details = new St.BoxLayout({ vertical: true });
|
||||||
|
content.add(details, { x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
|
let title = new St.Label({ style_class: 'list-search-result-title',
|
||||||
|
text: this.metaInfo['name'] })
|
||||||
|
details.add(title, { x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
this.actor.label_actor = title;
|
||||||
|
|
||||||
|
if (this.metaInfo['description']) {
|
||||||
|
let description = new St.Label({ style_class: 'list-search-result-description' });
|
||||||
|
description.clutter_text.set_markup(this.metaInfo['description']);
|
||||||
|
details.add(description, { x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.END });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const GridSearchResult = new Lang.Class({
|
||||||
|
Name: 'GridSearchResult',
|
||||||
|
Extends: SearchResult,
|
||||||
|
|
||||||
|
_init: function(provider, metaInfo) {
|
||||||
|
this.parent(provider, metaInfo);
|
||||||
|
|
||||||
|
this.actor.style_class = 'grid-search-result';
|
||||||
|
|
||||||
|
let content = provider.createResultObject(metaInfo);
|
||||||
|
let dragSource = null;
|
||||||
|
|
||||||
|
if (content == null) {
|
||||||
|
let actor = new St.Bin();
|
||||||
|
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
||||||
|
{ createIcon: this.metaInfo['createIcon'] });
|
||||||
|
actor.set_child(icon.actor);
|
||||||
|
actor.label_actor = icon.label;
|
||||||
|
dragSource = icon.icon;
|
||||||
|
content = { actor: actor, icon: icon };
|
||||||
|
} else {
|
||||||
|
if (content._delegate && content._delegate.getDragActorSource)
|
||||||
|
dragSource = content._delegate.getDragActorSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.actor.set_child(content.actor);
|
||||||
|
this.actor.label_actor = content.actor.label_actor;
|
||||||
|
this.icon = content.icon;
|
||||||
|
|
||||||
|
let draggable = DND.makeDraggable(this.actor);
|
||||||
|
draggable.connect('drag-begin',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
Main.overview.beginItemDrag(this);
|
||||||
|
}));
|
||||||
|
draggable.connect('drag-cancelled',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
Main.overview.cancelledItemDrag(this);
|
||||||
|
}));
|
||||||
|
draggable.connect('drag-end',
|
||||||
|
Lang.bind(this, function() {
|
||||||
|
Main.overview.endItemDrag(this);
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!dragSource)
|
||||||
|
// not exactly right, but alignment problems are hard to notice
|
||||||
|
dragSource = content;
|
||||||
|
this._dragActorSource = dragSource;
|
||||||
|
},
|
||||||
|
|
||||||
|
getDragActorSource: function() {
|
||||||
|
return this._dragActorSource;
|
||||||
|
},
|
||||||
|
|
||||||
|
getDragActor: function() {
|
||||||
|
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
|
||||||
|
},
|
||||||
|
|
||||||
|
shellWorkspaceLaunch: function(params) {
|
||||||
|
if (this.provider.dragActivateResult)
|
||||||
|
this.provider.dragActivateResult(this.metaInfo.id, params);
|
||||||
|
else
|
||||||
|
this.provider.activateResult(this.metaInfo.id, this.terms);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const SearchResultsBase = new Lang.Class({
|
||||||
|
Name: 'SearchResultsBase',
|
||||||
|
|
||||||
|
_init: function(provider) {
|
||||||
|
this.provider = provider;
|
||||||
|
|
||||||
|
this._terms = [];
|
||||||
|
|
||||||
|
this.actor = new St.BoxLayout({ style_class: 'search-section',
|
||||||
|
vertical: true });
|
||||||
|
|
||||||
|
this._resultDisplayBin = new St.Bin({ x_fill: true,
|
||||||
|
y_fill: true });
|
||||||
|
this.actor.add(this._resultDisplayBin, { expand: true });
|
||||||
|
|
||||||
|
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
|
||||||
|
this.actor.add(separator.actor);
|
||||||
|
|
||||||
|
this._resultDisplays = {};
|
||||||
|
|
||||||
|
this._cancellable = new Gio.Cancellable();
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy: function() {
|
||||||
|
this.actor.destroy();
|
||||||
|
this._terms = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearResultDisplay: function() {
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function() {
|
||||||
|
this._resultDisplays = {};
|
||||||
|
this._clearResultDisplay();
|
||||||
|
this.actor.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
_keyFocusIn: function(actor) {
|
||||||
|
this.emit('key-focus-in', actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_activateResult: function(result, id) {
|
||||||
|
this.provider.activateResult(id, this._terms);
|
||||||
|
Main.overview.toggle();
|
||||||
|
},
|
||||||
|
|
||||||
|
_setMoreIconVisible: function(visible) {
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureResultActors: function(results, callback) {
|
||||||
|
let metasNeeded = results.filter(Lang.bind(this, function(resultId) {
|
||||||
|
return this._resultDisplays[resultId] === undefined;
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (metasNeeded.length === 0) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
this._cancellable.cancel();
|
||||||
|
this._cancellable.reset();
|
||||||
|
|
||||||
|
this.provider.getResultMetas(metasNeeded, Lang.bind(this, function(metas) {
|
||||||
|
metasNeeded.forEach(Lang.bind(this, function(resultId, i) {
|
||||||
|
let meta = metas[i];
|
||||||
|
let display = this._createResultDisplay(meta);
|
||||||
|
display.connect('activate', Lang.bind(this, this._activateResult));
|
||||||
|
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||||
|
this._resultDisplays[resultId] = display;
|
||||||
|
}));
|
||||||
|
callback();
|
||||||
|
}), this._cancellable);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSearch: function(providerResults, terms, callback) {
|
||||||
|
this._terms = terms;
|
||||||
|
|
||||||
|
if (providerResults.length == 0) {
|
||||||
|
this._clearResultDisplay();
|
||||||
|
this.actor.hide();
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
let maxResults = this._getMaxDisplayedResults();
|
||||||
|
let results = this.provider.filterResults(providerResults, maxResults);
|
||||||
|
let hasMoreResults = results.length < providerResults.length;
|
||||||
|
|
||||||
|
this._ensureResultActors(results, Lang.bind(this, function() {
|
||||||
|
this._clearResultDisplay();
|
||||||
|
|
||||||
|
// To avoid CSS transitions causing flickering when
|
||||||
|
// the first search result stays the same, we hide the
|
||||||
|
// content while filling in the results.
|
||||||
|
this.actor.hide();
|
||||||
|
this._clearResultDisplay();
|
||||||
|
results.forEach(Lang.bind(this, function(resultId) {
|
||||||
|
this._addItem(this._resultDisplays[resultId]);
|
||||||
|
}));
|
||||||
|
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
|
||||||
|
this.actor.show();
|
||||||
|
callback();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ListSearchResults = new Lang.Class({
|
||||||
|
Name: 'ListSearchResults',
|
||||||
|
Extends: SearchResultsBase,
|
||||||
|
|
||||||
|
_init: function(provider) {
|
||||||
|
this.parent(provider);
|
||||||
|
|
||||||
|
this._container = new St.BoxLayout({ style_class: 'search-section-content' });
|
||||||
|
this.providerIcon = new ProviderIcon(provider);
|
||||||
|
this.providerIcon.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||||
|
this.providerIcon.connect('clicked', Lang.bind(this,
|
||||||
|
function() {
|
||||||
|
provider.launchSearch(this._terms);
|
||||||
|
Main.overview.toggle();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._container.add(this.providerIcon, { x_fill: false,
|
||||||
|
y_fill: false,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
this._content = new St.BoxLayout({ style_class: 'list-search-results',
|
||||||
|
vertical: true });
|
||||||
|
this._container.add(this._content, { expand: true });
|
||||||
|
|
||||||
|
this._resultDisplayBin.set_child(this._container);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setMoreIconVisible: function(visible) {
|
||||||
|
this.providerIcon.moreIcon.visible = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getMaxDisplayedResults: function() {
|
||||||
|
return MAX_LIST_SEARCH_RESULTS_ROWS;
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearResultDisplay: function () {
|
||||||
|
this._content.remove_all_children();
|
||||||
|
},
|
||||||
|
|
||||||
|
_createResultDisplay: function(meta) {
|
||||||
|
return new ListSearchResult(this.provider, meta);
|
||||||
|
},
|
||||||
|
|
||||||
|
_addItem: function(display) {
|
||||||
|
this._content.add_actor(display.actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
getFirstResult: function() {
|
||||||
|
if (this._content.get_n_children() > 0)
|
||||||
|
return this._content.get_child_at_index(0)._delegate;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(ListSearchResults.prototype);
|
||||||
|
|
||||||
|
const GridSearchResults = new Lang.Class({
|
||||||
|
Name: 'GridSearchResults',
|
||||||
|
Extends: SearchResultsBase,
|
||||||
|
|
||||||
|
_init: function(provider) {
|
||||||
|
this.parent(provider);
|
||||||
|
|
||||||
|
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
|
||||||
|
xAlign: St.Align.START });
|
||||||
|
this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
|
||||||
|
this._bin.set_child(this._grid.actor);
|
||||||
|
|
||||||
|
this._resultDisplayBin.set_child(this._bin);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getMaxDisplayedResults: function() {
|
||||||
|
return this._grid.columnsForWidth(this._bin.width) * this._grid.getRowLimit();
|
||||||
|
},
|
||||||
|
|
||||||
|
_renderResults: function(metas) {
|
||||||
|
for (let i = 0; i < metas.length; i++) {
|
||||||
|
let display = new GridSearchResult(this.provider, metas[i]);
|
||||||
|
display.connect('activate', Lang.bind(this, this._activateResult));
|
||||||
|
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||||
|
this._grid.addItem(display);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearResultDisplay: function () {
|
||||||
|
this._grid.removeAll();
|
||||||
|
},
|
||||||
|
|
||||||
|
_createResultDisplay: function(meta) {
|
||||||
|
return new GridSearchResult(this.provider, meta);
|
||||||
|
},
|
||||||
|
|
||||||
|
_addItem: function(display) {
|
||||||
|
this._grid.addItem(display);
|
||||||
|
},
|
||||||
|
|
||||||
|
getFirstResult: function() {
|
||||||
|
if (this._grid.visibleItemsCount() > 0)
|
||||||
|
return this._grid.getItemAtIndex(0)._delegate;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Signals.addSignalMethods(GridSearchResults.prototype);
|
||||||
|
|
||||||
|
const SearchResults = new Lang.Class({
|
||||||
|
Name: 'SearchResults',
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.actor = new St.BoxLayout({ name: 'searchResults',
|
||||||
|
vertical: true });
|
||||||
|
|
||||||
|
this._content = new St.BoxLayout({ name: 'searchResultsContent',
|
||||||
|
vertical: true });
|
||||||
|
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
|
||||||
|
x_fill: true,
|
||||||
|
y_fill: true,
|
||||||
|
child: this._content });
|
||||||
|
|
||||||
|
let scrollChild = new St.BoxLayout();
|
||||||
|
scrollChild.add(this._contentBin, { expand: true });
|
||||||
|
|
||||||
|
this._scrollView = new St.ScrollView({ x_fill: true,
|
||||||
|
y_fill: false,
|
||||||
|
overlay_scrollbars: true,
|
||||||
|
style_class: 'search-display vfade' });
|
||||||
|
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||||
|
this._scrollView.add_actor(scrollChild);
|
||||||
|
let action = new Clutter.PanAction({ interpolate: true });
|
||||||
|
action.connect('pan', Lang.bind(this, this._onPan));
|
||||||
|
this._scrollView.add_action(action);
|
||||||
|
|
||||||
|
this.actor.add(this._scrollView, { x_fill: true,
|
||||||
|
y_fill: true,
|
||||||
|
expand: true,
|
||||||
|
x_align: St.Align.START,
|
||||||
|
y_align: St.Align.START });
|
||||||
|
|
||||||
|
this._statusText = new St.Label({ style_class: 'search-statustext' });
|
||||||
|
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
|
||||||
|
y_align: St.Align.MIDDLE });
|
||||||
|
this._content.add(this._statusBin, { expand: true });
|
||||||
|
this._statusBin.add_actor(this._statusText);
|
||||||
|
|
||||||
|
this._highlightDefault = false;
|
||||||
|
this._defaultResult = null;
|
||||||
|
|
||||||
|
this._searchSystem = new SearchSystem();
|
||||||
|
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
|
||||||
|
this._searchSystem.connect('providers-changed', Lang.bind(this, this._updateProviderDisplays));
|
||||||
|
this._updateProviderDisplays();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onPan: function(action) {
|
||||||
|
let [dist, dx, dy] = action.get_motion_delta(0);
|
||||||
|
let adjustment = this._scrollView.vscroll.adjustment;
|
||||||
|
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_keyFocusIn: function(provider, actor) {
|
||||||
|
Util.ensureActorVisibleInScrollView(this._scrollView, actor);
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureProviderDisplay: function(provider) {
|
||||||
|
if (provider.display)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let providerDisplay;
|
||||||
|
if (provider.appInfo)
|
||||||
|
providerDisplay = new ListSearchResults(provider);
|
||||||
|
else
|
||||||
|
providerDisplay = new GridSearchResults(provider);
|
||||||
|
|
||||||
|
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||||
|
this._content.add(providerDisplay.actor);
|
||||||
|
provider.display = providerDisplay;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateProviderDisplays: function() {
|
||||||
|
this._searchSystem.getProviders().forEach(Lang.bind(this, this._ensureProviderDisplay));
|
||||||
|
},
|
||||||
|
|
||||||
|
_clearDisplay: function() {
|
||||||
|
this._searchSystem.getProviders().forEach(function(provider) {
|
||||||
|
provider.display.clear();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: function() {
|
||||||
|
this._searchSystem.reset();
|
||||||
|
this._statusBin.hide();
|
||||||
|
this._clearDisplay();
|
||||||
|
this._defaultResult = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
startingSearch: function() {
|
||||||
|
this.reset();
|
||||||
|
this._statusText.set_text(_("Searching…"));
|
||||||
|
this._statusBin.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
setTerms: function(terms) {
|
||||||
|
this._searchSystem.setTerms(terms);
|
||||||
|
},
|
||||||
|
|
||||||
|
_maybeSetInitialSelection: function() {
|
||||||
|
let newDefaultResult = null;
|
||||||
|
|
||||||
|
let providers = this._searchSystem.getProviders();
|
||||||
|
for (let i = 0; i < providers.length; i++) {
|
||||||
|
let provider = providers[i];
|
||||||
|
let display = provider.display;
|
||||||
|
|
||||||
|
if (!display.actor.visible)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let firstResult = display.getFirstResult();
|
||||||
|
if (firstResult) {
|
||||||
|
newDefaultResult = firstResult;
|
||||||
|
break; // select this one!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newDefaultResult != this._defaultResult) {
|
||||||
|
if (this._defaultResult)
|
||||||
|
this._defaultResult.setSelected(false);
|
||||||
|
if (newDefaultResult)
|
||||||
|
newDefaultResult.setSelected(this._highlightDefault);
|
||||||
|
|
||||||
|
this._defaultResult = newDefaultResult;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateStatusText: function () {
|
||||||
|
let haveResults = this._searchSystem.getProviders().some(function(provider) {
|
||||||
|
let display = provider.display;
|
||||||
|
return (display.getFirstResult() != null);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!haveResults) {
|
||||||
|
this._statusText.set_text(_("No results."));
|
||||||
|
this._statusBin.show();
|
||||||
|
} else {
|
||||||
|
this._statusBin.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateResults: function(searchSystem, provider, results) {
|
||||||
|
let terms = searchSystem.getTerms();
|
||||||
|
let display = provider.display;
|
||||||
|
|
||||||
|
display.updateSearch(results, terms, Lang.bind(this, function() {
|
||||||
|
this._maybeSetInitialSelection();
|
||||||
|
this._updateStatusText();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
activateDefault: function() {
|
||||||
|
if (this._defaultResult)
|
||||||
|
this._defaultResult.activate();
|
||||||
|
},
|
||||||
|
|
||||||
|
highlightDefault: function(highlight) {
|
||||||
|
this._highlightDefault = highlight;
|
||||||
|
if (this._defaultResult)
|
||||||
|
this._defaultResult.setSelected(highlight);
|
||||||
|
},
|
||||||
|
|
||||||
|
navigateFocus: function(direction) {
|
||||||
|
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
|
||||||
|
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
|
||||||
|
direction == (rtl ? Gtk.DirectionType.RIGHT
|
||||||
|
: Gtk.DirectionType.LEFT) ||
|
||||||
|
direction == Gtk.DirectionType.UP) {
|
||||||
|
this.actor.navigate_focus(null, direction, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let from = this._defaultResult ? this._defaultResult.actor : null;
|
||||||
|
this.actor.navigate_focus(from, direction, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ProviderIcon = new Lang.Class({
|
||||||
|
Name: 'ProviderIcon',
|
||||||
|
Extends: St.Button,
|
||||||
|
|
||||||
|
PROVIDER_ICON_SIZE: 48,
|
||||||
|
|
||||||
|
_init: function(provider) {
|
||||||
|
this.provider = provider;
|
||||||
|
this.parent({ style_class: 'search-provider-icon',
|
||||||
|
reactive: true,
|
||||||
|
can_focus: true,
|
||||||
|
accessible_name: provider.appInfo.get_name(),
|
||||||
|
track_hover: true });
|
||||||
|
|
||||||
|
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||||
|
this.set_child(this._content);
|
||||||
|
|
||||||
|
let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
|
||||||
|
|
||||||
|
this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
|
||||||
|
visible: false,
|
||||||
|
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
|
||||||
|
y_align: Clutter.ActorAlign.END,
|
||||||
|
x_expand: true,
|
||||||
|
y_expand: true });
|
||||||
|
|
||||||
|
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
|
||||||
|
gicon: provider.appInfo.get_icon() });
|
||||||
|
this._content.add_actor(icon);
|
||||||
|
this._content.add_actor(this.moreIcon);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,555 +0,0 @@
|
|||||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
||||||
|
|
||||||
const Clutter = imports.gi.Clutter;
|
|
||||||
const Lang = imports.lang;
|
|
||||||
const Gtk = imports.gi.Gtk;
|
|
||||||
const Meta = imports.gi.Meta;
|
|
||||||
const St = imports.gi.St;
|
|
||||||
const Atk = imports.gi.Atk;
|
|
||||||
|
|
||||||
const DND = imports.ui.dnd;
|
|
||||||
const IconGrid = imports.ui.iconGrid;
|
|
||||||
const Main = imports.ui.main;
|
|
||||||
const Overview = imports.ui.overview;
|
|
||||||
const Separator = imports.ui.separator;
|
|
||||||
const Search = imports.ui.search;
|
|
||||||
|
|
||||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
|
|
||||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
|
|
||||||
|
|
||||||
const SearchResult = new Lang.Class({
|
|
||||||
Name: 'SearchResult',
|
|
||||||
|
|
||||||
_init: function(provider, metaInfo, terms) {
|
|
||||||
this.provider = provider;
|
|
||||||
this.metaInfo = metaInfo;
|
|
||||||
this.terms = terms;
|
|
||||||
|
|
||||||
this.actor = new St.Button({ reactive: true,
|
|
||||||
can_focus: true,
|
|
||||||
track_hover: true,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_fill: true });
|
|
||||||
|
|
||||||
this.actor._delegate = this;
|
|
||||||
this.actor.connect('clicked', Lang.bind(this, this.activate));
|
|
||||||
},
|
|
||||||
|
|
||||||
activate: function() {
|
|
||||||
this.provider.activateResult(this.metaInfo.id, this.terms);
|
|
||||||
Main.overview.toggle();
|
|
||||||
},
|
|
||||||
|
|
||||||
setSelected: function(selected) {
|
|
||||||
if (selected)
|
|
||||||
this.actor.add_style_pseudo_class('selected');
|
|
||||||
else
|
|
||||||
this.actor.remove_style_pseudo_class('selected');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const ListSearchResult = new Lang.Class({
|
|
||||||
Name: 'ListSearchResult',
|
|
||||||
Extends: SearchResult,
|
|
||||||
|
|
||||||
ICON_SIZE: 64,
|
|
||||||
|
|
||||||
_init: function(provider, metaInfo, terms) {
|
|
||||||
this.parent(provider, metaInfo, terms);
|
|
||||||
|
|
||||||
this.actor.style_class = 'list-search-result';
|
|
||||||
this.actor.x_fill = true;
|
|
||||||
|
|
||||||
let content = new St.BoxLayout({ style_class: 'list-search-result-content',
|
|
||||||
vertical: false });
|
|
||||||
this.actor.set_child(content);
|
|
||||||
|
|
||||||
// An icon for, or thumbnail of, content
|
|
||||||
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
|
|
||||||
if (icon) {
|
|
||||||
content.add(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
let details = new St.BoxLayout({ vertical: true });
|
|
||||||
content.add(details, { x_fill: true,
|
|
||||||
y_fill: false,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
|
|
||||||
let title = new St.Label({ style_class: 'list-search-result-title',
|
|
||||||
text: this.metaInfo['name'] })
|
|
||||||
details.add(title, { x_fill: false,
|
|
||||||
y_fill: false,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_align: St.Align.START });
|
|
||||||
|
|
||||||
// TODO: should highlight terms in the description here
|
|
||||||
if (this.metaInfo['description']) {
|
|
||||||
let description = new St.Label({ style_class: 'list-search-result-description',
|
|
||||||
text: '"' + this.metaInfo['description'] + '"' });
|
|
||||||
details.add(description, { x_fill: false,
|
|
||||||
y_fill: false,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_align: St.Align.END });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const GridSearchResult = new Lang.Class({
|
|
||||||
Name: 'GridSearchResult',
|
|
||||||
Extends: SearchResult,
|
|
||||||
|
|
||||||
_init: function(provider, metaInfo, terms) {
|
|
||||||
this.parent(provider, metaInfo, terms);
|
|
||||||
|
|
||||||
this.actor.style_class = 'grid-search-result';
|
|
||||||
|
|
||||||
let content = provider.createResultActor(metaInfo, terms);
|
|
||||||
let dragSource = null;
|
|
||||||
|
|
||||||
if (content == null) {
|
|
||||||
content = new St.Bin();
|
|
||||||
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
|
||||||
{ createIcon: this.metaInfo['createIcon'] });
|
|
||||||
content.set_child(icon.actor);
|
|
||||||
content.label_actor = icon.label;
|
|
||||||
dragSource = icon.icon;
|
|
||||||
} else {
|
|
||||||
if (content._delegate && content._delegate.getDragActorSource)
|
|
||||||
dragSource = content._delegate.getDragActorSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.actor.set_child(content);
|
|
||||||
|
|
||||||
let draggable = DND.makeDraggable(this.actor);
|
|
||||||
draggable.connect('drag-begin',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
Main.overview.beginItemDrag(this);
|
|
||||||
}));
|
|
||||||
draggable.connect('drag-cancelled',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
Main.overview.cancelledItemDrag(this);
|
|
||||||
}));
|
|
||||||
draggable.connect('drag-end',
|
|
||||||
Lang.bind(this, function() {
|
|
||||||
Main.overview.endItemDrag(this);
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (!dragSource)
|
|
||||||
// not exactly right, but alignment problems are hard to notice
|
|
||||||
dragSource = content;
|
|
||||||
this._dragActorSource = dragSource;
|
|
||||||
},
|
|
||||||
|
|
||||||
getDragActorSource: function() {
|
|
||||||
return this._dragActorSource;
|
|
||||||
},
|
|
||||||
|
|
||||||
getDragActor: function() {
|
|
||||||
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
|
|
||||||
},
|
|
||||||
|
|
||||||
shellWorkspaceLaunch: function(params) {
|
|
||||||
if (this.provider.dragActivateResult)
|
|
||||||
this.provider.dragActivateResult(this.metaInfo.id, params);
|
|
||||||
else
|
|
||||||
this.provider.activateResult(this.metaInfo.id, this.terms);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const ListSearchResults = new Lang.Class({
|
|
||||||
Name: 'ListSearchResults',
|
|
||||||
|
|
||||||
_init: function(provider) {
|
|
||||||
this.provider = provider;
|
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ style_class: 'search-section-content' });
|
|
||||||
this.providerIcon = new ProviderIcon(provider);
|
|
||||||
this.providerIcon.connect('clicked', Lang.bind(this,
|
|
||||||
function() {
|
|
||||||
provider.launchSearch(this._terms);
|
|
||||||
Main.overview.toggle();
|
|
||||||
}));
|
|
||||||
|
|
||||||
this.actor.add(this.providerIcon, { x_fill: false,
|
|
||||||
y_fill: false,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_align: St.Align.START });
|
|
||||||
|
|
||||||
this._content = new St.BoxLayout({ style_class: 'list-search-results',
|
|
||||||
vertical: true });
|
|
||||||
this.actor.add_actor(this._content);
|
|
||||||
|
|
||||||
this._notDisplayedResult = [];
|
|
||||||
this._terms = [];
|
|
||||||
this._pendingClear = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
getResultsForDisplay: function() {
|
|
||||||
let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
|
|
||||||
let canDisplay = MAX_LIST_SEARCH_RESULTS_ROWS - alreadyVisible;
|
|
||||||
|
|
||||||
let newResults = this._notDisplayedResult.splice(0, canDisplay);
|
|
||||||
return newResults;
|
|
||||||
},
|
|
||||||
|
|
||||||
getVisibleResultCount: function() {
|
|
||||||
return this._content.get_n_children();
|
|
||||||
},
|
|
||||||
|
|
||||||
hasMoreResults: function() {
|
|
||||||
return this._notDisplayedResult.length > 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
setResults: function(results, terms) {
|
|
||||||
// copy the lists
|
|
||||||
this._notDisplayedResult = results.slice(0);
|
|
||||||
this._terms = terms.slice(0);
|
|
||||||
this._pendingClear = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
renderResults: function(metas) {
|
|
||||||
for (let i = 0; i < metas.length; i++) {
|
|
||||||
let display = new ListSearchResult(this.provider, metas[i], this._terms);
|
|
||||||
this._content.add_actor(display.actor);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
clear: function () {
|
|
||||||
this._content.destroy_all_children();
|
|
||||||
this._pendingClear = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
getFirstResult: function() {
|
|
||||||
if (this.getVisibleResultCount() > 0)
|
|
||||||
return this._content.get_child_at_index(0)._delegate;
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const GridSearchResults = new Lang.Class({
|
|
||||||
Name: 'GridSearchResults',
|
|
||||||
|
|
||||||
_init: function(provider) {
|
|
||||||
this.provider = provider;
|
|
||||||
|
|
||||||
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
|
|
||||||
xAlign: St.Align.START });
|
|
||||||
this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
|
|
||||||
|
|
||||||
this.actor.set_child(this._grid.actor);
|
|
||||||
|
|
||||||
this._notDisplayedResult = [];
|
|
||||||
this._terms = [];
|
|
||||||
this._pendingClear = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
getResultsForDisplay: function() {
|
|
||||||
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
|
|
||||||
let canDisplay = this._grid.childrenInRow(this.actor.width) * this._grid.getRowLimit()
|
|
||||||
- alreadyVisible;
|
|
||||||
|
|
||||||
let newResults = this._notDisplayedResult.splice(0, canDisplay);
|
|
||||||
return newResults;
|
|
||||||
},
|
|
||||||
|
|
||||||
getVisibleResultCount: function() {
|
|
||||||
return this._grid.visibleItemsCount();
|
|
||||||
},
|
|
||||||
|
|
||||||
hasMoreResults: function() {
|
|
||||||
return this._notDisplayedResult.length > 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
setResults: function(results, terms) {
|
|
||||||
// copy the lists
|
|
||||||
this._notDisplayedResult = results.slice(0);
|
|
||||||
this._terms = terms.slice(0);
|
|
||||||
this._pendingClear = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
renderResults: function(metas) {
|
|
||||||
for (let i = 0; i < metas.length; i++) {
|
|
||||||
let display = new GridSearchResult(this.provider, metas[i], this._terms);
|
|
||||||
this._grid.addItem(display.actor);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
clear: function () {
|
|
||||||
this._grid.removeAll();
|
|
||||||
this._pendingClear = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
getFirstResult: function() {
|
|
||||||
if (this.getVisibleResultCount() > 0)
|
|
||||||
return this._grid.getItemAtIndex(0)._delegate;
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const SearchResults = new Lang.Class({
|
|
||||||
Name: 'SearchResults',
|
|
||||||
|
|
||||||
_init: function(searchSystem) {
|
|
||||||
this._searchSystem = searchSystem;
|
|
||||||
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
|
|
||||||
|
|
||||||
this.actor = new St.BoxLayout({ name: 'searchResults',
|
|
||||||
vertical: true });
|
|
||||||
|
|
||||||
this._content = new St.BoxLayout({ name: 'searchResultsContent',
|
|
||||||
vertical: true });
|
|
||||||
|
|
||||||
this._scrollView = new St.ScrollView({ x_fill: true,
|
|
||||||
y_fill: false,
|
|
||||||
style_class: 'vfade' });
|
|
||||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
|
||||||
this._scrollView.add_actor(this._content);
|
|
||||||
let action = new Clutter.PanAction({ interpolate: true });
|
|
||||||
action.connect('pan', Lang.bind(this, this._onPan));
|
|
||||||
this._scrollView.add_action(action);
|
|
||||||
|
|
||||||
this.actor.add(this._scrollView, { x_fill: true,
|
|
||||||
y_fill: true,
|
|
||||||
expand: true,
|
|
||||||
x_align: St.Align.START,
|
|
||||||
y_align: St.Align.START });
|
|
||||||
|
|
||||||
this._statusText = new St.Label({ style_class: 'search-statustext' });
|
|
||||||
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
|
|
||||||
y_align: St.Align.MIDDLE });
|
|
||||||
this._content.add(this._statusBin, { expand: true });
|
|
||||||
this._statusBin.add_actor(this._statusText);
|
|
||||||
this._providers = this._searchSystem.getProviders();
|
|
||||||
this._providerMeta = [];
|
|
||||||
for (let i = 0; i < this._providers.length; i++) {
|
|
||||||
this.createProviderMeta(this._providers[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._highlightDefault = false;
|
|
||||||
this._defaultResult = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onPan: function(action) {
|
|
||||||
let [dist, dx, dy] = action.get_motion_delta(0);
|
|
||||||
let adjustment = this._scrollView.vscroll.adjustment;
|
|
||||||
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
createProviderMeta: function(provider) {
|
|
||||||
let providerBox = new St.BoxLayout({ style_class: 'search-section',
|
|
||||||
vertical: true });
|
|
||||||
let providerIcon = null;
|
|
||||||
let resultDisplay = null;
|
|
||||||
|
|
||||||
if (provider.appInfo) {
|
|
||||||
resultDisplay = new ListSearchResults(provider);
|
|
||||||
providerIcon = resultDisplay.providerIcon;
|
|
||||||
} else {
|
|
||||||
resultDisplay = new GridSearchResults(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
|
|
||||||
x_fill: true,
|
|
||||||
y_fill: true });
|
|
||||||
providerBox.add(resultDisplayBin, { expand: true });
|
|
||||||
|
|
||||||
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
|
|
||||||
providerBox.add(separator.actor);
|
|
||||||
|
|
||||||
this._providerMeta.push({ provider: provider,
|
|
||||||
actor: providerBox,
|
|
||||||
icon: providerIcon,
|
|
||||||
resultDisplay: resultDisplay });
|
|
||||||
this._content.add(providerBox);
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyProviderMeta: function(provider) {
|
|
||||||
for (let i=0; i < this._providerMeta.length; i++) {
|
|
||||||
let meta = this._providerMeta[i];
|
|
||||||
if (meta.provider == provider) {
|
|
||||||
meta.actor.destroy();
|
|
||||||
this._providerMeta.splice(i, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_clearDisplay: function() {
|
|
||||||
for (let i = 0; i < this._providerMeta.length; i++) {
|
|
||||||
let meta = this._providerMeta[i];
|
|
||||||
meta.resultDisplay.clear();
|
|
||||||
meta.actor.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_clearDisplayForProvider: function(provider) {
|
|
||||||
let meta = this._metaForProvider(provider);
|
|
||||||
meta.resultDisplay.clear();
|
|
||||||
meta.actor.hide();
|
|
||||||
},
|
|
||||||
|
|
||||||
reset: function() {
|
|
||||||
this._searchSystem.reset();
|
|
||||||
this._statusBin.hide();
|
|
||||||
this._clearDisplay();
|
|
||||||
},
|
|
||||||
|
|
||||||
startingSearch: function() {
|
|
||||||
this.reset();
|
|
||||||
this._statusText.set_text(_("Searching..."));
|
|
||||||
this._statusBin.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
doSearch: function (searchString) {
|
|
||||||
this._searchSystem.updateSearch(searchString);
|
|
||||||
},
|
|
||||||
|
|
||||||
_metaForProvider: function(provider) {
|
|
||||||
return this._providerMeta[this._providers.indexOf(provider)];
|
|
||||||
},
|
|
||||||
|
|
||||||
_maybeSetInitialSelection: function() {
|
|
||||||
let newDefaultResult = null;
|
|
||||||
|
|
||||||
for (let i = 0; i < this._providerMeta.length; i++) {
|
|
||||||
let meta = this._providerMeta[i];
|
|
||||||
|
|
||||||
if (!meta.actor.visible)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
let firstResult = meta.resultDisplay.getFirstResult();
|
|
||||||
if (firstResult) {
|
|
||||||
newDefaultResult = firstResult;
|
|
||||||
break; // select this one!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newDefaultResult != this._defaultResult) {
|
|
||||||
if (this._defaultResult)
|
|
||||||
this._defaultResult.setSelected(false);
|
|
||||||
if (newDefaultResult)
|
|
||||||
newDefaultResult.setSelected(this._highlightDefault);
|
|
||||||
|
|
||||||
this._defaultResult = newDefaultResult;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateStatusText: function () {
|
|
||||||
let haveResults = false;
|
|
||||||
|
|
||||||
for (let i = 0; i < this._providerMeta.length; ++i)
|
|
||||||
if (this._providerMeta[i].resultDisplay.getFirstResult()) {
|
|
||||||
haveResults = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!haveResults) {
|
|
||||||
this._statusText.set_text(_("No results."));
|
|
||||||
this._statusBin.show();
|
|
||||||
} else {
|
|
||||||
this._statusBin.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateResults: function(searchSystem, results) {
|
|
||||||
let terms = searchSystem.getTerms();
|
|
||||||
let [provider, providerResults] = results;
|
|
||||||
let meta = this._metaForProvider(provider);
|
|
||||||
|
|
||||||
if (providerResults.length == 0) {
|
|
||||||
this._clearDisplayForProvider(provider);
|
|
||||||
meta.resultDisplay.setResults([], []);
|
|
||||||
this._maybeSetInitialSelection();
|
|
||||||
this._updateStatusText();
|
|
||||||
} else {
|
|
||||||
meta.resultDisplay.setResults(providerResults, terms);
|
|
||||||
let results = meta.resultDisplay.getResultsForDisplay();
|
|
||||||
|
|
||||||
if (meta.icon)
|
|
||||||
meta.icon.moreIcon.visible =
|
|
||||||
meta.resultDisplay.hasMoreResults() &&
|
|
||||||
provider.canLaunchSearch;
|
|
||||||
|
|
||||||
provider.getResultMetas(results, Lang.bind(this, function(metas) {
|
|
||||||
this._clearDisplayForProvider(provider);
|
|
||||||
meta.actor.show();
|
|
||||||
|
|
||||||
// Hiding drops the key focus if we have it
|
|
||||||
let focus = global.stage.get_key_focus();
|
|
||||||
// To avoid CSS transitions causing flickering when
|
|
||||||
// the first search result stays the same, we hide the
|
|
||||||
// content while filling in the results.
|
|
||||||
this._content.hide();
|
|
||||||
|
|
||||||
meta.resultDisplay.renderResults(metas);
|
|
||||||
this._maybeSetInitialSelection();
|
|
||||||
this._updateStatusText();
|
|
||||||
|
|
||||||
this._content.show();
|
|
||||||
if (this._content.contains(focus))
|
|
||||||
global.stage.set_key_focus(focus);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
activateDefault: function() {
|
|
||||||
if (this._defaultResult)
|
|
||||||
this._defaultResult.activate();
|
|
||||||
},
|
|
||||||
|
|
||||||
highlightDefault: function(highlight) {
|
|
||||||
this._highlightDefault = highlight;
|
|
||||||
if (this._defaultResult)
|
|
||||||
this._defaultResult.setSelected(highlight);
|
|
||||||
},
|
|
||||||
|
|
||||||
navigateFocus: function(direction) {
|
|
||||||
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
|
|
||||||
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
|
|
||||||
direction == (rtl ? Gtk.DirectionType.RIGHT
|
|
||||||
: Gtk.DirectionType.LEFT) ||
|
|
||||||
direction == Gtk.DirectionType.UP) {
|
|
||||||
this.actor.navigate_focus(null, direction, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let from = this._defaultResult ? this._defaultResult.actor : null;
|
|
||||||
this.actor.navigate_focus(from, direction, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const ProviderIcon = new Lang.Class({
|
|
||||||
Name: 'ProviderIcon',
|
|
||||||
Extends: St.Button,
|
|
||||||
|
|
||||||
PROVIDER_ICON_SIZE: 48,
|
|
||||||
|
|
||||||
_init: function(provider) {
|
|
||||||
this.provider = provider;
|
|
||||||
this.parent({ style_class: 'search-provider-icon',
|
|
||||||
reactive: true,
|
|
||||||
can_focus: true,
|
|
||||||
track_hover: true });
|
|
||||||
|
|
||||||
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
|
||||||
this.set_child(this._content);
|
|
||||||
|
|
||||||
let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
|
|
||||||
|
|
||||||
this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
|
|
||||||
visible: false,
|
|
||||||
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
|
|
||||||
y_align: Clutter.ActorAlign.END,
|
|
||||||
x_expand: true,
|
|
||||||
y_expand: true });
|
|
||||||
|
|
||||||
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
|
|
||||||
gicon: provider.appInfo.get_icon() });
|
|
||||||
this._content.add_actor(icon);
|
|
||||||
this._content.add_actor(this.moreIcon);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user