Compare commits
1145 Commits
Author | SHA1 | Date | |
---|---|---|---|
72a43a6001 | |||
5180ab262c | |||
5fca85cd28 | |||
fb5b368ca7 | |||
62b6419332 | |||
65c136f4ed | |||
34606c0a8c | |||
260657c0b8 | |||
f5cc579272 | |||
b4ce0e7208 | |||
eb3fc7815e | |||
2f5a226bc2 | |||
01eb79a3cc | |||
7ea364ae8b | |||
48a6e6f309 | |||
0f63ad0fc1 | |||
49c4ba5656 | |||
642bf2b778 | |||
328bb1c21b | |||
38add2e78b | |||
2dc41c944e | |||
ea552ea157 | |||
a72dca3610 | |||
5e5035a0f7 | |||
ffdb85e003 | |||
6a969b934f | |||
8d3ff56846 | |||
d8d046f2b3 | |||
0bec76b6ee | |||
aa3aea7520 | |||
49fcc93a4b | |||
eaec459ee9 | |||
72c6f0025d | |||
aa3caff714 | |||
522ccaba3f | |||
3d00864599 | |||
bd3dfb8f6a | |||
1b3c89f32b | |||
a4475465f1 | |||
84f14cb61c | |||
d0de411a59 | |||
2b9645b268 | |||
4f08bebbf0 | |||
4e8c476153 | |||
1d374ac8bd | |||
83f0f4ea36 | |||
18f569280c | |||
fc0c98805f | |||
6722b69b8a | |||
f9d909f985 | |||
dd42cfa853 | |||
4ed4bb330e | |||
f02b007337 | |||
0035de4ab7 | |||
28552d4b38 | |||
42066a7c46 | |||
45847470d1 | |||
72b0a3d78f | |||
fc3ad390b7 | |||
926de53c0c | |||
e6cd112379 | |||
2cefc8be27 | |||
991179835e | |||
0ca2fee54f | |||
778ef365d9 | |||
45937ed85d | |||
65a9c09c9c | |||
67b14d5fe5 | |||
f976e46c46 | |||
7026209eae | |||
1e82d6edc2 | |||
50ac1dd197 | |||
9d933356e1 | |||
c2a5c00111 | |||
edb561a6ff | |||
14c2460fe4 | |||
598362448a | |||
845273d4c0 | |||
3aebfdc319 | |||
f423c371c8 | |||
6fe1ad62ba | |||
63efe1db11 | |||
002ae0e7f1 | |||
4f5e5e1999 | |||
f4feb409ff | |||
05a035e6fd | |||
7c2a3cc233 | |||
7f660fd4d7 | |||
c0730b610e | |||
b0cfcc4842 | |||
1afc9fefcc | |||
90bea5785e | |||
61dacf3d91 | |||
3ae45bddad | |||
9ad4070d58 | |||
1981b21ea8 | |||
9896135c97 | |||
0d340099da | |||
cacc780111 | |||
a9a6da08a1 | |||
ccb6b93b65 | |||
5623f3b4b5 | |||
a227d595f5 | |||
012955e341 | |||
a41cd7d3cc | |||
11d947f1e5 | |||
2bb717b0b7 | |||
770f8c6538 | |||
e76e0042a8 | |||
91e266113c | |||
ed2956a2ea | |||
aa6f352cb0 | |||
5a42a91ee0 | |||
4d682c7861 | |||
20fec42496 | |||
437df2f0bd | |||
445aa54622 | |||
7fa9ca0a9c | |||
0fe7ae1810 | |||
9c251d85cf | |||
959b5fe5cf | |||
75a8bf626a | |||
eb514f335c | |||
ad77b4ddfc | |||
534bf2b000 | |||
3821fd04a4 | |||
b55b9cc7a9 | |||
ac6462c7c6 | |||
aa7b0a285b | |||
0a63de8c8d | |||
0992bd41ed | |||
ed52a5a58b | |||
79bf770783 | |||
c7e3b68dcd | |||
1b9318c82f | |||
6a142c4260 | |||
c49f2e1384 | |||
9208473e59 | |||
08ece4c186 | |||
99c29366ee | |||
7a11964dfb | |||
c4922f6624 | |||
4f2070a7c6 | |||
03177dc474 | |||
b9b4886a6f | |||
33e35f269f | |||
b886656f61 | |||
8589bfb62e | |||
6a36a68f32 | |||
a0a701757e | |||
effe6fab3a | |||
8e560f98d1 | |||
58aabfcf5b | |||
19e3f794f8 | |||
2b1077aaa1 | |||
547cdf86cc | |||
7653175c6f | |||
a4c1b55111 | |||
93205eefb9 | |||
ec3f8d4b85 | |||
0258c7a518 | |||
ebfdb8ec3c | |||
6823bad2d8 | |||
491100c7ee | |||
762e770c84 | |||
c214e3b07a | |||
b656b1c22f | |||
d211680534 | |||
d44c3d0cd3 | |||
6cb1692841 | |||
593acb954d | |||
d209bc69b6 | |||
4d64bbcf7d | |||
5bd4329b11 | |||
02870e5363 | |||
2a59478b37 | |||
04030f22a6 | |||
2cc4558018 | |||
215eb5c65f | |||
5faef316b8 | |||
6751ca4c18 | |||
84e0a20701 | |||
e04e507659 | |||
6a48dee037 | |||
ea3fd0cf65 | |||
9c614057be | |||
8096e71c53 | |||
73977795a6 | |||
3b3445146d | |||
650dea017b | |||
70099872ab | |||
dc5618558f | |||
62481f4b7c | |||
d3418f6381 | |||
57dd862e35 | |||
db76fb8ff9 | |||
9e5704b498 | |||
3deaeb4a90 | |||
afb7f08e6b | |||
38c70e73b9 | |||
e60bf44c2e | |||
746a8692ac | |||
a00762ddd9 | |||
3981b27d8f | |||
62786c09a8 | |||
67c216a6fe | |||
e933302ae4 | |||
75d5e84a4b | |||
f160dda187 | |||
9c474a635a | |||
d445bd17eb | |||
584f8c8381 | |||
032a688a72 | |||
285a7467d0 | |||
3289105ac4 | |||
51861b1e6b | |||
d85f97c744 | |||
98847f2279 | |||
1d58ea25ab | |||
9de05994db | |||
c9f3afc38f | |||
3526bc0f0a | |||
079809af1d | |||
26719b02e4 | |||
3a45ddcaad | |||
f839100bc2 | |||
c93767768c | |||
0011755b41 | |||
ec932b2306 | |||
1c0c38574d | |||
78b81650f5 | |||
62bfde45aa | |||
5b624a34b8 | |||
9c008ab998 | |||
6edcd82103 | |||
d5aa276c41 | |||
d54efe0838 | |||
d0fe1211f2 | |||
3e20843d9c | |||
7c9d90b0aa | |||
b6e6e097b7 | |||
3842981c35 | |||
926177785d | |||
7d80647170 | |||
c2a21bb885 | |||
eaff1e9290 | |||
93c5e6d97e | |||
20fc9735fa | |||
d450b74e10 | |||
e8fa2b6417 | |||
c459ef6888 | |||
d1a3a000af | |||
5ce8980db3 | |||
8c32cff6fc | |||
bc99db9fd3 | |||
a5eae8e3d8 | |||
82a764ee93 | |||
ce2c90a534 | |||
28cc0da151 | |||
f1957dccb7 | |||
0af4dc0b4c | |||
687e1ebf69 | |||
805b686576 | |||
101daf6791 | |||
5d12ab415c | |||
0810ab62db | |||
6f00d81abf | |||
4184edc7f8 | |||
05f9f991d8 | |||
3df7ef6ce6 | |||
d836194e31 | |||
8ed3e2117f | |||
62fcda5d91 | |||
0a780376f3 | |||
efb9f167bd | |||
eb69f3aa76 | |||
c15a885418 | |||
fe60db64e0 | |||
b90cc5ff26 | |||
e9f95ca605 | |||
017c2468ee | |||
8c6a2874ff | |||
557130d2f2 | |||
84cbbafaae | |||
72a663f554 | |||
7c762ef9df | |||
3182aba744 | |||
e6339fbb45 | |||
a6d8c25494 | |||
cd4eda8bef | |||
a0bd4a02e4 | |||
42b54aaa21 | |||
bbfa616f27 | |||
7e31015ba2 | |||
aa2fc3c858 | |||
d4f0b5bdf3 | |||
eda27d5194 | |||
589e6c29f3 | |||
4b46533ce8 | |||
6687b9b739 | |||
91c4408d23 | |||
83adb2a864 | |||
5918faddf7 | |||
916c02a2f5 | |||
a84fb99c0a | |||
69d5cef3b2 | |||
38d05a8285 | |||
f8899cf274 | |||
dbbf4097a5 | |||
acb1497f4f | |||
e545ec59b9 | |||
da26a9daf8 | |||
a2f263dcbb | |||
ce5cd3bf30 | |||
9a05aea76f | |||
c9f6d5e2a1 | |||
eba2b999ed | |||
cceac0d8fb | |||
14eedf8651 | |||
9c6180afa2 | |||
f1b1dbcb00 | |||
ee23b8dbe0 | |||
c9e00bee08 | |||
9970671bb1 | |||
bb4502dca8 | |||
d77c7a407c | |||
554001c0ed | |||
4d153bc96f | |||
476394809a | |||
b6f3e15037 | |||
46c86e093c | |||
c6350aa557 | |||
4e56af39da | |||
9fff972946 | |||
e30925995f | |||
f0d4260c81 | |||
a7f82745c6 | |||
a6fa6519d5 | |||
a944dca60e | |||
fdc443aebe | |||
7e08e1e0e7 | |||
bd7938e02f | |||
a583f45cc6 | |||
1071ac5d25 | |||
45793d0e47 | |||
ea3866a07a | |||
e41879a5c4 | |||
24dc926660 | |||
932b895127 | |||
9c4ffc4bf3 | |||
1ea2e2bcab | |||
a8b15dd2cf | |||
b1b8147ab8 | |||
39c210abed | |||
4bb2a364d3 | |||
6d3ebdcb5e | |||
e865db57e0 | |||
19ba9a98b8 | |||
4a39af7f98 | |||
c326aad9d7 | |||
dc94f7b9f5 | |||
e5be41b667 | |||
775bd961b6 | |||
3c67d012e7 | |||
2fbd8f063e | |||
f285f2c69f | |||
e375e1a857 | |||
910c95fa9b | |||
b95c0682b0 | |||
9f460a36f6 | |||
41a3f10938 | |||
d850c8599e | |||
ec288d0e68 | |||
0b92cd0772 | |||
c7f5f172dd | |||
84bc445593 | |||
365bfcae12 | |||
47c9243271 | |||
8c67a70db0 | |||
5f4591e24c | |||
37ef0e4bed | |||
7d7b92419f | |||
309d40a92b | |||
02718357da | |||
cfef107114 | |||
b742b1eed2 | |||
d58be565a1 | |||
522ed3c21d | |||
2fb8781f30 | |||
2bda6db30f | |||
8abd18363c | |||
256bb532a2 | |||
6077e28f95 | |||
83cb26d70e | |||
6d66afc14e | |||
88faee4c79 | |||
66f5e4b44d | |||
772d8692e7 | |||
17f481f6fe | |||
b057e786a4 | |||
8b9904b6d0 | |||
43ae3b8140 | |||
f76dd4d6b2 | |||
079cc39166 | |||
bf0c7f731d | |||
5a8a293614 | |||
c768ee6175 | |||
75c2a723d9 | |||
32240df141 | |||
c532e3f1a5 | |||
b04c79643d | |||
4eca992db8 | |||
c36ca625e6 | |||
238466b3d6 | |||
4f28840a59 | |||
d98c1ba522 | |||
9bbb3e9c85 | |||
2710c56827 | |||
e22ff0e42d | |||
8c74a4fee0 | |||
234b90ac86 | |||
ce46b06f36 | |||
585930123d | |||
5d00c1a5ee | |||
f288c43e6e | |||
b981a591c7 | |||
3b7756b610 | |||
292f87caf7 | |||
ae2751a68b | |||
589becbc79 | |||
4c7fcf272c | |||
e51aecee03 | |||
d98e7dbd4a | |||
1b97778925 | |||
829e7623df | |||
89675c9061 | |||
f9df83802d | |||
e51eb723fc | |||
5b61f2d642 | |||
094669baee | |||
f6b5385495 | |||
524e2df708 | |||
9f887d9a28 | |||
214a41793f | |||
2e40ffc558 | |||
3584887938 | |||
0d6c002b8e | |||
ec714864f2 | |||
1b77149ec9 | |||
93c9e031e3 | |||
3ff4277f86 | |||
2a63267be0 | |||
2b365627ed | |||
b8f0d0f0dc | |||
df1bed941d | |||
604085fdb9 | |||
2d3c81390b | |||
516b8f6bf8 | |||
75fdca0b47 | |||
9f366118f0 | |||
c228a9a89a | |||
e747fcb16f | |||
a72a24ebff | |||
8811ba2ec0 | |||
38d8e465b3 | |||
5b3fb024be | |||
521f5f2b6b | |||
e1b30b2924 | |||
dd85670f8b | |||
625f3a5113 | |||
58c4a6c847 | |||
752aca811c | |||
a4cf0501ee | |||
4c7b992c36 | |||
264a51de3f | |||
625280bcd6 | |||
4f8265f82e | |||
ff07d3a46a | |||
5d11941638 | |||
f1f659571b | |||
c60d13b33e | |||
33060d382b | |||
4b1e412ac3 | |||
659730ab09 | |||
7c3a99b7ed | |||
e7af257814 | |||
c9190294bc | |||
e070e3c44a | |||
fe87de7cec | |||
2519e4f08d | |||
fe304d3c94 | |||
1f4e6872ab | |||
5ade2e7418 | |||
3e7d325e77 | |||
ef04a9d1ed | |||
011fef4b2b | |||
caf6389f79 | |||
c7306449ae | |||
8c45e6fa43 | |||
9504d21297 | |||
0832ca544a | |||
a000a1f76e | |||
ea8b02ff7f | |||
3cc7112283 | |||
50f0fc4e23 | |||
0a4ad01d8a | |||
19afabe2a1 | |||
103027a446 | |||
28c1f81f4a | |||
82ec6c08b8 | |||
551e57406d | |||
1c8036b863 | |||
f8bac5c197 | |||
6ece67b654 | |||
96411dfed5 | |||
24897169a9 | |||
05ddece9a0 | |||
df08ae7996 | |||
a244c1e987 | |||
bc182f78b1 | |||
2d18b06b3f | |||
61852df9a1 | |||
3969be38bd | |||
92f9aff784 | |||
815cfe6c20 | |||
052b5176d2 | |||
bccbcd8e4a | |||
90589fabee | |||
5947111f14 | |||
c8c56a5443 | |||
a83b9ed6d7 | |||
f9c83bccb6 | |||
2f720e22fc | |||
329028d3b9 | |||
5c3f9f6999 | |||
4a6b89d44c | |||
7fa1834ab6 | |||
c7bc9f8925 | |||
70d75ca311 | |||
9830b3b8f7 | |||
525c8780fd | |||
76c4ec8ee4 | |||
b4a48a7644 | |||
5087f0930c | |||
398cc5af85 | |||
f073945d31 | |||
c36abcb905 | |||
e2ccbe5528 | |||
68b2d5fcf0 | |||
cd2bd7685a | |||
85f811f147 | |||
e8fd8b58d0 | |||
2aa12e8f4b | |||
ee0c76c2b9 | |||
6ce6e77d2a | |||
0117fcb0e7 | |||
79bebe849d | |||
25eadc5559 | |||
a5784484e0 | |||
a701b006c5 | |||
2d68bbf94e | |||
df305314c1 | |||
d03239c009 | |||
71ccad4399 | |||
c916d43688 | |||
3e6d0bc252 | |||
79d0a848a4 | |||
a6af33d450 | |||
c6664adcce | |||
4e8a9470d1 | |||
1b88df9439 | |||
ec42278654 | |||
f435f249d0 | |||
9c88fec4fc | |||
9ecf466ce1 | |||
a22fdea0e3 | |||
90e52d7266 | |||
74d9b6c2bf | |||
b1e9873de5 | |||
6fdc52a64a | |||
b7d6792de9 | |||
c78dc55e65 | |||
af74bded14 | |||
e3c9a9c3e4 | |||
c68eecaf1c | |||
b0bdf7f6c3 | |||
d0f69a72dc | |||
8d8c75d32d | |||
e339e2658d | |||
bc4a75a732 | |||
e09e1bc3f5 | |||
87c50eb495 | |||
be291ee4f9 | |||
e634b49859 | |||
4dddaefa41 | |||
cda60455f0 | |||
a42b8870b0 | |||
daa66a6de6 | |||
6c6aed84bc | |||
c59314acc1 | |||
dd3cc78be5 | |||
54b0b6eec5 | |||
41654b22b3 | |||
07cc60d65a | |||
a7f9dc5114 | |||
b5ae23d544 | |||
75347cb4f7 | |||
03a44b6ec2 | |||
17d2349c49 | |||
4e98c44052 | |||
0bef281d66 | |||
54af25ec24 | |||
86ab02f400 | |||
ae01cd143f | |||
2974b29f15 | |||
1b78dd662b | |||
c22264a0ca | |||
48c3e3f534 | |||
6ccc134ba6 | |||
9aa36d7851 | |||
3278f77739 | |||
5d24f48e3b | |||
573c1c86cc | |||
374c5967ba | |||
66b71a36ce | |||
e70fd5a57a | |||
4589ce4d78 | |||
d6197b0904 | |||
30fb2b0d99 | |||
5cdefc324d | |||
b222d0fe44 | |||
fe4fddf0d5 | |||
c675c93733 | |||
29485ff24b | |||
f6ed3d9f88 | |||
39a36cb510 | |||
ff5550c82b | |||
7d5ce1a159 | |||
c492415386 | |||
5616bbd45b | |||
e117aa5297 | |||
17ac1382df | |||
057a026ea4 | |||
6ce6e86318 | |||
492558a2d2 | |||
b78e00f372 | |||
c2cc504837 | |||
ac76940530 | |||
55d1c7e2ab | |||
fdf264ff64 | |||
e917b7ce0f | |||
ec6facb9e7 | |||
60f3c09f90 | |||
afdfd6cebc | |||
edd66c40d9 | |||
fc4bc5277a | |||
821768a414 | |||
522f3bf171 | |||
fb7400ab85 | |||
210128f22b | |||
78ae233823 | |||
8f25da7cea | |||
7f52fdb435 | |||
0ba05b29b9 | |||
ef8123e3a2 | |||
257e1f3096 | |||
6441ae77d9 | |||
3f0938072f | |||
2fe06a28aa | |||
38750ba798 | |||
b4c01f8905 | |||
104d70c88e | |||
133a350f2f | |||
496ab55357 | |||
a391758e31 | |||
eaf8ad4949 | |||
2f583bdcf3 | |||
db19012a41 | |||
62be46884e | |||
3f2e6a48a9 | |||
ff124e5f74 | |||
c07421c195 | |||
de8348d3b9 | |||
184df8a853 | |||
12768a147c | |||
94161cea37 | |||
52a4ef7cf7 | |||
3432f71500 | |||
8282aa6c24 | |||
59f9eaa1c9 | |||
4433b735c4 | |||
9cd30fa6b5 | |||
2c7bbfb500 | |||
51a1d23bf9 | |||
c02e6e82bc | |||
e37a3fa7e6 | |||
e23c2ffecc | |||
744f11d045 | |||
b7eb1f3e8b | |||
3f28091e52 | |||
b4ee86955d | |||
8b866efe92 | |||
fb61ab8df7 | |||
990956ece7 | |||
414b592d53 | |||
751154d9da | |||
29addc499c | |||
caa98de581 | |||
1fd1ec4312 | |||
f4607626e4 | |||
b494c15e4b | |||
3c0defa125 | |||
f2df4d95de | |||
fabcf20e06 | |||
b9510b9ab7 | |||
52a2ebad04 | |||
89a2dc71fc | |||
adb0de43d8 | |||
e2a811a720 | |||
5bcafc5c17 | |||
2d24536caf | |||
b088c4086b | |||
3c58f4abd3 | |||
e2a9b27b2b | |||
fcd5f06c09 | |||
6d93c8b3fd | |||
2663e1be5d | |||
0fa6be4614 | |||
46163a6607 | |||
645ef093f7 | |||
7551e134da | |||
5bec5fb6cb | |||
c176af4da5 | |||
2631f03108 | |||
525c71658b | |||
10e5778deb | |||
6512a5fd6b | |||
1af40b1345 | |||
0418b68051 | |||
a7283864e8 | |||
4950bad2a7 | |||
470ac0eae3 | |||
87abbf9b20 | |||
3e7e88cd5f | |||
b7e1539699 | |||
8492f2ba24 | |||
737f4eb1c1 | |||
58191ea66b | |||
1f786df462 | |||
fa4c481aed | |||
d555fd7883 | |||
fe7ece1f5a | |||
2bb3aed729 | |||
488a42696c | |||
f43ff45683 | |||
bde1451896 | |||
fff2ca6f26 | |||
bec57a6cee | |||
a012ca4fac | |||
3ba49b0a50 | |||
314aa024b5 | |||
598f750859 | |||
8057848458 | |||
e80c28a530 | |||
5d05b66902 | |||
59634b2cf5 | |||
7c3892f5a2 | |||
19406a238b | |||
d6146197dd | |||
38f241479c | |||
aa45999824 | |||
3b7593ed7f | |||
f959cafb36 | |||
e92d204d42 | |||
f543161234 | |||
9cc1017912 | |||
fc719c19f9 | |||
ad97fc6855 | |||
407dc74502 | |||
e5e764b402 | |||
65ad65fe52 | |||
8d09d20510 | |||
5a5b04b2b0 | |||
3113bac8e6 | |||
9217f2c916 | |||
32a49b7846 | |||
12ef034b7b | |||
e70e4a21f2 | |||
7826fb4f04 | |||
8f1b8909dc | |||
3f7a989d38 | |||
4d3fd7598d | |||
620e3cef20 | |||
812a61939e | |||
793c6c2f7b | |||
f8f4d0f646 | |||
3a92aa751f | |||
6882273aa0 | |||
3b0197620f | |||
f6240e114c | |||
0f3c129b95 | |||
6f87b01c47 | |||
32110a9866 | |||
ba459f4d20 | |||
d868e6bfaf | |||
9f3499a7c3 | |||
ccec7732a7 | |||
3b980a173f | |||
246139f90b | |||
4e85fb7d8d | |||
ab32411b0c | |||
477f28a6bd | |||
96ef0a178d | |||
ab603e7ef7 | |||
d52104a62a | |||
8d8d1cfdd6 | |||
5451751513 | |||
92ae26bb9f | |||
3c8ee0c8cb | |||
20f76b8118 | |||
d8eeeead18 | |||
5452162bc3 | |||
a4adcba405 | |||
66da594382 | |||
aa426842f2 | |||
ed53a45228 | |||
2ddbcb2369 | |||
61a58ff3c9 | |||
638aee65c0 | |||
f21c49f8da | |||
583d2cb4e4 | |||
aa70dcfc8f | |||
ffb61c425b | |||
858cf5e0c9 | |||
881dd4666e | |||
c4aeaf7fe8 | |||
ea1f5a8fc6 | |||
7f1e420a0a | |||
1272eaf07f | |||
2fe760cc4b | |||
df3a50bae8 | |||
0db3605f33 | |||
173fa92116 | |||
02ca58c1eb | |||
4a22fe58bf | |||
3fc478a14b | |||
203bc674fe | |||
a4a9f0a04c | |||
918e7fffeb | |||
e1648de661 | |||
f7c94e6343 | |||
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 |
10
.gitignore
vendored
10
.gitignore
vendored
@ -23,11 +23,15 @@ data/gnome-shell-wayland.desktop
|
||||
data/gnome-shell-wayland.desktop.in
|
||||
data/gnome-shell-extension-prefs.desktop
|
||||
data/gnome-shell-extension-prefs.desktop.in
|
||||
data/gnome-shell-theme.gresource
|
||||
data/gschemas.compiled
|
||||
data/perf-background.xml
|
||||
data/org.gnome.shell.gschema.xml
|
||||
data/org.gnome.shell.gschema.valid
|
||||
data/org.gnome.shell.evolution.calendar.gschema.xml
|
||||
data/org.gnome.shell.evolution.calendar.gschema.valid
|
||||
data/org.gnome.Shell.PortalHelper.desktop
|
||||
data/org.gnome.Shell.PortalHelper.service
|
||||
docs/reference/*/*.args
|
||||
docs/reference/*/*.bak
|
||||
docs/reference/*/*.hierarchy
|
||||
@ -43,6 +47,8 @@ docs/reference/*/xml/
|
||||
docs/reference/shell/doc-gen-*
|
||||
gtk-doc.make
|
||||
js/misc/config.js
|
||||
js/js-resources.c
|
||||
js/js-resources.h
|
||||
intltool-extract.in
|
||||
intltool-merge.in
|
||||
intltool-update.in
|
||||
@ -76,11 +82,9 @@ src/gnome-shell-calendar-server
|
||||
src/gnome-shell-extension-prefs
|
||||
src/gnome-shell-extension-tool
|
||||
src/gnome-shell-hotplug-sniffer
|
||||
src/gnome-shell-jhbuild
|
||||
src/gnome-shell-perf-helper
|
||||
src/gnome-shell-perf-tool
|
||||
src/gnome-shell-real
|
||||
src/gnome-shell-wayland
|
||||
src/gnome-shell-portal-helper
|
||||
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
|
||||
src/run-js-test
|
||||
src/test-recorder
|
||||
|
41
COPYING
41
COPYING
@ -1,12 +1,12 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@ -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
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
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.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
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
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
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
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
@ -303,17 +303,16 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
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
|
||||
|
||||
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.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
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.
|
||||
|
@ -1,7 +1,11 @@
|
||||
# Point to our macro directory and pick up user flags from the environment
|
||||
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
|
||||
SUBDIRS += man
|
||||
|
475
NEWS
475
NEWS
@ -1,3 +1,478 @@
|
||||
3.15.1
|
||||
======
|
||||
* Use GResources for theme loading [Cosimo; #736936]
|
||||
* Reset the OSK to primary on monitor changes [Rui; #738536]
|
||||
* Use LC_TIME locale for format string translations [Florian; #738640]
|
||||
* Summarize queued up notifications [Devyani; #702460]
|
||||
* Improve handling of multi-day events [Andreas; #727302]
|
||||
* Support EXTERNAL scroll policy type [Florian; #739379]
|
||||
* Misc. bugfixes [Owen, Rui; #738652, #739252]
|
||||
|
||||
Contributors:
|
||||
Andreas Brauchli, Cosimo Cecchi, Devyani Kota, Rui Matos, Florian Müllner,
|
||||
Jasper St. Pierre, Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Bahodir Mansurov [uz@cyrillic]
|
||||
|
||||
3.14.1
|
||||
======
|
||||
* Fix pulse animation for scrolled app folders [Florian; #736885]
|
||||
* Fix updating background on file changes [Owen; #710756]
|
||||
* Obtain keyboard variant from IBus [Jinkyu; #735066]
|
||||
* Implement Ctrl-u / Ctrl-k keybindings in entries [Florian; #737346]
|
||||
* Pass VPN hints to auth dialog [Dan; #737592]
|
||||
* Only allow one screenshot request at a time [Adel; #737456]
|
||||
* Respect disable-save-to-disc lockdown setting [Florian; #737846]
|
||||
* Respect scaling-factor for profile pictures [Darcy; #735419]
|
||||
* Focus login screen after lifting the lock screen shield [Ray; #708105]
|
||||
* Speed up pulse animation for few items [Carlos S.; #737017]
|
||||
* Fix gap between workspace switcher and screen edge [Florian; #728899]
|
||||
* Disable unredirection during recordings [Adel; #738226]
|
||||
* Ensure there's always at least one input source [Rui; #738303]
|
||||
* Restrict width of dash icons' context menus [Adel; #738054]
|
||||
* Misc. bug fixes [Jasper, Florian, Carlos G., Owen; #736999, #737382, #737001,
|
||||
#738314, #738256, #738147]
|
||||
|
||||
Contributors:
|
||||
Darcy Beurle, Cosimo Cecchi, Adel Gadllah, Carlos Garnacho, Rui Matos,
|
||||
Florian Müllner, Carlos Soriano, Jasper St. Pierre, Ray Strode, Patrick Ward,
|
||||
Dan Williams, Owen W. Taylor, Jinkyu Yi
|
||||
|
||||
Translations:
|
||||
Мирослав Николић po/sr, sr@latin.po, Fran Diéguez [gl], Marek Černocký [cs],
|
||||
Saibal Ray [bn_IN], Rajesh Ranjan [hi], Friedel Wolff [af],
|
||||
Zhou Fang [zh_CN], Krishnababu Krothapalli [te], Kjartan Maraas [nb],
|
||||
Rūdolfs Mazurs [lv], Sweta Kothari [gu], Christian Kirbach [de],
|
||||
Cheng-Chia Tseng [zh_TW], Pedro Albuquerque [pt], Daniel Mustieles [es],
|
||||
Luca Ferretti [it], Baurzhan Muftakhidinov [kk], Arash Mousavi [fa],
|
||||
Milo Casagrande [it]
|
||||
|
||||
3.14.0
|
||||
======
|
||||
* Fix exposure of the accessible tree [Alejandro; #736821]
|
||||
* Hide empty app folders in app picker [Florian; #736910]
|
||||
|
||||
Contributors:
|
||||
Florian Müllner, Alejandro Piñeiro
|
||||
|
||||
Translations:
|
||||
Yuri Myasoedov [ru], Pawan Chitrakar [ne], Manoj Kumar Giri [or],
|
||||
Daniel Mustieles [es], GNOME Translation Robot [de], Rajesh Ranjan [hi],
|
||||
Shankar Prasad [kn], Kenneth Nielsen [da], Daniel Korostil [uk],
|
||||
Changwoo Ryu [ko], A S Alam [pa], Tom Tryfonidis [el], Petr Kovar [cs]
|
||||
|
||||
3.13.92
|
||||
=======
|
||||
* Fix submenu arrow animations [Hashem; #728927]
|
||||
* Always initialize clutter accessibility [Alejandro; #735908]
|
||||
* Adapt to mutter background changes [Owen; #735638]
|
||||
* Improve handling of outOfDate extensions in prefs [Florian; #736185]
|
||||
* Port offline updates to PackageKit's DBus interface [Kalev; #736337]
|
||||
* location: Translate accuracy levels for geoclue [Zeeshan; #736479]
|
||||
* Implement input source switching [Rui; #736435]
|
||||
* Fix crash when dragging window from workspace switcher [Carlos G.; #735972]
|
||||
* Clean out list of default favorites [Elad; #735682]
|
||||
* Add settings link to location submenu [Florian; #736542]
|
||||
* Fix keynav of message tray menu button [Florian; #707799]
|
||||
* Misc. bug fixes [Carlos G., Florian, Rui; #736110, #736329, #736343,
|
||||
#735927, #735976]
|
||||
|
||||
Contributors:
|
||||
Elad Alfassa, Zeeshan Ali (Khattak), Michael Catanzaro, Adel Gadllah,
|
||||
Carlos Garnacho, Kalev Lember, Rui Matos, Florian Müllner, Hashem Nasarat,
|
||||
Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre, Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Piotr Drąg [pl], Changwoo Ryu [ko], Yuri Myasoedov [ru], Zhou Fang [ja],
|
||||
Peter Mráz [sk], Ville-Pekka Vainio [fi], Sweta Kothari [gu],
|
||||
Marek Černocký [cs], A S Alam [pa], Christian Kirbach [de],
|
||||
Alexandre Franke [fr], Aurimas Černius [lt], Khaled Hosny [ar],
|
||||
Enrico Nicoletto [pt_BR], Andika Triwidada [id], Shantha kumar [ta],
|
||||
Matej Urbančič [sl], Pawan Chitrakar [ne], Yosef Or Boczko [he],
|
||||
Balázs Úr [hu], Dušan Kazik [sk], Gil Forcada [ca],
|
||||
Carles Ferrando [ca@valencia], Nilamdyuti Goswami [as], Ivaylo Valkov [bg],
|
||||
Sandeep Sheshrao Shedmake [mr], Umarzuki Bin Mochlis Moktar [ms],
|
||||
Muhammet Kara [tr], Jiro Matsuzawa [ja], Kris Thomsen [da],
|
||||
Mattias Eriksson [sv]
|
||||
|
||||
3.13.91
|
||||
=======
|
||||
* Fix keynav into session menu on login screen [Florian; #735614]
|
||||
* Add gesture to summon message tray [Carlos G.; #735625]
|
||||
* Accept scrolls/swipes either way on the screen shield [Jasper; #734874]
|
||||
* Animate opening the app picker and improve app folder animations
|
||||
[Carlos S.; #734726]
|
||||
* Animate app icons on launching a new window [Carlos S., Florian; #734726]
|
||||
* Show the on-screen keyboard when touch input is being used [David; #702015]
|
||||
* Misc. bug fixes [Bastien, Owen, Florian, Carlos G.; #735190, #735385,
|
||||
#735608, #735681]
|
||||
|
||||
Contributors:
|
||||
Carlos Garnacho, David King, Kalev Lember, Florian Müllner, Bastien Nocera,
|
||||
Carlos Soriano, Jasper St. Pierre, Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Fran Diéguez [gl], Sweta Kothari [gu], Manoj Kumar Giri [or],
|
||||
Alain Lojewski [fr], Praveen Illa [te], Arash Mousavi [fa],
|
||||
Andika Triwidada [id]
|
||||
|
||||
3.13.90
|
||||
=======
|
||||
* Make use of GLSL optional [Adel; #733623]
|
||||
* Update on-screen-keyboard position on monitor changes [Cosimo; #733790]
|
||||
* Improve window manager animations [Giovanni; #732857]
|
||||
* Handle touch events [Carlos G.; #733633]
|
||||
* Try to not show "New Window" action for single-window apps
|
||||
[Giovanni; #722554]
|
||||
* Fix overview exceeding screen size with many apps installed
|
||||
[Carlos S.; #723496]
|
||||
* Add Software to default favorites [Mathieu; #734406]
|
||||
* Improve app picker <-> desktop transition [Carlos S.; #732901]
|
||||
* Remove <shift>-magic for switcher popups [Christophe; #732296]
|
||||
* Add a special background to use for performance testing [Owen; #734610]
|
||||
* Add support for default disabled search providers [Giovanni; #734110]
|
||||
* Fix portals that don't redirect properly [Giovanni; #733848]
|
||||
* Fix history trimming in chat notifications [Giovanni; #733899]
|
||||
* Try to use default calendar application [Florian; #722333]
|
||||
* Only show location menu when geolocation is in use [Zeeshan; #731122]
|
||||
* Misc. bug fixes and cleanups [Giovanni, Carlos G., Zeeshan, Carlos S.,
|
||||
Cosimo; #711682, #733840, #734483, #734680, #733813, #735062]
|
||||
|
||||
Contributors:
|
||||
Zeeshan Ali (Khattak), Mathieu Bridon, Giovanni Campagna, Cosimo Cecchi,
|
||||
Piotr Drąg, Christophe Fergeau, Adel Gadllah, Carlos Garnacho,
|
||||
Florian Müllner, Carlos Soriano, Jasper St. Pierre, Olav Vitters,
|
||||
Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Aurimas Černius [lt], MarMav [el], Inaki Larranaga Murgoitio [eu],
|
||||
Reinout van Schouwen [nl], ngoswami [as], Fabio Tomat [fur],
|
||||
Chao-Hsiung Liao [zh_HK, zh_TW]
|
||||
|
||||
3.13.4
|
||||
======
|
||||
* Handle portal login requests [Giovanni; #704416]
|
||||
* Scale fonts on wayland on hiDPI devices [Adel; #732537]
|
||||
* Fix default ibus candidate index labels [Rui; #702944]
|
||||
* Add gestures for various system actions [Carlos G.]
|
||||
* Add performance test script for the perf.gnome.org [Owen; #732350]
|
||||
* Use new restart framework to improve restart visuals [Owen; #733026]
|
||||
* Improve keynav in app folder popups [Carlos S.; #731477]
|
||||
* Fix truncation of app search results [Carlos S.; #732416]
|
||||
* Automatically update renamed desktop files in favorites [Kalev; #729429]
|
||||
* Misc. bug fixes and cleanups [Giovanni, Yosef, Owen, Bastien, Javier;
|
||||
#729823, #726401, #732301, #732348, #732349, #733498, #733540]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Adel Gadllah, Carlos Garnacho, Javier Hernández,
|
||||
Kalev Lember, Rui Matos, Florian Müllner, Bastien Nocera, Yosef Or Boczko,
|
||||
Carlos Soriano, Jasper St. Pierre, Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Yuri Myasoedov [ru], Daniel Mustieles [es], Fran Diéguez [gl],
|
||||
Cheng-Chia Tseng [zh_TW], A S Alam [pa], Benjamin Steinwender [de],
|
||||
Enrico Nicoletto [pt_BR], MarMav [el], Yosef Or Boczko [he],
|
||||
Kjartan Maraas [nb]
|
||||
|
||||
3.13.3
|
||||
======
|
||||
* Don't allow closing windows with attached modals [Florian; #729886]
|
||||
* Fix self-restarting on OpenBSD [Antoine; #727763]
|
||||
* Improve behavior of window buttons with compositor menus [Florian; #731058]
|
||||
* Work around atspi-related performance regression [Alejandro; #730118]
|
||||
* Misc bug fixes and cleanups [Florian, Lan, Jasper, Christophe, Debarshi,
|
||||
Zeeshan; #728271, #726460, #703833, #731118, #731220, #695487, #730527,
|
||||
#728170, #731619, #731738, #731882, #731923]
|
||||
|
||||
Contributors:
|
||||
Zeeshan Ali (Khattak), Christophe Fergeau, Adel Gadllah, Antoine Jacoutot,
|
||||
Ting-Wei Lan, Florian Müllner, Alejandro Piñeiro, Debarshi Ray,
|
||||
Carlos Soriano, Jasper St. Pierre, Wim Taymans, Rico Tzschichholz
|
||||
|
||||
Translations:
|
||||
Philip Withnall [en_GB], Milo Casagrande [it], Aurimas Černius [lt],
|
||||
Enrico Nicoletto [pt_BR], Kjartan Maraas [nb], Balázs Meskó [hu],
|
||||
Muhammet Kara [tr], Daniel Mustieles [es], Yosef Or Boczko [he],
|
||||
Matej Urbančič [sl], Mattias Eriksson [sv]
|
||||
|
||||
3.13.2
|
||||
======
|
||||
* Make airplane mode menu insensitive in lock screen [Giovanni; #729224]
|
||||
* Don't always extend struts to the screen edge [Florian; #683819]
|
||||
* Fix keynav for alternatives in AltSwitcher [Florian; #727259]
|
||||
* Implement window menus in the shell [Jasper; #726352]
|
||||
* Support resource:/// URLs in GNOME_SHELL_JS envvar [Owen; #730409]
|
||||
* Fix switcher popups with keybindings containing Escape [Rui; #730739]
|
||||
* Update extension-prefs UI to follow GNOME 3 patterns [Florian; #730829]
|
||||
* Add support for fallback app menu in window decorations [Florian; #730752]
|
||||
* Fix keynav escaping open app folders [Florian; #726760]
|
||||
* Misc. bug fixes [Kalev, Florian, Owen; #729429, #728449, #730408, #730753,
|
||||
#730653]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Piotr Drąg, Kalev Lember, Rui Matos, Florian Müllner,
|
||||
Vadim Rutkovsky, Carlos Soriano, Jasper St. Pierre, Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Ihar Hrachyshka [be], Giovanni Campagna [it], Carles Ferrando [ca@valencia],
|
||||
Daniel Mustieles [es], Aurimas Černius [lt], Enrico Nicoletto [pt_BR],
|
||||
Yosef Or Boczko [he], Marek Černocký [cs], Muhammet Kara [tr],
|
||||
Georges Neto [pt_BR], Andika Triwidada [id]
|
||||
|
||||
3.13.1
|
||||
======
|
||||
* Ensure the currently focused app icon is viewable [Rui; #726759]
|
||||
* Improve language in location menu [Zeeshan; #726498]
|
||||
* Improve HiDpi support [Cosimo; #726907]
|
||||
* Set accessible role for window previews [Alejandro; #726670]
|
||||
* Fix bad antialiasing on panel menu buttons [Carlos; #727336]
|
||||
* Don't hide location menu [Zeeshan; #727398]
|
||||
* Fix IM candidate window obscuring current text [Rui; #727579]
|
||||
* Don't always extend struts to the screen edge [Florian; #663690]
|
||||
* Add shortcuts for switching to the last workspace [Elad; #659288]
|
||||
* Show OSD window on all monitors [Adel; #722684]
|
||||
* Improve consistency of labels in network menu [Paul; #727163]
|
||||
* Fix zombie search providers showing up [Jasper; #728597]
|
||||
* Remove ConsoleKit support [Florian; #686626]
|
||||
* Fix region screenshots with open shell menus [Florian; #709126]
|
||||
* Support <shift>insert in text entries [Florian; #648318]
|
||||
* Improve app picker scrolling on touch [Jasper; #729064]
|
||||
* Don't make date button clickable when on current date [Carlos; #726724]
|
||||
* Tweak heuristic for hiding workspace switcher [Florian; #662457]
|
||||
* Add option to show in Software to app context menu [Matthias; #643043]
|
||||
* Misc. bug fixes and cleanups [Bastien, Florian, Giovanni, Adel, Vadim,
|
||||
Carlos; #727983, #727948, #728512, #728681, #728897, #727384, #728820,
|
||||
#715042, #728449, #728343]
|
||||
|
||||
Contributors:
|
||||
Elad Alfassa, Zeeshan Ali (Khattak), Giovanni Campagna, Cosimo Cecchi,
|
||||
Matthias Clasen, Piotr Drąg, Adel Gadllah, Paul Lange, Rui Matos,
|
||||
Simon McVittie, Florian Müllner, Bastien Nocera, Alejandro Piñeiro,
|
||||
Vadim Rutkovsky, Carlos Soriano, Jasper St. Pierre
|
||||
|
||||
Translations:
|
||||
Khaled Hosny [ar], Piotr Drąg [pl], Yosef Or Boczko [he],
|
||||
Antonio Fernandes C. Neto [pt_BR], Marek Černocký [cs], maria thukididu [el],
|
||||
Andika Triwidada [id], Daniel Mustieles [es], Changwoo Ryu [ko],
|
||||
Benjamin Steinwender [de], Sphinx Jiang [zh_CN],
|
||||
Inaki Larranaga Murgoitio [eu], Marcus Lundblad [sv], Aurimas Černius [lt],
|
||||
Stas Solovey [ru], Alexandre Franke [fr], Matej Urbančič [sl],
|
||||
Fran Diéguez [gl], Pau Iranzo [ca], Luca Ferretti [it], Milo Casagrande [it],
|
||||
Tiago S [pt], Victor Ibragimov [tg], Dirgita [id], Khoem Sokhem [km],
|
||||
Rūdolfs Mazurs [lv], Balázs Úr [hu], Ask H. Larsen [da], Ikuya Awashiro [ja],
|
||||
Wouter Bolsterlee [nl], Daniel Korostil [uk], Daniel Șerbănescu [ro],
|
||||
Enrico Nicoletto [pt_BR]
|
||||
|
||||
3.12.0
|
||||
======
|
||||
* gdm: Reset greeter when coming back to login screen [Jasper; #726989]
|
||||
|
||||
Contributors:
|
||||
Jasper St. Pierre
|
||||
|
||||
Translations:
|
||||
Daniel Martinez [an], Yuri Myasoedov [ru], Inaki Larranaga Murgoitio [eu],
|
||||
Abderrahim Kitouni [ar], Praveen Illa [te], Matej Urbančič [sl],
|
||||
Chao-Hsiung Liao [zh_HK, zh_TW], Frédéric Péters [fr],
|
||||
Мирослав Николић [sr, sr@latin], Ask H. Larsen [da], Kenneth Nielsen [da],
|
||||
Jiro Matsuzawa [ja], Dušan Kazik [sk]
|
||||
|
||||
3.11.92
|
||||
=======
|
||||
* calendar: Grab key focus after changing day [Volker; #725606]
|
||||
* gdm: Don't load user list if disabled [Florian; #725905]
|
||||
* Don't show network-offline in the top bar [Jasper; #725340]
|
||||
* Improve radial shade effect of modal dialogs [Giovanni; #725830]
|
||||
* Fix broken suspend-on-idle functionality [Giovanni; #712706]
|
||||
* Close wifi selection dialog when device disappears [Giovanni; #723935]
|
||||
* Don't close chats when pressing Escape [Giovanni; #724178]
|
||||
* Improve smartcard support in login/lock screen [Ray; #726262, #726263]
|
||||
* Wake up screen when resuming from suspend [Giovanni; #726378]
|
||||
* Make bluetooth and location items insensitive when locked [Florian; #726319]
|
||||
* Don't show bluetooth icon when there is no adapter [Giovanni; #725057]
|
||||
* Make sure to keep the OSK on top of modal dialogs [Rui; #719451]
|
||||
* Misc. bug fixes and cleanups [Giovanni, Ray, Adel, Daniel, Jasper, Florian;
|
||||
#725832, #725958, #722149, #724977, #724798, #725020, #723976, #726119,
|
||||
#726238, #585500, #704844, #726323, #726322, #726120, #726414]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Daniel Drake, Adel Gadllah, Rui Matos, Florian Müllner,
|
||||
Volker Sobek, Jasper St. Pierre, Ray Strode
|
||||
|
||||
Translations:
|
||||
Fabio Tomat [fur], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
|
||||
Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Andika Triwidada [id],
|
||||
A S Alam [pa], Rūdolfs Mazurs [lv], Wylmer Wang [zh_CN],
|
||||
Aurimas Černius [lt], Cheng-Chia Tseng [zh_TW], Stas Solovey [ru],
|
||||
Tiagosdot [pt], Benjamin Steinwender [de], Frédéric Peters [fr],
|
||||
Daniel Korostil [uk], Yaron Shahrabani [he], Ville-Pekka Vainio [fi],
|
||||
maria thukididu [el], Victor Ibragimov [tg], Kjartan Maraas [nb],
|
||||
Gábor Kelemen [hu], Ask H. Larsen [da]
|
||||
|
||||
3.11.91
|
||||
=======
|
||||
* Don't use network profile name in menu [Giovanni; #725586]
|
||||
* calendar: Make date label clickable to return to current date [Vit; #641366]
|
||||
* Misc. bug fixes [Florian, Zeeshan, Adel, Jasper, Dan, Volker; #724813,
|
||||
#724686, #725082, #724870, #724779, #725533]
|
||||
|
||||
Contributors:
|
||||
Zeeshan Ali (Khattak), Giovanni Campagna, Piotr Drąg, Adel Gadllah,
|
||||
Florian Müllner, Volker Sobek, Vit Stanislav, Jasper St. Pierre, Dan Williams
|
||||
|
||||
Translations:
|
||||
Victor Ibragimov [tg], Aurimas Černius [lt], Dimitris Spingos [el],
|
||||
Andika Triwidada [id], Rafael Ferreira [pt_BR], Daniel Mustieles [es],
|
||||
Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Ihar Hrachyshka [be],
|
||||
eternalhui [zh_CN], Yosef Or Boczko [he], Fran Diéguez [gl],
|
||||
Khaled Hosny [ar], Ville-Pekka Vainio [fi], Piotr Drąg [pl],
|
||||
Kjartan Maraas [nb], Changwoo Ryu [ko]
|
||||
|
||||
3.11.90
|
||||
=======
|
||||
* Stop showing two bluetooth entries [Giovanni; #709353]
|
||||
* Improve styling of login/lock screen [Reda; #723833]
|
||||
* Fix magnifier crosshairs [Magdalen; #723709]
|
||||
* Make NetworkManager support optional [Michael; #669495]
|
||||
* Make middle-click open a new instance [Florian; #695010]
|
||||
* Scale the UI on high resolution displays [Cosimo, Adel; #705410, #724607]
|
||||
* Remove notification counter on screen shield [Carlos; #709275]
|
||||
* Improve app picker transition [Carlos; #722331]
|
||||
* Add geolocation indicator to status menu [Zeeshan; #723684]
|
||||
* Improve timestamps in chat notifications [Carlos; #708031, #715158]
|
||||
* Improve network menus [Giovanni; #723570]
|
||||
* Add "VPN Setting" item to VPN submenu [Giovanni; #709167]
|
||||
* Improve appearance of disclosure arrows [Carlos; #720206]
|
||||
* Add GSetting key to disable version validation of extensions [Adel; #724683]
|
||||
* Delay auto-removing empty workspaces [Florian; #709064]
|
||||
* Offer offline updates in the shutdown dialog [Kalev; #722898]
|
||||
* Animate tile previews [Florian; #665758]
|
||||
* Misc. bug fixes and cleanups [Giovanni, Ryan, Debarshi, Florian; #709128,
|
||||
#722342, #723661, #724184, #724256, #724293, #724305, #722554, #724282,
|
||||
#724690, #722928]
|
||||
|
||||
Contributors:
|
||||
Zeeshan Ali (Khattak), Magdalen Berns, Michael Biebl, Giovanni Campagna,
|
||||
Cosimo Cecchi, Adel Gadllah, Reda Lazri, Kalev Lember, Ryan Lortie,
|
||||
Florian Müllner, Debarshi Ray, Carlos Soriano, Jasper St. Pierre,
|
||||
Colin Walters
|
||||
|
||||
Translations:
|
||||
Victor Ibragimov [tg], Daniel Mustieles [es], Khaled Hosny [ar],
|
||||
Enrico Nicoletto [pt_BR], Yosef Or Boczko [he], Fran Diéguez [gl],
|
||||
Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Jorge Pérez Pérez [an],
|
||||
Kjartan Maraas [nb], David Lüder [de], Daniel Korostil [uk], ngoswami [as],
|
||||
Rafael Ferreira [pt_BR]
|
||||
|
||||
3.11.5
|
||||
======
|
||||
* Fix extension preference tool [Florian; #722334]
|
||||
* Fix keyboard activation of legacy tray icons [Giovanni; #721267]
|
||||
* Add radial background shade for modal dialogs [Giovanni; #669798]
|
||||
* Show attached modal windows in the overview [Giovanni; #650843]
|
||||
* Add support for desktop actions [Giovanni; #669603]
|
||||
* Indicate in system status when location service is used [Zeeshan; #709372]
|
||||
* Add support for extended app folder schema [Jasper; #723179]
|
||||
* Show status icon for wired network connections [Jasper; #708966]
|
||||
* Indicate airplane mode in network selection dialog [Giovanni; #709128]
|
||||
* Misc bug fixes and cleanups [Florian, Sebastian, Giovanni, Tim, Matt, Jasper;
|
||||
#722417, #722494, #722547, #722593, #722434, #722787, #722690, #722840,
|
||||
#722660, #722812, #723197, #722927, #723306, #723308, #723523, #709685,
|
||||
#723570]
|
||||
|
||||
Contributors:
|
||||
Zeeshan Ali (Khattak), Magdalen Berns, Giovanni Campagna, William Jon McCann,
|
||||
Sebastian Keller, Tim Lunn, Florian Müllner, Carlos Soriano,
|
||||
Jasper St. Pierre, Rico Tzschichholz, Matt Watson
|
||||
|
||||
Translations:
|
||||
Marek Černocký [cs], Mattias Põldaru [et], Tong Hui [zh_CN],
|
||||
Victor Ibragimov [tg], Enrico Nicoletto [pt_BR], Daniel Mustieles [es],
|
||||
Fran Diéguez [gl], Kjartan Maraas [nb], Nilamdyuti Goswami [as],
|
||||
Aurimas Černius [lt], Stas Solovey [ru], Yosef Or Boczko [he],
|
||||
Jorge Pérez Pérez [an], Dimitris Spingos [el], Baurzhan Muftakhidinov [kk],
|
||||
Chao-Hsiung Liao [zh_HK, zh_TW], Shankar Prasad [kn], Yaron Shahrabani [he],
|
||||
Andika Triwidada [id]
|
||||
|
||||
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]
|
||||
|
2
README
2
README
@ -8,7 +8,7 @@ For more information about GNOME Shell, including instructions on how
|
||||
to build GNOME Shell from source and how to get involved with the project,
|
||||
see:
|
||||
|
||||
http://live.gnome.org/GnomeShell
|
||||
https://wiki.gnome.org/Projects/GnomeShell
|
||||
|
||||
Bugs should be reported at http://bugzilla.gnome.org against the 'gnome-shell'
|
||||
product.
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
# Run this to generate all the initial makefiles, etc.
|
||||
|
||||
srcdir=`dirname $0`
|
||||
|
@ -13,9 +13,7 @@
|
||||
* 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.
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
@ -43,6 +41,8 @@
|
||||
|
||||
#define PLUGIN_API_VERSION 5
|
||||
|
||||
#define EXTENSION_DISABLE_VERSION_CHECK_KEY "disable-extension-version-validation"
|
||||
|
||||
typedef struct {
|
||||
GDBusProxy *proxy;
|
||||
} PluginData;
|
||||
@ -833,6 +833,16 @@ plugin_get_shell_version (PluginObject *obj,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_get_version_validation_enabled (PluginObject *obj,
|
||||
NPVariant *result)
|
||||
{
|
||||
gboolean is_enabled = !g_settings_get_boolean (obj->settings, EXTENSION_DISABLE_VERSION_CHECK_KEY);
|
||||
BOOLEAN_TO_NPVARIANT(is_enabled, *result);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define METHODS \
|
||||
METHOD (list_extensions) \
|
||||
METHOD (get_info) \
|
||||
@ -852,6 +862,8 @@ static NPIdentifier api_version_id;
|
||||
static NPIdentifier shell_version_id;
|
||||
static NPIdentifier onextension_changed_id;
|
||||
static NPIdentifier onrestart_id;
|
||||
static NPIdentifier version_validation_enabled_id;
|
||||
|
||||
|
||||
static bool
|
||||
plugin_object_has_method (NPObject *npobj,
|
||||
@ -894,7 +906,8 @@ plugin_object_has_property (NPObject *npobj,
|
||||
return (name == onextension_changed_id ||
|
||||
name == onrestart_id ||
|
||||
name == api_version_id ||
|
||||
name == shell_version_id);
|
||||
name == shell_version_id ||
|
||||
name == version_validation_enabled_id);
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -912,6 +925,8 @@ plugin_object_get_property (NPObject *npobj,
|
||||
return plugin_get_api_version (obj, result);
|
||||
else if (name == shell_version_id)
|
||||
return plugin_get_shell_version (obj, result);
|
||||
else if (name == version_validation_enabled_id)
|
||||
return plugin_get_version_validation_enabled (obj, result);
|
||||
else if (name == onextension_changed_id)
|
||||
{
|
||||
if (obj->listener)
|
||||
@ -990,6 +1005,7 @@ init_methods_and_properties (void)
|
||||
/* this is the JS public API; it is manipulated through NPIdentifiers for speed */
|
||||
api_version_id = funcs.getstringidentifier ("apiVersion");
|
||||
shell_version_id = funcs.getstringidentifier ("shellVersion");
|
||||
version_validation_enabled_id = funcs.getstringidentifier ("versionValidationEnabled");
|
||||
|
||||
get_info_id = funcs.getstringidentifier ("getExtensionInfo");
|
||||
list_extensions_id = funcs.getstringidentifier ("listExtensions");
|
||||
|
122
configure.ac
122
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.11.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.15.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -58,11 +58,26 @@ fi
|
||||
|
||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.13.4
|
||||
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
|
||||
GJS_MIN_VERSION=1.38.1
|
||||
MUTTER_MIN_VERSION=3.11.1
|
||||
GTK_MIN_VERSION=3.7.9
|
||||
GJS_MIN_VERSION=1.39.0
|
||||
MUTTER_MIN_VERSION=3.15.1
|
||||
GTK_MIN_VERSION=3.13.2
|
||||
GIO_MIN_VERSION=2.37.0
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVER_MIN_VERSION=3.5.3
|
||||
@ -71,7 +86,6 @@ POLKIT_MIN_VERSION=0.100
|
||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
GCR_MIN_VERSION=3.7.5
|
||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
|
||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||
NETWORKMANAGER_MIN_VERSION=0.9.8
|
||||
PULSE_MIN_VERS=2.0
|
||||
|
||||
@ -81,7 +95,6 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
gtk+-3.0 >= $GTK_MIN_VERSION
|
||||
atk-bridge-2.0
|
||||
gjs-internals-1.0 >= $GJS_MIN_VERSION
|
||||
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
|
||||
$recorder_modules
|
||||
gdk-x11-3.0 libsoup-2.4
|
||||
xtst
|
||||
@ -92,43 +105,37 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
libcanberra libcanberra-gtk3
|
||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION
|
||||
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
||||
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION"
|
||||
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_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
||||
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
||||
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_MSG_CHECKING([for bluetooth support])
|
||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.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_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)
|
||||
])
|
||||
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])
|
||||
AC_MSG_RESULT([no])])
|
||||
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)
|
||||
@ -148,6 +155,9 @@ AC_SUBST(MUTTER_TYPELIB_DIR)
|
||||
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
|
||||
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(mallinfo)
|
||||
AC_CHECK_HEADERS([sys/resource.h])
|
||||
@ -163,6 +173,38 @@ if test "$langinfo_ok" = "yes"; then
|
||||
[Define if _NL_TIME_FIRST_WEEKDAY is available])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(networkmanager,
|
||||
AS_HELP_STRING([--disable-networkmanager],
|
||||
[disable NetworkManager support @<:@default=auto@:>@]),,
|
||||
[enable_networkmanager=auto])
|
||||
|
||||
if test "x$enable_networkmanager" != "xno"; then
|
||||
PKG_CHECK_MODULES(NETWORKMANAGER,
|
||||
[libnm-glib
|
||||
libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
||||
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||
libsecret-unstable],
|
||||
[have_networkmanager=yes],
|
||||
[have_networkmanager=no])
|
||||
|
||||
GNOME_SHELL_CFLAGS="$GNOME_SHELL_CFLAGS $NETWORKMANAGER_CFLAGS"
|
||||
GNOME_SHELL_LIBS="$GNOME_SHELL_LIBS $NETWORKMANAGER_LIBS"
|
||||
else
|
||||
have_networkmanager="no (disabled)"
|
||||
fi
|
||||
|
||||
if test "x$have_networkmanager" = "xyes"; then
|
||||
AC_DEFINE(HAVE_NETWORKMANAGER, [1], [Define if we have NetworkManager])
|
||||
AC_SUBST([HAVE_NETWORKMANAGER], [1])
|
||||
else
|
||||
if test "x$enable_networkmanager" = "xyes"; then
|
||||
AC_MSG_ERROR([Couldn't find NetworkManager.])
|
||||
fi
|
||||
AC_SUBST([HAVE_NETWORKMANAGER], [0])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_NETWORKMANAGER, test "$have_networkmanager" = "yes")
|
||||
|
||||
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
|
||||
AM_PATH_GLIB_2_0()
|
||||
|
||||
@ -181,6 +223,14 @@ fi
|
||||
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
||||
|
||||
GNOME_COMPILE_WARNINGS([error])
|
||||
case "$WARN_CFLAGS" in
|
||||
*-Werror*)
|
||||
WARN_CFLAGS="$WARN_CFLAGS -Wno-error=deprecated-declarations"
|
||||
;;
|
||||
esac
|
||||
|
||||
AM_CFLAGS="$AM_CFLAGS $WARN_CFLAGS"
|
||||
AC_SUBST(AM_CFLAGS)
|
||||
|
||||
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
|
||||
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
|
||||
@ -204,3 +254,15 @@ AC_CONFIG_FILES([
|
||||
man/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
||||
echo "
|
||||
Build configuration:
|
||||
|
||||
Prefix: ${prefix}
|
||||
Source code location: ${srcdir}
|
||||
Compiler: ${CC}
|
||||
Compiler Warnings: $enable_compile_warnings
|
||||
|
||||
Support for NetworkManager: $have_networkmanager
|
||||
Support for GStreamer recording: $build_recorder
|
||||
"
|
||||
|
@ -1,9 +1,23 @@
|
||||
desktopdir=$(datadir)/applications
|
||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||
if HAVE_MUTTER_WAYLAND
|
||||
desktop_DATA += gnome-shell-wayland.desktop
|
||||
endif HAVE_MUTTER_WAYLAND
|
||||
CLEANFILES =
|
||||
|
||||
desktopdir=$(datadir)/applications
|
||||
desktop_DATA = gnome-shell.desktop gnome-shell-wayland.desktop gnome-shell-extension-prefs.desktop
|
||||
|
||||
if HAVE_NETWORKMANAGER
|
||||
desktop_DATA += org.gnome.Shell.PortalHelper.desktop
|
||||
|
||||
servicedir = $(datadir)/dbus-1/services
|
||||
service_DATA = org.gnome.Shell.PortalHelper.service
|
||||
|
||||
CLEANFILES += \
|
||||
org.gnome.Shell.PortalHelper.service \
|
||||
org.gnome.Shell.PortalHelper.desktop
|
||||
|
||||
endif
|
||||
|
||||
%.service: %.service.in
|
||||
$(AM_V_GEN) sed -e "s|@libexecdir[@]|$(libexecdir)|" \
|
||||
$< > $@ || rm $@
|
||||
|
||||
# We substitute in bindir so it works as an autostart
|
||||
# file when built in a non-system prefix
|
||||
@ -21,44 +35,18 @@ introspection_DATA = \
|
||||
org.gnome.ShellSearchProvider.xml \
|
||||
org.gnome.ShellSearchProvider2.xml
|
||||
|
||||
themedir = $(pkgdatadir)/theme
|
||||
dist_theme_DATA = \
|
||||
theme/calendar-arrow-left.svg \
|
||||
theme/calendar-arrow-right.svg \
|
||||
theme/calendar-today.svg \
|
||||
theme/checkbox-focused.svg \
|
||||
theme/checkbox-off-focused.svg \
|
||||
theme/checkbox-off.svg \
|
||||
theme/checkbox.svg \
|
||||
theme/close-window.svg \
|
||||
theme/close.svg \
|
||||
theme/corner-ripple-ltr.png \
|
||||
theme/corner-ripple-rtl.png \
|
||||
theme/dash-placeholder.svg \
|
||||
theme/filter-selected-ltr.svg \
|
||||
theme/filter-selected-rtl.svg \
|
||||
theme/gnome-shell.css \
|
||||
theme/logged-in-indicator.svg \
|
||||
theme/message-tray-background.png \
|
||||
theme/more-results.svg \
|
||||
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-highlight-narrow.svg \
|
||||
theme/panel-button-highlight-wide.svg \
|
||||
theme/process-working.svg \
|
||||
theme/running-indicator.svg \
|
||||
theme/source-button-border.svg \
|
||||
theme/summary-counter.svg \
|
||||
theme/toggle-off-us.svg \
|
||||
theme/toggle-off-intl.svg \
|
||||
theme/toggle-on-us.svg \
|
||||
theme/toggle-on-intl.svg \
|
||||
theme/ws-switch-arrow-up.png \
|
||||
theme/ws-switch-arrow-down.png
|
||||
resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir)/theme --generate-dependencies $(srcdir)/gnome-shell-theme.gresource.xml)
|
||||
gnome-shell-theme.gresource: gnome-shell-theme.gresource.xml $(resource_files)
|
||||
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir)/theme $<
|
||||
resourcedir = $(pkgdatadir)
|
||||
resource_DATA = gnome-shell-theme.gresource
|
||||
|
||||
backgrounddir = $(pkgdatadir)
|
||||
background_DATA = perf-background.xml
|
||||
|
||||
perf-background.xml: perf-background.xml.in
|
||||
$(AM_V_GEN) sed -e "s|@datadir[@]|$(datadir)|" \
|
||||
$< > $@ || rm $@
|
||||
|
||||
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
|
||||
keys_in_files = 50-gnome-shell-system.xml.in
|
||||
@ -92,15 +80,22 @@ EXTRA_DIST = \
|
||||
$(menu_DATA) \
|
||||
$(convert_DATA) \
|
||||
$(keys_in_files) \
|
||||
org.gnome.shell.gschema.xml.in.in
|
||||
perf-background.xml.in \
|
||||
org.gnome.Shell.PortalHelper.desktop.in \
|
||||
org.gnome.Shell.PortalHelper.service.in \
|
||||
org.gnome.shell.gschema.xml.in.in \
|
||||
gnome-shell-theme.gresource.xml \
|
||||
$(resource_files)
|
||||
|
||||
CLEANFILES = \
|
||||
CLEANFILES += \
|
||||
gnome-shell.desktop.in \
|
||||
gnome-shell-wayland.desktop.in \
|
||||
gnome-shell-extension-prefs.in \
|
||||
$(desktop_DATA) \
|
||||
$(keys_DATA) \
|
||||
$(gsettings_SCHEMAS) \
|
||||
perf-background.xml \
|
||||
gschemas.compiled \
|
||||
org.gnome.shell.gschema.valid \
|
||||
org.gnome.shell.gschema.xml.in
|
||||
org.gnome.shell.gschema.xml.in \
|
||||
gnome-shell-theme.gresource
|
||||
|
@ -1,5 +1,4 @@
|
||||
[org.gnome.shell.overrides]
|
||||
attach-modal-dialogs = /desktop/gnome/shell/windows/attach_modal_dialogs
|
||||
button-layout = /desktop/gnome/shell/windows/button_layout
|
||||
edge-tiling = /desktop/gnome/shell/windows/edge_tiling
|
||||
workspaces-only-on-primary = /desktop/gnome/shell/windows/workspaces_only_on_primary
|
||||
|
41
data/gnome-shell-theme.gresource.xml
Normal file
41
data/gnome-shell-theme.gresource.xml
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/org/gnome/shell/theme">
|
||||
<file>calendar-arrow-left.svg</file>
|
||||
<file>calendar-arrow-right.svg</file>
|
||||
<file>calendar-today.svg</file>
|
||||
<file>checkbox-focused.svg</file>
|
||||
<file>checkbox-off-focused.svg</file>
|
||||
<file>checkbox-off.svg</file>
|
||||
<file>checkbox.svg</file>
|
||||
<file>close-window.svg</file>
|
||||
<file>close.svg</file>
|
||||
<file>corner-ripple-ltr.png</file>
|
||||
<file>corner-ripple-rtl.png</file>
|
||||
<file>dash-placeholder.svg</file>
|
||||
<file>filter-selected-ltr.svg</file>
|
||||
<file>filter-selected-rtl.svg</file>
|
||||
<file>gnome-shell.css</file>
|
||||
<file>logged-in-indicator.svg</file>
|
||||
<file>message-tray-background.png</file>
|
||||
<file>more-results.svg</file>
|
||||
<file>noise-texture.png</file>
|
||||
<file>page-indicator-active.svg</file>
|
||||
<file>page-indicator-inactive.svg</file>
|
||||
<file>page-indicator-checked.svg</file>
|
||||
<file>page-indicator-hover.svg</file>
|
||||
<file>panel-button-border.svg</file>
|
||||
<file>panel-button-highlight-narrow.svg</file>
|
||||
<file>panel-button-highlight-wide.svg</file>
|
||||
<file>process-working.svg</file>
|
||||
<file>running-indicator.svg</file>
|
||||
<file>source-button-border.svg</file>
|
||||
<file>summary-counter.svg</file>
|
||||
<file>toggle-off-us.svg</file>
|
||||
<file>toggle-off-intl.svg</file>
|
||||
<file>toggle-on-us.svg</file>
|
||||
<file>toggle-on-intl.svg</file>
|
||||
<file>ws-switch-arrow-up.png</file>
|
||||
<file>ws-switch-arrow-down.png</file>
|
||||
</gresource>
|
||||
</gresources>
|
@ -2,7 +2,7 @@
|
||||
Type=Application
|
||||
_Name=GNOME Shell (wayland compositor)
|
||||
_Comment=Window management and application launching
|
||||
Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland
|
||||
Exec=@bindir@/gnome-shell --wayland --display-server
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=gnome-shell
|
||||
X-GNOME-Bugzilla-Component=general
|
||||
|
9
data/org.gnome.Shell.PortalHelper.desktop.in
Normal file
9
data/org.gnome.Shell.PortalHelper.desktop.in
Normal file
@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
_Name=Captive Portal
|
||||
Type=Application
|
||||
Exec=gapplication launch org.gnome.Shell.PortalHelper
|
||||
DBusActivatable=true
|
||||
NoDisplay=true
|
||||
Icon=network-workgroup
|
||||
StartupNotify=true
|
||||
OnlyShowIn=GNOME;
|
3
data/org.gnome.Shell.PortalHelper.service.in
Normal file
3
data/org.gnome.Shell.PortalHelper.service.in
Normal file
@ -0,0 +1,3 @@
|
||||
[D-BUS Service]
|
||||
Name=org.gnome.Shell.PortalHelper
|
||||
Exec=@libexecdir@/gnome-shell-portal-helper
|
@ -38,7 +38,6 @@
|
||||
<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>
|
||||
|
@ -13,36 +13,37 @@
|
||||
</key>
|
||||
<key name="enabled-extensions" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>Uuids of extensions to enable</_summary>
|
||||
<_summary>UUIDs of extensions to enable</_summary>
|
||||
<_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
|
||||
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="disable-extension-version-validation" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Disables the validation of extension version compatibility</_summary>
|
||||
<_description>
|
||||
GNOME Shell will only load extensions that claim to support the current
|
||||
running version. Enabling this option will disable this check and try to
|
||||
load all extensions regardless of the versions they claim to support.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="favorite-apps" type="as">
|
||||
<default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
|
||||
<default>[ 'epiphany.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
|
||||
<_summary>List of desktop file IDs for favorite applications</_summary>
|
||||
<_description>
|
||||
The applications corresponding to these identifiers
|
||||
will be displayed in the favorites area.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="app-folder-categories" type="as">
|
||||
<default>[ 'Utilities', 'Sundry' ]</default>
|
||||
<_summary>List of categories that should be displayed as folders</_summary>
|
||||
<_description>
|
||||
Each category name in this list will be represented as folder in the
|
||||
application view, rather than being displayed inline in the main view.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="app-picker-view" type="u">
|
||||
<default>0</default>
|
||||
<summary>App Picker View</summary>
|
||||
<description>
|
||||
<_summary>App Picker View</_summary>
|
||||
<_description>
|
||||
Index of the currently selected view in the application picker.
|
||||
</description>
|
||||
</_description>
|
||||
</key>
|
||||
<key name="command-history" type="as">
|
||||
<default>[]</default>
|
||||
@ -54,10 +55,10 @@
|
||||
</key>
|
||||
<key name="always-show-log-out" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
|
||||
<_summary>Always show the 'Log out' menu item in the user menu.</_summary>
|
||||
<_description>
|
||||
This key overrides the automatic hiding of the 'Log out'
|
||||
menuitem in single-user, single-session situations.
|
||||
menu item in single-user, single-session situations.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="remember-mount-password" type="b">
|
||||
@ -124,6 +125,11 @@
|
||||
Keybinding to focus the active notification.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="pause-resume-tweens" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</_summary>
|
||||
<_description></_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
|
||||
@ -142,11 +148,11 @@
|
||||
gettext-domain="@GETTEXT_PACKAGE@">
|
||||
<key type="b" name="current-workspace-only">
|
||||
<default>false</default>
|
||||
<summary>Limit switcher to current workspace.</summary>
|
||||
<description>
|
||||
<_summary>Limit switcher to current workspace.</_summary>
|
||||
<_description>
|
||||
If true, only applications that have windows on the current workspace are shown in the switcher.
|
||||
Otherwise, all applications are included.
|
||||
</description>
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
@ -169,11 +175,11 @@
|
||||
</key>
|
||||
<key type="b" name="current-workspace-only">
|
||||
<default>true</default>
|
||||
<summary>Limit switcher to current workspace.</summary>
|
||||
<description>
|
||||
<_summary>Limit switcher to current workspace.</_summary>
|
||||
<_description>
|
||||
If true, only windows from the current workspace are shown in the switcher.
|
||||
Otherwise, all windows are included.
|
||||
</description>
|
||||
</_description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
@ -188,15 +194,6 @@
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<key name="button-layout" type="s">
|
||||
<default>":close"</default>
|
||||
<_summary>Arrangement of buttons on the titlebar</_summary>
|
||||
<_description>
|
||||
This key overrides the key in org.gnome.desktop.wm.preferences when
|
||||
running GNOME Shell.
|
||||
</_description>
|
||||
</key>
|
||||
|
||||
<key name="edge-tiling" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Enable edge tiling when dropping windows on screen edges</_summary>
|
||||
|
31
data/perf-background.xml.in
Normal file
31
data/perf-background.xml.in
Normal file
@ -0,0 +1,31 @@
|
||||
<!-- With an animated background, performance will differ depending on whether
|
||||
one layer or two layers are being blended together. This messes up our
|
||||
benchmarks. We could just benchmark a single image, but since blended
|
||||
images are present for much of the day with the GNOME default background,
|
||||
we want to make sure that also performs well; for that reason we ship
|
||||
an "animated" background that animates super-slowly to use during
|
||||
performance tests; it will be in the blended state until 2030. -->
|
||||
<background>
|
||||
<starttime>
|
||||
<year>1990</year>
|
||||
<month>1</month>
|
||||
<day>1</day>
|
||||
<hour>0</hour>
|
||||
<minute>00</minute>
|
||||
<second>00</second>
|
||||
</starttime>
|
||||
|
||||
<!-- One transition that takes 40 years -->
|
||||
<transition type="overlay">
|
||||
<duration>1261440000.0</duration>
|
||||
<from>@datadir@/backgrounds/gnome/adwaita-morning.jpg</from>
|
||||
<to>@datadir@/backgrounds/gnome/adwaita-day.jpg</to>
|
||||
</transition>
|
||||
|
||||
<!-- A single slide doesn't work, so another slide for 1 minute after 40 years -->
|
||||
<static>
|
||||
<duration>60</duration>
|
||||
<file>/usr/share/backgrounds/gnome/Sandstone.jpg</file>
|
||||
</static>
|
||||
|
||||
</background>
|
@ -45,7 +45,7 @@ stage {
|
||||
/* small bold */
|
||||
.dash-label,
|
||||
.window-caption,
|
||||
.switcher-list,
|
||||
.switcher-list,
|
||||
.app-well-app > .overview-icon,
|
||||
.show-apps > .overview-icon,
|
||||
.grid-search-result .overview-icon {
|
||||
@ -153,18 +153,43 @@ StScrollBar StButton#vhandle:active {
|
||||
-arrow-rise: 11px;
|
||||
}
|
||||
|
||||
.popup-menu-boxpointer.fallback-app-menu {
|
||||
-arrow-border-radius: 4px;
|
||||
-arrow-background-color: #ededed;
|
||||
-arrow-border-width: 1px;
|
||||
-arrow-border-color: #a6a6a6;
|
||||
-arrow-base: 24px;
|
||||
-arrow-rise: 11px;
|
||||
}
|
||||
|
||||
.popup-menu-boxpointer.fallback-app-menu.dark {
|
||||
-arrow-background-color: #3f4747;
|
||||
-arrow-border-color: #282b2b;
|
||||
}
|
||||
|
||||
|
||||
.popup-menu {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.unicode-arrow {
|
||||
font-size: 120%;
|
||||
.popup-menu-arrow {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.popup-submenu-menu-item:open {
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
.fallback-app-menu .popup-submenu-menu-item:open {
|
||||
background-color: #888888;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.fallback-app-menu.dark .popup-submenu-menu-item:open {
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
.popup-sub-menu {
|
||||
background-gradient-start: rgba(80,80,80,0.3);
|
||||
background-gradient-end: rgba(80,80,80,0.4);
|
||||
@ -172,6 +197,20 @@ StScrollBar StButton#vhandle:active {
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
|
||||
}
|
||||
|
||||
.fallback-app-menu .popup-sub-menu {
|
||||
background-gradient-start: #dddddd;
|
||||
background-gradient-end: #dfdfdf;
|
||||
background-gradient-direction: vertical;
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.fallback-app-menu.dark .popup-sub-menu {
|
||||
background-gradient-start: #474747;
|
||||
background-gradient-end: #4b4b4b;
|
||||
background-gradient-direction: vertical;
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
|
||||
}
|
||||
|
||||
.popup-sub-menu:scrolled .popup-menu-item:ltr {
|
||||
padding-right: 0em;
|
||||
}
|
||||
@ -193,6 +232,10 @@ StScrollBar StButton#vhandle:active {
|
||||
border-width: 0px;
|
||||
}
|
||||
|
||||
.app-well-menu {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
/* The remaining popup-menu sizing is all done in ems, so that if you
|
||||
* override .popup-menu.font-size, everything else will scale with it.
|
||||
*/
|
||||
@ -220,7 +263,29 @@ StScrollBar StButton#vhandle:active {
|
||||
color: #9f9f9f;
|
||||
}
|
||||
|
||||
.popup-image-menu-item {
|
||||
.fallback-app-menu .popup-menu-item {
|
||||
color: #43484a;
|
||||
}
|
||||
|
||||
.fallback-app-menu .popup-menu-item:active {
|
||||
color: white;
|
||||
background-color: #4689cd;
|
||||
}
|
||||
|
||||
.fallback-app-menu .popup-menu-item:insensitive {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.fallback-app-menu.dark .popup-menu-item {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.fallback-app-menu.dark .popup-menu-item:active {
|
||||
background-color: #445f7d;
|
||||
}
|
||||
|
||||
.fallback-app-menu.dark .popup-menu-item:insensitive {
|
||||
color: #9f9f9f;
|
||||
}
|
||||
|
||||
.popup-separator-menu-item {
|
||||
@ -232,8 +297,15 @@ StScrollBar StButton#vhandle:active {
|
||||
padding: 8px 0px;
|
||||
}
|
||||
|
||||
.popup-alternating-menu-item:alternate {
|
||||
font-weight: bold;
|
||||
.fallback-app-menu .popup-separator-menu-item {
|
||||
-gradient-start: #a0a2a3;
|
||||
-gradient-end: #a0a2a3;
|
||||
-margin-horizontal: 0;
|
||||
}
|
||||
|
||||
.fallback-app-menu.dark .popup-separator-menu-item {
|
||||
-gradient-start: #818584;
|
||||
-gradient-end: #818584;
|
||||
}
|
||||
|
||||
.popup-status-menu-item {
|
||||
@ -289,6 +361,20 @@ StScrollBar StButton#vhandle:active {
|
||||
spacing: 10px;
|
||||
}
|
||||
|
||||
.nm-dialog-airplane-box {
|
||||
spacing: 12px;
|
||||
}
|
||||
|
||||
.nm-dialog-airplane-headline {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nm-dialog-airplane-text {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.nm-dialog-header-icon {
|
||||
icon-size: 32px;
|
||||
}
|
||||
@ -321,6 +407,14 @@ StScrollBar StButton#vhandle:active {
|
||||
icon-size: 16px;
|
||||
}
|
||||
|
||||
.no-networks-label {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.no-networks-box {
|
||||
spacing: 12px;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
|
||||
.candidate-page-button,
|
||||
@ -388,7 +482,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
/* Common radii */
|
||||
|
||||
#searchEntry,
|
||||
.search-entry,
|
||||
.modal-dialog-button,
|
||||
.notification-button,
|
||||
.hotplug-notification-item,
|
||||
@ -410,7 +504,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
/* Entries */
|
||||
|
||||
#searchEntry,
|
||||
.search-entry,
|
||||
.login-dialog StEntry,
|
||||
.notification StEntry,
|
||||
.modal-dialog StEntry {
|
||||
@ -422,7 +516,7 @@ StScrollBar StButton#vhandle:active {
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
#searchEntry,
|
||||
.search-entry,
|
||||
.login-dialog StEntry,
|
||||
.run-dialog-entry,
|
||||
.notification StEntry {
|
||||
@ -434,8 +528,8 @@ StScrollBar StButton#vhandle:active {
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
#searchEntry:focus,
|
||||
#searchEntry:hover,
|
||||
.search-entry:focus,
|
||||
.search-entry:hover,
|
||||
.login-dialog StEntry:focus,
|
||||
.notification StEntry:focus,
|
||||
.modal-dialog StEntry {
|
||||
@ -452,18 +546,18 @@ StScrollBar StButton#vhandle:active {
|
||||
border: 2px solid #3465a4;
|
||||
}
|
||||
|
||||
#searchEntry {
|
||||
.search-entry {
|
||||
border-color: rgba(245,245,245,0.3);
|
||||
color: rgb(192, 192, 192);
|
||||
caret-color: rgb(192, 192, 192);
|
||||
}
|
||||
|
||||
#searchEntry:hover {
|
||||
.search-entry:hover {
|
||||
color: rgb(128, 128, 128);
|
||||
caret-color: rgb(128, 128, 128);
|
||||
}
|
||||
|
||||
#searchEntry:focus {
|
||||
.search-entry:focus {
|
||||
color: rgb(64, 64, 64);
|
||||
caret-color: rgb(64, 64, 64);
|
||||
font-weight: bold;
|
||||
@ -567,11 +661,6 @@ StScrollBar StButton#vhandle:active {
|
||||
app-icon-bottom-clip: 2px;
|
||||
}
|
||||
|
||||
.app-menu-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.panel-button {
|
||||
-natural-hpadding: 12px;
|
||||
-minimum-hpadding: 6px;
|
||||
@ -655,7 +744,7 @@ StScrollBar StButton#vhandle:active {
|
||||
color: #e6e6e6;
|
||||
border-radius: 32px; /* wish we could do 50% */
|
||||
padding: 13px;
|
||||
border: 1px solid #5f5f5f; /* using rgba() is flaky unfortunately */
|
||||
border: 2px solid #5f5f5f; /* using rgba() is flaky unfortunately */
|
||||
}
|
||||
|
||||
.system-menu-action:hover,
|
||||
@ -663,7 +752,7 @@ StScrollBar StButton#vhandle:active {
|
||||
color: white;
|
||||
background-color: #4c4c4c;
|
||||
border: none;
|
||||
padding: 14px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.system-menu-action:active {
|
||||
@ -832,7 +921,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
/* Search Box */
|
||||
|
||||
#searchEntry {
|
||||
.search-entry {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
@ -841,8 +930,8 @@ StScrollBar StButton#vhandle:active {
|
||||
color: #c0c0c0;
|
||||
}
|
||||
|
||||
#searchEntry:hover .search-entry-icon,
|
||||
#searchEntry:focus .search-entry-icon {
|
||||
.search-entry:hover .search-entry-icon,
|
||||
.search-entry:focus .search-entry-icon {
|
||||
color: #8d8f8a;
|
||||
}
|
||||
|
||||
@ -964,6 +1053,8 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.app-folder-icon {
|
||||
padding: 5px;
|
||||
spacing-rows: 5px;
|
||||
spacing-columns: 5px;
|
||||
}
|
||||
|
||||
.dash-item-container > StButton {
|
||||
@ -1072,6 +1163,7 @@ StScrollBar StButton#vhandle:active {
|
||||
.show-apps:checked > .overview-icon,
|
||||
.show-apps:active > .overview-icon,
|
||||
.search-provider-icon:active,
|
||||
.grid-search-result:active .overview-icon,
|
||||
.list-search-result:active {
|
||||
background-gradient-start: rgba(255, 255, 255, .05);
|
||||
background-gradient-end: rgba(255, 255, 255, .15);
|
||||
@ -1140,11 +1232,6 @@ StScrollBar StButton#vhandle:active {
|
||||
text-shadow: black 0px 2px 2px;
|
||||
}
|
||||
|
||||
#LookingGlassDialog .lg-inspector-title {
|
||||
font-weight: bold;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.lg-dialog StEntry {
|
||||
selection-background-color: #bbbbbb;
|
||||
selected-color: #333333;
|
||||
@ -1224,14 +1311,8 @@ StScrollBar StButton#vhandle:active {
|
||||
width: 0.3em;
|
||||
}
|
||||
|
||||
#calendarPopup .calendar {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
padding: .4em 1.75em .8em 1.75em;
|
||||
spacing-rows: 0px;
|
||||
spacing-columns: 0px;
|
||||
}
|
||||
|
||||
.calendar-month-label {
|
||||
@ -1289,12 +1370,18 @@ StScrollBar StButton#vhandle:active {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
color: #eeeeec;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.datemenu-date-label:hover,
|
||||
.datemenu-date-label:focus {
|
||||
background-color: #999999;
|
||||
}
|
||||
|
||||
.datemenu-date-label:active {
|
||||
background-color: #aaaaaa;
|
||||
}
|
||||
|
||||
.calendar-day-base {
|
||||
font-size: 9pt;
|
||||
text-align: center;
|
||||
@ -1407,6 +1494,10 @@ StScrollBar StButton#vhandle:active {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.events-day-time-ellipses {
|
||||
color: rgba(153, 153, 153, 1.0);
|
||||
}
|
||||
|
||||
.events-day-time:rtl {
|
||||
text-align: left;
|
||||
}
|
||||
@ -1421,11 +1512,8 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-right: 8pt;
|
||||
}
|
||||
|
||||
.url-highlighter {
|
||||
link-color: #ccccff;
|
||||
}
|
||||
|
||||
/* Message Tray */
|
||||
|
||||
#message-tray {
|
||||
background: #2e3436 url(message-tray-background.png);
|
||||
background-repeat: repeat;
|
||||
@ -1448,8 +1536,11 @@ StScrollBar StButton#vhandle:active {
|
||||
color: #eeeeee;
|
||||
}
|
||||
|
||||
.no-messages-label,
|
||||
.no-networks-label {
|
||||
.url-highlighter {
|
||||
link-color: #ccccff;
|
||||
}
|
||||
|
||||
.no-messages-label {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
@ -1630,8 +1721,8 @@ StScrollBar StButton#vhandle:active {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.chat-group-sent, .chat-group-meta {
|
||||
padding: 8px 0;
|
||||
.chat-empty-line {
|
||||
font-size: 4px;
|
||||
}
|
||||
|
||||
.chat-received {
|
||||
@ -1656,6 +1747,7 @@ StScrollBar StButton#vhandle:active {
|
||||
.chat-meta-message {
|
||||
padding-left: 4px;
|
||||
font-size: 9pt;
|
||||
font-weight: bold;
|
||||
color: #bbbbbb;
|
||||
}
|
||||
|
||||
@ -1745,26 +1837,6 @@ StScrollBar StButton#vhandle:active {
|
||||
spacing: 8px;
|
||||
}
|
||||
|
||||
.thumbnail-scroll-gradient-left {
|
||||
background-gradient-direction: horizontal;
|
||||
background-gradient-start: rgba(51, 51, 51, 1.0);
|
||||
background-gradient-end: rgba(51, 51, 51, 0);
|
||||
border-radius: 24px;
|
||||
border-radius-topright: 0px;
|
||||
border-radius-bottomright: 0px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.thumbnail-scroll-gradient-right {
|
||||
background-gradient-direction: horizontal;
|
||||
background-gradient-start: rgba(51, 51, 51, 0);
|
||||
background-gradient-end: rgba(51, 51, 51, 1.0);
|
||||
border-radius: 24px;
|
||||
border-radius-topleft: 0px;
|
||||
border-radius-bottomleft: 0px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.switcher-list .item-box {
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
@ -1857,6 +1929,27 @@ StScrollBar StButton#vhandle:active {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* Tile previews */
|
||||
.tile-preview {
|
||||
background-color: rgba(74, 144, 217, 0.35);
|
||||
border: 1px solid #4a90d9; /* Adwaita selected bg color */
|
||||
}
|
||||
|
||||
.tile-preview-left.on-primary {
|
||||
/* keep in sync with -panel-corner-radius */
|
||||
border-radius: 6px 0 0 0;
|
||||
}
|
||||
|
||||
.tile-preview-right.on-primary {
|
||||
/* keep in sync with -panel-corner-radius */
|
||||
border-radius: 0 6px 0 0;
|
||||
}
|
||||
|
||||
.tile-preview-left.tile-preview-right.on-primary {
|
||||
/* keep in sync with -panel-corner-radius */
|
||||
border-radius: 6px 6px 0 0;
|
||||
}
|
||||
|
||||
/* Modal Dialogs */
|
||||
|
||||
/* Dialog Subject Text Style */
|
||||
@ -1927,44 +2020,57 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.end-session-dialog-subject {
|
||||
.end-session-dialog-layout {
|
||||
padding-left: 17px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.end-session-dialog-subject:rtl {
|
||||
padding-left: 0px;
|
||||
.end-session-dialog-layout:rtl {
|
||||
padding-right: 17px;
|
||||
}
|
||||
|
||||
.end-session-dialog-description {
|
||||
padding-left: 17px;
|
||||
width: 28em;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.end-session-dialog-description:rtl {
|
||||
padding-right: 17px;
|
||||
width: 28em;
|
||||
padding-bottom: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.end-session-dialog-warning {
|
||||
width: 28em;
|
||||
color: #f57900;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.end-session-dialog-warning:rtl {
|
||||
width: 28em;
|
||||
color: #f57900;
|
||||
padding-top: 6px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.end-session-dialog-logout-icon {
|
||||
border: 2px solid #8b8b8b;
|
||||
border-radius: 5px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.end-session-dialog-shutdown-icon {
|
||||
color: #bebebe;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.end-session-dialog-inhibitor-layout {
|
||||
spacing: 16px;
|
||||
max-height: 200px;
|
||||
padding-right: 50px;
|
||||
padding-left: 50px;
|
||||
padding-right: 65px;
|
||||
padding-left: 65px;
|
||||
}
|
||||
|
||||
.end-session-dialog-session-list,
|
||||
@ -1976,6 +2082,10 @@ StScrollBar StButton#vhandle:active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.end-session-dialog-list-header:rtl {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.end-session-dialog-app-list-item,
|
||||
.end-session-dialog-session-list-item {
|
||||
spacing: 1em;
|
||||
@ -1991,15 +2101,16 @@ StScrollBar StButton#vhandle:active {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
/* Restart message */
|
||||
.restart-message {
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
/* ShellMountOperation Dialogs */
|
||||
.shell-mount-operation-icon {
|
||||
icon-size: 48px;
|
||||
}
|
||||
|
||||
.mount-password-reask {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.show-processes-dialog,
|
||||
.mount-question-dialog {
|
||||
spacing: 24px;
|
||||
@ -2095,6 +2206,10 @@ StScrollBar StButton#vhandle:active {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.prompt-dialog-description:rtl {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.prompt-dialog-password-box {
|
||||
spacing: 1em;
|
||||
padding-bottom: 1em;
|
||||
@ -2291,13 +2406,6 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-title {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.login-dialog {
|
||||
/* Reset border and background */
|
||||
border: none;
|
||||
@ -2321,6 +2429,8 @@ StScrollBar StButton#vhandle:active {
|
||||
.login-dialog-user-list-item {
|
||||
border-radius: 5px;
|
||||
padding: .2em;
|
||||
color: #bfbfbf;
|
||||
text-shadow: black 0px 2px 2px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:ltr {
|
||||
@ -2331,24 +2441,6 @@ StScrollBar StButton#vhandle:active {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item .login-dialog-user-list-item-name {
|
||||
font-size: 20px;
|
||||
padding-left: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-dialog-user-list:expanded .login-dialog-user-list-item {
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item,
|
||||
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name,
|
||||
.login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name,
|
||||
.login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
|
||||
color: #bfbfbf;
|
||||
text-shadow: black 0px 2px 2px;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
@ -2362,10 +2454,6 @@ StScrollBar StButton#vhandle:active {
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item-text-box {
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item .login-dialog-timed-login-indicator {
|
||||
background-color: rgba(0,0,0,0.0);
|
||||
height: 2px;
|
||||
@ -2375,13 +2463,6 @@ StScrollBar StButton#vhandle:active {
|
||||
background-color: #8b8b8b;
|
||||
}
|
||||
|
||||
.login-dialog-user-list-item-icon {
|
||||
border: 2px solid #8b8b8b;
|
||||
border-radius: 3px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.login-dialog-not-listed-label {
|
||||
font-size: 10.5pt;
|
||||
font-weight: bold;
|
||||
@ -2402,12 +2483,13 @@ StScrollBar StButton#vhandle:active {
|
||||
color: #E8E8E8;
|
||||
}
|
||||
|
||||
.login-dialog-username {
|
||||
.login-dialog-username,
|
||||
.user-widget-label {
|
||||
font-size: 16pt;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
padding-left: 15px;
|
||||
text-shadow: black 0px 4px 3px 0px;
|
||||
text-shadow: rgba(0, 0, 0, 0.5) 0px 2px 1px 0px;
|
||||
}
|
||||
|
||||
.login-dialog-prompt-layout {
|
||||
@ -2432,6 +2514,7 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.login-dialog-session-list-button:hover,
|
||||
.login-dialog-session-list-button:focus,
|
||||
.login-dialog-session-list-button:active {
|
||||
color: white;
|
||||
}
|
||||
@ -2498,12 +2581,14 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.user-widget-label {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.user-widget-label:ltr {
|
||||
padding-left: 18px;
|
||||
color:white;
|
||||
text-shadow: black 0px 4px 3px 0px;
|
||||
}
|
||||
|
||||
.user-widget-label:rtl {
|
||||
padding-right: 18px;
|
||||
}
|
||||
|
||||
/* Screen shield */
|
||||
@ -2630,4 +2715,5 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.background-menu {
|
||||
-boxpointer-gap: 4px;
|
||||
-arrow-rise: 0px;
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ IGNORE_HFILES= \
|
||||
gactionmuxer.h \
|
||||
gactionobservable.h \
|
||||
gactionobserver.h \
|
||||
shell-network-agent.h \
|
||||
shell-recorder-src.h
|
||||
|
||||
if !BUILD_RECORDER
|
||||
@ -112,7 +113,7 @@ expand_content_files=
|
||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
|
||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
|
||||
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
|
||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(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.
|
||||
include $(top_srcdir)/gtk-doc.make
|
||||
|
@ -50,7 +50,6 @@
|
||||
<xi:include href="xml/shell-wm.xml"/>
|
||||
<xi:include href="xml/shell-util.xml"/>
|
||||
<xi:include href="xml/shell-mount-operation.xml"/>
|
||||
<xi:include href="xml/shell-network-agent.xml"/>
|
||||
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
|
||||
<xi:include href="xml/shell-tp-client.xml"/>
|
||||
</chapter>
|
||||
|
@ -17,19 +17,19 @@ packages. If you are interested in building GNOME Shell from source,
|
||||
we would recommend building from version control using the build
|
||||
script described at:
|
||||
|
||||
http://live.gnome.org/GnomeShell
|
||||
https://wiki.gnome.org/Projects/GnomeShell
|
||||
|
||||
Not only will that give you the very latest version of this rapidly
|
||||
changing project, it will be much easier than get GNOME Shell and
|
||||
its dependencies to build from tarballs.</description>
|
||||
<!--
|
||||
<homepage rdf:resource="http://live.gnome.org/GnomeShell" />
|
||||
-->
|
||||
<homepage rdf:resource="https://wiki.gnome.org/Projects/GnomeShell" />
|
||||
<mailing-list rdf:resource="http://mail.gnome.org/mailman/listinfo/gnome-shell-list" />
|
||||
<download-page rdf:resource="http://download.gnome.org/sources/gnome-shell/" />
|
||||
<bug-database rdf:resource="http://bugzilla.gnome.org/browse.cgi?product=gnome-shell" />
|
||||
<bug-database rdf:resource="https://bugzilla.gnome.org/browse.cgi?product=gnome-shell" />
|
||||
|
||||
<category rdf:resource="http://api.gnome.org/doap-extensions#desktop" />
|
||||
<category rdf:resource="http://api.gnome.org/doap-extensions#core" />
|
||||
<programming-language>JavaScript</programming-language>
|
||||
<programming-language>C</programming-language>
|
||||
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
|
131
js/Makefile.am
131
js/Makefile.am
@ -1,123 +1,38 @@
|
||||
NULL =
|
||||
|
||||
EXTRA_DIST = misc/config.js.in
|
||||
CLEANFILES = misc/config.js
|
||||
BUILT_SOURCES =
|
||||
|
||||
misc/config.js: misc/config.js.in Makefile
|
||||
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
|
||||
sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
|
||||
-e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
|
||||
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
|
||||
-e "s|[@]HAVE_NETWORKMANAGER@|$(HAVE_NETWORKMANAGER)|g" \
|
||||
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
|
||||
-e "s|[@]datadir@|$(datadir)|g" \
|
||||
-e "s|[@]libexecdir@|$(libexecdir)|g" \
|
||||
-e "s|[@]sysconfdir@|$(sysconfdir)|g" \
|
||||
$< > $@
|
||||
|
||||
jsdir = $(pkgdatadir)/js
|
||||
js_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --sourcedir=$(builddir) --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 = \
|
||||
gdm/authPrompt.js \
|
||||
gdm/batch.js \
|
||||
gdm/fingerprint.js \
|
||||
gdm/loginDialog.js \
|
||||
gdm/oVirt.js \
|
||||
gdm/realmd.js \
|
||||
gdm/util.js \
|
||||
extensionPrefs/main.js \
|
||||
misc/config.js \
|
||||
misc/extensionUtils.js \
|
||||
misc/fileUtils.js \
|
||||
misc/gnomeSession.js \
|
||||
misc/hash.js \
|
||||
misc/history.js \
|
||||
misc/jsParse.js \
|
||||
misc/loginManager.js \
|
||||
misc/modemManager.js \
|
||||
misc/objectManager.js \
|
||||
misc/params.js \
|
||||
misc/smartcardManager.js \
|
||||
misc/util.js \
|
||||
perf/core.js \
|
||||
ui/altTab.js \
|
||||
ui/animation.js \
|
||||
ui/appDisplay.js \
|
||||
ui/appFavorites.js \
|
||||
ui/backgroundMenu.js \
|
||||
ui/background.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/focusCaretTracker.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/slider.js \
|
||||
ui/notificationDaemon.js \
|
||||
ui/osdWindow.js \
|
||||
ui/overview.js \
|
||||
ui/overviewControls.js \
|
||||
ui/panel.js \
|
||||
ui/panelMenu.js \
|
||||
ui/pointerWatcher.js \
|
||||
ui/popupMenu.js \
|
||||
ui/remoteSearch.js \
|
||||
ui/remoteMenu.js \
|
||||
ui/runDialog.js \
|
||||
ui/screencast.js \
|
||||
ui/screenshot.js \
|
||||
ui/screenShield.js \
|
||||
ui/scripting.js \
|
||||
ui/search.js \
|
||||
ui/shellDBus.js \
|
||||
ui/status/accessibility.js \
|
||||
ui/status/brightness.js \
|
||||
ui/status/keyboard.js \
|
||||
ui/status/network.js \
|
||||
ui/status/power.js \
|
||||
ui/status/rfkill.js \
|
||||
ui/status/volume.js \
|
||||
ui/status/bluetooth.js \
|
||||
ui/status/screencast.js \
|
||||
ui/status/system.js \
|
||||
ui/switcherPopup.js \
|
||||
ui/tweener.js \
|
||||
ui/unlockDialog.js \
|
||||
ui/userWidget.js \
|
||||
ui/viewSelector.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/telepathyClient.js \
|
||||
ui/components/keyring.js \
|
||||
js_built_sources = js-resources.c js-resources.h
|
||||
|
||||
BUILT_SOURCES += $(js_built_sources)
|
||||
|
||||
all-local: $(js_built_sources)
|
||||
|
||||
js_resource_dist_files = $(filter-out misc/config.js, $(js_resource_files))
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(js_resource_dist_files) \
|
||||
js-resources.gresource.xml \
|
||||
misc/config.js.in \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES = \
|
||||
$(js_built_sources) \
|
||||
$(NULL)
|
||||
|
@ -23,6 +23,11 @@ const GnomeShellIface = '<node> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const customCss = '.prefs-button { \
|
||||
padding: 8px; \
|
||||
border-radius: 20px; \
|
||||
}';
|
||||
|
||||
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
|
||||
|
||||
function stripPrefix(string, prefix) {
|
||||
@ -46,13 +51,10 @@ const Application = new Lang.Class({
|
||||
|
||||
this._extensionPrefsModules = {};
|
||||
|
||||
this._extensionIters = {};
|
||||
this._startupUuid = null;
|
||||
},
|
||||
|
||||
_buildModel: function() {
|
||||
this._model = new Gtk.ListStore();
|
||||
this._model.set_column_types([GObject.TYPE_STRING, GObject.TYPE_STRING]);
|
||||
this._loaded = false;
|
||||
this._skipMainWindow = false;
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
|
||||
},
|
||||
|
||||
_extensionAvailable: function(uuid) {
|
||||
@ -61,20 +63,12 @@ const Application = new Lang.Class({
|
||||
if (!extension)
|
||||
return false;
|
||||
|
||||
if (ExtensionUtils.isOutOfDate(extension))
|
||||
return false;
|
||||
|
||||
if (!extension.dir.get_child('prefs.js').query_exists(null))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_setExtensionInsensitive: function(layout, cell, model, iter, data) {
|
||||
let uuid = model.get_value(iter, 0);
|
||||
cell.set_sensitive(this._extensionAvailable(uuid));
|
||||
},
|
||||
|
||||
_getExtensionPrefsModule: function(extension) {
|
||||
let uuid = extension.metadata.uuid;
|
||||
|
||||
@ -104,21 +98,23 @@ const Application = new Lang.Class({
|
||||
widget = this._buildErrorUI(extension, e);
|
||||
}
|
||||
|
||||
// Destroy the current prefs widget, if it exists
|
||||
if (this._extensionPrefsBin.get_child())
|
||||
this._extensionPrefsBin.get_child().destroy();
|
||||
let dialog = new Gtk.Dialog({ use_header_bar: true,
|
||||
modal: true,
|
||||
title: extension.metadata.name });
|
||||
|
||||
this._extensionPrefsBin.add(widget);
|
||||
this._extensionSelector.set_active_iter(this._extensionIters[uuid]);
|
||||
},
|
||||
if (this._skipMainWindow) {
|
||||
this.application.add_window(dialog);
|
||||
if (this._window)
|
||||
this._window.destroy();
|
||||
this._window = dialog;
|
||||
this._window.window_position = Gtk.WindowPosition.CENTER;
|
||||
} else {
|
||||
dialog.transient_for = this._window;
|
||||
}
|
||||
|
||||
_extensionSelected: function() {
|
||||
let [success, iter] = this._extensionSelector.get_active_iter();
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
let uuid = this._model.get_value(iter, 0);
|
||||
this._selectExtension(uuid);
|
||||
dialog.set_default_size(600, 400);
|
||||
dialog.get_content_area().add(widget);
|
||||
dialog.show();
|
||||
},
|
||||
|
||||
_buildErrorUI: function(extension, exc) {
|
||||
@ -151,48 +147,26 @@ const Application = new Lang.Class({
|
||||
|
||||
_buildUI: function(app) {
|
||||
this._window = new Gtk.ApplicationWindow({ application: app,
|
||||
window_position: Gtk.WindowPosition.CENTER,
|
||||
title: _("GNOME Shell Extension Preferences") });
|
||||
window_position: Gtk.WindowPosition.CENTER });
|
||||
|
||||
this._window.set_size_request(600, 400);
|
||||
this._window.set_size_request(800, 500);
|
||||
|
||||
let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||||
this._window.add(vbox);
|
||||
this._titlebar = new Gtk.HeaderBar({ show_close_button: true,
|
||||
title: _("GNOME Shell Extensions") });
|
||||
this._window.set_titlebar(this._titlebar);
|
||||
|
||||
let toolbar = new Gtk.Toolbar();
|
||||
toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
|
||||
vbox.add(toolbar);
|
||||
let toolitem;
|
||||
let scroll = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER,
|
||||
shadow_type: Gtk.ShadowType.IN,
|
||||
halign: Gtk.Align.CENTER,
|
||||
margin: 18 });
|
||||
this._window.add(scroll);
|
||||
|
||||
let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>',
|
||||
use_markup: true });
|
||||
toolitem = new Gtk.ToolItem({ child: label });
|
||||
toolbar.add(toolitem);
|
||||
this._extensionSelector = new Gtk.ListBox({ selection_mode: Gtk.SelectionMode.NONE });
|
||||
this._extensionSelector.set_sort_func(Lang.bind(this, this._sortList));
|
||||
this._extensionSelector.set_header_func(Lang.bind(this, this._updateHeader));
|
||||
|
||||
this._extensionSelector = new Gtk.ComboBox({ model: this._model,
|
||||
margin_left: 8,
|
||||
hexpand: true });
|
||||
this._extensionSelector.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
|
||||
scroll.add(this._extensionSelector);
|
||||
|
||||
let renderer = new Gtk.CellRendererText();
|
||||
this._extensionSelector.pack_start(renderer, true);
|
||||
this._extensionSelector.add_attribute(renderer, 'text', 1);
|
||||
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive));
|
||||
this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
|
||||
|
||||
toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
|
||||
toolitem.set_expand(true);
|
||||
toolbar.add(toolitem);
|
||||
|
||||
this._extensionPrefsBin = new Gtk.Frame();
|
||||
vbox.add(this._extensionPrefsBin);
|
||||
|
||||
let label = new Gtk.Label({
|
||||
label: _("Select an extension to configure using the combobox above."),
|
||||
vexpand: true
|
||||
});
|
||||
|
||||
this._extensionPrefsBin.add(label);
|
||||
|
||||
this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
|
||||
this._shellProxy.connectSignal('ExtensionStatusChanged', Lang.bind(this, function(proxy, senderName, [uuid, state, error]) {
|
||||
@ -203,23 +177,61 @@ const Application = new Lang.Class({
|
||||
this._window.show_all();
|
||||
},
|
||||
|
||||
_addCustomStyle: function() {
|
||||
let provider = new Gtk.CssProvider();
|
||||
|
||||
try {
|
||||
provider.load_from_data(customCss, -1);
|
||||
} catch(e) {
|
||||
log('Failed to add application style');
|
||||
return;
|
||||
}
|
||||
|
||||
let screen = this._window.window.get_screen();
|
||||
let priority = Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION;
|
||||
Gtk.StyleContext.add_provider_for_screen(screen, provider, priority);
|
||||
},
|
||||
|
||||
_sortList: function(row1, row2) {
|
||||
let name1 = ExtensionUtils.extensions[row1.uuid].metadata.name;
|
||||
let name2 = ExtensionUtils.extensions[row2.uuid].metadata.name;
|
||||
return name1.localeCompare(name2);
|
||||
},
|
||||
|
||||
_updateHeader: function(row, before) {
|
||||
if (!before || row.get_header())
|
||||
return;
|
||||
|
||||
let sep = new Gtk.Separator({ orientation: Gtk.Orientation.HORIZONTAL });
|
||||
row.set_header(sep);
|
||||
},
|
||||
|
||||
_scanExtensions: function() {
|
||||
let finder = new ExtensionUtils.ExtensionFinder();
|
||||
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
|
||||
finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
|
||||
finder.scanExtensions();
|
||||
this._extensionsLoaded();
|
||||
},
|
||||
|
||||
_extensionFound: function(signals, extension) {
|
||||
let iter = this._model.append();
|
||||
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
|
||||
this._extensionIters[extension.uuid] = iter;
|
||||
_extensionFound: function(finder, extension) {
|
||||
let row = new ExtensionRow(extension.uuid);
|
||||
|
||||
row.prefsButton.visible = this._extensionAvailable(row.uuid);
|
||||
row.prefsButton.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
this._selectExtension(row.uuid);
|
||||
}));
|
||||
|
||||
row.show_all();
|
||||
this._extensionSelector.add(row);
|
||||
},
|
||||
|
||||
_extensionsLoaded: function() {
|
||||
if (this._startupUuid && this._extensionAvailable(this._startupUuid))
|
||||
this._selectExtension(this._startupUuid);
|
||||
this._startupUuid = null;
|
||||
this._skipMainWindow = false;
|
||||
this._loaded = true;
|
||||
},
|
||||
|
||||
_onActivate: function() {
|
||||
@ -227,29 +239,137 @@ const Application = new Lang.Class({
|
||||
},
|
||||
|
||||
_onStartup: function(app) {
|
||||
this._buildModel();
|
||||
this._buildUI(app);
|
||||
this._addCustomStyle();
|
||||
this._scanExtensions();
|
||||
},
|
||||
|
||||
_onCommandLine: function(app, commandLine) {
|
||||
app.activate();
|
||||
let args = commandLine.get_arguments();
|
||||
|
||||
if (args.length) {
|
||||
let uuid = args[0];
|
||||
|
||||
this._skipMainWindow = true;
|
||||
|
||||
// Strip off "extension:///" prefix which fakes a URI, if it exists
|
||||
uuid = stripPrefix(uuid, "extension:///");
|
||||
|
||||
if (this._extensionAvailable(uuid))
|
||||
this._selectExtension(uuid);
|
||||
else
|
||||
else if (!this._loaded)
|
||||
this._startupUuid = uuid;
|
||||
else
|
||||
this._skipMainWindow = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
const ExtensionRow = new Lang.Class({
|
||||
Name: 'ExtensionRow',
|
||||
Extends: Gtk.ListBoxRow,
|
||||
|
||||
_init: function(uuid) {
|
||||
this.parent();
|
||||
|
||||
this.uuid = uuid;
|
||||
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' });
|
||||
this._settings.connect('changed::enabled-extensions', Lang.bind(this,
|
||||
function() {
|
||||
this._switch.state = this._isEnabled();
|
||||
}));
|
||||
this._settings.connect('changed::disable-extension-version-validation',
|
||||
Lang.bind(this, function() {
|
||||
this._switch.sensitive = this._canEnable();
|
||||
}));
|
||||
|
||||
this._buildUI();
|
||||
},
|
||||
|
||||
_buildUI: function() {
|
||||
let extension = ExtensionUtils.extensions[this.uuid];
|
||||
|
||||
let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL,
|
||||
hexpand: true, margin: 12, spacing: 6 });
|
||||
this.add(hbox);
|
||||
|
||||
let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL,
|
||||
spacing: 6, hexpand: true });
|
||||
hbox.add(vbox);
|
||||
|
||||
let name = GLib.markup_escape_text(extension.metadata.name, -1);
|
||||
let label = new Gtk.Label({ label: '<b>' + name + '</b>',
|
||||
use_markup: true,
|
||||
halign: Gtk.Align.START });
|
||||
vbox.add(label);
|
||||
|
||||
let desc = extension.metadata.description.split('\n')[0];
|
||||
label = new Gtk.Label({ label: desc,
|
||||
ellipsize: Pango.EllipsizeMode.END,
|
||||
halign: Gtk.Align.START });
|
||||
vbox.add(label);
|
||||
|
||||
let button = new Gtk.Button({ valign: Gtk.Align.CENTER,
|
||||
no_show_all: true });
|
||||
button.add(new Gtk.Image({ icon_name: 'emblem-system-symbolic',
|
||||
icon_size: Gtk.IconSize.BUTTON,
|
||||
visible: true }));
|
||||
button.get_style_context().add_class('prefs-button');
|
||||
hbox.add(button);
|
||||
|
||||
this.prefsButton = button;
|
||||
|
||||
this._switch = new Gtk.Switch({ valign: Gtk.Align.CENTER,
|
||||
sensitive: this._canEnable(),
|
||||
state: this._isEnabled() });
|
||||
this._switch.connect('notify::active', Lang.bind(this,
|
||||
function() {
|
||||
if (this._switch.active)
|
||||
this._enable();
|
||||
else
|
||||
this._disable();
|
||||
}));
|
||||
this._switch.connect('state-set', function() { return true; });
|
||||
hbox.add(this._switch);
|
||||
},
|
||||
|
||||
_canEnable: function() {
|
||||
let extension = ExtensionUtils.extensions[this.uuid];
|
||||
let checkVersion = !this._settings.get_boolean('disable-extension-version-validation');
|
||||
|
||||
return !(checkVersion && ExtensionUtils.isOutOfDate(extension));
|
||||
},
|
||||
|
||||
_isEnabled: function() {
|
||||
let extensions = this._settings.get_strv('enabled-extensions');
|
||||
return extensions.indexOf(this.uuid) != -1;
|
||||
},
|
||||
|
||||
_enable: function() {
|
||||
let extensions = this._settings.get_strv('enabled-extensions');
|
||||
if (extensions.indexOf(this.uuid) != -1)
|
||||
return;
|
||||
|
||||
extensions.push(this.uuid);
|
||||
this._settings.set_strv('enabled-extensions', extensions);
|
||||
},
|
||||
|
||||
_disable: function() {
|
||||
let extensions = this._settings.get_strv('enabled-extensions');
|
||||
let pos = extensions.indexOf(this.uuid);
|
||||
if (pos == -1)
|
||||
return;
|
||||
do {
|
||||
extensions.splice(pos, 1);
|
||||
pos = extensions.indexOf(this.uuid);
|
||||
} while (pos != -1);
|
||||
this._settings.set_strv('enabled-extensions', extensions);
|
||||
}
|
||||
});
|
||||
|
||||
function initEnvironment() {
|
||||
// Monkey-patch in a "global" object that fakes some Shell utilities
|
||||
// that ExtensionUtils depends on.
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
@ -80,6 +81,7 @@ const AuthPrompt = new Lang.Class({
|
||||
if (event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this.cancel();
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
|
||||
this._userWell = new St.Bin({ x_fill: true,
|
||||
@ -93,7 +95,7 @@ const AuthPrompt = new Lang.Class({
|
||||
|
||||
this.actor.add(this._label,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
x_fill: false,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START });
|
||||
this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||
@ -111,7 +113,7 @@ const AuthPrompt = new Lang.Class({
|
||||
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: true, y_align: St.Align.START });
|
||||
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 });
|
||||
@ -125,7 +127,7 @@ const AuthPrompt = new Lang.Class({
|
||||
|
||||
this._initButtons();
|
||||
|
||||
let spinnerIcon = global.datadir + '/theme/process-working.svg';
|
||||
let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg');
|
||||
this._spinner = new Animation.AnimatedIcon(spinnerIcon, DEFAULT_BUTTON_WELL_ICON_SIZE);
|
||||
this._spinner.actor.opacity = 0;
|
||||
this._spinner.actor.show();
|
||||
@ -133,8 +135,7 @@ const AuthPrompt = new Lang.Class({
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._userVerifier.clear();
|
||||
this._userVerifier.disconnectAll();
|
||||
this._userVerifier.destroy();
|
||||
this._userVerifier = null;
|
||||
},
|
||||
|
||||
@ -263,10 +264,8 @@ const AuthPrompt = new Lang.Class({
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) {
|
||||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||
this.reset();
|
||||
}
|
||||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||||
this.reset();
|
||||
},
|
||||
|
||||
addActorToDefaultButtonWell: function(actor) {
|
||||
@ -420,11 +419,13 @@ const AuthPrompt = new Lang.Class({
|
||||
},
|
||||
|
||||
setUser: function(user) {
|
||||
let oldChild = this._userWell.get_child();
|
||||
if (oldChild)
|
||||
oldChild.destroy();
|
||||
|
||||
if (user) {
|
||||
let userWidget = new UserWidget.UserWidget(user);
|
||||
this._userWell.set_child(userWidget.actor);
|
||||
} else {
|
||||
this._userWell.set_child(null);
|
||||
}
|
||||
},
|
||||
|
||||
@ -451,8 +452,7 @@ const AuthPrompt = new Lang.Class({
|
||||
// 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))) {
|
||||
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;
|
||||
|
@ -13,9 +13,7 @@
|
||||
* 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.
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
@ -13,9 +13,7 @@
|
||||
* 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.
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
@ -38,6 +36,7 @@ const BoxPointer = imports.ui.boxpointer;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const GdmUtil = imports.gdm.util;
|
||||
const Layout = imports.ui.layout;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Realmd = imports.gdm.realmd;
|
||||
@ -49,8 +48,6 @@ const _SCROLL_ANIMATION_TIME = 0.5;
|
||||
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
|
||||
const _LOGO_ICON_HEIGHT = 48;
|
||||
|
||||
let _loginDialog = null;
|
||||
|
||||
const UserListItem = new Lang.Class({
|
||||
Name: 'UserListItem',
|
||||
|
||||
@ -59,7 +56,7 @@ const UserListItem = new Lang.Class({
|
||||
this._userChangedId = this.user.connect('changed',
|
||||
Lang.bind(this, this._onUserChanged));
|
||||
|
||||
let layout = new St.BoxLayout({ vertical: false });
|
||||
let layout = new St.BoxLayout({ vertical: true });
|
||||
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
can_focus: true,
|
||||
@ -67,40 +64,21 @@ const UserListItem = new Lang.Class({
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
this.actor.connect('destroy',
|
||||
Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._userAvatar = new UserWidget.Avatar(this.user,
|
||||
{ styleClass: 'login-dialog-user-list-item-icon' });
|
||||
layout.add(this._userAvatar.actor);
|
||||
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
|
||||
vertical: true });
|
||||
layout.add(textLayout, { expand: true });
|
||||
|
||||
this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' });
|
||||
this.actor.label_actor = this._nameLabel;
|
||||
textLayout.add(this._nameLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.MIDDLE,
|
||||
expand: true });
|
||||
this._userWidget = new UserWidget.UserWidget(this.user);
|
||||
layout.add(this._userWidget.actor);
|
||||
|
||||
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
|
||||
scale_x: 0 });
|
||||
textLayout.add(this._timedLoginIndicator,
|
||||
{ x_fill: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_fill: false,
|
||||
y_align: St.Align.END });
|
||||
layout.add(this._timedLoginIndicator);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
this._onUserChanged();
|
||||
},
|
||||
|
||||
_onUserChanged: function() {
|
||||
this._nameLabel.set_text(this.user.get_real_name());
|
||||
this._userAvatar.update();
|
||||
this._updateLoggedIn();
|
||||
},
|
||||
|
||||
syncStyleClasses: function() {
|
||||
this._updateLoggedIn();
|
||||
},
|
||||
|
||||
@ -111,6 +89,10 @@ const UserListItem = new Lang.Class({
|
||||
this.actor.remove_style_pseudo_class('logged-in');
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this._user.disconnect(this._userChangedId);
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
this.emit('activate');
|
||||
},
|
||||
@ -189,7 +171,6 @@ const UserList = new Lang.Class({
|
||||
for (let userName in this._items) {
|
||||
let item = this._items[userName];
|
||||
item.actor.sync_hover();
|
||||
item.syncStyleClasses();
|
||||
}
|
||||
},
|
||||
|
||||
@ -398,16 +379,15 @@ const LoginDialog = new Lang.Class({
|
||||
if (GLib.getenv('GDM_GREETER_TEST') != '1') {
|
||||
this._greeter = gdmClient.get_greeter_sync(null);
|
||||
|
||||
this._greeter.connect('default-session-name-changed',
|
||||
Lang.bind(this, this._onDefaultSessionChanged));
|
||||
|
||||
this._greeter.connect('session-opened',
|
||||
Lang.bind(this, this._onSessionOpened));
|
||||
this._greeter.connect('timed-login-requested',
|
||||
Lang.bind(this, this._onTimedLoginRequested));
|
||||
this._defaultSessionChangedId = this._greeter.connect('default-session-name-changed',
|
||||
Lang.bind(this, this._onDefaultSessionChanged));
|
||||
this._sessionOpenedId = this._greeter.connect('session-opened',
|
||||
Lang.bind(this, this._onSessionOpened));
|
||||
this._timedLoginRequestedId = this._greeter.connect('timed-login-requested',
|
||||
Lang.bind(this, this._onTimedLoginRequested));
|
||||
}
|
||||
|
||||
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||
this._settings = new Gio.Settings({ schema_id: GdmUtil.LOGIN_SCREEN_SCHEMA });
|
||||
|
||||
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
|
||||
Lang.bind(this, this._updateBanner));
|
||||
@ -419,8 +399,8 @@ const LoginDialog = new Lang.Class({
|
||||
Lang.bind(this, this._updateLogo));
|
||||
|
||||
this._textureCache = St.TextureCache.get_default();
|
||||
this._textureCache.connect('texture-file-changed',
|
||||
Lang.bind(this, this._updateLogoTexture));
|
||||
this._updateLogoTextureId = this._textureCache.connect('texture-file-changed',
|
||||
Lang.bind(this, this._updateLogoTexture));
|
||||
|
||||
this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
@ -478,18 +458,6 @@ const LoginDialog = new Lang.Class({
|
||||
this.actor.add_child(this._logoBin);
|
||||
this._updateLogo();
|
||||
|
||||
if (!this._userManager.is_loaded)
|
||||
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
|
||||
Lang.bind(this, function() {
|
||||
if (this._userManager.is_loaded) {
|
||||
this._loadUserList();
|
||||
this._userManager.disconnect(this._userManagerLoadedId);
|
||||
this._userManagerLoadedId = 0;
|
||||
}
|
||||
}));
|
||||
else
|
||||
this._loadUserList();
|
||||
|
||||
this._userList.connect('activate',
|
||||
Lang.bind(this, function(userList, item) {
|
||||
this._onUserListActivated(item);
|
||||
@ -505,7 +473,33 @@ const LoginDialog = new Lang.Class({
|
||||
this._sessionMenuButton.actor.show();
|
||||
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
|
||||
|
||||
},
|
||||
this._disableUserList = undefined;
|
||||
this._userListLoaded = false;
|
||||
|
||||
LoginManager.getLoginManager().getCurrentSessionProxy(Lang.bind(this, this._gotGreeterSessionProxy));
|
||||
|
||||
// If the user list is enabled, it should take key focus; make sure the
|
||||
// screen shield is initialized first to prevent it from stealing the
|
||||
// focus later
|
||||
this._startupCompleteId = Main.layoutManager.connect('startup-complete',
|
||||
Lang.bind(this, this._updateDisableUserList));
|
||||
},
|
||||
|
||||
_ensureUserListLoaded: function() {
|
||||
if (!this._userManager.is_loaded) {
|
||||
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
|
||||
Lang.bind(this, function() {
|
||||
if (this._userManager.is_loaded) {
|
||||
this._loadUserList();
|
||||
this._userManager.disconnect(this._userManagerLoadedId);
|
||||
this._userManagerLoadedId = 0;
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._loadUserList));
|
||||
GLib.Source.set_name_by_id(id, '[gnome-shell] _loadUserList');
|
||||
}
|
||||
},
|
||||
|
||||
_updateDisableUserList: function() {
|
||||
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
|
||||
@ -543,21 +537,24 @@ const LoginDialog = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_updateLogoTexture: function(cache, uri) {
|
||||
if (this._logoFileUri != uri)
|
||||
_updateLogoTexture: function(cache, file) {
|
||||
if (this._logoFile && !this._logoFile.equal(file))
|
||||
return;
|
||||
|
||||
this._logoBin.destroy_all_children();
|
||||
if (this._logoFileUri)
|
||||
this._logoBin.add_child(this._textureCache.load_uri_async(this._logoFileUri,
|
||||
-1, _LOGO_ICON_HEIGHT));
|
||||
if (this._logoFile) {
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
this._logoBin.add_child(this._textureCache.load_file_async(this._logoFile,
|
||||
-1, _LOGO_ICON_HEIGHT,
|
||||
scaleFactor));
|
||||
}
|
||||
},
|
||||
|
||||
_updateLogo: function() {
|
||||
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
|
||||
|
||||
this._logoFileUri = path ? Gio.file_new_for_path(path).get_uri() : null;
|
||||
this._updateLogoTexture(this._textureCache, this._logoFileUri);
|
||||
this._logoFile = path ? Gio.file_new_for_path(path) : null;
|
||||
this._updateLogoTexture(this._textureCache, this._logoFile);
|
||||
},
|
||||
|
||||
_onPrompted: function() {
|
||||
@ -649,6 +646,38 @@ const LoginDialog = new Lang.Class({
|
||||
this._showPrompt();
|
||||
},
|
||||
|
||||
_loginScreenSessionActivated: function() {
|
||||
if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_SUCCEEDED)
|
||||
return;
|
||||
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: _FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onUpdate: function() {
|
||||
let children = Main.layoutManager.uiGroup.get_children();
|
||||
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (children[i] != Main.layoutManager.screenShieldGroup)
|
||||
children[i].opacity = this.actor.opacity;
|
||||
}
|
||||
},
|
||||
onUpdateScope: this,
|
||||
onComplete: function() {
|
||||
this._authPrompt.reset();
|
||||
},
|
||||
onCompleteScope: this });
|
||||
},
|
||||
|
||||
_gotGreeterSessionProxy: function(proxy) {
|
||||
this._greeterSessionProxy = proxy;
|
||||
this._greeterSessionProxyChangedId =
|
||||
proxy.connect('g-properties-changed', Lang.bind(this, function() {
|
||||
if (proxy.Active)
|
||||
this._loginScreenSessionActivated();
|
||||
}));
|
||||
},
|
||||
|
||||
_startSession: function(serviceName) {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
@ -664,10 +693,11 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
onUpdateScope: this,
|
||||
onComplete: function() {
|
||||
Mainloop.idle_add(Lang.bind(this, function() {
|
||||
let id = Mainloop.idle_add(Lang.bind(this, function() {
|
||||
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(id, '[gnome-shell] this._greeter.call_start_session_when_ready_sync');
|
||||
},
|
||||
onCompleteScope: this });
|
||||
},
|
||||
@ -721,7 +751,9 @@ const LoginDialog = new Lang.Class({
|
||||
function() {
|
||||
this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
|
||||
hold.release();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
GLib.Source.set_name_by_id(this._timedLoginIdleTimeOutId, '[gnome-shell] this._timedLoginAnimationTime');
|
||||
return hold;
|
||||
},
|
||||
|
||||
@ -785,7 +817,7 @@ const LoginDialog = new Lang.Class({
|
||||
global.stage.connect('captured-event',
|
||||
Lang.bind(this, function(actor, event) {
|
||||
if (this._timedLoginDelay == undefined)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (event.type() == Clutter.EventType.KEY_PRESS ||
|
||||
event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||
@ -798,7 +830,7 @@ const LoginDialog = new Lang.Class({
|
||||
this._resetTimedLogin();
|
||||
}
|
||||
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
},
|
||||
|
||||
@ -824,6 +856,7 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_showUserList: function() {
|
||||
this._ensureUserListLoaded();
|
||||
this._authPrompt.hide();
|
||||
this._sessionMenuButton.close();
|
||||
this._setUserListExpanded(true);
|
||||
@ -864,26 +897,55 @@ const LoginDialog = new Lang.Class({
|
||||
this._userManager.disconnect(this._userManagerLoadedId);
|
||||
this._userManagerLoadedId = 0;
|
||||
}
|
||||
if (this._userAddedId) {
|
||||
this._userManager.disconnect(this._userAddedId);
|
||||
this._userAddedId = 0;
|
||||
}
|
||||
if (this._userRemovedId) {
|
||||
this._userManager.disconnect(this._userRemovedId);
|
||||
this._userRemovedId = 0;
|
||||
}
|
||||
this._textureCache.disconnect(this._updateLogoTextureId);
|
||||
Main.layoutManager.disconnect(this._startupCompleteId);
|
||||
if (this._settings) {
|
||||
this._settings.run_dispose();
|
||||
this._settings = null;
|
||||
}
|
||||
if (this._greeter) {
|
||||
this._greeter.disconnect(this._defaultSessionChangedId);
|
||||
this._greeter.disconnect(this._sessionOpenedId);
|
||||
this._greeter.disconnect(this._timedLoginRequestedId);
|
||||
this._greeter = null;
|
||||
}
|
||||
if (this._greeterSessionProxy) {
|
||||
this._greeterSessionProxy.disconnect(this._greeterSessionProxyChangedId);
|
||||
this._greeterSessionProxy = null;
|
||||
}
|
||||
},
|
||||
|
||||
_loadUserList: function() {
|
||||
if (this._userListLoaded)
|
||||
return GLib.SOURCE_REMOVE;
|
||||
|
||||
this._userListLoaded = true;
|
||||
|
||||
let users = this._userManager.list_users();
|
||||
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
this._userList.addUser(users[i]);
|
||||
}
|
||||
|
||||
this._updateDisableUserList();
|
||||
this._userAddedId = this._userManager.connect('user-added',
|
||||
Lang.bind(this, function(userManager, user) {
|
||||
this._userList.addUser(user);
|
||||
}));
|
||||
|
||||
this._userManager.connect('user-added',
|
||||
Lang.bind(this, function(userManager, user) {
|
||||
this._userList.addUser(user);
|
||||
}));
|
||||
this._userRemovedId = this._userManager.connect('user-removed',
|
||||
Lang.bind(this, function(userManager, user) {
|
||||
this._userList.removeUser(user);
|
||||
}));
|
||||
|
||||
this._userManager.connect('user-removed',
|
||||
Lang.bind(this, function(userManager, user) {
|
||||
this._userList.removeUser(user);
|
||||
}));
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
open: function() {
|
||||
@ -895,6 +957,8 @@ const LoginDialog = new Lang.Class({
|
||||
this.actor.show();
|
||||
this.actor.opacity = 0;
|
||||
|
||||
Main.pushModal(this.actor, { keybindingMode: Shell.KeyBindingMode.LOGIN_SCREEN });
|
||||
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: 1,
|
||||
@ -904,7 +968,12 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
close: function() {
|
||||
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
|
||||
Main.popModal(this.actor);
|
||||
Main.ctrlAltTabManager.removeGroup(this.actor);
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this._authPrompt.cancel();
|
||||
},
|
||||
|
||||
addCharacter: function(unichar) {
|
||||
|
@ -128,7 +128,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
|
||||
this._client = client;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
|
||||
this._settings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA });
|
||||
this._settings.connect('changed',
|
||||
Lang.bind(this, this._updateDefaultService));
|
||||
this._updateDefaultService();
|
||||
@ -142,10 +142,10 @@ const ShellUserVerifier = new Lang.Class({
|
||||
// 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._smartcardInsertedId = this._smartcardManager.connect('smartcard-inserted',
|
||||
Lang.bind(this, this._checkForSmartcard));
|
||||
this._smartcardRemovedId = this._smartcardManager.connect('smartcard-removed',
|
||||
Lang.bind(this, this._checkForSmartcard));
|
||||
|
||||
this._messageQueue = [];
|
||||
this._messageQueueTimeoutId = 0;
|
||||
@ -159,8 +159,8 @@ const ShellUserVerifier = new Lang.Class({
|
||||
if (this._oVirtCredentialsManager.hasToken())
|
||||
this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
|
||||
|
||||
this._oVirtCredentialsManager.connect('user-authenticated',
|
||||
Lang.bind(this, this._oVirtUserAuthenticated));
|
||||
this._oVirtUserAuthenticatedId = this._oVirtCredentialsManager.connect('user-authenticated',
|
||||
Lang.bind(this, this._oVirtUserAuthenticated));
|
||||
},
|
||||
|
||||
begin: function(userName, hold) {
|
||||
@ -191,20 +191,37 @@ const ShellUserVerifier = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_clearUserVerifier: function() {
|
||||
if (this._userVerifier) {
|
||||
this._userVerifier.run_dispose();
|
||||
this._userVerifier = null;
|
||||
}
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
if (this._cancellable) {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable = null;
|
||||
}
|
||||
|
||||
if (this._userVerifier) {
|
||||
this._userVerifier.run_dispose();
|
||||
this._userVerifier = null;
|
||||
}
|
||||
|
||||
this._clearUserVerifier();
|
||||
this._clearMessageQueue();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.clear();
|
||||
|
||||
this._settings.run_dispose();
|
||||
this._settings = null;
|
||||
|
||||
this._smartcardManager.disconnect(this._smartcardInsertedId);
|
||||
this._smartcardManager.disconnect(this._smartcardRemovedId);
|
||||
this._smartcardManager = null;
|
||||
|
||||
this._oVirtCredentialsManager.disconnect(this._oVirtUserAuthenticatedId);
|
||||
this._oVirtCredentialsManager = null;
|
||||
},
|
||||
|
||||
answerQuery: function(serviceName, answer) {
|
||||
if (!this.hasPendingMessages) {
|
||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||
@ -250,7 +267,9 @@ const ShellUserVerifier = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this._messageQueueTimeoutId = 0;
|
||||
this._queueMessageTimeout();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(this._messageQueueTimeoutId, '[gnome-shell] this._queueMessageTimeout');
|
||||
},
|
||||
|
||||
_queueMessage: function(message, messageType) {
|
||||
@ -281,9 +300,10 @@ const ShellUserVerifier = new Lang.Class({
|
||||
|
||||
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
|
||||
function(device, error) {
|
||||
if (!error && device)
|
||||
if (!error && device) {
|
||||
this._haveFingerprintReader = true;
|
||||
this._updateDefaultService();
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
@ -297,7 +317,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
|
||||
if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
|
||||
smartcardDetected = false;
|
||||
else if (this.reauthenticating)
|
||||
else if (this._reauthOnly)
|
||||
smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
|
||||
else
|
||||
smartcardDetected = this._smartcardManager.hasInsertedTokens();
|
||||
@ -324,6 +344,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
|
||||
_reauthenticationChannelOpened: function(client, result) {
|
||||
try {
|
||||
this._clearUserVerifier();
|
||||
this._userVerifier = client.open_reauthentication_channel_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
@ -347,6 +368,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
|
||||
_userVerifierGot: function(client, result) {
|
||||
try {
|
||||
this._clearUserVerifier();
|
||||
this._userVerifier = client.get_user_verifier_finish(result);
|
||||
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
||||
return;
|
||||
|
121
js/js-resources.gresource.xml
Normal file
121
js/js-resources.gresource.xml
Normal file
@ -0,0 +1,121 @@
|
||||
<?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/ibusManager.js</file>
|
||||
<file>misc/jsParse.js</file>
|
||||
<file>misc/keyboardManager.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>perf/hwtest.js</file>
|
||||
|
||||
<file>portalHelper/main.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/edgeDragAction.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/windowMenu.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>
|
@ -6,6 +6,8 @@ const PACKAGE_NAME = '@PACKAGE_NAME@';
|
||||
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
|
||||
/* 1 if gnome-bluetooth is available, 0 otherwise */
|
||||
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
|
||||
/* 1 if networkmanager is available, 0 otherwise */
|
||||
const HAVE_NETWORKMANAGER = @HAVE_NETWORKMANAGER@;
|
||||
/* gettext package */
|
||||
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
|
||||
/* locale dir */
|
||||
|
@ -174,17 +174,9 @@ const ExtensionFinder = new Lang.Class({
|
||||
this.emit('extension-found', extension);
|
||||
},
|
||||
|
||||
_extensionsLoaded: function() {
|
||||
this.emit('extensions-loaded');
|
||||
},
|
||||
|
||||
scanExtensions: function() {
|
||||
let perUserDir = Gio.File.new_for_path(global.userdatadir);
|
||||
FileUtils.collectFromDatadirsAsync('extensions',
|
||||
{ processFile: Lang.bind(this, this._loadExtension),
|
||||
loadedCallback: Lang.bind(this, this._extensionsLoaded),
|
||||
includeUserDir: true,
|
||||
data: perUserDir });
|
||||
FileUtils.collectFromDatadirs('extensions', true, Lang.bind(this, this._loadExtension, perUserDir));
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ExtensionFinder.prototype);
|
||||
|
@ -5,80 +5,27 @@ const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
function listDirAsync(file, callback) {
|
||||
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;
|
||||
}
|
||||
|
||||
function collectFromDatadirs(subdir, includeUserDir, processFile) {
|
||||
let dataDirs = GLib.get_system_data_dirs();
|
||||
if (params.includeUserDir)
|
||||
if (includeUserDir)
|
||||
dataDirs.unshift(GLib.get_user_data_dir());
|
||||
loadState.numLoading = dataDirs.length;
|
||||
|
||||
for (let i = 0; i < dataDirs.length; i++) {
|
||||
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
144
js/misc/hash.js
144
js/misc/hash.js
@ -1,144 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const System = imports.system;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
|
||||
// This is an implementation of EcmaScript SameValue algorithm,
|
||||
// which returns true if two values are not observably distinguishable.
|
||||
// It was taken from http://wiki.ecmascript.org/doku.php?id=harmony:egal
|
||||
//
|
||||
// In the future, we may want to use the 'is' operator instead.
|
||||
function _sameValue(x, y) {
|
||||
if (x === y) {
|
||||
// 0 === -0, but they are not identical
|
||||
return x !== 0 || 1 / x === 1 / y;
|
||||
}
|
||||
|
||||
// NaN !== NaN, but they are identical.
|
||||
// NaNs are the only non-reflexive value, i.e., if x !== x,
|
||||
// then x is a NaN.
|
||||
// isNaN is broken: it converts its argument to number, so
|
||||
// isNaN("foo") => true
|
||||
return x !== x && y !== y;
|
||||
}
|
||||
|
||||
const _hashers = {
|
||||
object: function(o) { return o ? System.addressOf(o) : 'null'; },
|
||||
function: function(f) { return System.addressOf(f); },
|
||||
string: function(s) { return s; },
|
||||
number: function(n) { return String(n); },
|
||||
undefined: function() { return 'undefined'; },
|
||||
};
|
||||
|
||||
/* Map is meant to be similar in usage to ES6 Map, which is
|
||||
described at http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets,
|
||||
without requiring more than ES5 + Gjs extensions.
|
||||
|
||||
Known differences from other implementations:
|
||||
Polyfills around the web usually implement HashMaps for
|
||||
primitive values and reversed WeakMaps for object keys,
|
||||
but we want real maps with real O(1) semantics in all cases,
|
||||
and the easiest way is to have different hashers for different
|
||||
types.
|
||||
|
||||
Known differences from the ES6 specification:
|
||||
- Map is a Lang.Class, not a ES6 class, so inheritance,
|
||||
prototype, sealing, etc. work differently.
|
||||
- items(), keys() and values() don't return iterators,
|
||||
they return actual arrays, so they incur a full copy everytime
|
||||
they're called, and they don't see changes if you mutate
|
||||
the table while iterating
|
||||
(admittedly, the ES6 spec is a bit unclear on this, and
|
||||
the reference code would just blow up)
|
||||
*/
|
||||
const Map = new Lang.Class({
|
||||
Name: 'Map',
|
||||
|
||||
_init: function(iterable) {
|
||||
this._pool = { };
|
||||
this._size = 0;
|
||||
|
||||
if (iterable) {
|
||||
for (let i = 0; i < iterable.length; i++) {
|
||||
let [key, value] = iterable[i];
|
||||
this.set(key, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_hashKey: function(key) {
|
||||
let type = typeof(key);
|
||||
return type + ':' + _hashers[type](key);
|
||||
},
|
||||
|
||||
_internalGet: function(key) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node && _sameValue(node.key, key))
|
||||
return [true, node.value];
|
||||
else
|
||||
return [false, null];
|
||||
},
|
||||
|
||||
get: function(key) {
|
||||
return this._internalGet(key)[1];
|
||||
},
|
||||
|
||||
has: function(key) {
|
||||
return this._internalGet(key)[0];
|
||||
},
|
||||
|
||||
set: function(key, value) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node) {
|
||||
node.key = key;
|
||||
node.value = value;
|
||||
} else {
|
||||
this._pool[hash] = { key: key, value: value };
|
||||
this._size++;
|
||||
}
|
||||
},
|
||||
|
||||
delete: function(key) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node && _sameValue(node.key, key)) {
|
||||
delete this._pool[hash];
|
||||
this._size--;
|
||||
return [node.key, node.value];
|
||||
} else {
|
||||
return [null, null];
|
||||
}
|
||||
},
|
||||
|
||||
keys: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return pool[hash].key;
|
||||
});
|
||||
},
|
||||
|
||||
values: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return pool[hash].value;
|
||||
});
|
||||
},
|
||||
|
||||
items: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return [pool[hash].key, pool[hash].value];
|
||||
});
|
||||
},
|
||||
|
||||
size: function() {
|
||||
return this._size;
|
||||
},
|
||||
});
|
@ -89,7 +89,7 @@ const HistoryManager = new Lang.Class({
|
||||
} else if (symbol == Clutter.KEY_Down) {
|
||||
return this._setNextItem(entry.get_text());
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_indexChanged: function() {
|
||||
|
180
js/misc/ibusManager.js
Normal file
180
js/misc/ibusManager.js
Normal file
@ -0,0 +1,180 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
|
||||
try {
|
||||
var IBus = imports.gi.IBus;
|
||||
if (!('new_async' in IBus.Bus))
|
||||
throw "IBus version is too old";
|
||||
const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
|
||||
} catch (e) {
|
||||
var IBus = null;
|
||||
log(e);
|
||||
}
|
||||
|
||||
let _ibusManager = null;
|
||||
|
||||
function getIBusManager() {
|
||||
if (_ibusManager == null)
|
||||
_ibusManager = new IBusManager();
|
||||
return _ibusManager;
|
||||
}
|
||||
|
||||
const IBusManager = new Lang.Class({
|
||||
Name: 'IBusManager',
|
||||
|
||||
// This is the longest we'll keep the keyboard frozen until an input
|
||||
// source is active.
|
||||
_MAX_INPUT_SOURCE_ACTIVATION_TIME: 4000, // ms
|
||||
|
||||
_init: function() {
|
||||
if (!IBus)
|
||||
return;
|
||||
|
||||
IBus.init();
|
||||
|
||||
this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
|
||||
|
||||
this._panelService = null;
|
||||
this._engines = {};
|
||||
this._ready = false;
|
||||
this._registerPropertiesId = 0;
|
||||
this._currentEngineName = null;
|
||||
|
||||
this._ibus = IBus.Bus.new_async();
|
||||
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
|
||||
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
|
||||
// Need to set this to get 'global-engine-changed' emitions
|
||||
this._ibus.set_watch_ibus_signal(true);
|
||||
this._ibus.connect('global-engine-changed', Lang.bind(this, this._engineChanged));
|
||||
|
||||
this._spawn();
|
||||
},
|
||||
|
||||
_spawn: function() {
|
||||
try {
|
||||
Gio.Subprocess.new(['ibus-daemon', '--xim', '--panel', 'disable'],
|
||||
Gio.SubprocessFlags.NONE);
|
||||
} catch(e) {
|
||||
log('Failed to launch ibus-daemon: ' + e.message);
|
||||
}
|
||||
},
|
||||
|
||||
_clear: function() {
|
||||
if (this._panelService)
|
||||
this._panelService.destroy();
|
||||
|
||||
this._panelService = null;
|
||||
this._candidatePopup.setPanelService(null);
|
||||
this._engines = {};
|
||||
this._ready = false;
|
||||
this._registerPropertiesId = 0;
|
||||
this._currentEngineName = null;
|
||||
|
||||
this.emit('ready', false);
|
||||
|
||||
this._spawn();
|
||||
},
|
||||
|
||||
_onConnected: function() {
|
||||
this._ibus.list_engines_async(-1, null, Lang.bind(this, this._initEngines));
|
||||
this._ibus.request_name_async(IBus.SERVICE_PANEL,
|
||||
IBus.BusNameFlag.REPLACE_EXISTING,
|
||||
-1, null,
|
||||
Lang.bind(this, this._initPanelService));
|
||||
},
|
||||
|
||||
_initEngines: function(ibus, result) {
|
||||
let enginesList = this._ibus.list_engines_async_finish(result);
|
||||
if (enginesList) {
|
||||
for (let i = 0; i < enginesList.length; ++i) {
|
||||
let name = enginesList[i].get_name();
|
||||
this._engines[name] = enginesList[i];
|
||||
}
|
||||
this._updateReadiness();
|
||||
} else {
|
||||
this._clear();
|
||||
}
|
||||
},
|
||||
|
||||
_initPanelService: function(ibus, result) {
|
||||
let success = this._ibus.request_name_async_finish(result);
|
||||
if (success) {
|
||||
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
|
||||
object_path: IBus.PATH_PANEL });
|
||||
this._candidatePopup.setPanelService(this._panelService);
|
||||
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
|
||||
// If an engine is already active we need to get its properties
|
||||
this._ibus.get_global_engine_async(-1, null, Lang.bind(this, function(i, result) {
|
||||
let engine;
|
||||
try {
|
||||
engine = this._ibus.get_global_engine_async_finish(result);
|
||||
if (!engine)
|
||||
return;
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
this._engineChanged(this._ibus, engine.get_name());
|
||||
}));
|
||||
this._updateReadiness();
|
||||
} else {
|
||||
this._clear();
|
||||
}
|
||||
},
|
||||
|
||||
_updateReadiness: function() {
|
||||
this._ready = (Object.keys(this._engines).length > 0 &&
|
||||
this._panelService != null);
|
||||
this.emit('ready', this._ready);
|
||||
},
|
||||
|
||||
_engineChanged: function(bus, engineName) {
|
||||
if (!this._ready)
|
||||
return;
|
||||
|
||||
this._currentEngineName = engineName;
|
||||
|
||||
if (this._registerPropertiesId != 0)
|
||||
return;
|
||||
|
||||
this._registerPropertiesId =
|
||||
this._panelService.connect('register-properties', Lang.bind(this, function(p, props) {
|
||||
if (!props.get(0))
|
||||
return;
|
||||
|
||||
this._panelService.disconnect(this._registerPropertiesId);
|
||||
this._registerPropertiesId = 0;
|
||||
|
||||
this.emit('properties-registered', this._currentEngineName, props);
|
||||
}));
|
||||
},
|
||||
|
||||
_updateProperty: function(panel, prop) {
|
||||
this.emit('property-updated', this._currentEngineName, prop);
|
||||
},
|
||||
|
||||
activateProperty: function(key, state) {
|
||||
this._panelService.property_activate(key, state);
|
||||
},
|
||||
|
||||
getEngineDesc: function(id) {
|
||||
if (!IBus || !this._ready)
|
||||
return null;
|
||||
|
||||
return this._engines[id];
|
||||
},
|
||||
|
||||
setEngine: function(id, callback) {
|
||||
if (!IBus || !this._ready || id == this._currentEngineName) {
|
||||
if (callback)
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
this._ibus.set_global_engine_async(id, this._MAX_INPUT_SOURCE_ACTIVATION_TIME,
|
||||
null, callback);
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(IBusManager.prototype);
|
153
js/misc/keyboardManager.js
Normal file
153
js/misc/keyboardManager.js
Normal file
@ -0,0 +1,153 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const DEFAULT_LOCALE = 'en_US';
|
||||
const DEFAULT_LAYOUT = 'us';
|
||||
const DEFAULT_VARIANT = '';
|
||||
|
||||
let _xkbInfo = null;
|
||||
|
||||
function getXkbInfo() {
|
||||
if (_xkbInfo == null)
|
||||
_xkbInfo = new GnomeDesktop.XkbInfo();
|
||||
return _xkbInfo;
|
||||
}
|
||||
|
||||
let _keyboardManager = null;
|
||||
|
||||
function getKeyboardManager() {
|
||||
if (_keyboardManager == null)
|
||||
_keyboardManager = new KeyboardManager();
|
||||
return _keyboardManager;
|
||||
}
|
||||
|
||||
function releaseKeyboard() {
|
||||
if (Main.modalCount > 0)
|
||||
global.display.unfreeze_keyboard(global.get_current_time());
|
||||
else
|
||||
global.display.ungrab_keyboard(global.get_current_time());
|
||||
}
|
||||
|
||||
function holdKeyboard() {
|
||||
global.display.freeze_keyboard(global.get_current_time());
|
||||
}
|
||||
|
||||
const KeyboardManager = new Lang.Class({
|
||||
Name: 'KeyboardManager',
|
||||
|
||||
// The XKB protocol doesn't allow for more that 4 layouts in a
|
||||
// keymap. Wayland doesn't impose this limit and libxkbcommon can
|
||||
// handle up to 32 layouts but since we need to support X clients
|
||||
// even as a Wayland compositor, we can't bump this.
|
||||
MAX_LAYOUTS_PER_GROUP: 4,
|
||||
|
||||
_init: function() {
|
||||
this._xkbInfo = getXkbInfo();
|
||||
this._current = null;
|
||||
this._localeLayoutInfo = this._getLocaleLayout();
|
||||
this._layoutInfos = {};
|
||||
},
|
||||
|
||||
_applyLayoutGroup: function(group) {
|
||||
let options = this._buildOptionsString();
|
||||
let [layouts, variants] = this._buildGroupStrings(group);
|
||||
Meta.get_backend().set_keymap(layouts, variants, options);
|
||||
},
|
||||
|
||||
_applyLayoutGroupIndex: function(idx) {
|
||||
Meta.get_backend().lock_layout_group(idx);
|
||||
},
|
||||
|
||||
apply: function(id) {
|
||||
let info = this._layoutInfos[id];
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
if (this._current && this._current.group == info.group) {
|
||||
if (this._current.groupIndex != info.groupIndex)
|
||||
this._applyLayoutGroupIndex(info.groupIndex);
|
||||
} else {
|
||||
this._applyLayoutGroup(info.group);
|
||||
this._applyLayoutGroupIndex(info.groupIndex);
|
||||
}
|
||||
|
||||
this._current = info;
|
||||
},
|
||||
|
||||
reapply: function() {
|
||||
if (!this._current)
|
||||
return;
|
||||
|
||||
this._applyLayoutGroup(this._current.group);
|
||||
this._applyLayoutGroupIndex(this._current.groupIndex);
|
||||
},
|
||||
|
||||
setUserLayouts: function(ids) {
|
||||
this._current = null;
|
||||
this._layoutInfos = {};
|
||||
|
||||
for (let i = 0; i < ids.length; ++i) {
|
||||
let [found, , , _layout, _variant] = this._xkbInfo.get_layout_info(ids[i]);
|
||||
if (found)
|
||||
this._layoutInfos[ids[i]] = { id: ids[i], layout: _layout, variant: _variant };
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
let group = [];
|
||||
for (let id in this._layoutInfos) {
|
||||
// We need to leave one slot on each group free so that we
|
||||
// can add a layout containing the symbols for the
|
||||
// language used in UI strings to ensure that toolkits can
|
||||
// handle mnemonics like Alt+Ф even if the user is
|
||||
// actually typing in a different layout.
|
||||
let groupIndex = i % (this.MAX_LAYOUTS_PER_GROUP - 1);
|
||||
if (groupIndex == 0)
|
||||
group = [];
|
||||
|
||||
let info = this._layoutInfos[id];
|
||||
group[groupIndex] = info;
|
||||
info.group = group;
|
||||
info.groupIndex = groupIndex;
|
||||
|
||||
i += 1;
|
||||
}
|
||||
},
|
||||
|
||||
_getLocaleLayout: function() {
|
||||
let locale = GLib.get_language_names()[0];
|
||||
if (locale.indexOf('_') == -1)
|
||||
locale = DEFAULT_LOCALE;
|
||||
|
||||
let [found, , id] = GnomeDesktop.get_input_source_from_locale(locale);
|
||||
if (!found)
|
||||
[, , id] = GnomeDesktop.get_input_source_from_locale(DEFAULT_LOCALE);
|
||||
|
||||
let [found, , , _layout, _variant] = this._xkbInfo.get_layout_info(id);
|
||||
if (found)
|
||||
return { layout: _layout, variant: _variant };
|
||||
else
|
||||
return { layout: DEFAULT_LAYOUT, variant: DEFAULT_VARIANT };
|
||||
},
|
||||
|
||||
_buildGroupStrings: function(_group) {
|
||||
let group = _group.concat(this._localeLayoutInfo);
|
||||
let layouts = group.map(function(g) { return g.layout; }).join(',');
|
||||
let variants = group.map(function(g) { return g.variant; }).join(',');
|
||||
return [layouts, variants];
|
||||
},
|
||||
|
||||
setKeyboardOptions: function(options) {
|
||||
this._xkbOptions = options;
|
||||
},
|
||||
|
||||
_buildOptionsString: function() {
|
||||
let options = this._xkbOptions.join(',');
|
||||
return options;
|
||||
}
|
||||
});
|
@ -39,38 +39,13 @@ const SystemdLoginSessionIface = '<node> \
|
||||
<interface name="org.freedesktop.login1.Session"> \
|
||||
<signal name="Lock" /> \
|
||||
<signal name="Unlock" /> \
|
||||
<property name="Active" type="b" access="read" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
|
||||
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
||||
|
||||
const ConsoleKitManagerIface = '<node> \
|
||||
<interface name="org.freedesktop.ConsoleKit.Manager"> \
|
||||
<method name="CanRestart"> \
|
||||
<arg type="b" direction="out"/> \
|
||||
</method> \
|
||||
<method name="CanStop"> \
|
||||
<arg type="b" direction="out"/> \
|
||||
</method> \
|
||||
<method name="Restart" /> \
|
||||
<method name="Stop" /> \
|
||||
<method name="GetCurrentSession"> \
|
||||
<arg type="o" direction="out" /> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ConsoleKitSessionIface = '<node> \
|
||||
<interface name="org.freedesktop.ConsoleKit.Session"> \
|
||||
<signal name="Lock" /> \
|
||||
<signal name="Unlock" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
|
||||
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
|
||||
|
||||
function haveSystemd() {
|
||||
return GLib.access("/run/systemd/seats", 0) >= 0;
|
||||
}
|
||||
@ -100,7 +75,7 @@ function canLock() {
|
||||
-1, null);
|
||||
|
||||
let version = result.deep_unpack()[0].deep_unpack();
|
||||
return versionCompare('3.5.91', version);
|
||||
return haveSystemd() && versionCompare('3.5.91', version);
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
@ -118,7 +93,7 @@ function getLoginManager() {
|
||||
if (haveSystemd())
|
||||
_loginManager = new LoginManagerSystemd();
|
||||
else
|
||||
_loginManager = new LoginManagerConsoleKit();
|
||||
_loginManager = new LoginManagerDummy();
|
||||
}
|
||||
|
||||
return _loginManager;
|
||||
@ -135,9 +110,6 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
Lang.bind(this, this._prepareForSleep));
|
||||
},
|
||||
|
||||
// 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
|
||||
// Lock/Unlock signals, and that's all we count upon at the moment.
|
||||
getCurrentSessionProxy: function(callback) {
|
||||
if (this._currentSession) {
|
||||
callback (this._currentSession);
|
||||
@ -190,7 +162,7 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
let fd = -1;
|
||||
try {
|
||||
let [outVariant, fdList] = proxy.call_with_unix_fd_list_finish(result);
|
||||
fd = fdList.steal_fds(outVariant.deep_unpack())[0];
|
||||
fd = fdList.steal_fds()[0];
|
||||
callback(new Gio.UnixInputStream({ fd: fd }));
|
||||
} catch(e) {
|
||||
logError(e, "Error getting systemd inhibitor");
|
||||
@ -205,35 +177,13 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
});
|
||||
Signals.addSignalMethods(LoginManagerSystemd.prototype);
|
||||
|
||||
const LoginManagerConsoleKit = new Lang.Class({
|
||||
Name: 'LoginManagerConsoleKit',
|
||||
const LoginManagerDummy = new Lang.Class({
|
||||
Name: 'LoginManagerDummy',
|
||||
|
||||
_init: function() {
|
||||
this._proxy = new ConsoleKitManager(Gio.DBus.system,
|
||||
'org.freedesktop.ConsoleKit',
|
||||
'/org/freedesktop/ConsoleKit/Manager');
|
||||
},
|
||||
|
||||
// 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
|
||||
// Lock/Unlock signals, and that's all we count upon at the moment.
|
||||
getCurrentSessionProxy: function(callback) {
|
||||
if (this._currentSession) {
|
||||
callback (this._currentSession);
|
||||
return;
|
||||
}
|
||||
|
||||
this._proxy.GetCurrentSessionRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (error) {
|
||||
logError(error, 'Could not get a proxy for the current session');
|
||||
} else {
|
||||
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
|
||||
'org.freedesktop.ConsoleKit',
|
||||
result[0]);
|
||||
callback(this._currentSession);
|
||||
}
|
||||
}));
|
||||
// we could return a DummySession object that fakes whatever callers
|
||||
// expect (at the time of writing: connect() and connectSignal()
|
||||
// methods), but just never calling the callback should be safer
|
||||
},
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
@ -253,4 +203,4 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);
|
||||
Signals.addSignalMethods(LoginManagerDummy.prototype);
|
||||
|
@ -89,7 +89,7 @@ function spawnApp(argv) {
|
||||
let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
|
||||
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
|
||||
|
||||
let context = global.create_app_launch_context();
|
||||
let context = global.create_app_launch_context(0, -1);
|
||||
app.launch([], context);
|
||||
} catch(err) {
|
||||
_handleSpawnError(argv[0], err);
|
||||
@ -129,7 +129,7 @@ function trySpawn(argv)
|
||||
// Dummy child watch; we don't want to double-fork internally
|
||||
// because then we lose the parent-child relationship, which
|
||||
// can break polkit. See https://bugzilla.redhat.com//show_bug.cgi?id=819275
|
||||
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {}, null);
|
||||
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {});
|
||||
}
|
||||
|
||||
// trySpawnCommandLine:
|
||||
@ -153,7 +153,7 @@ function trySpawnCommandLine(command_line) {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,9 @@ function run() {
|
||||
Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view");
|
||||
Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view");
|
||||
|
||||
// Enable recording of timestamps for different points in the frame cycle
|
||||
global.frame_timestamps = true;
|
||||
|
||||
Main.overview.connect('shown', function() {
|
||||
Scripting.scriptEvent('overviewShowDone');
|
||||
});
|
||||
@ -87,7 +90,10 @@ function run() {
|
||||
yield Scripting.destroyTestWindows();
|
||||
|
||||
for (let k = 0; k < config.count; k++)
|
||||
yield Scripting.createTestWindow(config.width, config.height, config.alpha, config.maximized);
|
||||
yield Scripting.createTestWindow({ width: config.width,
|
||||
height: config.height,
|
||||
alpha: config.alpha,
|
||||
maximized: config.maximized });
|
||||
|
||||
yield Scripting.waitTestWindows();
|
||||
yield Scripting.sleep(1000);
|
||||
|
308
js/perf/hwtest.js
Normal file
308
js/perf/hwtest.js
Normal file
@ -0,0 +1,308 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Main = imports.ui.main;
|
||||
const Scripting = imports.ui.scripting;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
let METRICS = {
|
||||
timeToDesktop:
|
||||
{ description: "Time from starting graphical.target to desktop showing",
|
||||
units: "us" },
|
||||
|
||||
overviewShowTime:
|
||||
{ description: "Time to switch to overview view, first time",
|
||||
units: "us" },
|
||||
|
||||
applicationsShowTime:
|
||||
{ description: "Time to switch to applications view, first time",
|
||||
units: "us" },
|
||||
|
||||
mainViewRedrawTime:
|
||||
{ description: "Time to redraw the main view, full screen",
|
||||
units: "us" },
|
||||
|
||||
overviewRedrawTime:
|
||||
{ description: "Time to redraw the overview, full screen, 5 windows",
|
||||
units: "us" },
|
||||
|
||||
applicationRedrawTime:
|
||||
{ description: "Time to redraw frame with a maximized application update",
|
||||
units: "us" },
|
||||
|
||||
geditStartTime:
|
||||
{ description: "Time from gedit launch to window drawn",
|
||||
units: "us" },
|
||||
}
|
||||
|
||||
function waitAndDraw(milliseconds) {
|
||||
let cb;
|
||||
|
||||
let timeline = new Clutter.Timeline({ duration: milliseconds });
|
||||
timeline.start();
|
||||
|
||||
timeline.connect('new-frame',
|
||||
function(timeline, frame) {
|
||||
global.stage.queue_redraw();
|
||||
});
|
||||
|
||||
timeline.connect('completed',
|
||||
function() {
|
||||
timeline.stop();
|
||||
if (cb)
|
||||
cb();
|
||||
});
|
||||
|
||||
return function(callback) {
|
||||
cb = callback;
|
||||
};
|
||||
}
|
||||
|
||||
function waitSignal(object, signal) {
|
||||
let cb;
|
||||
|
||||
let id = object.connect(signal, function() {
|
||||
object.disconnect(id);
|
||||
if (cb)
|
||||
cb();
|
||||
});
|
||||
|
||||
return function(callback) {
|
||||
cb = callback;
|
||||
};
|
||||
}
|
||||
|
||||
function extractBootTimestamp() {
|
||||
let sp = Gio.Subprocess.new(['journalctl', '-b',
|
||||
'MESSAGE_ID=7d4958e842da4a758f6c1cdc7b36dcc5',
|
||||
'UNIT=graphical.target',
|
||||
'-o',
|
||||
'json'],
|
||||
Gio.SubprocessFlags.STDOUT_PIPE);
|
||||
let result = null;
|
||||
|
||||
let datastream = Gio.DataInputStream.new(sp.get_stdout_pipe());
|
||||
while (true) {
|
||||
let [line, length] = datastream.read_line_utf8(null);
|
||||
if (line === null)
|
||||
break;
|
||||
|
||||
let fields = JSON.parse(line);
|
||||
result = Number(fields['__MONOTONIC_TIMESTAMP']);
|
||||
}
|
||||
datastream.close(null);
|
||||
return result;
|
||||
}
|
||||
|
||||
function run() {
|
||||
Scripting.defineScriptEvent("desktopShown", "Finished initial animation");
|
||||
Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview");
|
||||
Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing");
|
||||
Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view");
|
||||
Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view");
|
||||
Scripting.defineScriptEvent("mainViewDrawStart", "Drawing main view");
|
||||
Scripting.defineScriptEvent("mainViewDrawDone", "Ending timing main view drawing");
|
||||
Scripting.defineScriptEvent("overviewDrawStart", "Drawing overview");
|
||||
Scripting.defineScriptEvent("overviewDrawDone", "Ending timing overview drawing");
|
||||
Scripting.defineScriptEvent("redrawTestStart", "Drawing application window");
|
||||
Scripting.defineScriptEvent("redrawTestDone", "Ending timing application window drawing");
|
||||
Scripting.defineScriptEvent("collectTimings", "Accumulate frame timings from redraw tests");
|
||||
Scripting.defineScriptEvent("geditLaunch", "gedit application launch");
|
||||
Scripting.defineScriptEvent("geditFirstFrame", "first frame of gedit window drawn");
|
||||
|
||||
yield Scripting.waitLeisure();
|
||||
Scripting.scriptEvent('desktopShown');
|
||||
|
||||
Gtk.Settings.get_default().gtk_enable_animations = false;
|
||||
|
||||
Scripting.scriptEvent('overviewShowStart');
|
||||
Main.overview.show();
|
||||
yield Scripting.waitLeisure();
|
||||
Scripting.scriptEvent('overviewShowDone');
|
||||
|
||||
yield Scripting.sleep(1000);
|
||||
|
||||
Scripting.scriptEvent('applicationsShowStart');
|
||||
Main.overview._dash.showAppsButton.checked = true;
|
||||
|
||||
yield Scripting.waitLeisure();
|
||||
Scripting.scriptEvent('applicationsShowDone');
|
||||
|
||||
yield Scripting.sleep(1000);
|
||||
|
||||
Main.overview.hide();
|
||||
yield Scripting.waitLeisure();
|
||||
|
||||
////////////////////////////////////////
|
||||
// Tests of redraw speed
|
||||
////////////////////////////////////////
|
||||
|
||||
global.frame_timestamps = true;
|
||||
global.frame_finish_timestamp = true;
|
||||
|
||||
for (let k = 0; k < 5; k++)
|
||||
yield Scripting.createTestWindow(640, 480,
|
||||
{ maximized: true });
|
||||
yield Scripting.waitTestWindows();
|
||||
|
||||
yield Scripting.sleep(1000);
|
||||
|
||||
Scripting.scriptEvent('mainViewDrawStart');
|
||||
yield waitAndDraw(1000);
|
||||
Scripting.scriptEvent('mainViewDrawDone');
|
||||
|
||||
Main.overview.show();
|
||||
Scripting.waitLeisure();
|
||||
|
||||
yield Scripting.sleep(1500);
|
||||
|
||||
Scripting.scriptEvent('overviewDrawStart');
|
||||
yield waitAndDraw(1000);
|
||||
Scripting.scriptEvent('overviewDrawDone');
|
||||
|
||||
yield Scripting.destroyTestWindows();
|
||||
Main.overview.hide();
|
||||
|
||||
yield Scripting.createTestWindow(640, 480,
|
||||
{ maximized: true,
|
||||
redraws: true});
|
||||
yield Scripting.waitTestWindows();
|
||||
|
||||
yield Scripting.sleep(1000);
|
||||
|
||||
Scripting.scriptEvent('redrawTestStart');
|
||||
yield Scripting.sleep(1000);
|
||||
Scripting.scriptEvent('redrawTestDone');
|
||||
|
||||
yield Scripting.sleep(1000);
|
||||
Scripting.scriptEvent('collectTimings');
|
||||
|
||||
yield Scripting.destroyTestWindows();
|
||||
|
||||
global.frame_timestamps = false;
|
||||
global.frame_finish_timestamp = false;
|
||||
|
||||
yield Scripting.sleep(1000);
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let app = appSys.lookup_app('org.gnome.gedit.desktop');
|
||||
|
||||
Scripting.scriptEvent('geditLaunch');
|
||||
app.activate();
|
||||
|
||||
let windows = app.get_windows();
|
||||
if (windows.length > 0)
|
||||
throw new Error('gedit was already running');
|
||||
|
||||
while (windows.length == 0) {
|
||||
yield waitSignal(global.display, 'window-created');
|
||||
windows = app.get_windows();
|
||||
}
|
||||
|
||||
let actor = windows[0].get_compositor_private();
|
||||
yield waitSignal(actor, 'first-frame');
|
||||
Scripting.scriptEvent('geditFirstFrame');
|
||||
|
||||
yield Scripting.sleep(1000);
|
||||
|
||||
windows[0].delete(global.get_current_time());
|
||||
|
||||
yield Scripting.sleep(1000);
|
||||
|
||||
Gtk.Settings.get_default().gtk_enable_animations = true;
|
||||
}
|
||||
|
||||
let overviewShowStart;
|
||||
let applicationsShowStart;
|
||||
let stagePaintStart;
|
||||
let redrawTiming;
|
||||
let redrawTimes = {};
|
||||
let geditLaunchTime;
|
||||
|
||||
function script_desktopShown(time) {
|
||||
let bootTimestamp = extractBootTimestamp();
|
||||
METRICS.timeToDesktop.value = time - bootTimestamp;
|
||||
}
|
||||
|
||||
function script_overviewShowStart(time) {
|
||||
overviewShowStart = time;
|
||||
}
|
||||
|
||||
function script_overviewShowDone(time) {
|
||||
METRICS.overviewShowTime.value = time - overviewShowStart;
|
||||
}
|
||||
|
||||
function script_applicationsShowStart(time) {
|
||||
applicationsShowStart = time;
|
||||
}
|
||||
|
||||
function script_applicationsShowDone(time) {
|
||||
METRICS.applicationsShowTime.value = time - applicationsShowStart;
|
||||
}
|
||||
|
||||
function script_mainViewDrawStart(time) {
|
||||
redrawTiming = 'mainView';
|
||||
}
|
||||
|
||||
function script_mainViewDrawDone(time) {
|
||||
redrawTiming = null;
|
||||
}
|
||||
|
||||
function script_overviewDrawStart(time) {
|
||||
redrawTiming = 'overview';
|
||||
}
|
||||
|
||||
function script_overviewDrawDone(time) {
|
||||
redrawTiming = null;
|
||||
}
|
||||
|
||||
function script_redrawTestStart(time) {
|
||||
redrawTiming = 'application';
|
||||
}
|
||||
|
||||
function script_redrawTestDone(time) {
|
||||
redrawTiming = null;
|
||||
}
|
||||
|
||||
function script_collectTimings(time) {
|
||||
for (let timing in redrawTimes) {
|
||||
let times = redrawTimes[timing];
|
||||
times.sort(function(a, b) { return a - b });
|
||||
|
||||
let len = times.length;
|
||||
let median;
|
||||
|
||||
if (len == 0)
|
||||
median = -1;
|
||||
else if (len % 2 == 1)
|
||||
median = times[(len - 1)/ 2];
|
||||
else
|
||||
median = Math.round((times[len / 2 - 1] + times[len / 2]) / 2);
|
||||
|
||||
METRICS[timing + 'RedrawTime'].value = median;
|
||||
}
|
||||
}
|
||||
|
||||
function script_geditLaunch(time) {
|
||||
geditLaunchTime = time;
|
||||
}
|
||||
|
||||
function script_geditFirstFrame(time) {
|
||||
METRICS.geditStartTime.value = time - geditLaunchTime;
|
||||
}
|
||||
|
||||
function clutter_stagePaintStart(time) {
|
||||
stagePaintStart = time;
|
||||
}
|
||||
|
||||
function clutter_paintCompletedTimestamp(time) {
|
||||
if (redrawTiming != null && stagePaintStart != null) {
|
||||
if (!(redrawTiming in redrawTimes))
|
||||
redrawTimes[redrawTiming] = [];
|
||||
redrawTimes[redrawTiming].push(time - stagePaintStart);
|
||||
}
|
||||
stagePaintStart = null;
|
||||
}
|
247
js/portalHelper/main.js
Normal file
247
js/portalHelper/main.js
Normal file
@ -0,0 +1,247 @@
|
||||
const Format = imports.format;
|
||||
const Gettext = imports.gettext;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Soup = imports.gi.Soup;
|
||||
const WebKit = imports.gi.WebKit2;
|
||||
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
|
||||
const PortalHelperResult = {
|
||||
CANCELLED: 0,
|
||||
COMPLETED: 1,
|
||||
RECHECK: 2
|
||||
};
|
||||
|
||||
const INACTIVITY_TIMEOUT = 30000; //ms
|
||||
const CONNECTIVITY_RECHECK_RATELIMIT_TIMEOUT = 30 * GLib.USEC_PER_SEC;
|
||||
|
||||
const HelperDBusInterface = '<node> \
|
||||
<interface name="org.gnome.Shell.PortalHelper"> \
|
||||
<method name="Authenticate"> \
|
||||
<arg type="o" direction="in" name="connection" /> \
|
||||
<arg type="s" direction="in" name="url" /> \
|
||||
<arg type="u" direction="in" name="timestamp" /> \
|
||||
</method> \
|
||||
<method name="Close"> \
|
||||
<arg type="o" direction="in" name="connection" /> \
|
||||
</method> \
|
||||
<method name="Refresh"> \
|
||||
<arg type="o" direction="in" name="connection" /> \
|
||||
</method> \
|
||||
<signal name="Done"> \
|
||||
<arg type="o" name="connection" /> \
|
||||
<arg type="u" name="result" /> \
|
||||
</signal> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const PortalWindow = new Lang.Class({
|
||||
Name: 'PortalWindow',
|
||||
Extends: Gtk.ApplicationWindow,
|
||||
|
||||
_init: function(application, url, timestamp, doneCallback) {
|
||||
this.parent({ application: application });
|
||||
|
||||
if (!url) {
|
||||
url = 'http://www.gnome.org';
|
||||
this._originalUrlWasGnome = true;
|
||||
} else {
|
||||
this._originalUrlWasGnome = false;
|
||||
}
|
||||
this._uri = new Soup.URI(url);
|
||||
this._everSeenRedirect = false;
|
||||
this._originalUrl = url;
|
||||
this._doneCallback = doneCallback;
|
||||
this._lastRecheck = 0;
|
||||
this._recheckAtExit = false;
|
||||
|
||||
this._webView = new WebKit.WebView();
|
||||
this._webView.connect('decide-policy', Lang.bind(this, this._onDecidePolicy));
|
||||
this._webView.load_uri(url);
|
||||
this._webView.connect('notify::title', Lang.bind(this, this._syncTitle));
|
||||
this._syncTitle();
|
||||
|
||||
this.add(this._webView);
|
||||
this._webView.show();
|
||||
this.maximize();
|
||||
this.present_with_time(timestamp);
|
||||
},
|
||||
|
||||
_syncTitle: function() {
|
||||
let title = this._webView.title;
|
||||
|
||||
if (title) {
|
||||
this.title = title;
|
||||
} else {
|
||||
// TRANSLATORS: this is the title of the wifi captive portal login
|
||||
// window, until we know the title of the actual login page
|
||||
this.title = _("Web Authentication Redirect");
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
this._everSeenRedirect = false;
|
||||
this._webView.load_uri(this._originalUrl);
|
||||
},
|
||||
|
||||
vfunc_delete_event: function(event) {
|
||||
if (this._recheckAtExit)
|
||||
this._doneCallback(PortalHelperResult.RECHECK);
|
||||
else
|
||||
this._doneCallback(PortalHelperResult.CANCELLED);
|
||||
return false;
|
||||
},
|
||||
|
||||
_onDecidePolicy: function(view, decision, type) {
|
||||
if (type == WebKit.PolicyDecisionType.NEW_WINDOW_ACTION) {
|
||||
decision.ignore();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type != WebKit.PolicyDecisionType.NAVIGATION_ACTION)
|
||||
return false;
|
||||
|
||||
let request = decision.get_request();
|
||||
let uri = new Soup.URI(request.get_uri());
|
||||
|
||||
if (!uri.host_equal(this._uri) && this._originalUrlWasGnome) {
|
||||
if (uri.get_host() == 'www.gnome.org' && this._everSeenRedirect) {
|
||||
// Yay, we got to gnome!
|
||||
decision.ignore();
|
||||
this._doneCallback(PortalHelperResult.COMPLETED);
|
||||
return true;
|
||||
} else if (uri.get_host() != 'www.gnome.org') {
|
||||
this._everSeenRedirect = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We *may* have finished here, but we don't know for
|
||||
// sure. Tell gnome-shell to run another connectivity check
|
||||
// (but ratelimit the checks, we don't want to spam
|
||||
// nmcheck.gnome.org for portals that have 10 or more internal
|
||||
// redirects - and unfortunately they exist)
|
||||
// If we hit the rate limit, we also queue a recheck
|
||||
// when the window is closed, just in case we miss the
|
||||
// final check and don't realize we're connected
|
||||
// This should not be a problem in the cancelled logic,
|
||||
// because if the user doesn't want to start the login,
|
||||
// we should not see any redirect at all, outside this._uri
|
||||
|
||||
let now = GLib.get_monotonic_time();
|
||||
let shouldRecheck = (now - this._lastRecheck) >
|
||||
CONNECTIVITY_RECHECK_RATELIMIT_TIMEOUT;
|
||||
|
||||
if (shouldRecheck) {
|
||||
this._lastRecheck = now;
|
||||
this._recheckAtExit = false;
|
||||
this._doneCallback(PortalHelperResult.RECHECK);
|
||||
} else {
|
||||
this._recheckAtExit = true;
|
||||
}
|
||||
|
||||
// Update the URI, in case of chained redirects, so we still
|
||||
// think we're doing the login until gnome-shell kills us
|
||||
this._uri = uri;
|
||||
|
||||
decision.use();
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
const WebPortalHelper = new Lang.Class({
|
||||
Name: 'WebPortalHelper',
|
||||
Extends: Gtk.Application,
|
||||
|
||||
_init: function() {
|
||||
this.parent({ application_id: 'org.gnome.Shell.PortalHelper',
|
||||
flags: Gio.ApplicationFlags.IS_SERVICE,
|
||||
inactivity_timeout: 30000 });
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(HelperDBusInterface, this);
|
||||
this._queue = [];
|
||||
},
|
||||
|
||||
vfunc_dbus_register: function(connection, path) {
|
||||
this._dbusImpl.export(connection, path);
|
||||
this.parent(connection, path);
|
||||
return true;
|
||||
},
|
||||
|
||||
vfunc_dbus_unregister: function(connection, path) {
|
||||
this._dbusImpl.unexport_from_connection(connection);
|
||||
this.parent(connection, path);
|
||||
},
|
||||
|
||||
vfunc_activate: function() {
|
||||
// If launched manually (for example for testing), force a dummy authentication
|
||||
// session with the default url
|
||||
this.Authenticate('/org/gnome/dummy', '', 0);
|
||||
},
|
||||
|
||||
Authenticate: function(connection, url, timestamp) {
|
||||
this._queue.push({ connection: connection, url: url, timestamp: timestamp });
|
||||
|
||||
this._processQueue();
|
||||
},
|
||||
|
||||
Close: function(connection) {
|
||||
for (let i = 0; i < this._queue.length; i++) {
|
||||
let obj = this._queue[i];
|
||||
|
||||
if (obj.connection == connection) {
|
||||
if (obj.window)
|
||||
obj.window.destroy();
|
||||
this._queue.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this._processQueue();
|
||||
},
|
||||
|
||||
Refresh: function(connection) {
|
||||
for (let i = 0; i < this._queue.length; i++) {
|
||||
let obj = this._queue[i];
|
||||
|
||||
if (obj.connection == connection) {
|
||||
if (obj.window)
|
||||
obj.window.refresh();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_processQueue: function() {
|
||||
if (this._queue.length == 0)
|
||||
return;
|
||||
|
||||
let top = this._queue[0];
|
||||
if (top.window != null)
|
||||
return;
|
||||
|
||||
top.window = new PortalWindow(this, top.uri, top.timestamp, Lang.bind(this, function(result) {
|
||||
this._dbusImpl.emit_signal('Done', new GLib.Variant('(ou)', [top.connection, result]));
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
function initEnvironment() {
|
||||
String.prototype.format = Format.format;
|
||||
}
|
||||
|
||||
function main(argv) {
|
||||
initEnvironment();
|
||||
|
||||
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
|
||||
Gettext.textdomain(Config.GETTEXT_PACKAGE);
|
||||
|
||||
let app = new WebPortalHelper();
|
||||
return app.run(argv);
|
||||
}
|
132
js/ui/altTab.js
132
js/ui/altTab.js
@ -2,6 +2,7 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -23,7 +24,7 @@ const WINDOW_PREVIEW_SIZE = 128;
|
||||
const APP_ICON_SIZE = 96;
|
||||
const APP_ICON_SIZE_SMALL = 48;
|
||||
|
||||
const iconSizes = [96, 64, 48, 32, 22];
|
||||
const baseIconSizes = [96, 64, 48, 32, 22];
|
||||
|
||||
const AppIconMode = {
|
||||
THUMBNAIL_ONLY: 1,
|
||||
@ -57,6 +58,14 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
this._currentWindow = -1;
|
||||
|
||||
this.thumbnailsVisible = false;
|
||||
|
||||
let apps = Shell.AppSystem.get_default().get_running ();
|
||||
|
||||
if (apps.length == 0)
|
||||
return;
|
||||
|
||||
this._switcherList = new AppSwitcher(apps, this);
|
||||
this._items = this._switcherList.icons;
|
||||
},
|
||||
|
||||
_allocate: function (actor, box, flags) {
|
||||
@ -98,20 +107,6 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_createSwitcher: function() {
|
||||
let apps = Shell.AppSystem.get_default().get_running ();
|
||||
|
||||
if (apps.length == 0)
|
||||
return false;
|
||||
|
||||
this._switcherList = new AppSwitcher(apps, this);
|
||||
this._items = this._switcherList.icons;
|
||||
if (this._items.length == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_initialSelection: function(backward, binding) {
|
||||
if (binding == 'switch-group') {
|
||||
if (backward) {
|
||||
@ -150,13 +145,13 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
this._items[this._selectedIndex].cachedWindows.length);
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, backwards, action) {
|
||||
_keyPressHandler: function(keysym, action) {
|
||||
if (action == Meta.KeyBindingAction.SWITCH_GROUP) {
|
||||
this._select(this._selectedIndex, backwards ? this._previousWindow() : this._nextWindow());
|
||||
this._select(this._selectedIndex, this._nextWindow());
|
||||
} else if (action == Meta.KeyBindingAction.SWITCH_GROUP_BACKWARD) {
|
||||
this._select(this._selectedIndex, this._previousWindow());
|
||||
} else if (action == Meta.KeyBindingAction.SWITCH_APPLICATIONS) {
|
||||
this._select(backwards ? this._previous() : this._next());
|
||||
this._select(this._next());
|
||||
} else if (action == Meta.KeyBindingAction.SWITCH_APPLICATIONS_BACKWARD) {
|
||||
this._select(this._previous());
|
||||
} else if (this._thumbnailsFocused) {
|
||||
@ -166,6 +161,8 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
this._select(this._selectedIndex, this._nextWindow());
|
||||
else if (keysym == Clutter.Up)
|
||||
this._select(this._selectedIndex, null, true);
|
||||
else
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
} else {
|
||||
if (keysym == Clutter.Left)
|
||||
this._select(this._previous());
|
||||
@ -173,7 +170,11 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
this._select(this._next());
|
||||
else if (keysym == Clutter.Down)
|
||||
this._select(this._selectedIndex, 0);
|
||||
else
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_scrollHandler: function(direction) {
|
||||
@ -304,6 +305,7 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
this._thumbnailTimeoutId = Mainloop.timeout_add (
|
||||
THUMBNAIL_POPUP_TIME,
|
||||
Lang.bind(this, this._timeoutPopupThumbnails));
|
||||
GLib.Source.set_name_by_id(this._thumbnailTimeoutId, '[gnome-shell] this._timeoutPopupThumbnails');
|
||||
}
|
||||
},
|
||||
|
||||
@ -312,7 +314,7 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
this._createThumbnails();
|
||||
this._thumbnailTimeoutId = 0;
|
||||
this._thumbnailsFocused = false;
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
_destroyThumbnails : function() {
|
||||
@ -357,44 +359,28 @@ const WindowSwitcherPopup = new Lang.Class({
|
||||
Name: 'WindowSwitcherPopup',
|
||||
Extends: SwitcherPopup.SwitcherPopup,
|
||||
|
||||
_init: function(items) {
|
||||
this.parent(items);
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
|
||||
},
|
||||
_init: function() {
|
||||
this.parent();
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.window-switcher' });
|
||||
|
||||
_getWindowList: function() {
|
||||
let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
|
||||
return global.display.get_tab_list(Meta.TabList.NORMAL, global.screen, workspace);
|
||||
},
|
||||
|
||||
_createSwitcher: function() {
|
||||
let windows = this._getWindowList();
|
||||
|
||||
if (windows.length == 0)
|
||||
return false;
|
||||
return;
|
||||
|
||||
let mode = this._settings.get_enum('app-icon-mode');
|
||||
this._switcherList = new WindowList(windows, mode);
|
||||
this._items = this._switcherList.icons;
|
||||
|
||||
if (this._items.length == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_initialSelection: function(backward, binding) {
|
||||
if (binding == 'switch-windows-backward' || backward)
|
||||
this._select(this._items.length - 1);
|
||||
else if (this._items.length == 1)
|
||||
this._select(0);
|
||||
else
|
||||
this._select(1);
|
||||
_getWindowList: function() {
|
||||
let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
|
||||
return global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, backwards, action) {
|
||||
_keyPressHandler: function(keysym, action) {
|
||||
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS) {
|
||||
this._select(backwards ? this._previous() : this._next());
|
||||
this._select(this._next());
|
||||
} else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD) {
|
||||
this._select(this._previous());
|
||||
} else {
|
||||
@ -402,7 +388,11 @@ const WindowSwitcherPopup = new Lang.Class({
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.Right)
|
||||
this._select(this._next());
|
||||
else
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_finish: function() {
|
||||
@ -429,7 +419,6 @@ const AppIcon = new Lang.Class({
|
||||
|
||||
set_size: function(size) {
|
||||
this.icon = this.app.create_icon_texture(size);
|
||||
this._iconBin.set_size(size, size);
|
||||
this._iconBin.child = this.icon;
|
||||
}
|
||||
});
|
||||
@ -445,11 +434,10 @@ const AppSwitcher = new Lang.Class({
|
||||
this._arrows = [];
|
||||
|
||||
let windowTracker = Shell.WindowTracker.get_default();
|
||||
let settings = new Gio.Settings({ schema: 'org.gnome.shell.app-switcher' });
|
||||
let settings = new Gio.Settings({ schema_id: 'org.gnome.shell.app-switcher' });
|
||||
let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
|
||||
: null;
|
||||
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
|
||||
global.screen, workspace);
|
||||
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
|
||||
|
||||
// Construct the AppIcons, add to the popup
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
@ -459,9 +447,10 @@ const AppSwitcher = new Lang.Class({
|
||||
appIcon.cachedWindows = allWindows.filter(function(w) {
|
||||
return windowTracker.get_window_app (w) == appIcon.app;
|
||||
});
|
||||
if (workspace == null || appIcon.cachedWindows.length > 0) {
|
||||
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;
|
||||
@ -477,12 +466,13 @@ const AppSwitcher = new Lang.Class({
|
||||
Mainloop.source_remove(this._mouseTimeOutId);
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||
_setIconSize: function() {
|
||||
let j = 0;
|
||||
while(this._items.length > 1 && this._items[j].style_class != 'item-box') {
|
||||
j++;
|
||||
}
|
||||
let themeNode = this._items[j].get_theme_node();
|
||||
|
||||
let iconPadding = themeNode.get_horizontal_padding();
|
||||
let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT);
|
||||
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
|
||||
@ -493,19 +483,22 @@ const AppSwitcher = new Lang.Class({
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
|
||||
let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
|
||||
let height = 0;
|
||||
|
||||
for(let i = 0; i < iconSizes.length; i++) {
|
||||
this._iconSize = iconSizes[i];
|
||||
height = iconSizes[i] + iconSpacing;
|
||||
let w = height * this._items.length + totalSpacing;
|
||||
if (w <= availWidth)
|
||||
break;
|
||||
}
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
let iconSizes = baseIconSizes.map(function(s) {
|
||||
return s * scaleFactor;
|
||||
});
|
||||
|
||||
if (this._items.length == 1) {
|
||||
this._iconSize = iconSizes[0];
|
||||
height = iconSizes[0] + iconSpacing;
|
||||
this._iconSize = baseIconSizes[0];
|
||||
} else {
|
||||
for(let i = 0; i < baseIconSizes.length; i++) {
|
||||
this._iconSize = baseIconSizes[i];
|
||||
let height = iconSizes[i] + iconSpacing;
|
||||
let w = height * this._items.length + totalSpacing;
|
||||
if (w <= availWidth)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(let i = 0; i < this.icons.length; i++) {
|
||||
@ -513,9 +506,11 @@ const AppSwitcher = new Lang.Class({
|
||||
break;
|
||||
this.icons[i].set_size(this._iconSize);
|
||||
}
|
||||
},
|
||||
|
||||
alloc.min_size = height;
|
||||
alloc.natural_size = height;
|
||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||
this._setIconSize();
|
||||
this.parent(actor, forWidth, alloc);
|
||||
},
|
||||
|
||||
_allocate: function (actor, box, flags) {
|
||||
@ -547,8 +542,9 @@ const AppSwitcher = new Lang.Class({
|
||||
Lang.bind(this, function () {
|
||||
this._enterItem(index);
|
||||
this._mouseTimeOutId = 0;
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(this._mouseTimeOutId, '[gnome-shell] this._enterItem');
|
||||
} else
|
||||
this._itemEntered(index);
|
||||
},
|
||||
@ -648,17 +644,19 @@ const ThumbnailList = new Lang.Class({
|
||||
totalPadding += this.actor.get_theme_node().get_horizontal_padding() + this.actor.get_theme_node().get_vertical_padding();
|
||||
let [labelMinHeight, labelNaturalHeight] = this._labels[0].get_preferred_height(-1);
|
||||
let spacing = this._items[0].child.get_theme_node().get_length('spacing');
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
let thumbnailSize = THUMBNAIL_DEFAULT_SIZE * scaleFactor;
|
||||
|
||||
availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, THUMBNAIL_DEFAULT_SIZE);
|
||||
availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, thumbnailSize);
|
||||
let binHeight = availHeight + this._items[0].get_theme_node().get_vertical_padding() + this.actor.get_theme_node().get_vertical_padding() - spacing;
|
||||
binHeight = Math.min(THUMBNAIL_DEFAULT_SIZE, binHeight);
|
||||
binHeight = Math.min(thumbnailSize, binHeight);
|
||||
|
||||
for (let i = 0; i < this._thumbnailBins.length; i++) {
|
||||
let mutterWindow = this._windows[i].get_compositor_private();
|
||||
if (!mutterWindow)
|
||||
continue;
|
||||
|
||||
let clone = _createWindowClone(mutterWindow, THUMBNAIL_DEFAULT_SIZE);
|
||||
let clone = _createWindowClone(mutterWindow, thumbnailSize);
|
||||
this._thumbnailBins[i].set_height(binHeight);
|
||||
this._thumbnailBins[i].add_actor(clone);
|
||||
this._clones.push(clone);
|
||||
|
@ -1,5 +1,6 @@
|
||||
// -*- 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;
|
||||
@ -11,7 +12,7 @@ const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
|
||||
const Animation = new Lang.Class({
|
||||
Name: 'Animation',
|
||||
|
||||
_init: function(filename, width, height, speed) {
|
||||
_init: function(file, width, height, speed) {
|
||||
this.actor = new St.Bin();
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this._speed = speed;
|
||||
@ -20,7 +21,9 @@ const Animation = new Lang.Class({
|
||||
this._isPlaying = false;
|
||||
this._timeoutId = 0;
|
||||
this._frame = 0;
|
||||
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
|
||||
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
this._animations = St.TextureCache.get_default().load_sliced_image (file, width, height, scaleFactor,
|
||||
Lang.bind(this, this._animationsLoaded));
|
||||
this.actor.set_child(this._animations);
|
||||
},
|
||||
@ -31,6 +34,7 @@ const Animation = new Lang.Class({
|
||||
this._showFrame(0);
|
||||
|
||||
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
|
||||
GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._update');
|
||||
}
|
||||
|
||||
this._isPlaying = true;
|
||||
@ -59,7 +63,7 @@ const Animation = new Lang.Class({
|
||||
|
||||
_update: function() {
|
||||
this._showFrame(this._frame + 1);
|
||||
return true;
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
},
|
||||
|
||||
_animationsLoaded: function() {
|
||||
@ -78,7 +82,7 @@ const AnimatedIcon = new Lang.Class({
|
||||
Name: 'AnimatedIcon',
|
||||
Extends: Animation,
|
||||
|
||||
_init: function(filename, size) {
|
||||
this.parent(filename, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
|
||||
_init: function(file, size) {
|
||||
this.parent(file, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
|
||||
}
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,36 @@ const Signals = imports.signals;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const RENAMED_DESKTOP_IDS = {
|
||||
'baobab.desktop': 'org.gnome.baobab.desktop',
|
||||
'cheese.desktop': 'org.gnome.Cheese.desktop',
|
||||
'dconf-editor.desktop': 'ca.desrt.dconf-editor.desktop',
|
||||
'file-roller.desktop': 'org.gnome.FileRoller.desktop',
|
||||
'gcalctool.desktop': 'gnome-calculator.desktop',
|
||||
'gedit.desktop': 'org.gnome.gedit.desktop',
|
||||
'glchess.desktop': 'gnome-chess.desktop',
|
||||
'glines.desktop': 'five-or-more.desktop',
|
||||
'gnect.desktop': 'four-in-a-row.desktop',
|
||||
'gnibbles.desktop': 'gnome-nibbles.desktop',
|
||||
'gnobots2.desktop': 'gnome-robots.desktop',
|
||||
'gnome-boxes.desktop': 'org.gnome.Boxes.desktop',
|
||||
'gnome-clocks.desktop': 'org.gnome.clocks.desktop',
|
||||
'gnome-contacts.desktop': 'org.gnome.Contacts.desktop',
|
||||
'gnome-documents.desktop': 'org.gnome.Documents.desktop',
|
||||
'gnome-font-viewer.desktop': 'org.gnome.font-viewer.desktop',
|
||||
'gnome-photos.desktop': 'org.gnome.Photos.desktop',
|
||||
'gnome-screenshot.desktop': 'org.gnome.Screenshot.desktop',
|
||||
'gnome-software.desktop': 'org.gnome.Software.desktop',
|
||||
'gnome-weather.desktop': 'org.gnome.Weather.Application.desktop',
|
||||
'gnomine.desktop': 'gnome-mines.desktop',
|
||||
'gnotravex.desktop': 'gnome-tetravex.desktop',
|
||||
'gnotski.desktop': 'gnome-klotski.desktop',
|
||||
'gtali.desktop': 'tali.desktop',
|
||||
'nautilus.desktop': 'org.gnome.Nautilus.desktop',
|
||||
'polari.desktop': 'org.gnome.Polari.desktop',
|
||||
'totem.desktop': 'org.gnome.Totem.desktop',
|
||||
};
|
||||
|
||||
const AppFavorites = new Lang.Class({
|
||||
Name: 'AppFavorites',
|
||||
|
||||
@ -24,6 +54,21 @@ const AppFavorites = new Lang.Class({
|
||||
|
||||
reload: function() {
|
||||
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
|
||||
|
||||
// Map old desktop file names to the current ones
|
||||
let updated = false;
|
||||
ids = ids.map(function (id) {
|
||||
let newId = RENAMED_DESKTOP_IDS[id];
|
||||
if (newId !== undefined) {
|
||||
updated = true;
|
||||
return newId;
|
||||
}
|
||||
return id;
|
||||
});
|
||||
// ... and write back the updated desktop file names
|
||||
if (updated)
|
||||
global.settings.set_strv(this.FAVORITE_APPS_KEY, ids);
|
||||
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let apps = ids.map(function (id) {
|
||||
return appSys.lookup_app(id);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,8 +13,8 @@ const BackgroundMenu = new Lang.Class({
|
||||
Name: 'BackgroundMenu',
|
||||
Extends: PopupMenu.PopupMenu,
|
||||
|
||||
_init: function(source) {
|
||||
this.parent(source, 0, St.Side.TOP);
|
||||
_init: function(layoutManager) {
|
||||
this.parent(layoutManager.dummyCursor, 0, St.Side.TOP);
|
||||
|
||||
this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
@ -22,47 +22,51 @@ const BackgroundMenu = new Lang.Class({
|
||||
|
||||
this.actor.add_style_class_name('background-menu');
|
||||
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
layoutManager.uiGroup.add_actor(this.actor);
|
||||
this.actor.hide();
|
||||
}
|
||||
});
|
||||
|
||||
function addBackgroundMenu(actor) {
|
||||
let cursor = new St.Bin({ opacity: 0 });
|
||||
Main.uiGroup.add_actor(cursor);
|
||||
|
||||
function addBackgroundMenu(actor, layoutManager) {
|
||||
actor.reactive = true;
|
||||
actor._backgroundMenu = new BackgroundMenu(cursor);
|
||||
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();
|
||||
cursor.set_position(x, y);
|
||||
function openMenu(x, y) {
|
||||
Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0);
|
||||
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;
|
||||
return ((action.get_button() == 0 ||
|
||||
action.get_button() == 1) &&
|
||||
!actor._backgroundMenu.isOpen);
|
||||
if (state == Clutter.LongPressState.ACTIVATE) {
|
||||
openMenu();
|
||||
let [x, y] = action.get_coords();
|
||||
openMenu(x, y);
|
||||
actor._backgroundManager.ignoreRelease();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
clickAction.connect('clicked', function(action) {
|
||||
if (action.get_button() == 3)
|
||||
openMenu();
|
||||
if (action.get_button() == 3) {
|
||||
let [x, y] = action.get_coords();
|
||||
openMenu(x, y);
|
||||
}
|
||||
});
|
||||
actor.add_action(clickAction);
|
||||
|
||||
actor.connect('destroy', function() {
|
||||
actor._backgroundMenu.destroy();
|
||||
actor._backgroundMenu = null;
|
||||
actor._backgroundManager = null;
|
||||
let grabOpBeginId = global.display.connect('grab-op-begin', function () {
|
||||
clickAction.release();
|
||||
});
|
||||
|
||||
cursor.destroy();
|
||||
});
|
||||
actor.connect('destroy', function() {
|
||||
actor._backgroundMenu.destroy();
|
||||
actor._backgroundMenu = null;
|
||||
actor._backgroundManager = null;
|
||||
global.display.disconnect(grabOpBeginId);
|
||||
});
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ const BoxPointer = new Lang.Class({
|
||||
_muteInput: function() {
|
||||
if (this._capturedEventId == 0)
|
||||
this._capturedEventId = this.actor.connect('captured-event',
|
||||
function() { return true; });
|
||||
function() { return Clutter.EVENT_STOP; });
|
||||
},
|
||||
|
||||
_unmuteInput: function() {
|
||||
@ -121,6 +121,9 @@ const BoxPointer = new Lang.Class({
|
||||
},
|
||||
|
||||
hide: function(animate, onComplete) {
|
||||
if (!this.actor.visible)
|
||||
return;
|
||||
|
||||
let xOffset = 0;
|
||||
let yOffset = 0;
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
@ -284,38 +287,40 @@ const BoxPointer = new Lang.Class({
|
||||
let skipBottomLeft = false;
|
||||
let skipBottomRight = false;
|
||||
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
if (this._arrowOrigin == x1)
|
||||
skipTopLeft = true;
|
||||
else if (this._arrowOrigin == x2)
|
||||
skipTopRight = true;
|
||||
break;
|
||||
if (rise) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
if (this._arrowOrigin == x1)
|
||||
skipTopLeft = true;
|
||||
else if (this._arrowOrigin == x2)
|
||||
skipTopRight = true;
|
||||
break;
|
||||
|
||||
case St.Side.RIGHT:
|
||||
if (this._arrowOrigin == y1)
|
||||
skipTopRight = true;
|
||||
else if (this._arrowOrigin == y2)
|
||||
skipBottomRight = true;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
if (this._arrowOrigin == y1)
|
||||
skipTopRight = true;
|
||||
else if (this._arrowOrigin == y2)
|
||||
skipBottomRight = true;
|
||||
break;
|
||||
|
||||
case St.Side.BOTTOM:
|
||||
if (this._arrowOrigin == x1)
|
||||
skipBottomLeft = true;
|
||||
else if (this._arrowOrigin == x2)
|
||||
skipBottomRight = true;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
if (this._arrowOrigin == x1)
|
||||
skipBottomLeft = true;
|
||||
else if (this._arrowOrigin == x2)
|
||||
skipBottomRight = true;
|
||||
break;
|
||||
|
||||
case St.Side.LEFT:
|
||||
if (this._arrowOrigin == y1)
|
||||
skipTopLeft = true;
|
||||
else if (this._arrowOrigin == y2)
|
||||
skipBottomLeft = true;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
if (this._arrowOrigin == y1)
|
||||
skipTopLeft = true;
|
||||
else if (this._arrowOrigin == y2)
|
||||
skipBottomLeft = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cr.moveTo(x1 + borderRadius, y1);
|
||||
if (this._arrowSide == St.Side.TOP) {
|
||||
if (this._arrowSide == St.Side.TOP && rise) {
|
||||
if (skipTopLeft) {
|
||||
cr.moveTo(x1, y2 - borderRadius);
|
||||
cr.lineTo(x1, y1 - rise);
|
||||
@ -337,7 +342,7 @@ const BoxPointer = new Lang.Class({
|
||||
3*Math.PI/2, Math.PI*2);
|
||||
}
|
||||
|
||||
if (this._arrowSide == St.Side.RIGHT) {
|
||||
if (this._arrowSide == St.Side.RIGHT && rise) {
|
||||
if (skipTopRight) {
|
||||
cr.lineTo(x2 + rise, y1);
|
||||
cr.lineTo(x2 + rise, y1 + halfBase);
|
||||
@ -358,7 +363,7 @@ const BoxPointer = new Lang.Class({
|
||||
0, Math.PI/2);
|
||||
}
|
||||
|
||||
if (this._arrowSide == St.Side.BOTTOM) {
|
||||
if (this._arrowSide == St.Side.BOTTOM && rise) {
|
||||
if (skipBottomLeft) {
|
||||
cr.lineTo(x1 + halfBase, y2);
|
||||
cr.lineTo(x1, y2 + rise);
|
||||
@ -379,7 +384,7 @@ const BoxPointer = new Lang.Class({
|
||||
Math.PI/2, Math.PI);
|
||||
}
|
||||
|
||||
if (this._arrowSide == St.Side.LEFT) {
|
||||
if (this._arrowSide == St.Side.LEFT && rise) {
|
||||
if (skipTopLeft) {
|
||||
cr.lineTo(x1, y1 + halfBase);
|
||||
cr.lineTo(x1 - rise, y1);
|
||||
|
@ -13,20 +13,27 @@ const Shell = imports.gi.Shell;
|
||||
|
||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
|
||||
const ELLIPSIS_CHAR = '\u2026';
|
||||
|
||||
// alias to prevent xgettext from picking up strings translated in GTK+
|
||||
const gtk30_ = Gettext_gtk30.gettext;
|
||||
const NC_ = function(context, str) { return str; };
|
||||
|
||||
// in org.gnome.desktop.interface
|
||||
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) {
|
||||
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
|
||||
* Sunday are non-work days (not true in e.g. Israel, it's Sunday and
|
||||
* Monday there)
|
||||
@ -53,19 +60,21 @@ function _getEndOfDay(date) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
function _formatEventTime(event, clockFormat) {
|
||||
function _formatEventTime(event, clockFormat, periodBegin, periodEnd) {
|
||||
let ret;
|
||||
if (event.allDay) {
|
||||
let allDay = (event.allDay || (event.date <= periodBegin && event.end >= periodEnd));
|
||||
if (allDay) {
|
||||
/* Translators: Shown in calendar event list for all day events
|
||||
* Keep it short, best if you can use less then 10 characters
|
||||
*/
|
||||
ret = C_("event list time", "All Day");
|
||||
} else {
|
||||
let date = event.date >= periodBegin ? event.date : event.end;
|
||||
switch (clockFormat) {
|
||||
case '24h':
|
||||
/* Translators: Shown in calendar event list, if 24h format,
|
||||
\u2236 is a ratio character, similar to : */
|
||||
ret = event.date.toLocaleFormat(C_("event list time", "%H\u2236%M"));
|
||||
ret = date.toLocaleFormat(C_("event list time", "%H\u2236%M"));
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -74,7 +83,7 @@ function _formatEventTime(event, clockFormat) {
|
||||
/* Translators: Shown in calendar event list, if 12h format,
|
||||
\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"));
|
||||
ret = date.toLocaleFormat(C_("event list time", "%l\u2236%M\u2009%p"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -329,25 +338,22 @@ const DBusEventSource = new Lang.Class({
|
||||
return;
|
||||
|
||||
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._curRequestEnd.getTime() / 1000,
|
||||
forceReload,
|
||||
Lang.bind(this, this._onEventsReceived),
|
||||
callFlags);
|
||||
Gio.DBusCallFlags.NONE);
|
||||
}
|
||||
},
|
||||
|
||||
requestRange: function(begin, end, forceReload) {
|
||||
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
||||
requestRange: function(begin, end) {
|
||||
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
||||
this.isLoading = true;
|
||||
this._lastRequestBegin = begin;
|
||||
this._lastRequestEnd = end;
|
||||
this._curRequestBegin = begin;
|
||||
this._curRequestEnd = end;
|
||||
this._loadEvents(forceReload);
|
||||
this._loadEvents(false);
|
||||
}
|
||||
},
|
||||
|
||||
@ -359,6 +365,12 @@ const DBusEventSource = new Lang.Class({
|
||||
result.push(event);
|
||||
}
|
||||
}
|
||||
result.sort(function(event1, event2) {
|
||||
// sort events by end time on ending day
|
||||
let d1 = event1.date < begin && event1.end <= end ? event1.end : event1.date;
|
||||
let d2 = event2.date < begin && event2.end <= end ? event2.end : event2.date;
|
||||
return d1.getTime() - d2.getTime();
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
@ -381,14 +393,14 @@ const Calendar = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell.calendar' });
|
||||
|
||||
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
|
||||
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
||||
|
||||
// Find the ordering for month/year in the calendar heading
|
||||
this._headerFormatWithoutYear = '%B';
|
||||
switch (Gettext_gtk30.gettext('calendar:MY')) {
|
||||
switch (gtk30_('calendar:MY')) {
|
||||
case 'calendar:MY':
|
||||
this._headerFormat = '%B %Y';
|
||||
break;
|
||||
@ -404,9 +416,11 @@ const Calendar = new Lang.Class({
|
||||
// Start off with the current date
|
||||
this._selectedDate = new Date();
|
||||
|
||||
this.actor = new St.Table({ homogeneous: false,
|
||||
style_class: 'calendar',
|
||||
reactive: true });
|
||||
this._shouldDateGrabFocus = false;
|
||||
|
||||
this.actor = new St.Widget({ style_class: 'calendar',
|
||||
layout_manager: new Clutter.GridLayout(),
|
||||
reactive: true });
|
||||
|
||||
this.actor.connect('scroll-event',
|
||||
Lang.bind(this, this._onScroll));
|
||||
@ -419,31 +433,31 @@ const Calendar = new Lang.Class({
|
||||
setEventSource: function(eventSource) {
|
||||
this._eventSource = eventSource;
|
||||
this._eventSource.connect('changed', Lang.bind(this, function() {
|
||||
this._update(false);
|
||||
this._rebuildCalendar();
|
||||
this._update();
|
||||
}));
|
||||
this._update(true);
|
||||
this._rebuildCalendar();
|
||||
this._update();
|
||||
},
|
||||
|
||||
// Sets the calendar to show a specific date
|
||||
setDate: function(date, forceReload) {
|
||||
if (!_sameDay(date, this._selectedDate)) {
|
||||
this._selectedDate = date;
|
||||
this._update(forceReload);
|
||||
this.emit('selected-date-changed', new Date(this._selectedDate));
|
||||
} else {
|
||||
if (forceReload)
|
||||
this._update(forceReload);
|
||||
}
|
||||
setDate: function(date) {
|
||||
if (_sameDay(date, this._selectedDate))
|
||||
return;
|
||||
|
||||
this._selectedDate = date;
|
||||
this._update();
|
||||
this.emit('selected-date-changed', new Date(this._selectedDate));
|
||||
},
|
||||
|
||||
_buildHeader: function() {
|
||||
let layout = this.actor.layout_manager;
|
||||
let offsetCols = this._useWeekdate ? 1 : 0;
|
||||
this.actor.destroy_all_children();
|
||||
|
||||
// Top line of the calendar '<| September 2009 |>'
|
||||
this._topBox = new St.BoxLayout();
|
||||
this.actor.add(this._topBox,
|
||||
{ row: 0, col: 0, col_span: offsetCols + 7 });
|
||||
layout.attach(this._topBox, 0, 0, offsetCols + 7, 1);
|
||||
|
||||
this._backButton = new St.Button({ style_class: 'calendar-change-month-back',
|
||||
accessible_name: _("Previous month"),
|
||||
@ -475,10 +489,12 @@ const Calendar = new Lang.Class({
|
||||
let customDayAbbrev = _getCalendarDayAbbreviation(iter.getDay());
|
||||
let label = new St.Label({ style_class: 'calendar-day-base calendar-day-heading',
|
||||
text: customDayAbbrev });
|
||||
this.actor.add(label,
|
||||
{ row: 1,
|
||||
col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
|
||||
x_fill: false, x_align: St.Align.MIDDLE });
|
||||
let col;
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
col = 6 - (7 + iter.getDay() - this._weekStart) % 7;
|
||||
else
|
||||
col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
|
||||
layout.attach(label, col, 1, 1, 1);
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
}
|
||||
|
||||
@ -497,6 +513,7 @@ const Calendar = new Lang.Class({
|
||||
this._onNextMonthButtonClicked();
|
||||
break;
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onPrevMonthButtonClicked: function() {
|
||||
@ -520,7 +537,7 @@ const Calendar = new Lang.Class({
|
||||
|
||||
this._backButton.grab_key_focus();
|
||||
|
||||
this.setDate(newDate, false);
|
||||
this.setDate(newDate);
|
||||
},
|
||||
|
||||
_onNextMonthButtonClicked: function() {
|
||||
@ -544,28 +561,26 @@ const Calendar = new Lang.Class({
|
||||
|
||||
this._forwardButton.grab_key_focus();
|
||||
|
||||
this.setDate(newDate, false);
|
||||
this.setDate(newDate);
|
||||
},
|
||||
|
||||
_onSettingsChange: function() {
|
||||
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
|
||||
this._buildHeader();
|
||||
this._update(false);
|
||||
this._rebuildCalendar();
|
||||
this._update();
|
||||
},
|
||||
|
||||
_update: function(forceReload) {
|
||||
_rebuildCalendar: 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);
|
||||
|
||||
// Remove everything but the topBox and the weekday labels
|
||||
let children = this.actor.get_children();
|
||||
for (let i = this._firstDayIndex; i < children.length; i++)
|
||||
children[i].destroy();
|
||||
|
||||
this._buttons = [];
|
||||
|
||||
// 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
|
||||
@ -583,11 +598,13 @@ const Calendar = new Lang.Class({
|
||||
// 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);
|
||||
beginDate.setDate(1);
|
||||
beginDate.setSeconds(0);
|
||||
beginDate.setHours(12);
|
||||
|
||||
this._calendarBegin = new Date(beginDate);
|
||||
|
||||
let year = beginDate.getYear();
|
||||
|
||||
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
|
||||
@ -596,6 +613,7 @@ const Calendar = new Lang.Class({
|
||||
|
||||
beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
|
||||
|
||||
let layout = this.actor.layout_manager;
|
||||
let iter = new Date(beginDate);
|
||||
let row = 2;
|
||||
// nRows here means 6 weeks + one header + one navbar
|
||||
@ -608,13 +626,10 @@ const Calendar = new Lang.Class({
|
||||
if (this._eventSource.isDummy)
|
||||
button.reactive = false;
|
||||
|
||||
let iterStr = iter.toUTCString();
|
||||
button._date = new Date(iter);
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
this._shouldDateGrabFocus = true;
|
||||
|
||||
let newlySelectedDate = new Date(iterStr);
|
||||
this.setDate(newlySelectedDate, false);
|
||||
|
||||
this.setDate(button._date);
|
||||
this._shouldDateGrabFocus = false;
|
||||
}));
|
||||
|
||||
@ -622,9 +637,9 @@ const Calendar = new Lang.Class({
|
||||
let styleClass = 'calendar-day-base calendar-day';
|
||||
|
||||
if (_isWorkDay(iter))
|
||||
styleClass += ' calendar-work-day'
|
||||
styleClass += ' calendar-work-day';
|
||||
else
|
||||
styleClass += ' calendar-nonwork-day'
|
||||
styleClass += ' calendar-nonwork-day';
|
||||
|
||||
// Hack used in lieu of border-collapse - see gnome-shell.css
|
||||
if (row == 2)
|
||||
@ -641,26 +656,24 @@ const Calendar = new Lang.Class({
|
||||
styleClass += ' calendar-other-month-day';
|
||||
|
||||
if (hasEvents)
|
||||
styleClass += ' calendar-day-with-events'
|
||||
styleClass += ' calendar-day-with-events';
|
||||
|
||||
button.style_class = styleClass;
|
||||
|
||||
let offsetCols = this._useWeekdate ? 1 : 0;
|
||||
this.actor.add(button,
|
||||
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
|
||||
let col;
|
||||
if (rtl)
|
||||
col = 6 - (7 + iter.getDay() - this._weekStart) % 7;
|
||||
else
|
||||
col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
|
||||
layout.attach(button, col, row, 1, 1);
|
||||
|
||||
if (_sameDay(this._selectedDate, iter)) {
|
||||
button.add_style_pseudo_class('active');
|
||||
|
||||
if (this._shouldDateGrabFocus)
|
||||
button.grab_key_focus();
|
||||
}
|
||||
this._buttons.push(button);
|
||||
|
||||
if (this._useWeekdate && iter.getDay() == 4) {
|
||||
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
|
||||
style_class: 'calendar-day-base calendar-week-number'});
|
||||
this.actor.add(label,
|
||||
{ row: row, col: 0, y_align: St.Align.MIDDLE });
|
||||
layout.attach(label, rtl ? 7 : 0, row, 1, 1);
|
||||
}
|
||||
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
@ -668,9 +681,32 @@ const Calendar = new Lang.Class({
|
||||
if (iter.getDay() == this._weekStart)
|
||||
row++;
|
||||
}
|
||||
|
||||
// Signal to the event source that we are interested in events
|
||||
// only from this date range
|
||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
||||
this._eventSource.requestRange(beginDate, iter);
|
||||
},
|
||||
|
||||
_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');
|
||||
if (this._shouldDateGrabFocus)
|
||||
button.grab_key_focus();
|
||||
}
|
||||
else
|
||||
button.remove_style_pseudo_class('active');
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
@ -680,9 +716,12 @@ const EventsList = new Lang.Class({
|
||||
Name: 'EventsList',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.Table({ style_class: 'events-table' });
|
||||
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
|
||||
this.actor = new St.Widget({ style_class: 'events-table',
|
||||
layout_manager: layout });
|
||||
layout.hookup_style(this.actor);
|
||||
this._date = new Date();
|
||||
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
|
||||
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
|
||||
this._weekStart = Shell.util_get_week_start();
|
||||
},
|
||||
@ -692,68 +731,82 @@ const EventsList = new Lang.Class({
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
},
|
||||
|
||||
_addEvent: function(event, index, includeDayName) {
|
||||
_addEvent: function(event, index, includeDayName, periodBegin, periodEnd) {
|
||||
let dayString;
|
||||
if (includeDayName)
|
||||
dayString = _getEventDayAbbreviation(event.date.getDay());
|
||||
else
|
||||
if (includeDayName) {
|
||||
if (event.date >= periodBegin)
|
||||
dayString = _getEventDayAbbreviation(event.date.getDay());
|
||||
else /* show event end day if it began earlier */
|
||||
dayString = _getEventDayAbbreviation(event.end.getDay());
|
||||
} else {
|
||||
dayString = '';
|
||||
}
|
||||
|
||||
let dayLabel = new St.Label({ style_class: 'events-day-dayname',
|
||||
text: dayString });
|
||||
text: dayString,
|
||||
x_align: Clutter.ActorAlign.END,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
dayLabel.clutter_text.line_wrap = false;
|
||||
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 rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
let layout = this.actor.layout_manager;
|
||||
layout.attach(dayLabel, rtl ? 2 : 0, index, 1, 1);
|
||||
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
|
||||
let timeString = _formatEventTime(event, clockFormat);
|
||||
let timeString = _formatEventTime(event, clockFormat, periodBegin, periodEnd);
|
||||
let timeLabel = new St.Label({ style_class: 'events-day-time',
|
||||
text: timeString });
|
||||
text: timeString,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
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 preEllipsisLabel = new St.Label({ style_class: 'events-day-time-ellipses',
|
||||
text: ELLIPSIS_CHAR,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
let postEllipsisLabel = new St.Label({ style_class: 'events-day-time-ellipses',
|
||||
text: ELLIPSIS_CHAR,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
if (event.allDay || event.date >= periodBegin)
|
||||
preEllipsisLabel.opacity = 0;
|
||||
if (event.allDay || event.end <= periodEnd)
|
||||
postEllipsisLabel.opacity = 0;
|
||||
|
||||
let timeLabelBoxLayout = new St.BoxLayout();
|
||||
timeLabelBoxLayout.add(preEllipsisLabel);
|
||||
timeLabelBoxLayout.add(timeLabel);
|
||||
timeLabelBoxLayout.add(postEllipsisLabel);
|
||||
layout.attach(timeLabelBoxLayout, 1, index, 1, 1);
|
||||
|
||||
let titleLabel = new St.Label({ style_class: 'events-day-task',
|
||||
text: event.summary });
|
||||
text: event.summary,
|
||||
x_expand: true });
|
||||
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 });
|
||||
layout.attach(titleLabel, rtl ? 0 : 2, index, 1, 1);
|
||||
},
|
||||
|
||||
_addPeriod: function(header, index, begin, end, includeDayName, showNothingScheduled) {
|
||||
let events = this._eventSource.getEvents(begin, end);
|
||||
_addPeriod: function(header, index, periodBegin, periodEnd, includeDayName, showNothingScheduled) {
|
||||
let events = this._eventSource.getEvents(periodBegin, periodEnd);
|
||||
|
||||
if (events.length == 0 && !showNothingScheduled)
|
||||
return index;
|
||||
|
||||
this.actor.add(new St.Label({ style_class: 'events-day-header', text: header }),
|
||||
{ row: index, col: 0, col_span: 3,
|
||||
// In theory, x_expand should be true here, but x_expand
|
||||
// is a property of the column for StTable, ie all day cells
|
||||
// get it too
|
||||
x_expand: false, x_align: St.Align.START,
|
||||
y_fill: false, y_align: St.Align.START });
|
||||
let label = new St.Label({ style_class: 'events-day-header', text: header });
|
||||
let layout = this.actor.layout_manager;
|
||||
layout.attach(label, 0, index, 3, 1);
|
||||
index++;
|
||||
|
||||
for (let n = 0; n < events.length; n++) {
|
||||
this._addEvent(events[n], index, includeDayName);
|
||||
this._addEvent(events[n], index, includeDayName, periodBegin, periodEnd);
|
||||
index++;
|
||||
}
|
||||
|
||||
if (events.length == 0 && showNothingScheduled) {
|
||||
let now = new Date();
|
||||
/* Translators: Text to show if there are no events */
|
||||
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
|
||||
this._addEvent(nothingEvent, index, false);
|
||||
let nothingEvent = new CalendarEvent(periodBegin, periodBegin, _("Nothing Scheduled"), true);
|
||||
this._addEvent(nothingEvent, index, false, periodBegin, periodEnd);
|
||||
index++;
|
||||
}
|
||||
|
||||
@ -766,14 +819,17 @@ const EventsList = new Lang.Class({
|
||||
let dayBegin = _getBeginningOfDay(day);
|
||||
let dayEnd = _getEndOfDay(day);
|
||||
|
||||
let dayString;
|
||||
let dayFormat;
|
||||
let now = new Date();
|
||||
if (_sameYear(day, now))
|
||||
/* Translators: Shown on calendar heading when selected day occurs on current year */
|
||||
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d"));
|
||||
dayFormat = Shell.util_translate_time_string(NC_("calendar heading",
|
||||
"%A, %B %d"));
|
||||
else
|
||||
/* Translators: Shown on calendar heading when selected day occurs on different year */
|
||||
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
|
||||
dayFormat = Shell.util_translate_time_string(NC_("calendar heading",
|
||||
"%A, %B %d, %Y"));
|
||||
let dayString = day.toLocaleFormat(dayFormat);
|
||||
this._addPeriod(dayString, 0, dayBegin, dayEnd, false, true);
|
||||
},
|
||||
|
||||
|
@ -23,7 +23,7 @@ const AutomountManager = new Lang.Class({
|
||||
Name: 'AutomountManager',
|
||||
|
||||
_init: function() {
|
||||
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||
this._settings = new Gio.Settings({ schema_id: SETTINGS_SCHEMA });
|
||||
this._volumeQueue = [];
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._session.connectSignal('InhibitorAdded',
|
||||
@ -43,6 +43,7 @@ const AutomountManager = new Lang.Class({
|
||||
this._driveEjectButtonId = this._volumeMonitor.connect('drive-eject-button', Lang.bind(this, this._onDriveEjectButton));
|
||||
|
||||
this._mountAllId = Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
|
||||
GLib.Source.set_name_by_id(this._mountAllId, '[gnome-shell] this._startupMountAll');
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
@ -77,7 +78,7 @@ const AutomountManager = new Lang.Class({
|
||||
}));
|
||||
|
||||
this._mountAllId = 0;
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
_onDriveConnected: function() {
|
||||
@ -234,10 +235,11 @@ const AutomountManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_allowAutorunExpire: function(volume) {
|
||||
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
|
||||
let id = Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
|
||||
volume.allowAutorun = false;
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
GLib.Source.set_name_by_id(id, '[gnome-shell] volume.allowAutorun');
|
||||
}
|
||||
});
|
||||
const Component = AutomountManager;
|
||||
|
@ -64,7 +64,7 @@ function startAppForMount(app, mount) {
|
||||
|
||||
try {
|
||||
retval = app.launch(files,
|
||||
global.create_app_launch_context())
|
||||
global.create_app_launch_context(0, -1))
|
||||
} catch (e) {
|
||||
log('Unable to launch the application ' + app.get_name()
|
||||
+ ': ' + e.toString());
|
||||
@ -96,7 +96,7 @@ const ContentTypeDiscoverer = new Lang.Class({
|
||||
|
||||
_init: function(callback) {
|
||||
this._callback = callback;
|
||||
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||
this._settings = new Gio.Settings({ schema_id: SETTINGS_SCHEMA });
|
||||
},
|
||||
|
||||
guessContentTypes: function(mount) {
|
||||
@ -441,7 +441,7 @@ const AutorunTransientDispatcher = new Lang.Class({
|
||||
_init: function(manager) {
|
||||
this._manager = manager;
|
||||
this._sources = [];
|
||||
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
|
||||
this._settings = new Gio.Settings({ schema_id: SETTINGS_SCHEMA });
|
||||
},
|
||||
|
||||
_getAutorunSettingForType: function(contentType) {
|
||||
|
@ -45,7 +45,9 @@ const KeyringDialog = new Lang.Class({
|
||||
this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
this._messageBox.add(subject,
|
||||
{ y_fill: false,
|
||||
{ x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let description = new St.Label({ style_class: 'prompt-dialog-description' });
|
||||
@ -78,44 +80,58 @@ const KeyringDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_buildControlTable: function() {
|
||||
let layout = new Clutter.TableLayout();
|
||||
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
|
||||
let table = new St.Widget({ style_class: 'keyring-dialog-control-table',
|
||||
layout_manager: layout });
|
||||
layout.hookup_style(table);
|
||||
let rtl = table.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
let row = 0;
|
||||
|
||||
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',
|
||||
x_align: Clutter.ActorAlign.START,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
label.set_text(_("Password:"));
|
||||
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
layout.pack(label, 0, row);
|
||||
layout.child_set(label, { x_expand: false, y_fill: false,
|
||||
x_align: Clutter.TableAlignment.START });
|
||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: '',
|
||||
can_focus: true });
|
||||
can_focus: true,
|
||||
x_expand: true });
|
||||
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate));
|
||||
layout.pack(this._passwordEntry, 1, row);
|
||||
|
||||
if (rtl) {
|
||||
layout.attach(this._passwordEntry, 0, row, 1, 1);
|
||||
layout.attach(label, 1, row, 1, 1);
|
||||
} else {
|
||||
layout.attach(label, 0, row, 1, 1);
|
||||
layout.attach(this._passwordEntry, 1, row, 1, 1);
|
||||
}
|
||||
row++;
|
||||
} else {
|
||||
this._passwordEntry = null;
|
||||
}
|
||||
|
||||
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',
|
||||
x_align: Clutter.ActorAlign.START,
|
||||
y_align: Clutter.ActorAlign.CENTER }));
|
||||
label.set_text(_("Type again:"));
|
||||
layout.pack(label, 0, row);
|
||||
layout.child_set(label, { x_expand: false, y_fill: false,
|
||||
x_align: Clutter.TableAlignment.START });
|
||||
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: '',
|
||||
can_focus: true });
|
||||
can_focus: true,
|
||||
x_expand: true });
|
||||
this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||
ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
|
||||
this._confirmEntry.clutter_text.connect('activate', Lang.bind(this, this._onConfirmActivate));
|
||||
layout.pack(this._confirmEntry, 1, row);
|
||||
if (rtl) {
|
||||
layout.attach(this._confirmEntry, 0, row, 1, 1);
|
||||
layout.attach(label, 1, row, 1, 1);
|
||||
} else {
|
||||
layout.attach(label, 0, row, 1, 1);
|
||||
layout.attach(this._confirmEntry, 1, row, 1, 1);
|
||||
}
|
||||
row++;
|
||||
} else {
|
||||
this._confirmEntry = null;
|
||||
@ -128,14 +144,15 @@ const KeyringDialog = new Lang.Class({
|
||||
let choice = new CheckBox.CheckBox();
|
||||
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);
|
||||
layout.pack(choice.actor, 1, row);
|
||||
layout.attach(choice.actor, rtl ? 0 : 1, row, 1, 1);
|
||||
row++;
|
||||
}
|
||||
|
||||
let warning = new St.Label({ style_class: 'prompt-dialog-error-label' });
|
||||
let warning = new St.Label({ style_class: 'prompt-dialog-error-label',
|
||||
x_align: Clutter.ActorAlign.START });
|
||||
warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
warning.clutter_text.line_wrap = true;
|
||||
layout.pack(warning, 1, row);
|
||||
layout.attach(warning, rtl ? 0 : 1, row, 1, 1);
|
||||
this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
|
||||
this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
|
@ -72,24 +72,28 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
expand: true });
|
||||
}
|
||||
|
||||
let layout = new Clutter.TableLayout();
|
||||
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
|
||||
let secretTable = new St.Widget({ style_class: 'network-dialog-secret-table',
|
||||
layout_manager: layout });
|
||||
layout.hookup_style(secretTable);
|
||||
|
||||
let rtl = secretTable.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
let initialFocusSet = false;
|
||||
let pos = 0;
|
||||
for (let i = 0; i < this._content.secrets.length; i++) {
|
||||
let secret = this._content.secrets[i];
|
||||
let label = new St.Label({ style_class: 'prompt-dialog-password-label',
|
||||
text: secret.label });
|
||||
text: secret.label,
|
||||
x_align: Clutter.ActorAlign.START,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
|
||||
let reactive = secret.key != null;
|
||||
|
||||
secret.entry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||
text: secret.value, can_focus: reactive,
|
||||
reactive: reactive });
|
||||
reactive: reactive,
|
||||
x_expand: true });
|
||||
ShellEntry.addContextMenu(secret.entry,
|
||||
{ isPassword: secret.password });
|
||||
|
||||
@ -116,10 +120,13 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
} else
|
||||
secret.valid = true;
|
||||
|
||||
layout.pack(label, 0, pos);
|
||||
layout.child_set(label, { x_expand: false, y_fill: false,
|
||||
x_align: Clutter.TableAlignment.START });
|
||||
layout.pack(secret.entry, 1, pos);
|
||||
if (rtl) {
|
||||
layout.attach(secret.entry, 0, pos, 1, 1);
|
||||
layout.attach(label, 1, pos, 1, 1);
|
||||
} else {
|
||||
layout.attach(label, 0, pos, 1, 1);
|
||||
layout.attach(secret.entry, 1, pos, 1, 1);
|
||||
}
|
||||
pos++;
|
||||
|
||||
if (secret.password)
|
||||
@ -138,6 +145,8 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
key: Clutter.KEY_Escape,
|
||||
},
|
||||
this._okButton]);
|
||||
|
||||
this._updateOkButton();
|
||||
},
|
||||
|
||||
_updateOkButton: function() {
|
||||
@ -253,6 +262,7 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
case 'leap':
|
||||
case 'ttls':
|
||||
case 'peap':
|
||||
case 'fast':
|
||||
// TTLS and PEAP are actually much more complicated, but this complication
|
||||
// is not visible here since we only care about phase2 authentication
|
||||
// (and don't even care of which one)
|
||||
@ -306,7 +316,7 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
wirelessSetting = this._connection.get_setting_wireless();
|
||||
ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
|
||||
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);
|
||||
break;
|
||||
case '802-3-ethernet':
|
||||
@ -333,7 +343,7 @@ const NetworkSecretDialog = new Lang.Class({
|
||||
case 'cdma':
|
||||
case 'bluetooth':
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
@ -370,6 +380,12 @@ const VPNRequestHandler = new Lang.Class({
|
||||
argv.push('-i');
|
||||
if (flags & NMClient.SecretAgentGetSecretsFlags.REQUEST_NEW)
|
||||
argv.push('-r');
|
||||
if (authHelper.supportsHints) {
|
||||
for (let i = 0; i < hints.length; i++) {
|
||||
argv.push('-t');
|
||||
argv.push(hints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this._newStylePlugin = authHelper.externalUIMode;
|
||||
|
||||
@ -432,6 +448,7 @@ const VPNRequestHandler = new Lang.Class({
|
||||
},
|
||||
|
||||
_vpnChildFinished: function(pid, status, requestObj) {
|
||||
this._childWatch = 0;
|
||||
if (this._newStylePlugin) {
|
||||
// For new style plugin, all work is done in the async reading functions
|
||||
// Just reap the process here
|
||||
@ -506,10 +523,12 @@ const VPNRequestHandler = new Lang.Class({
|
||||
|
||||
_showNewStyleDialog: function() {
|
||||
let keyfile = new GLib.KeyFile();
|
||||
let data;
|
||||
let contentOverride;
|
||||
|
||||
try {
|
||||
let data = this._dataStdout.peek_buffer();
|
||||
data = this._dataStdout.peek_buffer();
|
||||
|
||||
keyfile.load_from_data(data.toString(), data.length,
|
||||
GLib.KeyFileFlags.NONE);
|
||||
|
||||
@ -542,13 +561,16 @@ const VPNRequestHandler = new Lang.Class({
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
logError(e, 'error while reading VPN plugin output keyfile');
|
||||
// No output is a valid case it means "both secrets are stored"
|
||||
if (data.length > 0) {
|
||||
logError(e, 'error while reading VPN plugin output keyfile');
|
||||
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
|
||||
return;
|
||||
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (contentOverride.secrets.length) {
|
||||
if (contentOverride && contentOverride.secrets.length) {
|
||||
// Only show the dialog if we actually have something to ask
|
||||
this._shellDialog = new NetworkSecretDialog(this._agent, this._requestId, this._connection, 'vpn', [], contentOverride);
|
||||
this._shellDialog.open(global.get_current_time());
|
||||
@ -582,7 +604,9 @@ const NetworkAgent = new Lang.Class({
|
||||
Name: 'NetworkAgent',
|
||||
|
||||
_init: function() {
|
||||
this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent' });
|
||||
this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent',
|
||||
capabilities: NMClient.SecretAgentCapabilities.VPN_HINTS
|
||||
});
|
||||
|
||||
this._dialogs = { };
|
||||
this._vpnRequests = { };
|
||||
@ -682,16 +706,23 @@ const NetworkAgent = new Lang.Class({
|
||||
let service = keyfile.get_string('VPN Connection', 'service');
|
||||
let binary = keyfile.get_string('GNOME', 'auth-dialog');
|
||||
let externalUIMode = false;
|
||||
let hints = false;
|
||||
|
||||
try {
|
||||
externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode');
|
||||
} catch(e) { } // ignore errors if key does not exist
|
||||
|
||||
try {
|
||||
hints = keyfile.get_boolean('GNOME', 'supports-hints');
|
||||
} catch(e) { } // ignore errors if key does not exist
|
||||
|
||||
let path = binary;
|
||||
if (!GLib.path_is_absolute(path)) {
|
||||
path = GLib.build_filenamev([Config.LIBEXECDIR, path]);
|
||||
}
|
||||
|
||||
if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE))
|
||||
this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode };
|
||||
this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode, supportsHints: hints };
|
||||
else
|
||||
throw new Error('VPN plugin at %s is not executable'.format(path));
|
||||
} catch(e) {
|
||||
|
@ -54,7 +54,9 @@ const AuthenticationDialog = new Lang.Class({
|
||||
text: _("Authentication Required") });
|
||||
|
||||
messageBox.add(this._subjectLabel,
|
||||
{ y_fill: false,
|
||||
{ x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
|
||||
@ -63,7 +65,9 @@ const AuthenticationDialog = new Lang.Class({
|
||||
this._descriptionLabel.clutter_text.line_wrap = true;
|
||||
|
||||
messageBox.add(this._descriptionLabel,
|
||||
{ y_fill: true,
|
||||
{ x_fill: false,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
if (userNames.length > 1) {
|
||||
@ -95,7 +99,8 @@ const AuthenticationDialog = new Lang.Class({
|
||||
if (userIsRoot) {
|
||||
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label',
|
||||
text: userRealName }));
|
||||
messageBox.add(userLabel);
|
||||
messageBox.add(userLabel, { x_fill: false,
|
||||
x_align: St.Align.START });
|
||||
} else {
|
||||
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
|
||||
vertical: false });
|
||||
@ -137,7 +142,7 @@ const AuthenticationDialog = new Lang.Class({
|
||||
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' });
|
||||
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
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._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' });
|
||||
|
@ -1,5 +1,6 @@
|
||||
// -*- 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 Lang = imports.lang;
|
||||
@ -28,11 +29,15 @@ const SCROLLBACK_HISTORY_LINES = 10;
|
||||
// See Notification._onEntryChanged
|
||||
const COMPOSING_STOP_TIMEOUT = 5;
|
||||
|
||||
const CLOCK_FORMAT_KEY = 'clock-format';
|
||||
|
||||
const NotificationDirection = {
|
||||
SENT: 'chat-sent',
|
||||
RECEIVED: 'chat-received'
|
||||
};
|
||||
|
||||
const N_ = function(s) { return s; };
|
||||
|
||||
function makeMessageFromTpMessage(tpMessage, direction) {
|
||||
let [text, flags] = tpMessage.to_text();
|
||||
|
||||
@ -621,7 +626,11 @@ const ChatSource = new Lang.Class({
|
||||
this.notify();
|
||||
},
|
||||
|
||||
_channelClosed: function() {
|
||||
destroy: function(reason) {
|
||||
if (this._destroyed)
|
||||
return;
|
||||
|
||||
this._destroyed = true;
|
||||
this._channel.disconnect(this._closedId);
|
||||
this._channel.disconnect(this._receivedId);
|
||||
this._channel.disconnect(this._pendingId);
|
||||
@ -631,7 +640,14 @@ const ChatSource = new Lang.Class({
|
||||
this._contact.disconnect(this._notifyAvatarId);
|
||||
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 */
|
||||
@ -667,6 +683,7 @@ const ChatSource = new Lang.Class({
|
||||
Mainloop.source_remove(this._notifyTimeoutId);
|
||||
this._notifyTimeoutId = Mainloop.timeout_add(500,
|
||||
Lang.bind(this, this._notifyTimeout));
|
||||
GLib.Source.set_name_by_id(this._notifyTimeoutId, '[gnome-shell] this._notifyTimeout');
|
||||
},
|
||||
|
||||
_notifyTimeout: function() {
|
||||
@ -675,7 +692,7 @@ const ChatSource = new Lang.Class({
|
||||
|
||||
this._notifyTimeoutId = 0;
|
||||
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
// This is called for both messages we send from
|
||||
@ -768,7 +785,6 @@ const ChatNotification = new Lang.Class({
|
||||
|
||||
this._createScrollArea();
|
||||
this._lastGroup = null;
|
||||
this._lastGroupActor = null;
|
||||
|
||||
// Keep track of the bottom position for the current adjustment and
|
||||
// force a scroll to the bottom if things change while we were at the
|
||||
@ -849,13 +865,6 @@ const ChatNotification = new Lang.Class({
|
||||
for (let i = 0; i < expired.length; i++)
|
||||
expired[i].actor.destroy();
|
||||
}
|
||||
|
||||
let groups = this._contentArea.get_children();
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
let group = groups[i];
|
||||
if (group.get_n_children() == 0)
|
||||
group.destroy();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -894,30 +903,35 @@ const ChatNotification = new Lang.Class({
|
||||
|
||||
let group = props.group;
|
||||
if (group != this._lastGroup) {
|
||||
let style = 'chat-group-' + group;
|
||||
this._lastGroup = group;
|
||||
this._lastGroupActor = new St.BoxLayout({ style_class: style,
|
||||
vertical: true });
|
||||
this.addActor(this._lastGroupActor);
|
||||
let emptyLine = new St.Label({ style_class: 'chat-empty-line' });
|
||||
this.addActor(emptyLine);
|
||||
this._history.unshift({ actor: emptyLine, time: timestamp,
|
||||
realMessage: false });
|
||||
}
|
||||
|
||||
this._lastGroupActor.add(body, props.childProps);
|
||||
let lineBox = new St.BoxLayout({ vertical: false });
|
||||
lineBox.add(body, props.childProps);
|
||||
this.addActor(lineBox);
|
||||
this._lastMessageBox = lineBox;
|
||||
|
||||
this.updated();
|
||||
|
||||
let timestamp = props.timestamp;
|
||||
this._history.unshift({ actor: body, time: timestamp,
|
||||
this._history.unshift({ actor: lineBox, time: timestamp,
|
||||
realMessage: group != 'meta' });
|
||||
|
||||
if (!props.noTimestamp) {
|
||||
if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME)
|
||||
if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) {
|
||||
this.appendTimestamp();
|
||||
else
|
||||
} else {
|
||||
// Schedule a new timestamp in SCROLLBACK_IMMEDIATE_TIME
|
||||
// from the timestamp of the message.
|
||||
this._timestampTimeoutId = Mainloop.timeout_add_seconds(
|
||||
SCROLLBACK_IMMEDIATE_TIME - (currentTime - timestamp),
|
||||
Lang.bind(this, this.appendTimestamp));
|
||||
GLib.Source.set_name_by_id(this._timestampTimeoutId, '[gnome-shell] this.appendTimestamp');
|
||||
}
|
||||
}
|
||||
|
||||
this._filterMessages();
|
||||
@ -930,50 +944,98 @@ const ChatNotification = new Lang.Class({
|
||||
|
||||
let format;
|
||||
|
||||
// Show only the hour if date is on today
|
||||
if(daysAgo < 1){
|
||||
format = "<b>%H:%M</b>";
|
||||
}
|
||||
// Show the word "Yesterday" and time if date is on yesterday
|
||||
else if(daysAgo <2){
|
||||
/* Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"*/
|
||||
// xgettext:no-c-format
|
||||
format = _("<b>Yesterday</b>, <b>%H:%M</b>");
|
||||
}
|
||||
// Show a week day and time if date is in the last week
|
||||
else if (daysAgo < 7) {
|
||||
/* Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30*/
|
||||
// xgettext:no-c-format
|
||||
format = _("<b>%A</b>, <b>%H:%M</b>");
|
||||
let desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
|
||||
let clockFormat = desktopSettings.get_string(CLOCK_FORMAT_KEY);
|
||||
let hasAmPm = date.toLocaleFormat('%p') != '';
|
||||
|
||||
} else if (date.getYear() == now.getYear()) {
|
||||
/* Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"*/
|
||||
// xgettext:no-c-format
|
||||
format = _("<b>%B</b> <b>%d</b>, <b>%H:%M</b>");
|
||||
if (clockFormat == '24h' || !hasAmPm) {
|
||||
// Show only the time if date is on today
|
||||
if(daysAgo < 1){
|
||||
/* Translators: Time in 24h format */
|
||||
format = N_("%H\u2236%M");
|
||||
}
|
||||
// Show the word "Yesterday" and time if date is on yesterday
|
||||
else if(daysAgo <2){
|
||||
/* Translators: this is the word "Yesterday" followed by a
|
||||
time string in 24h format. i.e. "Yesterday, 14:30" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("Yesterday, %H\u2236%M");
|
||||
}
|
||||
// Show a week day and time if date is in the last week
|
||||
else if (daysAgo < 7) {
|
||||
/* Translators: this is the week day name followed by a time
|
||||
string in 24h format. i.e. "Monday, 14:30" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("%A, %H\u2236%M");
|
||||
|
||||
} else if (date.getYear() == now.getYear()) {
|
||||
/* Translators: this is the month name and day number
|
||||
followed by a time string in 24h format.
|
||||
i.e. "May 25, 14:30" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("%B %d, %H\u2236%M");
|
||||
} else {
|
||||
/* Translators: this is the month name, day number, year
|
||||
number followed by a time string in 24h format.
|
||||
i.e. "May 25 2012, 14:30" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("%B %d %Y, %H\u2236%M");
|
||||
}
|
||||
} else {
|
||||
/* Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"*/
|
||||
// xgettext:no-c-format
|
||||
format = _("<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> ");
|
||||
}
|
||||
// Show only the time if date is on today
|
||||
if(daysAgo < 1){
|
||||
/* Translators: Time in 24h format */
|
||||
format = N_("%l\u2236%M %p");
|
||||
}
|
||||
// Show the word "Yesterday" and time if date is on yesterday
|
||||
else if(daysAgo <2){
|
||||
/* Translators: this is the word "Yesterday" followed by a
|
||||
time string in 12h format. i.e. "Yesterday, 2:30 pm" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("Yesterday, %l\u2236%M %p");
|
||||
}
|
||||
// Show a week day and time if date is in the last week
|
||||
else if (daysAgo < 7) {
|
||||
/* Translators: this is the week day name followed by a time
|
||||
string in 12h format. i.e. "Monday, 2:30 pm" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("%A, %l\u2236%M %p");
|
||||
|
||||
return date.toLocaleFormat(format);
|
||||
} else if (date.getYear() == now.getYear()) {
|
||||
/* Translators: this is the month name and day number
|
||||
followed by a time string in 12h format.
|
||||
i.e. "May 25, 2:30 pm" */
|
||||
// xgettext:no-c-format
|
||||
format = N_("%B %d, %l\u2236%M %p");
|
||||
} else {
|
||||
/* Translators: this is the month name, day number, year
|
||||
number followed by a time string in 12h format.
|
||||
i.e. "May 25 2012, 2:30 pm"*/
|
||||
// xgettext:no-c-format
|
||||
format = N_("%B %d %Y, %l\u2236%M %p");
|
||||
}
|
||||
}
|
||||
return date.toLocaleFormat(Shell.util_translate_time_string(format));
|
||||
},
|
||||
|
||||
appendTimestamp: function() {
|
||||
this._timestampTimeoutId = 0;
|
||||
|
||||
let lastMessageTime = this._history[0].time;
|
||||
let lastMessageDate = new Date(lastMessageTime * 1000);
|
||||
|
||||
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
|
||||
group: 'meta',
|
||||
styles: ['chat-meta-message'],
|
||||
childProps: { expand: true, x_fill: false,
|
||||
x_align: St.Align.END },
|
||||
noTimestamp: true,
|
||||
timestamp: lastMessageTime });
|
||||
let timeLabel = new St.Label({ text: this._formatTimestamp(lastMessageDate),
|
||||
style_class: 'chat-meta-message',
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
x_align: Clutter.ActorAlign.END,
|
||||
y_align: Clutter.ActorAlign.END });
|
||||
|
||||
this._lastMessageBox.add_actor(timeLabel);
|
||||
|
||||
this._filterMessages();
|
||||
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
appendAliasChange: function(oldAlias, newAlias) {
|
||||
@ -1011,7 +1073,7 @@ const ChatNotification = new Lang.Class({
|
||||
|
||||
this.source.setChatState(Tp.ChannelChatState.PAUSED);
|
||||
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
_onEntryChanged: function() {
|
||||
@ -1034,6 +1096,7 @@ const ChatNotification = new Lang.Class({
|
||||
this._composingTimeoutId = Mainloop.timeout_add_seconds(
|
||||
COMPOSING_STOP_TIMEOUT,
|
||||
Lang.bind(this, this._composingStopTimeout));
|
||||
GLib.Source.set_name_by_id(this._composingTimeoutId, '[gnome-shell] this._composingStopTimeout');
|
||||
} else {
|
||||
this.source.setChatState(Tp.ChannelChatState.ACTIVE);
|
||||
}
|
||||
@ -1203,8 +1266,8 @@ const SubscriptionRequestNotification = new Lang.Class({
|
||||
let file = contact.get_avatar_file();
|
||||
|
||||
if (file) {
|
||||
let uri = file.get_uri();
|
||||
iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size);
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
iconBox.child = textureCache.load_file_async(file, iconBox._size, iconBox._size, scaleFactor);
|
||||
}
|
||||
else {
|
||||
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
|
||||
@ -1337,7 +1400,7 @@ const AccountNotification = new Lang.Class({
|
||||
let cmd = 'empathy-accounts --select-account=' +
|
||||
account.get_path_suffix();
|
||||
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));
|
||||
}));
|
||||
|
||||
this._enabledId = account.connect('notify::enabled',
|
||||
@ -1355,7 +1418,12 @@ const AccountNotification = new Lang.Class({
|
||||
if (status == Tp.ConnectionStatus.CONNECTED) {
|
||||
this.destroy();
|
||||
} else if (status == Tp.ConnectionStatus.DISCONNECTED) {
|
||||
this.update(this.title, this._getMessage(account.connection_error));
|
||||
let connectionError = account.connection_error;
|
||||
|
||||
if (connectionError == Tp.error_get_dbus_name(Tp.Error.CANCELLED))
|
||||
this.destroy();
|
||||
else
|
||||
this.update(this.title, this._getMessage(connectionError));
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
@ -87,7 +87,7 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
if (Main.sessionMode.hasWindows && !Main.overview.visible) {
|
||||
let screen = global.screen;
|
||||
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.get_active_workspace ());
|
||||
let windowTracker = Shell.WindowTracker.get_default();
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
@ -140,31 +140,25 @@ const CtrlAltTabPopup = new Lang.Class({
|
||||
Name: 'CtrlAltTabPopup',
|
||||
Extends: SwitcherPopup.SwitcherPopup,
|
||||
|
||||
_createSwitcher: function() {
|
||||
_init: function(items) {
|
||||
this.parent(items);
|
||||
|
||||
this._switcherList = new CtrlAltTabSwitcher(this._items);
|
||||
return true;
|
||||
},
|
||||
|
||||
_initialSelection: function(backward, binding) {
|
||||
if (binding == 'switch-panels') {
|
||||
if (backward)
|
||||
this._selectedIndex = this._items.length - 1;
|
||||
} else if (binding == 'switch-panels-backward') {
|
||||
if (!backward)
|
||||
this._selectedIndex = this._items.length - 1;
|
||||
}
|
||||
this._select(this._selectedIndex);
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, backwards, action) {
|
||||
_keyPressHandler: function(keysym, action) {
|
||||
if (action == Meta.KeyBindingAction.SWITCH_PANELS)
|
||||
this._select(backwards ? this._previous() : this._next());
|
||||
this._select(this._next());
|
||||
else if (action == Meta.KeyBindingAction.SWITCH_PANELS_BACKWARD)
|
||||
this._select(backwards ? this._next() : this._previous());
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.Left)
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.Right)
|
||||
this._select(this._next());
|
||||
else
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_finish : function(time) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Signals = imports.signals;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -380,6 +381,8 @@ const DashActor = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const baseIconSizes = [ 16, 22, 24, 32, 48, 64 ];
|
||||
|
||||
const Dash = new Lang.Class({
|
||||
Name: 'Dash',
|
||||
|
||||
@ -576,8 +579,10 @@ const Dash = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this._labelShowing = true;
|
||||
item.showLabel();
|
||||
return false;
|
||||
this._showLabelTimeoutId = 0;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(this._showLabelTimeoutId, '[gnome-shell] item.showLabel');
|
||||
if (this._resetHoverTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._resetHoverTimeoutId);
|
||||
this._resetHoverTimeoutId = 0;
|
||||
@ -592,8 +597,10 @@ const Dash = new Lang.Class({
|
||||
this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
|
||||
Lang.bind(this, function() {
|
||||
this._labelShowing = false;
|
||||
return false;
|
||||
this._resetHoverTimeoutId = 0;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(this._resetHoverTimeoutId, '[gnome-shell] this._labelShowing');
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -629,25 +636,24 @@ const Dash = new Lang.Class({
|
||||
let minHeight, natHeight;
|
||||
|
||||
// Enforce the current icon size during the size request
|
||||
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
|
||||
|
||||
firstIcon.icon.set_size(this.iconSize, this.iconSize);
|
||||
firstIcon.setIconSize(this.iconSize);
|
||||
[minHeight, natHeight] = firstButton.get_preferred_height(-1);
|
||||
|
||||
firstIcon.icon.set_size(currentWidth, currentHeight);
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
let iconSizes = baseIconSizes.map(function(s) {
|
||||
return s * scaleFactor;
|
||||
});
|
||||
|
||||
// Subtract icon padding and box spacing from the available height
|
||||
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
|
||||
availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) +
|
||||
(iconChildren.length - 1) * spacing;
|
||||
|
||||
let availSize = availHeight / iconChildren.length;
|
||||
|
||||
let iconSizes = [ 16, 22, 24, 32, 48, 64 ];
|
||||
|
||||
let newIconSize = 16;
|
||||
let newIconSize = baseIconSizes[0];
|
||||
for (let i = 0; i < iconSizes.length; i++) {
|
||||
if (iconSizes[i] < availSize)
|
||||
newIconSize = iconSizes[i];
|
||||
newIconSize = baseIconSizes[i];
|
||||
}
|
||||
|
||||
if (newIconSize == this.iconSize)
|
||||
|
@ -3,6 +3,7 @@
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Cairo = imports.cairo;
|
||||
@ -18,8 +19,7 @@ const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Calendar = imports.ui.calendar;
|
||||
|
||||
function _onVertSepRepaint (area)
|
||||
{
|
||||
function _onVertSepRepaint(area) {
|
||||
let cr = area.get_context();
|
||||
let themeNode = area.get_theme_node();
|
||||
let [width, height] = area.get_surface_size();
|
||||
@ -33,7 +33,7 @@ function _onVertSepRepaint (area)
|
||||
cr.setLineWidth(stippleWidth);
|
||||
cr.stroke();
|
||||
cr.$dispose();
|
||||
};
|
||||
}
|
||||
|
||||
const DateMenuButton = new Lang.Class({
|
||||
Name: 'DateMenuButton',
|
||||
@ -63,9 +63,17 @@ const DateMenuButton = new Lang.Class({
|
||||
hbox.add(vbox);
|
||||
|
||||
// Date
|
||||
this._date = new St.Label({ style_class: 'datemenu-date-label',
|
||||
can_focus: true });
|
||||
vbox.add(this._date);
|
||||
// Having the ability to go to the current date if the user is already
|
||||
// on the current date can be confusing. So don't make the button reactive
|
||||
// until the selected date changes.
|
||||
this._date = new St.Button({ style_class: 'datemenu-date-label',
|
||||
reactive: false
|
||||
});
|
||||
this._date.connect('clicked',
|
||||
Lang.bind(this, function() {
|
||||
this._calendar.setDate(new Date(), false);
|
||||
}));
|
||||
vbox.add(this._date, { x_fill: false });
|
||||
|
||||
this._eventList = new Calendar.EventsList();
|
||||
this._calendar = new Calendar.Calendar();
|
||||
@ -77,6 +85,9 @@ const DateMenuButton = new Lang.Class({
|
||||
// and the calender makes those dates unclickable when instantiated with
|
||||
// a null event source
|
||||
this._eventList.setDate(date);
|
||||
|
||||
// Make the button reactive only if the selected date is not the current date.
|
||||
this._date.can_focus = this._date.reactive = !this._isToday(date)
|
||||
}));
|
||||
vbox.add(this._calendar.actor);
|
||||
|
||||
@ -113,35 +124,32 @@ const DateMenuButton = new Lang.Class({
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
if (isOpen) {
|
||||
let now = new Date();
|
||||
/* Passing true to setDate() forces events to be reloaded. We
|
||||
* 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);
|
||||
|
||||
/* Translators: This is the date format to use when the calendar popup is
|
||||
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
*/
|
||||
this._calendar.setDate(now, true);
|
||||
// No need to update this._eventList as ::selected-date-changed
|
||||
// signal will fire
|
||||
let dateFormat = Shell.util_translate_time_string ("%A %B %e, %Y");
|
||||
this._date.set_label(now.toLocaleFormat(dateFormat));
|
||||
}
|
||||
}));
|
||||
|
||||
// Done with hbox for calendar and event list
|
||||
|
||||
this._clock = new GnomeDesktop.WallClock();
|
||||
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
|
||||
this._updateClockAndDate();
|
||||
this._clock.bind_property('clock', this._clockDisplay, 'text', GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_isToday: function(date) {
|
||||
let now = new Date();
|
||||
return now.getYear() == date.getYear() &&
|
||||
now.getMonth() == date.getMonth() &&
|
||||
now.getDay() == date.getDay();
|
||||
},
|
||||
|
||||
_appInstalledChanged: function() {
|
||||
this._calendarApp = undefined;
|
||||
this._updateEventsVisibility();
|
||||
@ -194,30 +202,23 @@ const DateMenuButton = new Lang.Class({
|
||||
this._dateAndTimeSeparator.actor.visible = Main.sessionMode.allowSettings;
|
||||
},
|
||||
|
||||
_updateClockAndDate: function() {
|
||||
this._clockDisplay.set_text(this._clock.clock);
|
||||
/* Translators: This is the date format to use when the calendar popup is
|
||||
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
*/
|
||||
let dateFormat = _("%A %B %e, %Y");
|
||||
let displayDate = new Date();
|
||||
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
|
||||
if (apps && (apps.length > 0)) {
|
||||
let app = Gio.AppInfo.get_default_for_type('text/calendar', false);
|
||||
let defaultInRecommended = apps.some(function(a) { return a.equal(app); });
|
||||
this._calendarApp = defaultInRecommended ? app : apps[0];
|
||||
} else {
|
||||
this._calendarApp = null;
|
||||
}
|
||||
return this._calendarApp;
|
||||
},
|
||||
|
||||
_getClockApp: function() {
|
||||
return Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
|
||||
return Shell.AppSystem.get_default().lookup_app('org.gnome.clocks.desktop');
|
||||
},
|
||||
|
||||
_onOpenCalendarActivate: function() {
|
||||
@ -226,7 +227,7 @@ const DateMenuButton = new Lang.Class({
|
||||
let app = this._getCalendarApp();
|
||||
if (app.get_id() == 'evolution.desktop')
|
||||
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
|
||||
app.launch([], global.create_app_launch_context());
|
||||
app.launch([], global.create_app_launch_context(0, -1));
|
||||
},
|
||||
|
||||
_onOpenClocksActivate: function() {
|
||||
|
32
js/ui/dnd.js
32
js/ui/dnd.js
@ -106,10 +106,10 @@ const _Draggable = new Lang.Class({
|
||||
|
||||
_onButtonPress : function (actor, event) {
|
||||
if (event.get_button() != 1)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (Tweener.getTweenCount(actor))
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
this._buttonDown = true;
|
||||
this._grabActor();
|
||||
@ -118,7 +118,7 @@ const _Draggable = new Lang.Class({
|
||||
this._dragStartX = stageX;
|
||||
this._dragStartY = stageY;
|
||||
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_grabActor: function() {
|
||||
@ -164,11 +164,11 @@ const _Draggable = new Lang.Class({
|
||||
} else if (this._dragActor != null && !this._animationInProgress) {
|
||||
// Drag must have been cancelled with Esc.
|
||||
this._dragComplete();
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
} else {
|
||||
// Drag has never started.
|
||||
this._ungrabActor();
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
// 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
|
||||
@ -184,11 +184,11 @@ const _Draggable = new Lang.Class({
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.Escape) {
|
||||
this._cancelDrag(event.get_time());
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -236,7 +236,7 @@ const _Draggable = new Lang.Class({
|
||||
|
||||
if (this.actor._delegate && this.actor._delegate.getDragActor) {
|
||||
this._dragActor = this.actor._delegate.getDragActor();
|
||||
this._dragActor.reparent(Main.uiGroup);
|
||||
Main.uiGroup.add_child(this._dragActor);
|
||||
this._dragActor.raise_top();
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||
|
||||
@ -285,7 +285,8 @@ const _Draggable = new Lang.Class({
|
||||
this._dragOffsetX = actorStageX - this._dragStartX;
|
||||
this._dragOffsetY = actorStageY - this._dragStartY;
|
||||
|
||||
this._dragActor.reparent(Main.uiGroup);
|
||||
this._dragOrigParent.remove_actor(this._dragActor);
|
||||
Main.uiGroup.add_child(this._dragActor);
|
||||
this._dragActor.raise_top();
|
||||
Shell.util_set_hidden_from_pick(this._dragActor, true);
|
||||
}
|
||||
@ -345,6 +346,7 @@ const _Draggable = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateDragHover : function () {
|
||||
this._updateHoverId = 0;
|
||||
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
|
||||
this._dragX, this._dragY);
|
||||
let dragEvent = {
|
||||
@ -360,7 +362,7 @@ const _Draggable = new Lang.Class({
|
||||
let result = motionFunc(dragEvent);
|
||||
if (result != DragMotionResult.CONTINUE) {
|
||||
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -378,21 +380,22 @@ const _Draggable = new Lang.Class({
|
||||
0);
|
||||
if (result != DragMotionResult.CONTINUE) {
|
||||
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
}
|
||||
target = target.get_parent();
|
||||
}
|
||||
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
_queueUpdateDragHover: function() {
|
||||
if (this._updateHoverId)
|
||||
GLib.source_remove(this._updateHoverId);
|
||||
return;
|
||||
|
||||
this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
|
||||
Lang.bind(this, this._updateDragHover));
|
||||
GLib.Source.set_name_by_id(this._updateHoverId, '[gnome-shell] this._updateDragHover');
|
||||
},
|
||||
|
||||
_updateDragPosition : function (event) {
|
||||
@ -555,7 +558,8 @@ const _Draggable = new Lang.Class({
|
||||
|
||||
_onAnimationComplete : function (dragActor, eventTime) {
|
||||
if (this._dragOrigParent) {
|
||||
dragActor.reparent(this._dragOrigParent);
|
||||
Main.uiGroup.remove_child(this._dragActor);
|
||||
this._dragOrigParent.add_actor(this._dragActor);
|
||||
dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
|
||||
dragActor.set_position(this._dragOrigX, this._dragOrigY);
|
||||
} else {
|
||||
|
78
js/ui/edgeDragAction.js
Normal file
78
js/ui/edgeDragAction.js
Normal file
@ -0,0 +1,78 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const EDGE_THRESHOLD = 20;
|
||||
const DRAG_DISTANCE = 80;
|
||||
|
||||
const EdgeDragAction = new Lang.Class({
|
||||
Name: 'EdgeDragAction',
|
||||
Extends: Clutter.GestureAction,
|
||||
|
||||
_init : function(side) {
|
||||
this.parent();
|
||||
this._side = side;
|
||||
this.set_n_touch_points(1);
|
||||
|
||||
global.display.connect('grab-op-begin', Lang.bind(this, function() {
|
||||
this.cancel();
|
||||
}));
|
||||
},
|
||||
|
||||
_getMonitorRect : function (x, y) {
|
||||
let rect = new Meta.Rectangle({ x: x - 1, y: y - 1, width: 1, height: 1 });
|
||||
let monitorIndex = global.screen.get_monitor_index_for_rect(rect);
|
||||
|
||||
return global.screen.get_monitor_geometry(monitorIndex);
|
||||
},
|
||||
|
||||
vfunc_gesture_prepare : function(action, actor) {
|
||||
if (this.get_n_current_points() == 0)
|
||||
return false;
|
||||
|
||||
let [x, y] = this.get_press_coords(0);
|
||||
let monitorRect = this._getMonitorRect(x, y);
|
||||
|
||||
return ((this._side == St.Side.LEFT && x < monitorRect.x + EDGE_THRESHOLD) ||
|
||||
(this._side == St.Side.RIGHT && x > monitorRect.x + monitorRect.width - EDGE_THRESHOLD) ||
|
||||
(this._side == St.Side.TOP && y < monitorRect.y + EDGE_THRESHOLD) ||
|
||||
(this._side == St.Side.BOTTOM && y > monitorRect.y + monitorRect.height - EDGE_THRESHOLD));
|
||||
},
|
||||
|
||||
vfunc_gesture_progress : function (action, actor) {
|
||||
let [startX, startY] = this.get_press_coords(0);
|
||||
let [x, y] = this.get_motion_coords(0);
|
||||
let offsetX = Math.abs (x - startX);
|
||||
let offsetY = Math.abs (y - startY);
|
||||
|
||||
if (offsetX < EDGE_THRESHOLD && offsetY < EDGE_THRESHOLD)
|
||||
return true;
|
||||
|
||||
if ((offsetX > offsetY &&
|
||||
(this._side == St.Side.TOP || this._side == St.Side.BOTTOM)) ||
|
||||
(offsetY > offsetX &&
|
||||
(this._side == St.Side.LEFT || this._side == St.Side.RIGHT))) {
|
||||
this.cancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
vfunc_gesture_end : function (action, actor) {
|
||||
let [startX, startY] = this.get_press_coords(0);
|
||||
let [x, y] = this.get_motion_coords(0);
|
||||
let monitorRect = this._getMonitorRect(startX, startY);
|
||||
|
||||
if ((this._side == St.Side.TOP && y > monitorRect.y + DRAG_DISTANCE) ||
|
||||
(this._side == St.Side.BOTTOM && y < monitorRect.y + monitorRect.height - DRAG_DISTANCE) ||
|
||||
(this._side == St.Side.LEFT && x > monitorRect.x + DRAG_DISTANCE) ||
|
||||
(this._side == St.Side.RIGHT && x < monitorRect.x + monitorRect.width - DRAG_DISTANCE))
|
||||
this.emit('activated');
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(EdgeDragAction.prototype);
|
@ -13,9 +13,7 @@
|
||||
* 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.
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const Lang = imports.lang;
|
||||
@ -27,9 +25,11 @@ const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Polkit = imports.gi.Polkit;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const CheckBox = imports.ui.checkBox;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
@ -39,7 +39,7 @@ const UserWidget = imports.ui.userWidget;
|
||||
let _endSessionDialog = null;
|
||||
|
||||
const _ITEM_ICON_SIZE = 48;
|
||||
const _DIALOG_ICON_SIZE = 32;
|
||||
const _DIALOG_ICON_SIZE = 48;
|
||||
|
||||
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
|
||||
|
||||
@ -73,6 +73,7 @@ const logoutDialogContent = {
|
||||
"You will be logged out automatically in %d seconds.",
|
||||
seconds).format(seconds);
|
||||
},
|
||||
showBatteryWarning: false,
|
||||
confirmButtons: [{ signal: 'ConfirmedLogout',
|
||||
label: C_("button", "Log Out") }],
|
||||
iconStyleClass: 'end-session-dialog-logout-icon',
|
||||
@ -81,11 +82,14 @@ const logoutDialogContent = {
|
||||
|
||||
const shutdownDialogContent = {
|
||||
subject: C_("title", "Power Off"),
|
||||
subjectWithUpdates: C_("title", "Install Updates & Power Off"),
|
||||
description: function(seconds) {
|
||||
return ngettext("The system will power off automatically in %d second.",
|
||||
"The system will power off automatically in %d seconds.",
|
||||
seconds).format(seconds);
|
||||
},
|
||||
checkBoxText: C_("checkbox", "Install pending software updates"),
|
||||
showBatteryWarning: true,
|
||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||
label: C_("button", "Restart") },
|
||||
{ signal: 'ConfirmedShutdown',
|
||||
@ -102,6 +106,7 @@ const restartDialogContent = {
|
||||
"The system will restart automatically in %d seconds.",
|
||||
seconds).format(seconds);
|
||||
},
|
||||
showBatteryWarning: false,
|
||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||
label: C_("button", "Restart") }],
|
||||
iconName: 'view-refresh-symbolic',
|
||||
@ -117,18 +122,28 @@ const restartInstallDialogContent = {
|
||||
"The system will automatically restart and install updates in %d seconds.",
|
||||
seconds).format(seconds);
|
||||
},
|
||||
showBatteryWarning: true,
|
||||
confirmButtons: [{ signal: 'ConfirmedReboot',
|
||||
label: C_("button", "Restart & Install") }],
|
||||
label: C_("button", "Restart & Install") }],
|
||||
unusedFutureButtonForTranslation: C_("button", "Install & Power Off"),
|
||||
unusedFutureCheckBoxForTranslation: C_("checkbox", "Power off after updates are installed"),
|
||||
iconName: 'view-refresh-symbolic',
|
||||
iconStyleClass: 'end-session-dialog-shutdown-icon',
|
||||
showOtherSessions: true,
|
||||
};
|
||||
|
||||
const DialogType = {
|
||||
LOGOUT: 0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */,
|
||||
SHUTDOWN: 1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */,
|
||||
RESTART: 2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */,
|
||||
UPDATE_RESTART: 3
|
||||
};
|
||||
|
||||
const DialogContent = {
|
||||
0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent,
|
||||
1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent,
|
||||
2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent,
|
||||
3: restartInstallDialogContent
|
||||
0 /* DialogType.LOGOUT */: logoutDialogContent,
|
||||
1 /* DialogType.SHUTDOWN */: shutdownDialogContent,
|
||||
2 /* DialogType.RESTART */: restartDialogContent,
|
||||
3 /* DialogType.UPDATE_RESTART */: restartInstallDialogContent
|
||||
};
|
||||
|
||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
|
||||
@ -145,6 +160,27 @@ const LogindSessionIface = '<node> \
|
||||
|
||||
const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
|
||||
|
||||
const PkOfflineIface = '<node> \
|
||||
<interface name="org.freedesktop.PackageKit.Offline"> \
|
||||
<property name="UpdatePrepared" type="b" access="read"/> \
|
||||
<property name="TriggerAction" type="s" access="read"/> \
|
||||
<method name="Trigger"> \
|
||||
<arg type="s" name="action" direction="in"/> \
|
||||
</method> \
|
||||
<method name="Cancel"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const PkOfflineProxy = Gio.DBusProxy.makeProxyWrapper(PkOfflineIface);
|
||||
|
||||
const UPowerIface = '<node> \
|
||||
<interface name="org.freedesktop.UPower"> \
|
||||
<property name="OnBattery" type="b" access="read"/> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(UPowerIface);
|
||||
|
||||
function findAppFromInhibitor(inhibitor) {
|
||||
let desktopFile;
|
||||
try {
|
||||
@ -197,6 +233,18 @@ function _setLabelText(label, text) {
|
||||
}
|
||||
}
|
||||
|
||||
function _setCheckBoxLabel(checkBox, text) {
|
||||
let label = checkBox.getLabelActor();
|
||||
|
||||
if (text) {
|
||||
label.set_text(text);
|
||||
checkBox.actor.show();
|
||||
} else {
|
||||
label.set_text('');
|
||||
checkBox.actor.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
// This always returns the same singleton object
|
||||
// By instantiating it initially, we register the
|
||||
@ -215,7 +263,26 @@ const EndSessionDialog = new Lang.Class({
|
||||
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._pkOfflineProxy = new PkOfflineProxy(Gio.DBus.system,
|
||||
'org.freedesktop.PackageKit',
|
||||
'/org/freedesktop/PackageKit',
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error)
|
||||
log(error.message);
|
||||
}));
|
||||
this._powerProxy = new UPowerProxy(Gio.DBus.system,
|
||||
'org.freedesktop.UPower',
|
||||
'/org/freedesktop/UPower',
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
this._powerProxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
this._sync();
|
||||
}));
|
||||
|
||||
this._secondsLeft = 0;
|
||||
this._totalSecondsToStayOpen = 0;
|
||||
@ -242,14 +309,17 @@ const EndSessionDialog = new Lang.Class({
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let messageLayout = new St.BoxLayout({ vertical: true });
|
||||
let messageLayout = new St.BoxLayout({ vertical: true,
|
||||
style_class: 'end-session-dialog-layout' });
|
||||
mainContentLayout.add(messageLayout,
|
||||
{ y_align: St.Align.START });
|
||||
|
||||
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
|
||||
|
||||
messageLayout.add(this._subjectLabel,
|
||||
{ y_fill: false,
|
||||
{ x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
|
||||
@ -260,6 +330,16 @@ const EndSessionDialog = new Lang.Class({
|
||||
{ y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._checkBox = new CheckBox.CheckBox();
|
||||
this._checkBox.actor.connect('clicked', Lang.bind(this, this._sync));
|
||||
messageLayout.add(this._checkBox.actor);
|
||||
|
||||
this._batteryWarning = new St.Label({ style_class: 'end-session-dialog-warning',
|
||||
text: _("Running on battery power: please plug in before installing updates.") });
|
||||
this._batteryWarning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||
this._batteryWarning.clutter_text.line_wrap = true;
|
||||
messageLayout.add(this._batteryWarning);
|
||||
|
||||
this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' });
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this.contentLayout.add(this._scrollView,
|
||||
@ -285,6 +365,12 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._inhibitorSection.add_actor(this._sessionHeader);
|
||||
this._inhibitorSection.add_actor(this._sessionList);
|
||||
|
||||
try {
|
||||
this._updatesPermission = Polkit.Permission.new_sync("org.freedesktop.packagekit.trigger-offline-update", null, null);
|
||||
} catch(e) {
|
||||
log('No permission to trigger offline updates: %s'.format(e.toString()));
|
||||
}
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
|
||||
},
|
||||
@ -299,13 +385,22 @@ const EndSessionDialog = new Lang.Class({
|
||||
if (!open)
|
||||
return;
|
||||
|
||||
if (this._type == 2 && this._updatesFile.query_exists(null))
|
||||
this._type = 3;
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
|
||||
let subject = dialogContent.subject;
|
||||
|
||||
// Use different title when we are installing updates
|
||||
if (dialogContent.subjectWithUpdates && this._checkBox.actor.checked)
|
||||
subject = dialogContent.subjectWithUpdates;
|
||||
|
||||
if (dialogContent.showBatteryWarning) {
|
||||
// Warn when running on battery power
|
||||
if (this._powerProxy.OnBattery && this._checkBox.actor.checked)
|
||||
this._batteryWarning.opacity = 255;
|
||||
else
|
||||
this._batteryWarning.opacity = 0;
|
||||
}
|
||||
|
||||
let description;
|
||||
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
|
||||
this._secondsLeft,
|
||||
@ -388,15 +483,73 @@ const EndSessionDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_confirm: function(signal) {
|
||||
this._fadeOutDialog();
|
||||
this._stopTimer();
|
||||
this._dbusImpl.emit_signal(signal, null);
|
||||
let callback = Lang.bind(this, function() {
|
||||
this._fadeOutDialog();
|
||||
this._stopTimer();
|
||||
this._dbusImpl.emit_signal(signal, null);
|
||||
});
|
||||
|
||||
// Offline update not available; just emit the signal
|
||||
if (!this._checkBox.actor.visible) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Trigger the offline update as requested
|
||||
if (this._checkBox.actor.checked) {
|
||||
switch (signal) {
|
||||
case "ConfirmedReboot":
|
||||
this._triggerOfflineUpdateReboot(callback);
|
||||
break;
|
||||
case "ConfirmedShutdown":
|
||||
// To actually trigger the offline update, we need to
|
||||
// reboot to do the upgrade. When the upgrade is complete,
|
||||
// the computer will shut down automatically.
|
||||
signal = "ConfirmedReboot";
|
||||
this._triggerOfflineUpdateShutdown(callback);
|
||||
break;
|
||||
default:
|
||||
callback();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this._triggerOfflineUpdateCancel(callback);
|
||||
}
|
||||
},
|
||||
|
||||
_onOpened: function() {
|
||||
this._sync();
|
||||
},
|
||||
|
||||
_triggerOfflineUpdateReboot: function(callback) {
|
||||
this._pkOfflineProxy.TriggerRemote('reboot',
|
||||
function (result, error) {
|
||||
if (error)
|
||||
log(error.message);
|
||||
|
||||
callback();
|
||||
});
|
||||
},
|
||||
|
||||
_triggerOfflineUpdateShutdown: function(callback) {
|
||||
this._pkOfflineProxy.TriggerRemote('power-off',
|
||||
function (result, error) {
|
||||
if (error)
|
||||
log(error.message);
|
||||
|
||||
callback();
|
||||
});
|
||||
},
|
||||
|
||||
_triggerOfflineUpdateCancel: function(callback) {
|
||||
this._pkOfflineProxy.CancelRemote(function (result, error) {
|
||||
if (error)
|
||||
log(error.message);
|
||||
|
||||
callback();
|
||||
});
|
||||
},
|
||||
|
||||
_startTimer: function() {
|
||||
let startTime = GLib.get_monotonic_time();
|
||||
this._secondsLeft = this._totalSecondsToStayOpen;
|
||||
@ -409,15 +562,17 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
|
||||
if (this._secondsLeft > 0) {
|
||||
this._sync();
|
||||
return true;
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
||||
this._confirm(button.signal);
|
||||
this._timerId = 0;
|
||||
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(this._timerId, '[gnome-shell] this._confirm');
|
||||
},
|
||||
|
||||
_stopTimer: function() {
|
||||
@ -543,6 +698,10 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
|
||||
this._type = type;
|
||||
|
||||
if (this._type == DialogType.RESTART &&
|
||||
this._pkOfflineProxy.TriggerAction == 'reboot')
|
||||
this._type = DialogType.UPDATE_RESTART;
|
||||
|
||||
this._applications = [];
|
||||
this._applicationList.destroy_all_children();
|
||||
|
||||
@ -555,6 +714,8 @@ const EndSessionDialog = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
|
||||
for (let i = 0; i < inhibitorObjectPaths.length; i++) {
|
||||
let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], Lang.bind(this, function(proxy, error) {
|
||||
this._onInhibitorLoaded(proxy);
|
||||
@ -563,9 +724,23 @@ const EndSessionDialog = new Lang.Class({
|
||||
this._applications.push(inhibitor);
|
||||
}
|
||||
|
||||
if (DialogContent[type].showOtherSessions)
|
||||
if (dialogContent.showOtherSessions)
|
||||
this._loadSessions();
|
||||
|
||||
let updateAlreadyTriggered = this._pkOfflineProxy.TriggerAction == 'power-off' || this._pkOfflineProxy.TriggerAction == 'reboot';
|
||||
let updatePrepared = this._pkOfflineProxy.UpdatePrepared;
|
||||
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
|
||||
|
||||
_setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText);
|
||||
this._checkBox.actor.visible = (dialogContent.checkBoxText && updatePrepared && updatesAllowed);
|
||||
this._checkBox.actor.checked = (updatePrepared && updateAlreadyTriggered);
|
||||
|
||||
// We show the warning either together with the checkbox, or when
|
||||
// updates have already been triggered, but the user doesn't have
|
||||
// enough permissions to cancel them.
|
||||
this._batteryWarning.visible = (dialogContent.showBatteryWarning &&
|
||||
(this._checkBox.actor.visible || updatePrepared && updateAlreadyTriggered && !updatesAllowed));
|
||||
|
||||
this._updateButtons();
|
||||
|
||||
if (!this.open(timestamp)) {
|
||||
|
@ -5,6 +5,8 @@ imports.gi.versions.Gio = '2.0';
|
||||
imports.gi.versions.Gdk = '3.0';
|
||||
imports.gi.versions.GdkPixbuf = '2.0';
|
||||
imports.gi.versions.Gtk = '3.0';
|
||||
imports.gi.versions.TelepathyGLib = '0.12';
|
||||
imports.gi.versions.TelepathyLogger = '0.2';
|
||||
|
||||
const Clutter = imports.gi.Clutter;;
|
||||
const Gettext = imports.gettext;
|
||||
@ -45,8 +47,11 @@ function _patchLayoutClass(layoutClass, 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]);
|
||||
for (let prop in styleProps) {
|
||||
let [found, length] = node.lookup_length(styleProps[prop], false);
|
||||
if (found)
|
||||
this[prop] = length;
|
||||
}
|
||||
}));
|
||||
};
|
||||
layoutClass.prototype.child_set = function(actor, props) {
|
||||
|
@ -201,7 +201,7 @@ const InstallExtensionDialog = new Lang.Class({
|
||||
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();
|
||||
this.contentLayout.add(box);
|
||||
|
@ -38,6 +38,7 @@ const connect = Lang.bind(_signals, _signals.connect);
|
||||
const disconnect = Lang.bind(_signals, _signals.disconnect);
|
||||
|
||||
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
|
||||
const EXTENSION_DISABLE_VERSION_CHECK_KEY = 'disable-extension-version-validation';
|
||||
|
||||
var initted = false;
|
||||
var enabled;
|
||||
@ -73,7 +74,7 @@ function disableExtension(uuid) {
|
||||
|
||||
if (extension.stylesheet) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
theme.unload_stylesheet(extension.stylesheet.get_path());
|
||||
theme.unload_stylesheet(extension.stylesheet);
|
||||
}
|
||||
|
||||
try {
|
||||
@ -117,7 +118,7 @@ function enableExtension(uuid) {
|
||||
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
|
||||
if (stylesheetFile.query_exists(null)) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
theme.load_stylesheet(stylesheetFile.get_path());
|
||||
theme.load_stylesheet(stylesheetFile);
|
||||
extension.stylesheet = stylesheetFile;
|
||||
break;
|
||||
}
|
||||
@ -156,7 +157,9 @@ function loadExtension(extension) {
|
||||
// Default to error, we set success as the last step
|
||||
extension.state = ExtensionState.ERROR;
|
||||
|
||||
if (ExtensionUtils.isOutOfDate(extension)) {
|
||||
let checkVersion = !global.settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY);
|
||||
|
||||
if (checkVersion && ExtensionUtils.isOutOfDate(extension)) {
|
||||
extension.state = ExtensionState.OUT_OF_DATE;
|
||||
} else {
|
||||
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
|
||||
@ -253,7 +256,7 @@ function onEnabledExtensionsChanged() {
|
||||
newEnabledExtensions.filter(function(uuid) {
|
||||
return enabledExtensions.indexOf(uuid) == -1;
|
||||
}).forEach(function(uuid) {
|
||||
enableExtension(uuid);
|
||||
enableExtension(uuid);
|
||||
});
|
||||
|
||||
// Find and disable all the newly disabled extensions: UUIDs found in the
|
||||
@ -261,18 +264,36 @@ function onEnabledExtensionsChanged() {
|
||||
enabledExtensions.filter(function(item) {
|
||||
return newEnabledExtensions.indexOf(item) == -1;
|
||||
}).forEach(function(uuid) {
|
||||
disableExtension(uuid);
|
||||
disableExtension(uuid);
|
||||
});
|
||||
|
||||
enabledExtensions = newEnabledExtensions;
|
||||
}
|
||||
|
||||
function _onVersionValidationChanged() {
|
||||
// we want to reload all extensions, but only enable
|
||||
// extensions when allowed by the sessionMode, so
|
||||
// temporarily disable them all
|
||||
enabledExtensions = [];
|
||||
for (let uuid in ExtensionUtils.extensions)
|
||||
reloadExtension(ExtensionUtils.extensions[uuid]);
|
||||
enabledExtensions = getEnabledExtensions();
|
||||
|
||||
if (Main.sessionMode.allowExtensions) {
|
||||
enabledExtensions.forEach(function(uuid) {
|
||||
enableExtension(uuid);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function _loadExtensions() {
|
||||
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
|
||||
global.settings.connect('changed::' + EXTENSION_DISABLE_VERSION_CHECK_KEY, _onVersionValidationChanged);
|
||||
|
||||
enabledExtensions = getEnabledExtensions();
|
||||
|
||||
let finder = new ExtensionUtils.ExtensionFinder();
|
||||
finder.connect('extension-found', function(signals, extension) {
|
||||
finder.connect('extension-found', function(finder, extension) {
|
||||
loadExtension(extension);
|
||||
});
|
||||
finder.scanExtensions();
|
||||
|
@ -32,9 +32,11 @@ 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));
|
||||
|
||||
this._atspiInited = false;
|
||||
this._focusListenerRegistered = false;
|
||||
this._caretListenerRegistered = false;
|
||||
},
|
||||
|
||||
_onChanged: function(event) {
|
||||
@ -44,22 +46,50 @@ const FocusCaretTracker = new Lang.Class({
|
||||
this.emit('caret-moved', event);
|
||||
},
|
||||
|
||||
_initAtspi: function() {
|
||||
if (!this._atspiInited) {
|
||||
Atspi.init();
|
||||
Atspi.set_timeout(250, 250);
|
||||
this._atspiInited = true;
|
||||
}
|
||||
},
|
||||
|
||||
registerFocusListener: function() {
|
||||
return this._atspiListener.register(STATECHANGED + ':focused') &&
|
||||
this._atspiListener.register(STATECHANGED + ':selected');
|
||||
if (this._focusListenerRegistered)
|
||||
return;
|
||||
|
||||
this._initAtspi();
|
||||
|
||||
this._atspiListener.register(STATECHANGED + ':focused');
|
||||
this._atspiListener.register(STATECHANGED + ':selected');
|
||||
this._focusListenerRegistered = true;
|
||||
},
|
||||
|
||||
registerCaretListener: function() {
|
||||
return this._atspiListener.register(CARETMOVED);
|
||||
if (this._caretListenerRegistered)
|
||||
return;
|
||||
|
||||
this._initAtspi();
|
||||
|
||||
this._atspiListener.register(CARETMOVED);
|
||||
this._caretListenerRegistered = true;
|
||||
},
|
||||
|
||||
deregisterFocusListener: function() {
|
||||
return this._atspiListener.deregister(STATECHANGED + ':focused') &&
|
||||
this._atspiListener.deregister(STATECHANGED + ':selected');
|
||||
if (!this._focusListenerRegistered)
|
||||
return;
|
||||
|
||||
this._atspiListener.deregister(STATECHANGED + ':focused');
|
||||
this._atspiListener.deregister(STATECHANGED + ':selected');
|
||||
this._focusListenerRegistered = false;
|
||||
},
|
||||
|
||||
deregisterCaretListener: function() {
|
||||
return this._atspiListener.deregister(CARETMOVED);
|
||||
if (!this._caretListenerRegistered)
|
||||
return;
|
||||
|
||||
this._atspiListener.deregister(CARETMOVED);
|
||||
this._caretListenerRegistered = false;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(FocusCaretTracker.prototype);
|
||||
|
@ -56,7 +56,7 @@ const GrabHelper = new Lang.Class({
|
||||
this._grabStack = [];
|
||||
|
||||
this._actors = [];
|
||||
this._ignoreRelease = false;
|
||||
this._ignoreUntilRelease = false;
|
||||
|
||||
this._modalCount = 0;
|
||||
},
|
||||
@ -215,7 +215,7 @@ const GrabHelper = new Lang.Class({
|
||||
|
||||
_popGrabHelper(this);
|
||||
|
||||
this._ignoreRelease = false;
|
||||
this._ignoreUntilRelease = false;
|
||||
|
||||
Main.popModal(this._owner);
|
||||
global.sync_pointer();
|
||||
@ -228,7 +228,7 @@ const GrabHelper = new Lang.Class({
|
||||
// like the ComboBoxMenu that go away on press, but need to eat
|
||||
// the next release event.
|
||||
ignoreRelease: function() {
|
||||
this._ignoreRelease = true;
|
||||
this._ignoreUntilRelease = true;
|
||||
},
|
||||
|
||||
// ungrab:
|
||||
@ -280,34 +280,45 @@ const GrabHelper = new Lang.Class({
|
||||
if (type == Clutter.EventType.KEY_PRESS &&
|
||||
event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this.ungrab({ isUser: true });
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
let motion = type == Clutter.EventType.MOTION;
|
||||
let press = type == Clutter.EventType.BUTTON_PRESS;
|
||||
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
||||
let button = press || release;
|
||||
|
||||
if (release && this._ignoreRelease) {
|
||||
this._ignoreRelease = false;
|
||||
return true;
|
||||
let touchUpdate = type == Clutter.EventType.TOUCH_UPDATE;
|
||||
let touchBegin = type == Clutter.EventType.TOUCH_BEGIN;
|
||||
let touchEnd = type == Clutter.EventType.TOUCH_END;
|
||||
let touch = touchUpdate || touchBegin || touchEnd;
|
||||
|
||||
if (touch && !global.display.is_pointer_emulating_sequence (event.get_event_sequence()))
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (this._ignoreUntilRelease && (motion || release || touch)) {
|
||||
if (release || touchEnd)
|
||||
this._ignoreUntilRelease = false;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
if (this._isWithinGrabbedActor(event.get_source()))
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (Main.keyboard.shouldTakeEvent(event))
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (button || touchBegin) {
|
||||
// If we have a press event, ignore the next
|
||||
// motion/release events.
|
||||
if (press || touchBegin)
|
||||
this._ignoreUntilRelease = true;
|
||||
|
||||
if (button) {
|
||||
// If we have a press event, ignore the next event,
|
||||
// which should be a release event.
|
||||
if (press)
|
||||
this._ignoreRelease = true;
|
||||
let i = this._actorInGrabStack(event.get_source()) + 1;
|
||||
this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
});
|
||||
|
@ -11,6 +11,9 @@ const Main = imports.ui.main;
|
||||
|
||||
const MAX_CANDIDATES_PER_PAGE = 16;
|
||||
|
||||
const DEFAULT_INDEX_LABELS = [ '1', '2', '3', '4', '5', '6', '7', '8',
|
||||
'9', '0', 'a', 'b', 'c', 'd', 'e', 'f' ];
|
||||
|
||||
const CandidateArea = new Lang.Class({
|
||||
Name: 'CandidateArea',
|
||||
|
||||
@ -32,6 +35,7 @@ const CandidateArea = new Lang.Class({
|
||||
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;
|
||||
}));
|
||||
}
|
||||
|
||||
@ -88,7 +92,7 @@ const CandidateArea = new Lang.Class({
|
||||
if (!visible)
|
||||
continue;
|
||||
|
||||
box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : '%x'.format(i + 1));
|
||||
box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : DEFAULT_INDEX_LABELS[i]);
|
||||
box._candidateLabel.text = candidates[i];
|
||||
}
|
||||
|
||||
@ -114,9 +118,6 @@ const CandidatePopup = new Lang.Class({
|
||||
Name: 'CandidatePopup',
|
||||
|
||||
_init: function() {
|
||||
this._cursor = new St.Bin({ opacity: 0 });
|
||||
Main.uiGroup.add_actor(this._cursor);
|
||||
|
||||
this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
|
||||
this._boxPointer.actor.visible = false;
|
||||
this._boxPointer.actor.style_class = 'candidate-popup-boxpointer';
|
||||
@ -157,10 +158,9 @@ const CandidatePopup = new Lang.Class({
|
||||
|
||||
panelService.connect('set-cursor-location',
|
||||
Lang.bind(this, function(ps, x, y, w, h) {
|
||||
this._cursor.set_position(x, y);
|
||||
this._cursor.set_size(w, h);
|
||||
Main.layoutManager.setDummyCursorGeometry(x, y, w, h);
|
||||
if (this._boxPointer.actor.visible)
|
||||
this._boxPointer.setPosition(this._cursor, 0);
|
||||
this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
|
||||
}));
|
||||
panelService.connect('update-preedit-text',
|
||||
Lang.bind(this, function(ps, text, cursorPosition, visible) {
|
||||
@ -252,7 +252,7 @@ const CandidatePopup = new Lang.Class({
|
||||
this._candidateArea.actor.visible);
|
||||
|
||||
if (isVisible) {
|
||||
this._boxPointer.setPosition(this._cursor, 0);
|
||||
this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
|
||||
this._boxPointer.show(BoxPointer.PopupAnimation.NONE);
|
||||
this._boxPointer.actor.raise_top();
|
||||
} else {
|
||||
|
@ -10,12 +10,30 @@ const St = imports.gi.St;
|
||||
const Lang = imports.lang;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const ICON_SIZE = 96;
|
||||
const MIN_ICON_SIZE = 16;
|
||||
|
||||
const EXTRA_SPACE_ANIMATION_TIME = 0.25;
|
||||
|
||||
const ANIMATION_TIME_IN = 0.350;
|
||||
const ANIMATION_TIME_OUT = 1/2 * ANIMATION_TIME_IN;
|
||||
const ANIMATION_MAX_DELAY_FOR_ITEM = 2/3 * ANIMATION_TIME_IN;
|
||||
const ANIMATION_BASE_DELAY_FOR_ITEM = 1/4 * ANIMATION_MAX_DELAY_FOR_ITEM;
|
||||
const ANIMATION_MAX_DELAY_OUT_FOR_ITEM = 2/3 * ANIMATION_TIME_OUT;
|
||||
const ANIMATION_FADE_IN_TIME_FOR_ITEM = 1/4 * ANIMATION_TIME_IN;
|
||||
|
||||
const ANIMATION_BOUNCE_ICON_SCALE = 1.1;
|
||||
|
||||
const AnimationDirection = {
|
||||
IN: 0,
|
||||
OUT: 1
|
||||
};
|
||||
|
||||
const APPICON_ANIMATION_OUT_SCALE = 3;
|
||||
const APPICON_ANIMATION_OUT_TIME = 0.25;
|
||||
|
||||
const BaseIcon = new Lang.Class({
|
||||
Name: 'BaseIcon',
|
||||
|
||||
@ -143,11 +161,6 @@ const BaseIcon = new Lang.Class({
|
||||
this.icon = this.createIcon(this.iconSize);
|
||||
|
||||
this._iconBin.child = this.icon;
|
||||
|
||||
// The icon returned by createIcon() might actually be smaller than
|
||||
// the requested icon size (for instance StTextureCache does this
|
||||
// for fallback icons), so set the size explicitly.
|
||||
this._iconBin.set_size(this.iconSize, this.iconSize);
|
||||
},
|
||||
|
||||
_onStyleChanged: function() {
|
||||
@ -178,9 +191,55 @@ const BaseIcon = new Lang.Class({
|
||||
|
||||
_onIconThemeChanged: function() {
|
||||
this._createIconTexture(this.iconSize);
|
||||
},
|
||||
|
||||
animateZoomOut: function() {
|
||||
// Animate only the child instead of the entire actor, so the
|
||||
// styles like hover and running are not applied while
|
||||
// animating.
|
||||
zoomOutActor(this.actor.child);
|
||||
}
|
||||
});
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.max(Math.min(value, max), min);
|
||||
};
|
||||
|
||||
function zoomOutActor(actor) {
|
||||
let actorClone = new Clutter.Clone({ source: actor,
|
||||
reactive: false });
|
||||
let [width, height] = actor.get_transformed_size();
|
||||
let [x, y] = actor.get_transformed_position();
|
||||
actorClone.set_size(width, height);
|
||||
actorClone.set_position(x, y);
|
||||
actorClone.opacity = 255;
|
||||
actorClone.set_pivot_point(0.5, 0.5);
|
||||
|
||||
Main.uiGroup.add_actor(actorClone);
|
||||
|
||||
// Avoid monitor edges to not zoom outside the current monitor
|
||||
let monitor = Main.layoutManager.findMonitorForActor(actor);
|
||||
let scaledWidth = width * APPICON_ANIMATION_OUT_SCALE;
|
||||
let scaledHeight = height * APPICON_ANIMATION_OUT_SCALE;
|
||||
let scaledX = x - (scaledWidth - width) / 2;
|
||||
let scaledY = y - (scaledHeight - height) / 2;
|
||||
let containedX = clamp(scaledX, monitor.x, monitor.x + monitor.width - scaledWidth);
|
||||
let containedY = clamp(scaledY, monitor.y, monitor.y + monitor.height - scaledHeight);
|
||||
|
||||
Tweener.addTween(actorClone,
|
||||
{ time: APPICON_ANIMATION_OUT_TIME,
|
||||
scale_x: APPICON_ANIMATION_OUT_SCALE,
|
||||
scale_y: APPICON_ANIMATION_OUT_SCALE,
|
||||
translation_x: containedX - scaledX,
|
||||
translation_y: containedY - scaledY,
|
||||
opacity: 0,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
actorClone.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const IconGrid = new Lang.Class({
|
||||
Name: 'IconGrid',
|
||||
|
||||
@ -219,6 +278,20 @@ const IconGrid = new Lang.Class({
|
||||
this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this._grid.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this._grid.connect('actor-added', Lang.bind(this, this._childAdded));
|
||||
this._grid.connect('actor-removed', Lang.bind(this, this._childRemoved));
|
||||
},
|
||||
|
||||
_keyFocusIn: function(actor) {
|
||||
this.emit('key-focus-in', actor);
|
||||
},
|
||||
|
||||
_childAdded: function(grid, child) {
|
||||
child._iconGridKeyFocusInId = child.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
},
|
||||
|
||||
_childRemoved: function(grid, child) {
|
||||
child.disconnect(child._iconGridKeyFocusInId);
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (grid, forHeight, alloc) {
|
||||
@ -329,15 +402,202 @@ const IconGrid = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_calculateChildBox: function(child, x, y, box) {
|
||||
let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight] =
|
||||
child.get_preferred_size();
|
||||
/**
|
||||
* Intended to be override by subclasses if they need a different
|
||||
* set of items to be animated.
|
||||
*/
|
||||
_getChildrenToAnimate: function() {
|
||||
return this._getVisibleChildren();
|
||||
},
|
||||
|
||||
_animationDone: function() {
|
||||
this._animating = false;
|
||||
this.emit('animation-done');
|
||||
},
|
||||
|
||||
animatePulse: function(animationDirection) {
|
||||
if (animationDirection != AnimationDirection.IN)
|
||||
throw new Error("Pulse animation only implements 'in' animation direction");
|
||||
|
||||
if (this._animating)
|
||||
return;
|
||||
|
||||
this._animating = true;
|
||||
|
||||
let actors = this._getChildrenToAnimate();
|
||||
if (actors.length == 0) {
|
||||
this._animationDone();
|
||||
return;
|
||||
}
|
||||
|
||||
// For few items the animation can be slow, so use a smaller
|
||||
// delay when there are less than 4 items
|
||||
// (ANIMATION_BASE_DELAY_FOR_ITEM = 1/4 *
|
||||
// ANIMATION_MAX_DELAY_FOR_ITEM)
|
||||
let maxDelay = Math.min(ANIMATION_BASE_DELAY_FOR_ITEM * actors.length,
|
||||
ANIMATION_MAX_DELAY_FOR_ITEM);
|
||||
|
||||
for (let index = 0; index < actors.length; index++) {
|
||||
let actor = actors[index];
|
||||
actor.reactive = false;
|
||||
actor.set_scale(0, 0);
|
||||
actor.set_pivot_point(0.5, 0.5);
|
||||
|
||||
let delay = index / actors.length * maxDelay;
|
||||
let bounceUpTime = ANIMATION_TIME_IN / 4;
|
||||
let isLastItem = index == actors.length - 1;
|
||||
Tweener.addTween(actor,
|
||||
{ time: bounceUpTime,
|
||||
transition: 'easeInOutQuad',
|
||||
delay: delay,
|
||||
scale_x: ANIMATION_BOUNCE_ICON_SCALE,
|
||||
scale_y: ANIMATION_BOUNCE_ICON_SCALE,
|
||||
onComplete: Lang.bind(this, function() {
|
||||
Tweener.addTween(actor,
|
||||
{ time: ANIMATION_TIME_IN - bounceUpTime,
|
||||
transition: 'easeInOutQuad',
|
||||
scale_x: 1,
|
||||
scale_y: 1,
|
||||
onComplete: Lang.bind(this, function() {
|
||||
if (isLastItem)
|
||||
this._animationDone();
|
||||
actor.reactive = true;
|
||||
})
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
animateSpring: function(animationDirection, sourceActor) {
|
||||
if (this._animating)
|
||||
return;
|
||||
|
||||
this._animating = true;
|
||||
|
||||
let actors = this._getChildrenToAnimate();
|
||||
if (actors.length == 0) {
|
||||
this._animationDone();
|
||||
return;
|
||||
}
|
||||
|
||||
let [sourceX, sourceY] = sourceActor.get_transformed_position();
|
||||
let [sourceWidth, sourceHeight] = sourceActor.get_size();
|
||||
// Get the center
|
||||
let [sourceCenterX, sourceCenterY] = [sourceX + sourceWidth / 2, sourceY + sourceHeight / 2];
|
||||
// Design decision, 1/2 of the source actor size.
|
||||
let [sourceScaledWidth, sourceScaledHeight] = [sourceWidth / 2, sourceHeight / 2];
|
||||
|
||||
actors.forEach(function(actor) {
|
||||
let [actorX, actorY] = actor._transformedPosition = actor.get_transformed_position();
|
||||
let [x, y] = [actorX - sourceX, actorY - sourceY];
|
||||
actor._distance = Math.sqrt(x * x + y * y);
|
||||
});
|
||||
let maxDist = actors.reduce(function(prev, cur) {
|
||||
return Math.max(prev, cur._distance);
|
||||
}, 0);
|
||||
let minDist = actors.reduce(function(prev, cur) {
|
||||
return Math.min(prev, cur._distance);
|
||||
}, Infinity);
|
||||
let normalization = maxDist - minDist;
|
||||
|
||||
for (let index = 0; index < actors.length; index++) {
|
||||
let actor = actors[index];
|
||||
actor.opacity = 0;
|
||||
actor.reactive = false;
|
||||
|
||||
let actorClone = new Clutter.Clone({ source: actor });
|
||||
Main.uiGroup.add_actor(actorClone);
|
||||
|
||||
let [width, height,,] = this._getAllocatedChildSizeAndSpacing(actor);
|
||||
actorClone.set_size(width, height);
|
||||
let scaleX = sourceScaledWidth / width;
|
||||
let scaleY = sourceScaledHeight / height;
|
||||
let [adjustedSourcePositionX, adjustedSourcePositionY] = [sourceCenterX - sourceScaledWidth / 2, sourceCenterY - sourceScaledHeight / 2];
|
||||
|
||||
let movementParams, fadeParams;
|
||||
if (animationDirection == AnimationDirection.IN) {
|
||||
let isLastItem = actor._distance == minDist;
|
||||
|
||||
actorClone.opacity = 0;
|
||||
actorClone.set_scale(scaleX, scaleY);
|
||||
|
||||
actorClone.set_position(adjustedSourcePositionX, adjustedSourcePositionY);
|
||||
|
||||
let delay = (1 - (actor._distance - minDist) / normalization) * ANIMATION_MAX_DELAY_FOR_ITEM;
|
||||
let [finalX, finalY] = actor._transformedPosition;
|
||||
movementParams = { time: ANIMATION_TIME_IN,
|
||||
transition: 'easeInOutQuad',
|
||||
delay: delay,
|
||||
x: finalX,
|
||||
y: finalY,
|
||||
scale_x: 1,
|
||||
scale_y: 1,
|
||||
onComplete: Lang.bind(this, function() {
|
||||
if (isLastItem)
|
||||
this._animationDone();
|
||||
|
||||
actor.opacity = 255;
|
||||
actor.reactive = true;
|
||||
actorClone.destroy();
|
||||
})};
|
||||
fadeParams = { time: ANIMATION_FADE_IN_TIME_FOR_ITEM,
|
||||
transition: 'easeInOutQuad',
|
||||
delay: delay,
|
||||
opacity: 255 };
|
||||
} else {
|
||||
let isLastItem = actor._distance == maxDist;
|
||||
|
||||
let [startX, startY] = actor._transformedPosition;
|
||||
actorClone.set_position(startX, startY);
|
||||
|
||||
let delay = (actor._distance - minDist) / normalization * ANIMATION_MAX_DELAY_OUT_FOR_ITEM;
|
||||
movementParams = { time: ANIMATION_TIME_OUT,
|
||||
transition: 'easeInOutQuad',
|
||||
delay: delay,
|
||||
x: adjustedSourcePositionX,
|
||||
y: adjustedSourcePositionY,
|
||||
scale_x: scaleX,
|
||||
scale_y: scaleY,
|
||||
onComplete: Lang.bind(this, function() {
|
||||
if (isLastItem) {
|
||||
this._animationDone();
|
||||
this._restoreItemsOpacity();
|
||||
}
|
||||
actor.reactive = true;
|
||||
actorClone.destroy();
|
||||
})};
|
||||
fadeParams = { time: ANIMATION_FADE_IN_TIME_FOR_ITEM,
|
||||
transition: 'easeInOutQuad',
|
||||
delay: ANIMATION_TIME_OUT + delay - ANIMATION_FADE_IN_TIME_FOR_ITEM,
|
||||
opacity: 0 };
|
||||
}
|
||||
|
||||
|
||||
Tweener.addTween(actorClone, movementParams);
|
||||
Tweener.addTween(actorClone, fadeParams);
|
||||
}
|
||||
},
|
||||
|
||||
_restoreItemsOpacity: function() {
|
||||
for (let index = 0; index < this._items.length; index++) {
|
||||
this._items[index].actor.opacity = 255;
|
||||
}
|
||||
},
|
||||
|
||||
_getAllocatedChildSizeAndSpacing: function(child) {
|
||||
let [,, natWidth, natHeight] = child.get_preferred_size();
|
||||
let width = Math.min(this._getHItemSize(), natWidth);
|
||||
let xSpacing = Math.max(0, width - natWidth) / 2;
|
||||
let height = Math.min(this._getVItemSize(), natHeight);
|
||||
let ySpacing = Math.max(0, height - natHeight) / 2;
|
||||
return [width, height, xSpacing, ySpacing];
|
||||
},
|
||||
|
||||
_calculateChildBox: function(child, x, y, box) {
|
||||
/* 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 [width, height, childXSpacing, childYSpacing] =
|
||||
this._getAllocatedChildSizeAndSpacing(child);
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
|
||||
@ -413,15 +673,18 @@ const IconGrid = new Lang.Class({
|
||||
},
|
||||
|
||||
removeAll: function() {
|
||||
this._items = [];
|
||||
this._grid.remove_all_children();
|
||||
},
|
||||
|
||||
destroyAll: function() {
|
||||
this._items = [];
|
||||
this._grid.destroy_all_children();
|
||||
},
|
||||
|
||||
addItem: function(item, index) {
|
||||
if (!item.icon || !item.icon instanceof BaseIcon) {
|
||||
log('Only items with a BaseIcon icon property can be added to IconGrid');
|
||||
return;
|
||||
}
|
||||
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)
|
||||
@ -430,6 +693,10 @@ const IconGrid = new Lang.Class({
|
||||
this._grid.add_actor(item.actor);
|
||||
},
|
||||
|
||||
removeItem: function(item) {
|
||||
this._grid.remove_child(item.actor);
|
||||
},
|
||||
|
||||
getItemAtIndex: function(index) {
|
||||
return this._grid.get_child_at_index(index);
|
||||
},
|
||||
@ -506,25 +773,22 @@ const IconGrid = new Lang.Class({
|
||||
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); }));
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||||
Lang.bind(this, this._updateIconSizes));
|
||||
},
|
||||
|
||||
// Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up
|
||||
_updateChildrenScale: function(scale) {
|
||||
_updateIconSizes: function() {
|
||||
let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize, this._vItemSize);
|
||||
let newIconSize = Math.floor(ICON_SIZE * scale);
|
||||
for (let i in this._items) {
|
||||
let newIconSize = Math.floor(ICON_SIZE * scale);
|
||||
this._items[i].icon.setIconSize(newIconSize);
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(IconGrid.prototype);
|
||||
|
||||
const PaginatedIconGrid = new Lang.Class({
|
||||
Name: 'PaginatedIconGrid',
|
||||
@ -533,6 +797,7 @@ const PaginatedIconGrid = new Lang.Class({
|
||||
_init: function(params) {
|
||||
this.parent(params);
|
||||
this._nPages = 0;
|
||||
this.currentPage = 0;
|
||||
this._rowsPerPage = 0;
|
||||
this._spaceBetweenPages = 0;
|
||||
this._childrenPerPage = 0;
|
||||
@ -596,6 +861,15 @@ const PaginatedIconGrid = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
// Overriden from IconGrid
|
||||
_getChildrenToAnimate: function() {
|
||||
let children = this._getVisibleChildren();
|
||||
let firstIndex = this._childrenPerPage * this.currentPage;
|
||||
let lastIndex = firstIndex + this._childrenPerPage;
|
||||
|
||||
return children.slice(firstIndex, lastIndex);
|
||||
},
|
||||
|
||||
_computePages: function (availWidthPerPage, availHeightPerPage) {
|
||||
let [nColumns, usedWidth] = this._computeLayout(availWidthPerPage);
|
||||
let nRows;
|
||||
@ -628,6 +902,10 @@ const PaginatedIconGrid = new Lang.Class({
|
||||
return this._nPages;
|
||||
},
|
||||
|
||||
getPageHeight: function() {
|
||||
return this._availableHeightPerPageForItems();
|
||||
},
|
||||
|
||||
getPageY: function(pageNumber) {
|
||||
if (!this._nPages)
|
||||
return 0;
|
||||
|
@ -23,6 +23,12 @@ const KEYBOARD_TYPE = 'keyboard-type';
|
||||
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
|
||||
|
||||
const CURSOR_BUS_NAME = 'org.gnome.SettingsDaemon.Cursor';
|
||||
const CURSOR_OBJECT_PATH = '/org/gnome/SettingsDaemon/Cursor';
|
||||
|
||||
const CARIBOU_BUS_NAME = 'org.gnome.Caribou.Daemon';
|
||||
const CARIBOU_OBJECT_PATH = '/org/gnome/Caribou/Daemon';
|
||||
|
||||
const CaribouKeyboardIface = '<node> \
|
||||
<interface name="org.gnome.Caribou.Keyboard"> \
|
||||
<method name="Show"> \
|
||||
@ -47,13 +53,29 @@ const CaribouKeyboardIface = '<node> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const CaribouDaemonIface = '<node> \
|
||||
<interface name="org.gnome.Caribou.Daemon"> \
|
||||
<method name="Run" /> \
|
||||
<method name="Quit" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const CursorManagerIface = '<node> \
|
||||
<interface name="org.gnome.SettingsDaemon.Cursor"> \
|
||||
<property name="ShowOSK" type="b" access="read" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const CaribouDaemonProxy = Gio.DBusProxy.makeProxyWrapper(CaribouDaemonIface);
|
||||
const CursorManagerProxy = Gio.DBusProxy.makeProxyWrapper(CursorManagerIface);
|
||||
|
||||
const Key = new Lang.Class({
|
||||
Name: 'Key',
|
||||
|
||||
_init : function(key) {
|
||||
this._key = key;
|
||||
|
||||
this.actor = this._makeKey();
|
||||
this.actor = this._makeKey(key, GLib.markup_escape_text(key.label, -1));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._extended_keys = this._key.get_extended_keys();
|
||||
this._extended_keyboard = null;
|
||||
@ -76,14 +98,28 @@ const Key = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_makeKey: function () {
|
||||
let label = GLib.markup_escape_text(this._key.label, -1);
|
||||
_onDestroy: function() {
|
||||
if (this._boxPointer) {
|
||||
this._boxPointer.actor.destroy();
|
||||
this._boxPointer = null;
|
||||
}
|
||||
},
|
||||
|
||||
_makeKey: function (key, label) {
|
||||
let button = new St.Button ({ label: label,
|
||||
style_class: 'keyboard-key' });
|
||||
|
||||
button.key_width = this._key.width;
|
||||
button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
|
||||
button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
|
||||
button.connect('button-press-event', Lang.bind(this,
|
||||
function () {
|
||||
key.press();
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
button.connect('button-release-event', Lang.bind(this,
|
||||
function () {
|
||||
key.release();
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
|
||||
return button;
|
||||
},
|
||||
@ -104,10 +140,9 @@ const Key = new Lang.Class({
|
||||
for (let i = 0; i < this._extended_keys.length; ++i) {
|
||||
let extended_key = this._extended_keys[i];
|
||||
let label = this._getUnichar(extended_key);
|
||||
let key = new St.Button({ label: label, style_class: 'keyboard-key' });
|
||||
let key = this._makeKey(extended_key, label);
|
||||
|
||||
key.extended_key = extended_key;
|
||||
key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
|
||||
key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
|
||||
this._extended_keyboard.add(key);
|
||||
}
|
||||
this._boxPointer.bin.add_actor(this._extended_keyboard);
|
||||
@ -145,11 +180,33 @@ const Keyboard = new Lang.Class({
|
||||
|
||||
this._timestamp = global.display.get_current_time_roundtrip();
|
||||
|
||||
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
||||
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
|
||||
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||
this._settingsChanged();
|
||||
this._keyboardSettings = new Gio.Settings({ schema_id: KEYBOARD_SCHEMA });
|
||||
this._keyboardSettings.connect('changed', Lang.bind(this, this._sync));
|
||||
this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA });
|
||||
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._sync));
|
||||
this._watchNameId = Gio.bus_watch_name(Gio.BusType.SESSION, CURSOR_BUS_NAME, 0,
|
||||
Lang.bind(this, this._sync),
|
||||
Lang.bind(this, this._sync));
|
||||
this._daemonProxy = new CaribouDaemonProxy(Gio.DBus.session, CARIBOU_BUS_NAME,
|
||||
CARIBOU_OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
}));
|
||||
this._cursorProxy = new CursorManagerProxy(Gio.DBus.session, CURSOR_BUS_NAME,
|
||||
CURSOR_OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
this._cursorProxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._sync));
|
||||
this._sync();
|
||||
}));
|
||||
this._sync();
|
||||
|
||||
this._showIdleId = 0;
|
||||
this._subkeysBoxPointer = null;
|
||||
@ -167,8 +224,9 @@ const Keyboard = new Lang.Class({
|
||||
this._redraw();
|
||||
},
|
||||
|
||||
_settingsChanged: function (settings, key) {
|
||||
this._enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD);
|
||||
_sync: function () {
|
||||
this._enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD) ||
|
||||
this._cursorProxy.ShowOSK;
|
||||
if (!this._enableKeyboard && !this._keyboard)
|
||||
return;
|
||||
if (this._enableKeyboard && this._keyboard &&
|
||||
@ -198,9 +256,22 @@ const Keyboard = new Lang.Class({
|
||||
this.actor = null;
|
||||
|
||||
this._destroySource();
|
||||
this._daemonProxy.QuitRemote(function (result, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_setupKeyboard: function() {
|
||||
this._daemonProxy.RunRemote(function (result, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
|
||||
Main.layoutManager.keyboardBox.add_actor(this.actor);
|
||||
Main.layoutManager.trackChrome(this.actor);
|
||||
@ -250,9 +321,14 @@ const Keyboard = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._showIdleId)
|
||||
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
|
||||
Lang.bind(this, function() { this.Show(time); }));
|
||||
if (!this._showIdleId) {
|
||||
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
|
||||
Lang.bind(this, function() {
|
||||
this.Show(time);
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(this._showIdleId, '[gnome-shell] this.Show');
|
||||
}
|
||||
},
|
||||
|
||||
_createLayersForGroup: function (gname) {
|
||||
@ -294,7 +370,7 @@ const Keyboard = new Lang.Class({
|
||||
else if (release && this._capturedPress)
|
||||
this._hideSubkeys();
|
||||
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_addRows : function (keys, layout) {
|
||||
@ -438,7 +514,6 @@ const Keyboard = new Lang.Class({
|
||||
_createSource: function () {
|
||||
if (this._source == null) {
|
||||
this._source = new KeyboardSource(this);
|
||||
this._source.setTransient(true);
|
||||
Main.messageTray.add(this._source);
|
||||
}
|
||||
},
|
||||
@ -480,7 +555,9 @@ const Keyboard = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this._clearKeyboardRestTimer();
|
||||
this._show(monitor);
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(this._keyboardRestingId, '[gnome-shell] this._clearKeyboardRestTimer');
|
||||
},
|
||||
|
||||
_show: function(monitor) {
|
||||
@ -505,7 +582,9 @@ const Keyboard = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this._clearKeyboardRestTimer();
|
||||
this._hide();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(this._keyboardRestingId, '[gnome-shell] this._clearKeyboardRestTimer');
|
||||
},
|
||||
|
||||
_hide: function() {
|
||||
@ -531,7 +610,7 @@ const Keyboard = new Lang.Class({
|
||||
|
||||
_moveTemporarily: function () {
|
||||
let currentWindow = global.screen.get_display().focus_window;
|
||||
let rect = currentWindow.get_outer_rect();
|
||||
let rect = currentWindow.get_frame_rect();
|
||||
|
||||
let newX = rect.x;
|
||||
let newY = 3 * this.actor.height / 2;
|
||||
|
190
js/ui/layout.js
190
js/ui/layout.js
@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
@ -21,7 +20,6 @@ const Tweener = imports.ui.tweener;
|
||||
const STARTUP_ANIMATION_TIME = 0.5;
|
||||
const KEYBOARD_ANIMATION_TIME = 0.15;
|
||||
const BACKGROUND_FADE_ANIMATION_TIME = 1.0;
|
||||
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
||||
|
||||
// The message tray takes this much pressure
|
||||
// in the pressure barrier at once to release it.
|
||||
@ -161,10 +159,10 @@ const LayoutManager = new Lang.Class({
|
||||
this._isPopupWindowVisible = false;
|
||||
this._startingUp = true;
|
||||
|
||||
// Normally, the stage is always covered so Clutter doesn't need to clear
|
||||
// it; however it becomes visible during the startup animation
|
||||
// See the comment below for a longer explanation
|
||||
global.stage.color = DEFAULT_BACKGROUND_COLOR;
|
||||
// We don't want to paint the stage background color because either
|
||||
// the SystemBackground we create or the MetaBackgroundActor inside
|
||||
// global.window_group covers the entirety of the screen.
|
||||
global.stage.no_clear_hint = true;
|
||||
|
||||
// Set up stage hierarchy to group all UI actors under one container.
|
||||
this.uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
|
||||
@ -213,15 +211,28 @@ const LayoutManager = new Lang.Class({
|
||||
this.addChrome(this.trayBox);
|
||||
this._setupTrayPressure();
|
||||
|
||||
this.modalDialogGroup = new St.Widget({ name: 'modalDialogGroup',
|
||||
layout_manager: new Clutter.BinLayout() });
|
||||
this.uiGroup.add_actor(this.modalDialogGroup);
|
||||
|
||||
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
this.addChrome(this.keyboardBox);
|
||||
this._keyboardHeightNotifyId = 0;
|
||||
|
||||
// A dummy actor that tracks the mouse or text cursor, based on the
|
||||
// position and size set in setDummyCursorGeometry.
|
||||
this.dummyCursor = new St.Widget({ width: 0, height: 0, visible: false });
|
||||
this.uiGroup.add_actor(this.dummyCursor);
|
||||
|
||||
global.stage.remove_actor(global.top_window_group);
|
||||
this.uiGroup.add_actor(global.top_window_group);
|
||||
|
||||
let feedbackGroup = Meta.get_feedback_group_for_screen(global.screen);
|
||||
global.stage.remove_actor(feedbackGroup);
|
||||
this.uiGroup.add_actor(feedbackGroup);
|
||||
|
||||
this._backgroundGroup = new Meta.BackgroundGroup();
|
||||
global.window_group.add_child(this._backgroundGroup);
|
||||
this._backgroundGroup.lower_bottom();
|
||||
@ -251,7 +262,6 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
this._inOverview = true;
|
||||
this._updateVisibility();
|
||||
this._updateRegions();
|
||||
},
|
||||
|
||||
hideOverview: function() {
|
||||
@ -259,7 +269,6 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
this._inOverview = false;
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
@ -352,28 +361,28 @@ const LayoutManager = new Lang.Class({
|
||||
this.emit('hot-corners-changed');
|
||||
},
|
||||
|
||||
_createBackground: function(monitorIndex) {
|
||||
_addBackgroundMenu: function(bgManager) {
|
||||
BackgroundMenu.addBackgroundMenu(bgManager.backgroundActor, this);
|
||||
},
|
||||
|
||||
_createBackgroundManager: function(monitorIndex) {
|
||||
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
|
||||
layoutManager: this,
|
||||
monitorIndex: monitorIndex });
|
||||
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
|
||||
|
||||
bgManager.connect('changed', Lang.bind(this, function() {
|
||||
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
|
||||
}));
|
||||
bgManager.connect('changed', Lang.bind(this, this._addBackgroundMenu));
|
||||
this._addBackgroundMenu(bgManager);
|
||||
|
||||
this._bgManagers[monitorIndex] = bgManager;
|
||||
|
||||
return bgManager.background;
|
||||
return bgManager;
|
||||
},
|
||||
|
||||
_createSecondaryBackgrounds: function() {
|
||||
_showSecondaryBackgrounds: function() {
|
||||
for (let i = 0; i < this.monitors.length; i++) {
|
||||
if (i != this.primaryIndex) {
|
||||
let background = this._createBackground(i);
|
||||
|
||||
background.actor.opacity = 0;
|
||||
Tweener.addTween(background.actor,
|
||||
let backgroundActor = this._bgManagers[i].backgroundActor;
|
||||
backgroundActor.show();
|
||||
backgroundActor.opacity = 0;
|
||||
Tweener.addTween(backgroundActor,
|
||||
{ opacity: 255,
|
||||
time: BACKGROUND_FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
@ -381,10 +390,6 @@ const LayoutManager = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_createPrimaryBackground: function() {
|
||||
this._createBackground(this.primaryIndex);
|
||||
},
|
||||
|
||||
_updateBackgrounds: function() {
|
||||
let i;
|
||||
for (i = 0; i < this._bgManagers.length; i++)
|
||||
@ -395,14 +400,21 @@ const LayoutManager = new Lang.Class({
|
||||
if (Main.sessionMode.isGreeter)
|
||||
return;
|
||||
|
||||
if (this._startingUp)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < this.monitors.length; i++) {
|
||||
this._createBackground(i);
|
||||
let bgManager = this._createBackgroundManager(i);
|
||||
this._bgManagers.push(bgManager);
|
||||
|
||||
if (i != this.primaryIndex && this._startingUp)
|
||||
bgManager.backgroundActor.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_updateKeyboardBox: function() {
|
||||
this.keyboardBox.set_position(this.keyboardMonitor.x,
|
||||
this.keyboardMonitor.y + this.keyboardMonitor.height);
|
||||
this.keyboardBox.set_size(this.keyboardMonitor.width, -1);
|
||||
},
|
||||
|
||||
_updateBoxes: function() {
|
||||
this.screenShieldGroup.set_position(0, 0);
|
||||
this.screenShieldGroup.set_size(global.screen_width, global.screen_height);
|
||||
@ -410,8 +422,7 @@ const LayoutManager = new Lang.Class({
|
||||
this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
|
||||
this.panelBox.set_size(this.primaryMonitor.width, -1);
|
||||
|
||||
if (this.keyboardIndex < 0)
|
||||
this.keyboardIndex = this.primaryIndex;
|
||||
this.keyboardIndex = this.primaryIndex;
|
||||
|
||||
this.trayBox.set_position(this.bottomMonitor.x,
|
||||
this.bottomMonitor.y + this.bottomMonitor.height);
|
||||
@ -536,9 +547,7 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
set keyboardIndex(v) {
|
||||
this._keyboardIndex = v;
|
||||
this.keyboardBox.set_position(this.keyboardMonitor.x,
|
||||
this.keyboardMonitor.y + this.keyboardMonitor.height);
|
||||
this.keyboardBox.set_size(this.keyboardMonitor.width, -1);
|
||||
this._updateKeyboardBox();
|
||||
},
|
||||
|
||||
get keyboardIndex() {
|
||||
@ -578,10 +587,6 @@ const LayoutManager = new Lang.Class({
|
||||
//
|
||||
// When starting a normal user session, we want to grow it out of the middle
|
||||
// of the screen.
|
||||
//
|
||||
// Usually, we don't want to paint the stage background color because the
|
||||
// MetaBackgroundActor inside global.window_group covers the entirety of the
|
||||
// screen. So, we set no_clear_hint at the end of the animation.
|
||||
|
||||
_prepareStartupAnimation: function() {
|
||||
// During the initial transition, add a simple actor to block all events,
|
||||
@ -592,13 +597,15 @@ const LayoutManager = new Lang.Class({
|
||||
reactive: true });
|
||||
this.addChrome(this._coverPane);
|
||||
|
||||
if (Main.sessionMode.isGreeter) {
|
||||
if (Meta.is_restart()) {
|
||||
// On restart, we don't do an animation
|
||||
} else if (Main.sessionMode.isGreeter) {
|
||||
this.panelBox.translation_y = -this.panelBox.height;
|
||||
} else {
|
||||
this._createPrimaryBackground();
|
||||
this._updateBackgrounds();
|
||||
|
||||
// We need to force an update of the regions now before we scale
|
||||
// the UI group to get the coorect allocation for the struts.
|
||||
// the UI group to get the correct allocation for the struts.
|
||||
this._updateRegions();
|
||||
|
||||
this.trayBox.hide();
|
||||
@ -610,7 +617,7 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
this.uiGroup.set_pivot_point(x / global.screen_width,
|
||||
y / global.screen_height);
|
||||
this.uiGroup.scale_x = this.uiGroup.scale_y = 0.5;
|
||||
this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75;
|
||||
this.uiGroup.opacity = 0;
|
||||
global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
|
||||
}
|
||||
@ -623,14 +630,17 @@ const LayoutManager = new Lang.Class({
|
||||
// until the event loop is uncontended and idle.
|
||||
// This helps to prevent us from running the animation
|
||||
// when the system is bogged down
|
||||
GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
|
||||
let id = GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
|
||||
this._startupAnimation();
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(id, '[gnome-shell] this._startupAnimation');
|
||||
},
|
||||
|
||||
_startupAnimation: function() {
|
||||
if (Main.sessionMode.isGreeter)
|
||||
if (Meta.is_restart())
|
||||
this._startupAnimationComplete();
|
||||
else if (Main.sessionMode.isGreeter)
|
||||
this._startupAnimationGreeter();
|
||||
else
|
||||
this._startupAnimationSession();
|
||||
@ -657,10 +667,6 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_startupAnimationComplete: function() {
|
||||
// At this point, the UI group is covering everything, so
|
||||
// we no longer need to clear the stage
|
||||
global.stage.no_clear_hint = true;
|
||||
|
||||
this._coverPane.destroy();
|
||||
this._coverPane = null;
|
||||
|
||||
@ -673,7 +679,7 @@ const LayoutManager = new Lang.Class({
|
||||
this.keyboardBox.show();
|
||||
|
||||
if (!Main.sessionMode.isGreeter) {
|
||||
this._createSecondaryBackgrounds();
|
||||
this._showSecondaryBackgrounds();
|
||||
global.window_group.remove_clip();
|
||||
}
|
||||
|
||||
@ -723,6 +729,21 @@ const LayoutManager = new Lang.Class({
|
||||
this._updateRegions();
|
||||
},
|
||||
|
||||
// setDummyCursorGeometry:
|
||||
//
|
||||
// The cursor dummy is a standard widget commonly used for popup
|
||||
// menus and box pointers to track, as the box pointer API only
|
||||
// tracks actors. If you want to pop up a menu based on where the
|
||||
// user clicked, or where the text cursor is, the cursor dummy
|
||||
// is what you should use. Given that the menu should not track
|
||||
// the actual mouse pointer as it moves, you need to call this
|
||||
// function before you show the menu to ensure it is at the right
|
||||
// position and has the right size.
|
||||
setDummyCursorGeometry: function(x, y, w, h) {
|
||||
this.dummyCursor.set_position(Math.round(x), Math.round(y));
|
||||
this.dummyCursor.set_size(Math.round(w), Math.round(h));
|
||||
},
|
||||
|
||||
// addChrome:
|
||||
// @actor: an actor to add to the chrome
|
||||
// @params: (optional) additional params
|
||||
@ -814,13 +835,12 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
let actorData = Params.parse(params, defaultParams);
|
||||
actorData.actor = actor;
|
||||
actorData.isToplevel = actor.get_parent() == this.uiGroup;
|
||||
actorData.visibleId = actor.connect('notify::visible',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
actorData.allocationId = actor.connect('notify::allocation',
|
||||
Lang.bind(this, this._queueUpdateRegions));
|
||||
actorData.parentSetId = actor.connect('parent-set',
|
||||
Lang.bind(this, this._actorReparented));
|
||||
actorData.destroyId = actor.connect('destroy',
|
||||
Lang.bind(this, this._untrackActor));
|
||||
// Note that destroying actor will unset its parent, so we don't
|
||||
// need to connect to 'destroy' too.
|
||||
|
||||
@ -838,22 +858,11 @@ const LayoutManager = new Lang.Class({
|
||||
this._trackedActors.splice(i, 1);
|
||||
actor.disconnect(actorData.visibleId);
|
||||
actor.disconnect(actorData.allocationId);
|
||||
actor.disconnect(actorData.parentSetId);
|
||||
actor.disconnect(actorData.destroyId);
|
||||
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
|
||||
_actorReparented: function(actor, oldParent) {
|
||||
let newParent = actor.get_parent();
|
||||
if (!newParent) {
|
||||
this._untrackActor(actor);
|
||||
} else {
|
||||
let i = this._findActor(actor);
|
||||
let actorData = this._trackedActors[i];
|
||||
actorData.isToplevel = (newParent == this.uiGroup);
|
||||
}
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview;
|
||||
|
||||
@ -864,8 +873,6 @@ const LayoutManager = new Lang.Class({
|
||||
let actorData = this._trackedActors[i], visible;
|
||||
if (!actorData.trackFullscreen)
|
||||
continue;
|
||||
if (!actorData.isToplevel)
|
||||
continue;
|
||||
|
||||
if (!windowsVisible)
|
||||
visible = true;
|
||||
@ -898,15 +905,12 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_queueUpdateRegions: function() {
|
||||
if (Main.sessionMode.isGreeter)
|
||||
return;
|
||||
|
||||
if (this._startingUp)
|
||||
return;
|
||||
|
||||
if (!this._updateRegionIdle)
|
||||
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
|
||||
Meta.PRIORITY_BEFORE_REDRAW);
|
||||
this._updateRegionIdle = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||||
Lang.bind(this, this._updateRegions));
|
||||
},
|
||||
|
||||
_getWindowActorsForWorkspace: function(workspace) {
|
||||
@ -934,13 +938,16 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateRegions: function() {
|
||||
let rects = [], struts = [], i;
|
||||
|
||||
if (this._updateRegionIdle) {
|
||||
Mainloop.source_remove(this._updateRegionIdle);
|
||||
Meta.later_remove(this._updateRegionIdle);
|
||||
delete this._updateRegionIdle;
|
||||
}
|
||||
|
||||
// No need to update when we have a modal.
|
||||
if (Main.modalCount > 0)
|
||||
return GLib.SOURCE_REMOVE;
|
||||
|
||||
let rects = [], struts = [], i;
|
||||
let isPopupMenuVisible = global.top_window_group.get_children().some(isPopupMetaWindow);
|
||||
let wantsInputRegion = !isPopupMenuVisible;
|
||||
|
||||
@ -1010,23 +1017,6 @@ const LayoutManager = new Lang.Class({
|
||||
else
|
||||
continue;
|
||||
|
||||
// Ensure that the strut rects goes all the way to the screen edge,
|
||||
// as this really what mutter expects.
|
||||
switch (side) {
|
||||
case Meta.Side.TOP:
|
||||
y1 = 0;
|
||||
break;
|
||||
case Meta.Side.BOTTOM:
|
||||
y2 = global.screen_height;
|
||||
break;
|
||||
case Meta.Side.LEFT:
|
||||
x1 = 0;
|
||||
break;
|
||||
case Meta.Side.RIGHT:
|
||||
x2 = global.screen_width;
|
||||
break;
|
||||
}
|
||||
|
||||
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
|
||||
let strut = new Meta.Strut({ rect: strutRect, side: side });
|
||||
struts.push(strut);
|
||||
@ -1042,8 +1032,14 @@ const LayoutManager = new Lang.Class({
|
||||
workspace.set_builtin_struts(struts);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
modalEnded: function() {
|
||||
// We don't update the stage input region while in a modal,
|
||||
// so queue an update now.
|
||||
this._queueUpdateRegions();
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(LayoutManager.prototype);
|
||||
|
||||
@ -1229,20 +1225,20 @@ const HotCorner = new Lang.Class({
|
||||
this._entered = true;
|
||||
this._toggleOverview();
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onCornerLeft : function(actor, event) {
|
||||
if (event.get_related() != this.actor)
|
||||
this._entered = false;
|
||||
// Consume event, otherwise this will confuse onEnvironsLeft
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_onEnvironsLeft : function(actor, event) {
|
||||
if (event.get_related() != this._corner)
|
||||
this._entered = false;
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -5,11 +5,67 @@ const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const DEFAULT_FADE_FACTOR = 0.4;
|
||||
const VIGNETTE_BRIGHTNESS = 0.8;
|
||||
const VIGNETTE_SHARPNESS = 0.7;
|
||||
|
||||
const VIGNETTE_DECLARATIONS = '\
|
||||
uniform float brightness;\n\
|
||||
uniform float vignette_sharpness;\n';
|
||||
|
||||
const VIGNETTE_CODE = '\
|
||||
cogl_color_out.a = cogl_color_in.a;\n\
|
||||
cogl_color_out.rgb = vec3(0.0, 0.0, 0.0);\n\
|
||||
vec2 position = cogl_tex_coord_in[0].xy - 0.5;\n\
|
||||
float t = length(2.0 * position);\n\
|
||||
t = clamp(t, 0.0, 1.0);\n\
|
||||
float pixel_brightness = mix(1.0, 1.0 - vignette_sharpness, t);\n\
|
||||
cogl_color_out.a = cogl_color_out.a * (1 - pixel_brightness * brightness);';
|
||||
|
||||
const RadialShaderQuad = new Lang.Class({
|
||||
Name: 'RadialShaderQuad',
|
||||
Extends: Shell.GLSLQuad,
|
||||
|
||||
_init: function(params) {
|
||||
this.parent(params);
|
||||
|
||||
this._brightnessLocation = this.get_uniform_location('brightness');
|
||||
this._sharpnessLocation = this.get_uniform_location('vignette_sharpness');
|
||||
|
||||
this.brightness = 1.0;
|
||||
this.vignetteSharpness = 0.0;
|
||||
},
|
||||
|
||||
vfunc_build_pipeline: function() {
|
||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT,
|
||||
VIGNETTE_DECLARATIONS, VIGNETTE_CODE, true);
|
||||
},
|
||||
|
||||
get brightness() {
|
||||
return this._brightness;
|
||||
},
|
||||
|
||||
set brightness(v) {
|
||||
this._brightness = v;
|
||||
this.set_uniform_float(this._brightnessLocation,
|
||||
1, [this._brightness]);
|
||||
},
|
||||
|
||||
get vignetteSharpness() {
|
||||
return this._sharpness;
|
||||
},
|
||||
|
||||
set vignetteSharpness(v) {
|
||||
this._sharpness = v;
|
||||
this.set_uniform_float(this._sharpnessLocation,
|
||||
1, [this._sharpness]);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Lightbox:
|
||||
@ -43,15 +99,23 @@ const Lightbox = new Lang.Class({
|
||||
width: null,
|
||||
height: null,
|
||||
fadeFactor: DEFAULT_FADE_FACTOR,
|
||||
radialEffect: false,
|
||||
});
|
||||
|
||||
this._container = container;
|
||||
this._children = container.get_children();
|
||||
this._fadeFactor = params.fadeFactor;
|
||||
this.actor = new St.Bin({ x: 0,
|
||||
y: 0,
|
||||
style_class: 'lightbox',
|
||||
reactive: params.inhibitEvents });
|
||||
this._radialEffect = Clutter.feature_available(Clutter.FeatureFlags.SHADERS_GLSL) && params.radialEffect;
|
||||
if (this._radialEffect)
|
||||
this.actor = new RadialShaderQuad({ x: 0,
|
||||
y: 0,
|
||||
reactive: params.inhibitEvents });
|
||||
else
|
||||
this.actor = new St.Bin({ x: 0,
|
||||
y: 0,
|
||||
opacity: 0,
|
||||
style_class: 'lightbox',
|
||||
reactive: params.inhibitEvents });
|
||||
|
||||
container.add_actor(this.actor);
|
||||
this.actor.raise_top();
|
||||
@ -101,9 +165,18 @@ const Lightbox = new Lang.Class({
|
||||
fadeInTime = fadeInTime || 0;
|
||||
|
||||
Tweener.removeTweens(this.actor);
|
||||
if (fadeInTime != 0) {
|
||||
this.shown = false;
|
||||
this.actor.opacity = 0;
|
||||
if (this._radialEffect) {
|
||||
Tweener.addTween(this.actor,
|
||||
{ brightness: VIGNETTE_BRIGHTNESS,
|
||||
vignetteSharpness: VIGNETTE_SHARPNESS,
|
||||
time: fadeInTime,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.shown = true;
|
||||
this.emit('shown');
|
||||
})
|
||||
});
|
||||
} else {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255 * this._fadeFactor,
|
||||
time: fadeInTime,
|
||||
@ -113,11 +186,8 @@ const Lightbox = new Lang.Class({
|
||||
this.emit('shown');
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.actor.opacity = 255 * this._fadeFactor;
|
||||
this.shown = true;
|
||||
this.emit('shown');
|
||||
}
|
||||
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
@ -126,7 +196,18 @@ const Lightbox = new Lang.Class({
|
||||
|
||||
this.shown = false;
|
||||
Tweener.removeTweens(this.actor);
|
||||
if (fadeOutTime != 0) {
|
||||
if (this._radialEffect) {
|
||||
Tweener.addTween(this.actor,
|
||||
{ brightness: 1.0,
|
||||
vignetteSharpness: 0.0,
|
||||
opacity: 0,
|
||||
time: fadeOutTime,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.actor.hide();
|
||||
})
|
||||
});
|
||||
} else {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: fadeOutTime,
|
||||
@ -135,8 +216,6 @@ const Lightbox = new Lang.Class({
|
||||
this.actor.hide();
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.actor.hide();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -27,6 +27,8 @@ const CHEVRON = '>>> ';
|
||||
/* Imports...feel free to add here as needed */
|
||||
var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
'const GLib = imports.gi.GLib; ' +
|
||||
'const GObject = imports.gi.GObject; ' +
|
||||
'const Gio = imports.gi.Gio; ' +
|
||||
'const Gtk = imports.gi.Gtk; ' +
|
||||
'const Mainloop = imports.mainloop; ' +
|
||||
'const Meta = imports.gi.Meta; ' +
|
||||
@ -109,6 +111,7 @@ const AutoComplete = new Lang.Class({
|
||||
}
|
||||
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",
|
||||
@ -558,7 +561,7 @@ const Inspector = new Lang.Class({
|
||||
_onKeyPressEvent: function (actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape)
|
||||
this._close();
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_onButtonPressEvent: function (actor, event) {
|
||||
@ -567,7 +570,7 @@ const Inspector = new Lang.Class({
|
||||
this.emit('target', this._target, stageX, stageY);
|
||||
}
|
||||
this._close();
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_onScrollEvent: function (actor, event) {
|
||||
@ -601,12 +604,12 @@ const Inspector = new Lang.Class({
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_onMotionEvent: function (actor, event) {
|
||||
this._update(event);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_update: function(event) {
|
||||
@ -669,13 +672,13 @@ const Extensions = new Lang.Class({
|
||||
_onViewSource: function (actor) {
|
||||
let extension = actor._extension;
|
||||
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();
|
||||
},
|
||||
|
||||
_onWebPage: function (actor) {
|
||||
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();
|
||||
},
|
||||
|
||||
@ -794,7 +797,7 @@ const LookingGlass = new Lang.Class({
|
||||
reactive: true });
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._globalKeyPressEvent));
|
||||
|
||||
this._interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._interfaceSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
|
||||
this._interfaceSettings.connect('changed::monospace-font-name',
|
||||
Lang.bind(this, this._updateFont));
|
||||
this._updateFont();
|
||||
@ -828,7 +831,7 @@ const LookingGlass = new Lang.Class({
|
||||
global.stage.set_key_focus(this._entry);
|
||||
}));
|
||||
this.actor.hide();
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}));
|
||||
|
||||
let gcIcon = new St.Icon({ icon_name: 'gnome-fs-trash-full',
|
||||
@ -840,8 +843,11 @@ const LookingGlass = new Lang.Class({
|
||||
System.gc();
|
||||
this._timeoutId = Mainloop.timeout_add(500, Lang.bind(this, function () {
|
||||
gcIcon.icon_name = 'gnome-fs-trash-full';
|
||||
Mainloop.source_remove(this._timeoutId);
|
||||
this._timeoutId = 0;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] gcIcon.icon_name = \'gnome-fs-trash-full\'');
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
|
||||
let notebook = new Notebook();
|
||||
@ -1063,7 +1069,7 @@ const LookingGlass = new Lang.Class({
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
// Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
|
||||
if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
|
||||
@ -1073,7 +1079,7 @@ const LookingGlass = new Lang.Class({
|
||||
this._notebook.nextTab();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
open : function() {
|
||||
|
@ -11,6 +11,7 @@ const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Background = imports.ui.background;
|
||||
const FocusCaretTracker = imports.ui.focusCaretTracker;
|
||||
const Main = imports.ui.main;
|
||||
const MagnifierDBus = imports.ui.magnifierDBus;
|
||||
@ -57,20 +58,6 @@ const Magnifier = new Lang.Class({
|
||||
// Magnifier is a manager of 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.
|
||||
let cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
|
||||
this._mouseSprite = new Clutter.Texture();
|
||||
@ -86,11 +73,15 @@ const Magnifier = new Lang.Class({
|
||||
|
||||
let aZoomRegion = new ZoomRegion(this, this._cursorRoot);
|
||||
this._zoomRegions.push(aZoomRegion);
|
||||
this._settingsInitRegion(aZoomRegion);
|
||||
let showAtLaunch = this._settingsInit(aZoomRegion);
|
||||
aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
|
||||
|
||||
cursorTracker.connect('cursor-changed', Lang.bind(this, this._updateMouseSprite));
|
||||
this._cursorTracker = cursorTracker;
|
||||
|
||||
// Export to dbus.
|
||||
magDBusService = new MagnifierDBus.ShellMagnifier();
|
||||
this.setActive(showAtLaunch);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -115,20 +106,21 @@ const Magnifier = new Lang.Class({
|
||||
* @activate: Boolean to activate or de-activate the magnifier.
|
||||
*/
|
||||
setActive: function(activate) {
|
||||
if (activate == this.isActive())
|
||||
return;
|
||||
|
||||
if (activate)
|
||||
this._initialize();
|
||||
let isActive = this.isActive();
|
||||
|
||||
this._zoomRegions.forEach (function(zoomRegion, index, array) {
|
||||
zoomRegion.setActive(activate);
|
||||
});
|
||||
|
||||
if (activate)
|
||||
this.startTrackingMouse();
|
||||
else
|
||||
this.stopTrackingMouse();
|
||||
if (isActive != activate) {
|
||||
if (activate) {
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
this.startTrackingMouse();
|
||||
} else {
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
this.stopTrackingMouse();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure system mouse pointer is shown when all zoom regions are
|
||||
// invisible.
|
||||
@ -448,68 +440,64 @@ const Magnifier = new Lang.Class({
|
||||
this._mouseSprite.set_anchor_point(xHot, yHot);
|
||||
},
|
||||
|
||||
_settingsInitRegion: function(zoomRegion) {
|
||||
// Mag factor is accurate to two decimal places.
|
||||
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
|
||||
if (aPref != 0.0)
|
||||
zoomRegion.setMagFactor(aPref, aPref);
|
||||
_settingsInit: function(zoomRegion) {
|
||||
this._appSettings = new Gio.Settings({ schema_id: APPLICATIONS_SCHEMA });
|
||||
this._settings = new Gio.Settings({ schema_id: MAGNIFIER_SCHEMA });
|
||||
|
||||
aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setScreenPosition(aPref);
|
||||
if (zoomRegion) {
|
||||
// Mag factor is accurate to two decimal places.
|
||||
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
|
||||
if (aPref != 0.0)
|
||||
zoomRegion.setMagFactor(aPref, aPref);
|
||||
|
||||
zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
|
||||
zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
|
||||
aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setScreenPosition(aPref);
|
||||
|
||||
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setMouseTrackingMode(aPref);
|
||||
zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
|
||||
zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
|
||||
|
||||
aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setFocusTrackingMode(aPref);
|
||||
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setMouseTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_enum(CARET_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setCaretTrackingMode(aPref);
|
||||
aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setFocusTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setInvertLightness(aPref);
|
||||
aPref = this._settings.get_enum(CARET_TRACKING_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setCaretTrackingMode(aPref);
|
||||
|
||||
aPref = this._settings.get_double(COLOR_SATURATION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setColorSaturation(aPref);
|
||||
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setInvertLightness(aPref);
|
||||
|
||||
let bc = {};
|
||||
bc.r = this._settings.get_double(BRIGHT_RED_KEY);
|
||||
bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
|
||||
zoomRegion.setBrightness(bc);
|
||||
aPref = this._settings.get_double(COLOR_SATURATION_KEY);
|
||||
if (aPref)
|
||||
zoomRegion.setColorSaturation(aPref);
|
||||
|
||||
bc.r = this._settings.get_double(CONTRAST_RED_KEY);
|
||||
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||
zoomRegion.setContrast(bc);
|
||||
},
|
||||
let bc = {};
|
||||
bc.r = this._settings.get_double(BRIGHT_RED_KEY);
|
||||
bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
|
||||
zoomRegion.setBrightness(bc);
|
||||
|
||||
_settingsInit: function() {
|
||||
this._appSettings = new Gio.Settings({ schema: APPLICATIONS_SCHEMA });
|
||||
this._settings = new Gio.Settings({ schema: MAGNIFIER_SCHEMA });
|
||||
bc.r = this._settings.get_double(CONTRAST_RED_KEY);
|
||||
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
|
||||
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
|
||||
zoomRegion.setContrast(bc);
|
||||
}
|
||||
|
||||
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);
|
||||
this.addCrosshairs();
|
||||
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,
|
||||
Lang.bind(this, this._updateScreenPosition));
|
||||
this._settings.connect('changed::' + MAG_FACTOR_KEY,
|
||||
@ -573,6 +561,7 @@ const Magnifier = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY));
|
||||
}));
|
||||
return this._appSettings.get_boolean(SHOW_KEY);
|
||||
},
|
||||
|
||||
_updateScreenPosition: function() {
|
||||
@ -731,8 +720,16 @@ const ZoomRegion = new Lang.Class({
|
||||
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]
|
||||
let extents;
|
||||
try {
|
||||
extents = component.get_extents(Atspi.CoordType.SCREEN);
|
||||
} catch(e) {
|
||||
log('Failed to read extents of focused component: ' + e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
[this._xFocus, this._yFocus] = [extents.x + (extents.width / 2),
|
||||
extents.y + (extents.height / 2)];
|
||||
this._centerFromFocusPosition();
|
||||
},
|
||||
|
||||
@ -740,7 +737,14 @@ const ZoomRegion = new Lang.Class({
|
||||
let text = event.source.get_text_iface();
|
||||
if (!text)
|
||||
return;
|
||||
let extents = text.get_character_extents(text.get_caret_offset(), 0);
|
||||
let extents;
|
||||
try {
|
||||
extents = text.get_character_extents(text.get_caret_offset(), 0);
|
||||
} catch(e) {
|
||||
log('Failed to read extents of text caret: ' + e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
[this._xCaret, this._yCaret] = [extents.x, extents.y];
|
||||
this._centerFromCaretPosition();
|
||||
},
|
||||
@ -763,6 +767,9 @@ const ZoomRegion = new Lang.Class({
|
||||
} else {
|
||||
this._destroyActors();
|
||||
}
|
||||
|
||||
this._syncCaretTracking();
|
||||
this._syncFocusTracking();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -822,10 +829,7 @@ const ZoomRegion = new Lang.Class({
|
||||
*/
|
||||
setFocusTrackingMode: function(mode) {
|
||||
this._focusTrackingMode = mode;
|
||||
if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.NONE)
|
||||
this._focusCaretTracker.deregisterFocusListener();
|
||||
else
|
||||
this._focusCaretTracker.registerFocusListener();
|
||||
this._syncFocusTracking();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -834,10 +838,27 @@ const ZoomRegion = new Lang.Class({
|
||||
*/
|
||||
setCaretTrackingMode: function(mode) {
|
||||
this._caretTrackingMode = mode;
|
||||
if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.NONE)
|
||||
this._focusCaretTracker.deregisterCaretListener();
|
||||
this._syncCaretTracking();
|
||||
},
|
||||
|
||||
_syncFocusTracking: function() {
|
||||
let enabled = this._focusTrackingMode != GDesktopEnums.MagnifierFocusTrackingMode.NONE &&
|
||||
this.isActive();
|
||||
|
||||
if (enabled)
|
||||
this._focusCaretTracker.registerFocusListener();
|
||||
else
|
||||
this._focusCaretTracker.deregisterFocusListener();
|
||||
},
|
||||
|
||||
_syncCaretTracking: function() {
|
||||
let enabled = this._caretTrackingMode != GDesktopEnums.MagnifierCaretTrackingMode.NONE &&
|
||||
this.isActive();
|
||||
|
||||
if (enabled)
|
||||
this._focusCaretTracker.registerCaretListener();
|
||||
else
|
||||
this._focusCaretTracker.deregisterCaretListener();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1177,14 +1198,13 @@ const ZoomRegion = new Lang.Class({
|
||||
|
||||
// Add a background for when the magnified uiGroup is scrolled
|
||||
// out of view (don't want to see desktop showing through).
|
||||
this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR,
|
||||
width: global.screen_width,
|
||||
height: global.screen_height });
|
||||
this._background = (new Background.SystemBackground()).actor;
|
||||
mainGroup.add_actor(this._background);
|
||||
|
||||
// Clone the group that contains all of UI on the screen. This is the
|
||||
// chrome, the windows, etc.
|
||||
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
|
||||
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup,
|
||||
clip_to_allocation: true });
|
||||
mainGroup.add_actor(this._uiGroupClone);
|
||||
|
||||
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
|
||||
|
126
js/ui/main.js
126
js/ui/main.js
@ -18,6 +18,7 @@ const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
const Keyboard = imports.ui.keyboard;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const OsdWindow = imports.ui.osdWindow;
|
||||
const Overview = imports.ui.overview;
|
||||
const Panel = imports.ui.panel;
|
||||
@ -39,10 +40,9 @@ const Magnifier = imports.ui.magnifier;
|
||||
const XdndHandler = imports.ui.xdndHandler;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
||||
|
||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
||||
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
|
||||
const GNOMESHELL_STARTED_MESSAGE_ID = 'f3ea493c22934e26811cd62abe8e203a';
|
||||
|
||||
let componentManager = null;
|
||||
let panel = null;
|
||||
@ -55,7 +55,7 @@ let screenShield = null;
|
||||
let notificationDaemon = null;
|
||||
let windowAttentionHandler = null;
|
||||
let ctrlAltTabManager = null;
|
||||
let osdWindow = null;
|
||||
let osdWindowManager = null;
|
||||
let sessionMode = null;
|
||||
let shellDBusService = null;
|
||||
let shellMountOpDBusService = null;
|
||||
@ -73,7 +73,6 @@ let _startDate;
|
||||
let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
let _a11ySettings = null;
|
||||
let dynamicWorkspacesSchema = null;
|
||||
|
||||
function _sessionUpdated() {
|
||||
_loadDefaultStylesheet();
|
||||
@ -103,22 +102,13 @@ function start() {
|
||||
global.logError = 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
|
||||
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
|
||||
|
||||
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
||||
|
||||
sessionMode = new SessionMode.SessionMode();
|
||||
sessionMode.connect('sessions-loaded', _sessionsLoaded);
|
||||
sessionMode.init();
|
||||
}
|
||||
|
||||
function _sessionsLoaded() {
|
||||
sessionMode.connect('updated', _sessionUpdated);
|
||||
_initializePrefs();
|
||||
_initializeUI();
|
||||
|
||||
shellDBusService = new ShellDBus.GnomeShell();
|
||||
@ -127,17 +117,6 @@ function _sessionsLoaded() {
|
||||
_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
|
||||
// also initialize ShellAppSystem first. ShellAppSystem
|
||||
@ -150,6 +129,9 @@ function _initializeUI() {
|
||||
Shell.WindowTracker.get_default();
|
||||
Shell.AppUsage.get_default();
|
||||
|
||||
let resource = Gio.Resource.load(global.datadir + '/gnome-shell-theme.gresource');
|
||||
resource._register();
|
||||
|
||||
_loadDefaultStylesheet();
|
||||
|
||||
// Setup the stage hierarchy early
|
||||
@ -163,7 +145,7 @@ function _initializeUI() {
|
||||
screencastService = new Screencast.ScreencastService();
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
osdWindow = new OsdWindow.OsdWindow();
|
||||
osdWindowManager = new OsdWindow.OsdWindowManager();
|
||||
overview = new Overview.Overview();
|
||||
wm = new WindowManager.WindowManager();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
@ -180,13 +162,23 @@ function _initializeUI() {
|
||||
layoutManager.init();
|
||||
overview.init();
|
||||
|
||||
_a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
|
||||
_a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA });
|
||||
|
||||
global.display.connect('overlay-key', Lang.bind(overview, function () {
|
||||
if (!_a11ySettings.get_boolean (STICKY_KEYS_ENABLE))
|
||||
overview.toggle();
|
||||
}));
|
||||
|
||||
global.display.connect('show-restart-message', function(display, message) {
|
||||
showRestartMessage(message);
|
||||
return true;
|
||||
});
|
||||
|
||||
global.display.connect('restart', function() {
|
||||
global.reexec_self();
|
||||
return true;
|
||||
});
|
||||
|
||||
// Provide the bus object for gnome-session to
|
||||
// initiate logouts.
|
||||
EndSessionDialog.init();
|
||||
@ -196,8 +188,6 @@ function _initializeUI() {
|
||||
|
||||
_startDate = new Date();
|
||||
|
||||
log('GNOME Shell started at ' + _startDate);
|
||||
|
||||
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
|
||||
if (perfModuleName) {
|
||||
let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
|
||||
@ -215,21 +205,46 @@ function _initializeUI() {
|
||||
}
|
||||
|
||||
layoutManager.connect('startup-complete', function() {
|
||||
if (keybindingMode == Shell.KeyBindingMode.NONE) {
|
||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
}
|
||||
if (screenShield) {
|
||||
screenShield.lockIfWasLocked();
|
||||
}
|
||||
});
|
||||
if (keybindingMode == Shell.KeyBindingMode.NONE) {
|
||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
}
|
||||
if (screenShield) {
|
||||
screenShield.lockIfWasLocked();
|
||||
}
|
||||
if (LoginManager.haveSystemd() &&
|
||||
sessionMode.currentMode != 'gdm' &&
|
||||
sessionMode.currentMode != 'initial-setup') {
|
||||
// Do not import globally to not depend
|
||||
// on systemd on non-systemd systems.
|
||||
let GSystem = imports.gi.GSystem;
|
||||
GSystem.log_structured_print('GNOME Shell started at ' + _startDate,
|
||||
['MESSAGE_ID=' + GNOMESHELL_STARTED_MESSAGE_ID]);
|
||||
} else {
|
||||
log('GNOME Shell started at ' + _startDate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _getDefaultStylesheet() {
|
||||
let stylesheet;
|
||||
|
||||
stylesheet = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/' + sessionMode.stylesheetName);
|
||||
if (stylesheet.query_exists(null))
|
||||
return stylesheet;
|
||||
|
||||
stylesheet = Gio.File.new_for_path(global.datadir + '/theme/' + sessionMode.stylesheetName);
|
||||
if (stylesheet.query_exists(null))
|
||||
return stylesheet;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function _loadDefaultStylesheet() {
|
||||
if (!sessionMode.isPrimary)
|
||||
return;
|
||||
|
||||
let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
|
||||
if (_defaultCssStylesheet == stylesheet)
|
||||
let stylesheet = _getDefaultStylesheet();
|
||||
if (_defaultCssStylesheet && _defaultCssStylesheet.equal(stylesheet))
|
||||
return;
|
||||
|
||||
_defaultCssStylesheet = stylesheet;
|
||||
@ -244,8 +259,7 @@ function _loadDefaultStylesheet() {
|
||||
* Returns: A file path that contains the theme CSS,
|
||||
* null if using the default
|
||||
*/
|
||||
function getThemeStylesheet()
|
||||
{
|
||||
function getThemeStylesheet() {
|
||||
return _cssStylesheet;
|
||||
}
|
||||
|
||||
@ -256,9 +270,8 @@ function getThemeStylesheet()
|
||||
*
|
||||
* Set the theme CSS file that the shell will load
|
||||
*/
|
||||
function setThemeStylesheet(cssStylesheet)
|
||||
{
|
||||
_cssStylesheet = cssStylesheet;
|
||||
function setThemeStylesheet(cssStylesheet) {
|
||||
_cssStylesheet = Gio.File.new_for_path(cssStylesheet);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -456,6 +469,7 @@ function popModal(actor, timestamp) {
|
||||
if (modalCount > 0)
|
||||
return;
|
||||
|
||||
layoutManager.modalEnded();
|
||||
global.end_modal(timestamp);
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
@ -614,7 +628,33 @@ function queueDeferredWork(workId) {
|
||||
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
|
||||
_runAllDeferredWork();
|
||||
_deferredTimeoutId = 0;
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
GLib.Source.set_name_by_id(_deferredTimeoutId, '[gnome-shell] _runAllDeferredWork');
|
||||
}
|
||||
}
|
||||
|
||||
const RestartMessage = new Lang.Class({
|
||||
Name: 'RestartMessage',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init : function(message) {
|
||||
this.parent({ shellReactive: true,
|
||||
styleClass: 'restart-message',
|
||||
shouldFadeIn: false,
|
||||
destroyOnClose: true });
|
||||
|
||||
let label = new St.Label({ text: message });
|
||||
|
||||
this.contentLayout.add(label, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.MIDDLE,
|
||||
y_align: St.Align.MIDDLE });
|
||||
this.buttonLayout.hide();
|
||||
}
|
||||
});
|
||||
|
||||
function showRestartMessage(message) {
|
||||
let restartMessage = new RestartMessage(message);
|
||||
restartMessage.open();
|
||||
}
|
||||
|
@ -15,11 +15,11 @@ const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
|
||||
const EdgeDragAction = imports.ui.edgeDragAction;
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const GrabHelper = imports.ui.grabHelper;
|
||||
const Hash = imports.misc.hash;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const PointerWatcher = imports.ui.pointerWatcher;
|
||||
@ -27,6 +27,7 @@ const PopupMenu = imports.ui.popupMenu;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Util = imports.misc.util;
|
||||
const ViewSelector = imports.ui.viewSelector;
|
||||
|
||||
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
||||
|
||||
@ -76,7 +77,7 @@ const Urgency = {
|
||||
NORMAL: 1,
|
||||
HIGH: 2,
|
||||
CRITICAL: 3
|
||||
}
|
||||
};
|
||||
|
||||
function _fixMarkup(text, allowMarkup) {
|
||||
if (allowMarkup) {
|
||||
@ -112,7 +113,6 @@ const FocusGrabber = new Lang.Class({
|
||||
if (this._focused)
|
||||
return;
|
||||
|
||||
this._prevFocusedWindow = global.display.focus_window;
|
||||
this._prevKeyFocusActor = global.stage.get_key_focus();
|
||||
|
||||
this._focusActorChangedId = global.stage.connect('notify::key-focus', Lang.bind(this, this._focusActorChanged));
|
||||
@ -187,7 +187,7 @@ const URLHighlighter = new Lang.Class({
|
||||
// The MessageTray doesn't actually hide us, so
|
||||
// we need to check for paint opacities as well.
|
||||
if (!actor.visible || actor.get_paint_opacity() == 0)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
// Keep Notification.actor from seeing this and taking
|
||||
// a pointer grab, which would block our button-release-event
|
||||
@ -196,7 +196,7 @@ const URLHighlighter = new Lang.Class({
|
||||
}));
|
||||
this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
|
||||
if (!actor.visible || actor.get_paint_opacity() == 0)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let urlId = this._findUrlAtPos(event);
|
||||
if (urlId != -1) {
|
||||
@ -204,14 +204,14 @@ const URLHighlighter = new Lang.Class({
|
||||
if (url.indexOf(':') == -1)
|
||||
url = 'http://' + url;
|
||||
|
||||
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
|
||||
return true;
|
||||
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1));
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
this.actor.connect('motion-event', Lang.bind(this, function(actor, event) {
|
||||
if (!actor.visible || actor.get_paint_opacity() == 0)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let urlId = this._findUrlAtPos(event);
|
||||
if (urlId != -1 && !this._cursorChanged) {
|
||||
@ -221,16 +221,17 @@ const URLHighlighter = new Lang.Class({
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
this._cursorChanged = false;
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
this.actor.connect('leave-event', Lang.bind(this, function() {
|
||||
if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
|
||||
return;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (this._cursorChanged) {
|
||||
this._cursorChanged = false;
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
},
|
||||
|
||||
@ -316,7 +317,7 @@ const NotificationGenericPolicy = new Lang.Class({
|
||||
|
||||
this.id = 'generic';
|
||||
|
||||
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
|
||||
this._masterSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.notifications' });
|
||||
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
|
||||
},
|
||||
|
||||
@ -366,8 +367,8 @@ const NotificationApplicationPolicy = new Lang.Class({
|
||||
this.id = id;
|
||||
this._canonicalId = this._canonicalizeId(id);
|
||||
|
||||
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications.application',
|
||||
this._masterSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.notifications' });
|
||||
this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.notifications.application',
|
||||
path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
|
||||
|
||||
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
|
||||
@ -504,7 +505,6 @@ const Notification = new Lang.Class({
|
||||
this.bannerBodyMarkup = false;
|
||||
this._bannerBodyAdded = false;
|
||||
this._titleFitsInBannerMode = true;
|
||||
this._titleDirection = Clutter.TextDirection.DEFAULT;
|
||||
this._spacing = 0;
|
||||
this._scrollPolicy = Gtk.PolicyType.AUTOMATIC;
|
||||
this._imageBin = null;
|
||||
@ -643,10 +643,11 @@ const Notification = new Lang.Class({
|
||||
title = title ? _fixMarkup(title.replace(/\n/g, ' '), false) : '';
|
||||
this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>');
|
||||
|
||||
let titleDirection;
|
||||
if (Pango.find_base_dir(title, -1) == Pango.Direction.RTL)
|
||||
this._titleDirection = Clutter.TextDirection.RTL;
|
||||
titleDirection = Clutter.TextDirection.RTL;
|
||||
else
|
||||
this._titleDirection = Clutter.TextDirection.LTR;
|
||||
titleDirection = Clutter.TextDirection.LTR;
|
||||
|
||||
// Let the title's text direction control the overall direction
|
||||
// of the notification - in case where different scripts are used
|
||||
@ -654,7 +655,7 @@ const Notification = new Lang.Class({
|
||||
// arguably for action buttons as well. Labels other than the title
|
||||
// will be allocated at the available width, so that their alignment
|
||||
// is done correctly automatically.
|
||||
this._table.set_text_direction(this._titleDirection);
|
||||
this._table.set_text_direction(titleDirection);
|
||||
|
||||
// Unless the notification has custom content, we save this.bannerBodyText
|
||||
// to add it to the content of the notification if the notification is
|
||||
@ -923,7 +924,7 @@ const Notification = new Lang.Class({
|
||||
let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(availWidth);
|
||||
let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(availWidth);
|
||||
|
||||
let rtl = (this._titleDirection == Clutter.TextDirection.RTL);
|
||||
let rtl = (this._table.text_direction == Clutter.TextDirection.RTL);
|
||||
let x = rtl ? availWidth : 0;
|
||||
|
||||
if (this._secondaryIcon) {
|
||||
@ -1001,6 +1002,9 @@ const Notification = new Lang.Class({
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||||
Lang.bind(this,
|
||||
function() {
|
||||
if (this._destroyed)
|
||||
return false;
|
||||
|
||||
if (this._canExpandContent()) {
|
||||
this._addBannerBody();
|
||||
this._table.add_style_class_name('multi-line-notification');
|
||||
@ -1155,38 +1159,19 @@ const SourceActor = new Lang.Class({
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this.actor.connect('destroy', Lang.bind(this, function() {
|
||||
this._source.disconnect(this._iconUpdatedId);
|
||||
this._actorDestroyed = true;
|
||||
}));
|
||||
this._actorDestroyed = false;
|
||||
|
||||
this._counterLabel = new St.Label( {x_align: Clutter.ActorAlign.CENTER,
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
y_expand: true });
|
||||
|
||||
this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
|
||||
child: this._counterLabel,
|
||||
layout_manager: new Clutter.BinLayout() });
|
||||
this._counterBin.hide();
|
||||
|
||||
this._counterBin.connect('style-changed', Lang.bind(this, function() {
|
||||
let themeNode = this._counterBin.get_theme_node();
|
||||
this._counterBin.translation_x = themeNode.get_length('-shell-counter-overlap-x');
|
||||
this._counterBin.translation_y = themeNode.get_length('-shell-counter-overlap-y');
|
||||
}));
|
||||
|
||||
this._iconBin = new St.Bin({ width: size,
|
||||
height: size,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
let scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
this._iconBin = new St.Bin({ x_fill: true,
|
||||
height: size * scale_factor,
|
||||
width: size * scale_factor });
|
||||
|
||||
this.actor.add_actor(this._iconBin);
|
||||
this.actor.add_actor(this._counterBin);
|
||||
|
||||
this._source.connect('count-updated', Lang.bind(this, this._updateCount));
|
||||
this._updateCount();
|
||||
|
||||
this._source.connect('icon-updated', Lang.bind(this, this._updateIcon));
|
||||
this._iconUpdatedId = this._source.connect('icon-updated', Lang.bind(this, this._updateIcon));
|
||||
this._updateIcon();
|
||||
},
|
||||
|
||||
@ -1208,6 +1193,52 @@ const SourceActor = new Lang.Class({
|
||||
_allocate: function(actor, box, flags) {
|
||||
// the iconBin should fill our entire box
|
||||
this._iconBin.allocate(box, flags);
|
||||
},
|
||||
|
||||
_updateIcon: function() {
|
||||
if (this._actorDestroyed)
|
||||
return;
|
||||
|
||||
if (!this._iconSet)
|
||||
this._iconBin.child = this._source.createIcon(this._size);
|
||||
}
|
||||
});
|
||||
|
||||
const SourceActorWithLabel = new Lang.Class({
|
||||
Name: 'SourceActorWithLabel',
|
||||
Extends: SourceActor,
|
||||
|
||||
_init: function(source, size) {
|
||||
this.parent(source, size);
|
||||
|
||||
this._counterLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
y_expand: true });
|
||||
|
||||
this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
|
||||
child: this._counterLabel,
|
||||
layout_manager: new Clutter.BinLayout() });
|
||||
this._counterBin.hide();
|
||||
|
||||
this._counterBin.connect('style-changed', Lang.bind(this, function() {
|
||||
let themeNode = this._counterBin.get_theme_node();
|
||||
this._counterBin.translation_x = themeNode.get_length('-shell-counter-overlap-x');
|
||||
this._counterBin.translation_y = themeNode.get_length('-shell-counter-overlap-y');
|
||||
}));
|
||||
|
||||
this.actor.add_actor(this._counterBin);
|
||||
|
||||
this._countUpdatedId = this._source.connect('count-updated', Lang.bind(this, this._updateCount));
|
||||
this._updateCount();
|
||||
|
||||
this.actor.connect('destroy', function() {
|
||||
this._source.disconnect(this._countUpdatedId);
|
||||
});
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
this.parent(actor, box, flags);
|
||||
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
@ -1230,14 +1261,6 @@ const SourceActor = new Lang.Class({
|
||||
this._counterBin.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
_updateIcon: function() {
|
||||
if (this._actorDestroyed)
|
||||
return;
|
||||
|
||||
if (!this._iconSet)
|
||||
this._iconBin.child = this._source.createIcon(this._size);
|
||||
},
|
||||
|
||||
_updateCount: function() {
|
||||
if (this._actorDestroyed)
|
||||
return;
|
||||
@ -1263,7 +1286,6 @@ const Source = new Lang.Class({
|
||||
this.title = title;
|
||||
this.iconName = iconName;
|
||||
|
||||
this.isTransient = false;
|
||||
this.isChat = false;
|
||||
this.isMuted = false;
|
||||
this.keepTrayOnSummaryClick = false;
|
||||
@ -1323,10 +1345,6 @@ const Source = new Lang.Class({
|
||||
return rightClickMenu;
|
||||
},
|
||||
|
||||
setTransient: function(isTransient) {
|
||||
this.isTransient = isTransient;
|
||||
},
|
||||
|
||||
setTitle: function(newTitle) {
|
||||
this.title = newTitle;
|
||||
this.emit('title-changed');
|
||||
@ -1355,7 +1373,7 @@ const Source = new Lang.Class({
|
||||
if (this._mainIcon)
|
||||
return;
|
||||
|
||||
this._mainIcon = new SourceActor(this, this.SOURCE_ICON_SIZE);
|
||||
this._mainIcon = new SourceActorWithLabel(this, this.SOURCE_ICON_SIZE);
|
||||
},
|
||||
|
||||
// Unlike createIcon, this always returns the same actor;
|
||||
@ -1528,9 +1546,9 @@ const SummaryItem = new Lang.Class({
|
||||
_onKeyPress: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.KEY_Up) {
|
||||
actor.emit('clicked', 1);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
prepareNotificationStackForShowing: function() {
|
||||
@ -1625,7 +1643,7 @@ const MessageTrayMenu = new Lang.Class({
|
||||
this._clearItem = this.addAction(_("Clear Messages"), function() {
|
||||
let toDestroy = tray.getSources().filter(function(source) {
|
||||
return source.isClearable;
|
||||
})
|
||||
});
|
||||
|
||||
toDestroy.forEach(function(source) {
|
||||
source.destroy();
|
||||
@ -1788,6 +1806,7 @@ const MessageTray = new Lang.Class({
|
||||
this._setClickedSummaryItem(null);
|
||||
this._updateState();
|
||||
actor.grab_key_focus();
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
global.focus_manager.add_group(this.actor);
|
||||
this._summary = new St.BoxLayout({ style_class: 'message-tray-summary',
|
||||
@ -1798,6 +1817,13 @@ const MessageTray = new Lang.Class({
|
||||
y_expand: true });
|
||||
this.actor.add_actor(this._summary);
|
||||
|
||||
this._focusTrap = new ViewSelector.FocusTrap({ can_focus: true });
|
||||
this._focusTrap.connect('key-focus-in', Lang.bind(this,
|
||||
function() {
|
||||
this._messageTrayMenuButton.actor.grab_key_focus();
|
||||
}));
|
||||
this._summary.add_actor(this._focusTrap);
|
||||
|
||||
this._summaryMotionId = 0;
|
||||
|
||||
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||
@ -1888,21 +1914,21 @@ const MessageTray = new Lang.Class({
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
|
||||
Main.wm.addKeybinding('toggle-message-tray',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.MESSAGE_TRAY |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this.toggleAndNavigate));
|
||||
Main.wm.addKeybinding('focus-active-notification',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.MESSAGE_TRAY |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._expandActiveNotification));
|
||||
|
||||
this._sources = new Hash.Map();
|
||||
this._sources = new Map();
|
||||
this._chatSummaryItemsCount = 0;
|
||||
|
||||
this._trayDwellTimeoutId = 0;
|
||||
@ -1921,6 +1947,13 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
this._messageTrayMenuButton = new MessageTrayMenuButton(this);
|
||||
this.actor.add_actor(this._messageTrayMenuButton.actor);
|
||||
|
||||
this._messageTrayMenuButton.actor.connect('key-press-event',
|
||||
Lang.bind(this, this._onTrayButtonKeyPress));
|
||||
|
||||
let gesture = new EdgeDragAction.EdgeDragAction(St.Side.BOTTOM);
|
||||
gesture.connect('activated', Lang.bind(this, this.toggle));
|
||||
global.stage.add_action(gesture);
|
||||
},
|
||||
|
||||
close: function() {
|
||||
@ -1939,7 +1972,7 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateNoMessagesLabel: function() {
|
||||
this._noMessages.visible = this._sources.size() == 0;
|
||||
this._noMessages.visible = this._sources.size == 0;
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
@ -1974,6 +2007,7 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
this._trayDwellTimeoutId = Mainloop.timeout_add(TRAY_DWELL_TIME,
|
||||
Lang.bind(this, this._trayDwellTimeout));
|
||||
GLib.Source.set_name_by_id(this._trayDwellTimeoutId, '[gnome-shell] this._trayDwellTimeout');
|
||||
}
|
||||
this._trayDwelling = true;
|
||||
} else {
|
||||
@ -1993,32 +2027,47 @@ const MessageTray = new Lang.Class({
|
||||
this._trayDwellTimeoutId = 0;
|
||||
|
||||
if (Main.layoutManager.bottomMonitor.inFullscreen)
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
|
||||
// We don't want to open the tray when a modal dialog
|
||||
// is up, so we check the modal count for that. When we are in the
|
||||
// overview we have to take the overview's modal push into account
|
||||
if (Main.modalCount > (Main.overview.visible ? 1 : 0))
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
|
||||
// If the user interacted with the focus window since we started the tray
|
||||
// dwell (by clicking or typing), don't activate the message tray
|
||||
let focusWindow = global.display.focus_window;
|
||||
let currentUserTime = focusWindow ? focusWindow.user_time : 0;
|
||||
if (currentUserTime != this._trayDwellUserTime)
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
|
||||
this.openTray();
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
_onTrayButtonKeyPress: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.ISO_Left_Tab) {
|
||||
this._focusTrap.can_focus = false;
|
||||
this._summary.navigate_focus(null, Gtk.DirectionType.TAB_BACKWARD, false);
|
||||
this._focusTrap.can_focus = true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onNotificationKeyRelease: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) {
|
||||
this._closeNotification();
|
||||
return true;
|
||||
this._expireNotification();
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_expireNotification: function() {
|
||||
this._notificationExpired = true;
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_closeNotification: function() {
|
||||
@ -2102,7 +2151,8 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_removeSource: function(source) {
|
||||
let [, obj] = this._sources.delete(source);
|
||||
let obj = this._sources.get(source);
|
||||
this._sources.delete(source);
|
||||
let summaryItem = obj.summaryItem;
|
||||
|
||||
if (source.isChat)
|
||||
@ -2123,7 +2173,7 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
getSources: function() {
|
||||
return this._sources.keys();
|
||||
return [k for (k of this._sources.keys())];
|
||||
},
|
||||
|
||||
_onSourceEnableChanged: function(policy, source) {
|
||||
@ -2248,6 +2298,16 @@ const MessageTray = new Lang.Class({
|
||||
this._grabHelper.addActor(corner.actor);
|
||||
},
|
||||
|
||||
_resetNotificationLeftTimeout: function() {
|
||||
this._useLongerNotificationLeftTimeout = false;
|
||||
if (this._notificationLeftTimeoutId) {
|
||||
Mainloop.source_remove(this._notificationLeftTimeoutId);
|
||||
this._notificationLeftTimeoutId = 0;
|
||||
this._notificationLeftMouseX = -1;
|
||||
this._notificationLeftMouseY = -1;
|
||||
}
|
||||
},
|
||||
|
||||
_onNotificationHoverChanged: function() {
|
||||
if (this._notificationWidget.hover == this._notificationHovered)
|
||||
return;
|
||||
@ -2257,14 +2317,7 @@ const MessageTray = new Lang.Class({
|
||||
// No dwell inside notifications at the bottom of the screen
|
||||
this._cancelTrayDwell();
|
||||
|
||||
this._useLongerNotificationLeftTimeout = false;
|
||||
if (this._notificationLeftTimeoutId) {
|
||||
Mainloop.source_remove(this._notificationLeftTimeoutId);
|
||||
this._notificationLeftTimeoutId = 0;
|
||||
this._notificationLeftMouseX = -1;
|
||||
this._notificationLeftMouseY = -1;
|
||||
return;
|
||||
}
|
||||
this._resetNotificationLeftTimeout();
|
||||
|
||||
if (this._showNotificationMouseX >= 0) {
|
||||
let actorAtShowNotificationPosition =
|
||||
@ -2281,6 +2334,7 @@ const MessageTray = new Lang.Class({
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._pointerInNotification = true;
|
||||
this._updateState();
|
||||
} else {
|
||||
@ -2297,6 +2351,7 @@ const MessageTray = new Lang.Class({
|
||||
// That gives the user more time to mouse away from the notification and mouse back in in order to expand it.
|
||||
let timeout = this._useLongerNotificationLeftTimeout ? LONGER_HIDE_TIMEOUT * 1000 : HIDE_TIMEOUT * 1000;
|
||||
this._notificationLeftTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, this._onNotificationLeftTimeout));
|
||||
GLib.Source.set_name_by_id(this._notificationLeftTimeoutId, '[gnome-shell] this._onNotificationLeftTimeout');
|
||||
}
|
||||
},
|
||||
|
||||
@ -2332,6 +2387,7 @@ const MessageTray = new Lang.Class({
|
||||
this._notificationLeftMouseX = -1;
|
||||
this._notificationLeftTimeoutId = Mainloop.timeout_add(LONGER_HIDE_TIMEOUT * 1000,
|
||||
Lang.bind(this, this._onNotificationLeftTimeout));
|
||||
GLib.Source.set_name_by_id(this._notificationLeftTimeoutId, '[gnome-shell] this._onNotificationLeftTimeout');
|
||||
} else {
|
||||
this._notificationLeftTimeoutId = 0;
|
||||
this._useLongerNotificationLeftTimeout = false;
|
||||
@ -2339,7 +2395,7 @@ const MessageTray = new Lang.Class({
|
||||
this._updateNotificationTimeout(0);
|
||||
this._updateState();
|
||||
}
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
_escapeTray: function() {
|
||||
@ -2356,6 +2412,13 @@ const MessageTray = new Lang.Class({
|
||||
// _updateState() figures out what (if anything) needs to be done
|
||||
// at the present time.
|
||||
_updateState: function() {
|
||||
// If our state changes caused _updateState to be called,
|
||||
// just exit now to prevent reentrancy issues.
|
||||
if (this._updatingState)
|
||||
return;
|
||||
|
||||
this._updatingState = true;
|
||||
|
||||
// Filter out acknowledged notifications.
|
||||
this._notificationQueue = this._notificationQueue.filter(function(n) {
|
||||
return !n.acknowledged;
|
||||
@ -2369,24 +2432,37 @@ const MessageTray = new Lang.Class({
|
||||
if (shouldShowNotification && nextNotification) {
|
||||
let limited = this._busy || Main.layoutManager.bottomMonitor.inFullscreen;
|
||||
let showNextNotification = (!limited || nextNotification.forFeedback || nextNotification.urgency == Urgency.CRITICAL);
|
||||
if (showNextNotification)
|
||||
this._showNotification();
|
||||
if (showNextNotification) {
|
||||
let len = this._notificationQueue.length;
|
||||
if (len > 1) {
|
||||
this._notificationQueue.length = 0;
|
||||
let source = new SystemNotificationSource();
|
||||
this.add(source);
|
||||
let notification = new Notification(source, ngettext("%d new message", "%d new messages", len).format(len));
|
||||
notification.setTransient(true);
|
||||
notification.connect('clicked', Lang.bind(this, function() {
|
||||
this.openTray();
|
||||
}));
|
||||
source.notify(notification);
|
||||
} else {
|
||||
this._showNotification();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (this._notificationState == State.SHOWN) {
|
||||
let pinned = this._pointerInNotification && !this._notificationRemoved;
|
||||
let expired = (this._userActiveWhileNotificationShown &&
|
||||
this._notificationTimeoutId == 0 &&
|
||||
!(this._notification.urgency == Urgency.CRITICAL) &&
|
||||
this._notification.urgency != Urgency.CRITICAL &&
|
||||
!this._notification.focused &&
|
||||
!this._pointerInNotification);
|
||||
!this._pointerInNotification) || this._notificationExpired;
|
||||
let mustClose = (this._notificationRemoved || !hasNotifications || expired || this._traySummoned);
|
||||
|
||||
if (mustClose) {
|
||||
let animate = hasNotifications && !this._notificationRemoved;
|
||||
this._hideNotification(animate);
|
||||
} else if (pinned && !this._notification.expanded) {
|
||||
} else if (this._pointerInNotification && !this._notification.expanded) {
|
||||
this._expandNotification(false);
|
||||
} else if (pinned) {
|
||||
} else if (this._pointerInNotification) {
|
||||
this._ensureNotificationFocused();
|
||||
}
|
||||
}
|
||||
@ -2433,11 +2509,16 @@ const MessageTray = new Lang.Class({
|
||||
this._desktopCloneState == State.SHOWN);
|
||||
let desktopCloneShouldBeVisible = (trayShouldBeVisible);
|
||||
|
||||
if (!desktopCloneIsVisible && desktopCloneShouldBeVisible) {
|
||||
if (!desktopCloneIsVisible && desktopCloneShouldBeVisible)
|
||||
this._showDesktopClone();
|
||||
} else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible) {
|
||||
else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible)
|
||||
this._hideDesktopClone();
|
||||
}
|
||||
|
||||
this._updatingState = false;
|
||||
|
||||
// Clean transient variables that are used to communicate actions
|
||||
// to updateState()
|
||||
this._notificationExpired = false;
|
||||
},
|
||||
|
||||
_tween: function(actor, statevar, value, params) {
|
||||
@ -2594,6 +2675,8 @@ const MessageTray = new Lang.Class({
|
||||
// the mouse is moving towards it or within it.
|
||||
this._lastSeenMouseX = x;
|
||||
this._lastSeenMouseY = y;
|
||||
|
||||
this._resetNotificationLeftTimeout();
|
||||
},
|
||||
|
||||
_updateShowingNotification: function() {
|
||||
@ -2638,10 +2721,12 @@ const MessageTray = new Lang.Class({
|
||||
Mainloop.source_remove(this._notificationTimeoutId);
|
||||
this._notificationTimeoutId = 0;
|
||||
}
|
||||
if (timeout > 0)
|
||||
if (timeout > 0) {
|
||||
this._notificationTimeoutId =
|
||||
Mainloop.timeout_add(timeout,
|
||||
Lang.bind(this, this._notificationTimeout));
|
||||
GLib.Source.set_name_by_id(this._notificationTimeoutId, '[gnome-shell] this._notificationTimeout');
|
||||
}
|
||||
},
|
||||
|
||||
_notificationTimeout: function() {
|
||||
@ -2665,7 +2750,7 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
this._lastSeenMouseX = x;
|
||||
this._lastSeenMouseY = y;
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
_hideNotification: function(animate) {
|
||||
@ -2684,13 +2769,7 @@ const MessageTray = new Lang.Class({
|
||||
this._notificationUnfocusedId = 0;
|
||||
}
|
||||
|
||||
this._useLongerNotificationLeftTimeout = false;
|
||||
if (this._notificationLeftTimeoutId) {
|
||||
Mainloop.source_remove(this._notificationLeftTimeoutId);
|
||||
this._notificationLeftTimeoutId = 0;
|
||||
this._notificationLeftMouseX = -1;
|
||||
this._notificationLeftMouseY = -1;
|
||||
}
|
||||
this._resetNotificationLeftTimeout();
|
||||
|
||||
if (animate) {
|
||||
this._tween(this._notificationWidget, '_notificationState', State.HIDDEN,
|
||||
@ -2756,12 +2835,17 @@ const MessageTray = new Lang.Class({
|
||||
} else if (this._notification.y != expandedY) {
|
||||
// Tween also opacity here, to override a possible tween that's
|
||||
// currently hiding the notification.
|
||||
this._tween(this._notificationWidget, '_notificationState', State.SHOWN,
|
||||
{ y: expandedY,
|
||||
opacity: 255,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
Tweener.addTween(this._notificationWidget,
|
||||
{ y: expandedY,
|
||||
opacity: 255,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
// HACK: Drive the state machine here better,
|
||||
// instead of overwriting tweens
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this._notificationState = State.SHOWN;
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@ -2802,13 +2886,13 @@ const MessageTray = new Lang.Class({
|
||||
Lang.bind(this, this._onSourceDoneDisplayingContent));
|
||||
|
||||
this._summaryBoxPointer.bin.child = child;
|
||||
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
|
||||
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
|
||||
|
||||
this._summaryBoxPointer.actor.opacity = 0;
|
||||
this._summaryBoxPointer.actor.show();
|
||||
this._adjustSummaryBoxPointerPosition();
|
||||
|
||||
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
|
||||
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
|
||||
|
||||
this._summaryBoxPointerState = State.SHOWING;
|
||||
this._summaryBoxPointer.show(BoxPointer.PopupAnimation.FULL, Lang.bind(this, function() {
|
||||
this._summaryBoxPointerState = State.SHOWN;
|
||||
@ -2863,13 +2947,13 @@ const MessageTray = new Lang.Class({
|
||||
case Clutter.KEY_Escape:
|
||||
this._setClickedSummaryItem(null);
|
||||
this._updateState();
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
case Clutter.KEY_Delete:
|
||||
this._clickedSummaryItem.source.destroy();
|
||||
this._escapeTray();
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onSummaryBoxPointerUngrabbed: function() {
|
||||
@ -2907,8 +2991,6 @@ const MessageTray = new Lang.Class({
|
||||
this._summaryBoxPointerItem.doneShowingNotificationStack();
|
||||
this._summaryBoxPointerItem = null;
|
||||
|
||||
if (source.isTransient && !this._reNotifyAfterHideNotification)
|
||||
source.destroy(NotificationDestroyedReason.EXPIRED);
|
||||
if (this._reNotifyAfterHideNotification) {
|
||||
this._onNotify(this._reNotifyAfterHideNotification.source, this._reNotifyAfterHideNotification);
|
||||
this._reNotifyAfterHideNotification = null;
|
||||
@ -2927,7 +3009,6 @@ const SystemNotificationSource = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this.parent(_("System Information"), 'dialog-information-symbolic');
|
||||
this.setTransient(true);
|
||||
},
|
||||
|
||||
open: function() {
|
||||
|
@ -41,9 +41,9 @@ const ModalDialog = new Lang.Class({
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { shellReactive: false,
|
||||
styleClass: null,
|
||||
parentActor: Main.uiGroup,
|
||||
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
|
||||
shouldFadeIn: true,
|
||||
shouldFadeOut: true,
|
||||
destroyOnClose: true });
|
||||
|
||||
this.state = State.CLOSED;
|
||||
@ -51,13 +51,14 @@ const ModalDialog = new Lang.Class({
|
||||
this._keybindingMode = params.keybindingMode;
|
||||
this._shellReactive = params.shellReactive;
|
||||
this._shouldFadeIn = params.shouldFadeIn;
|
||||
this._shouldFadeOut = params.shouldFadeOut;
|
||||
this._destroyOnClose = params.destroyOnClose;
|
||||
|
||||
this._group = new St.Widget({ visible: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
accessible_role: Atk.Role.DIALOG });
|
||||
params.parentActor.add_actor(this._group);
|
||||
Main.layoutManager.modalDialogGroup.add_actor(this._group);
|
||||
|
||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||
coordinate: Clutter.BindCoordinate.ALL });
|
||||
@ -89,7 +90,8 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
if (!this._shellReactive) {
|
||||
this._lightbox = new Lightbox.Lightbox(this._group,
|
||||
{ inhibitEvents: true });
|
||||
{ inhibitEvents: true,
|
||||
radialEffect: true });
|
||||
this._lightbox.highlight(this._backgroundBin);
|
||||
|
||||
this._eventBlocker = new Clutter.Actor({ reactive: true });
|
||||
@ -192,7 +194,7 @@ const ModalDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
placeSpinner: function(layoutInfo) {
|
||||
let spinnerIcon = global.datadir + '/theme/process-working.svg';
|
||||
let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg');
|
||||
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
|
||||
this._workSpinner.actor.opacity = 0;
|
||||
this._workSpinner.actor.show();
|
||||
@ -229,6 +231,7 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
_onKeyPressEvent: function(object, event) {
|
||||
this._pressedKey = event.get_key_symbol();
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onKeyReleaseEvent: function(object, event) {
|
||||
@ -237,21 +240,21 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol != pressedKey)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let buttonInfo = this._buttonKeys[symbol];
|
||||
if (!buttonInfo)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let button = buttonInfo['button'];
|
||||
let action = buttonInfo['action'];
|
||||
|
||||
if (action && button.reactive) {
|
||||
action();
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onGroupDestroy: function() {
|
||||
@ -306,6 +309,15 @@ const ModalDialog = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
_closeComplete: function() {
|
||||
this.state = State.CLOSED;
|
||||
this._group.hide();
|
||||
this.emit('closed');
|
||||
|
||||
if (this._destroyOnClose)
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
close: function(timestamp) {
|
||||
if (this.state == State.CLOSED || this.state == State.CLOSING)
|
||||
return;
|
||||
@ -314,20 +326,16 @@ const ModalDialog = new Lang.Class({
|
||||
this.popModal(timestamp);
|
||||
this._savedKeyFocus = null;
|
||||
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
time: OPEN_AND_CLOSE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this.state = State.CLOSED;
|
||||
this._group.hide();
|
||||
this.emit('closed');
|
||||
|
||||
if (this._destroyOnClose)
|
||||
this.destroy();
|
||||
})
|
||||
});
|
||||
if (this._shouldFadeOut)
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
time: OPEN_AND_CLOSE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
this._closeComplete)
|
||||
})
|
||||
else
|
||||
this._closeComplete();
|
||||
},
|
||||
|
||||
// Drop modal status without closing the dialog; this makes the
|
||||
|
@ -120,15 +120,12 @@ const FdoNotificationDaemon = new Lang.Class({
|
||||
|
||||
this._nextNotificationId = 1;
|
||||
|
||||
Shell.WindowTracker.get_default().connect('notify::focus-app', Lang.bind(this, this._onFocusAppChanged));
|
||||
Main.overview.connect('hidden', Lang.bind(this, this._onFocusAppChanged));
|
||||
|
||||
this._trayManager = new Shell.TrayManager();
|
||||
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));
|
||||
|
||||
Shell.WindowTracker.get_default().connect('notify::focus-app',
|
||||
Lang.bind(this, this._onFocusAppChanged));
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._onFocusAppChanged));
|
||||
|
||||
this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
|
||||
},
|
||||
|
||||
@ -181,12 +178,10 @@ const FdoNotificationDaemon = new Lang.Class({
|
||||
},
|
||||
|
||||
// Returns the source associated with ndata.notification if it is set.
|
||||
// Otherwise, returns the source associated with the title and pid if
|
||||
// such source is stored in this._sources and the notification is not
|
||||
// transient. If the existing or requested source is associated with
|
||||
// a tray icon and passed in pid matches a pid of an existing source,
|
||||
// the title match is ignored to enable representing a tray icon and
|
||||
// notifications from the same application with a single source.
|
||||
// If the existing or requested source is associated with a tray icon
|
||||
// and passed in pid matches a pid of an existing 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
|
||||
// pid is provided.
|
||||
@ -204,32 +199,20 @@ const FdoNotificationDaemon = new Lang.Class({
|
||||
if (ndata && ndata.notification)
|
||||
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);
|
||||
if (source) {
|
||||
source.setTitle(title);
|
||||
return source;
|
||||
}
|
||||
let source = this._lookupSource(title, pid, trayIcon);
|
||||
if (source) {
|
||||
source.setTitle(title);
|
||||
return source;
|
||||
}
|
||||
|
||||
let source = new FdoNotificationDaemonSource(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
|
||||
source.setTransient(isForTransientNotification);
|
||||
|
||||
if (!isForTransientNotification) {
|
||||
this._sources.push(source);
|
||||
source.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
let index = this._sources.indexOf(source);
|
||||
if (index >= 0)
|
||||
this._sources.splice(index, 1);
|
||||
}));
|
||||
}
|
||||
this._sources.push(source);
|
||||
source.connect('destroy', Lang.bind(this, function() {
|
||||
let index = this._sources.indexOf(source);
|
||||
if (index >= 0)
|
||||
this._sources.splice(index, 1);
|
||||
}));
|
||||
|
||||
Main.messageTray.add(source);
|
||||
return source;
|
||||
@ -258,11 +241,12 @@ const FdoNotificationDaemon = new Lang.Class({
|
||||
// Ignore replacesId since we already sent back a
|
||||
// NotificationClosed for that id.
|
||||
id = this._nextNotificationId++;
|
||||
Mainloop.idle_add(Lang.bind(this,
|
||||
function () {
|
||||
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
||||
return false;
|
||||
}));
|
||||
let idle_id = Mainloop.idle_add(Lang.bind(this,
|
||||
function () {
|
||||
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(idle_id, '[gnome-shell] this._emitNotificationClosed');
|
||||
return invocation.return_value(GLib.Variant.new('(u)', [id]));
|
||||
}
|
||||
|
||||
@ -336,20 +320,10 @@ const FdoNotificationDaemon = new Lang.Class({
|
||||
let [pid] = result;
|
||||
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;
|
||||
source.connect('destroy', Lang.bind(this, function() {
|
||||
delete this._senderToPid[sender];
|
||||
}));
|
||||
}
|
||||
this._senderToPid[sender] = pid;
|
||||
source.connect('destroy', Lang.bind(this, function() {
|
||||
delete this._senderToPid[sender];
|
||||
}));
|
||||
this._notifyForSource(source, ndata);
|
||||
}));
|
||||
|
||||
@ -582,7 +556,7 @@ const FdoNotificationDaemonSource = new Lang.Class({
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
if (this.app) {
|
||||
if (this.app && this.app.get_app_info()) {
|
||||
let id = this.app.get_id().replace(/\.desktop$/,'');
|
||||
return new MessageTray.NotificationApplicationPolicy(id);
|
||||
} else {
|
||||
@ -715,6 +689,12 @@ const FdoNotificationDaemonSource = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const PRIORITY_URGENCY_MAP = {
|
||||
low: MessageTray.Urgency.LOW,
|
||||
normal: MessageTray.Urgency.NORMAL,
|
||||
high: MessageTray.Urgency.HIGH,
|
||||
urgent: MessageTray.Urgency.CRITICAL
|
||||
};
|
||||
|
||||
const GtkNotificationDaemonNotification = new Lang.Class({
|
||||
Name: 'GtkNotificationDaemonNotification',
|
||||
@ -728,12 +708,20 @@ const GtkNotificationDaemonNotification = new Lang.Class({
|
||||
"body": body,
|
||||
"icon": gicon,
|
||||
"urgent": urgent,
|
||||
"priority": priority,
|
||||
"buttons": buttons,
|
||||
"default-action": defaultAction,
|
||||
"default-action-target": defaultActionTarget } = notification;
|
||||
|
||||
this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL
|
||||
: MessageTray.Urgency.NORMAL);
|
||||
if (priority) {
|
||||
let urgency = PRIORITY_URGENCY_MAP[priority.unpack()];
|
||||
this.setUrgency(urgency != undefined ? urgency : MessageTray.Urgency.NORMAL);
|
||||
} else if (urgent) {
|
||||
this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL
|
||||
: MessageTray.Urgency.NORMAL);
|
||||
} else {
|
||||
this.setUrgency(MessageTray.Urgency.NORMAL);
|
||||
}
|
||||
|
||||
if (buttons) {
|
||||
buttons.deep_unpack().forEach(Lang.bind(this, function(button) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- 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;
|
||||
@ -65,19 +66,24 @@ const LevelBar = new Lang.Class({
|
||||
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() {
|
||||
_init: function(monitorIndex) {
|
||||
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.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||
|
||||
this._monitorIndex = monitorIndex;
|
||||
let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
|
||||
this.actor.add_constraint(constraint);
|
||||
|
||||
this._box = new St.BoxLayout({ style_class: 'osd-window',
|
||||
vertical: true });
|
||||
this.actor.add_actor(this._box);
|
||||
@ -106,7 +112,6 @@ const OsdWindow = new Lang.Class({
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
this._monitorsChanged();
|
||||
|
||||
Main.uiGroup.add_child(this.actor);
|
||||
},
|
||||
|
||||
@ -122,13 +127,15 @@ const OsdWindow = new Lang.Class({
|
||||
|
||||
setLevel: function(level) {
|
||||
this._level.actor.visible = (level != undefined);
|
||||
if (this.actor.visible)
|
||||
Tweener.addTween(this._level,
|
||||
{ level: level,
|
||||
time: LEVEL_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
else
|
||||
this._level.level = level;
|
||||
if (level != undefined) {
|
||||
if (this.actor.visible)
|
||||
Tweener.addTween(this._level,
|
||||
{ level: level,
|
||||
time: LEVEL_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
else
|
||||
this._level.level = level;
|
||||
}
|
||||
},
|
||||
|
||||
show: function() {
|
||||
@ -151,6 +158,7 @@ const OsdWindow = new Lang.Class({
|
||||
Mainloop.source_remove(this._hideTimeoutId);
|
||||
this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
|
||||
Lang.bind(this, this._hide));
|
||||
GLib.Source.set_name_by_id(this._hideTimeoutId, '[gnome-shell] this._hide');
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
@ -172,6 +180,7 @@ const OsdWindow = new Lang.Class({
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
})
|
||||
});
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
@ -182,14 +191,18 @@ const OsdWindow = new Lang.Class({
|
||||
|
||||
_monitorsChanged: function() {
|
||||
/* assume 110x110 on a 640x480 display and scale from there */
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
let monitor = Main.layoutManager.monitors[this._monitorIndex];
|
||||
if (!monitor)
|
||||
return; // we are about to be removed
|
||||
|
||||
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);
|
||||
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
this._icon.icon_size = this._popupSize / (2 * scaleFactor);
|
||||
this._box.translation_y = monitor.height / 4;
|
||||
this._icon.icon_size = this._popupSize / 2;
|
||||
this._box.style_changed();
|
||||
},
|
||||
|
||||
@ -205,6 +218,60 @@ const OsdWindow = new Lang.Class({
|
||||
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));
|
||||
// minWidth/minHeight here are in real pixels,
|
||||
// but the theme takes measures in unscaled dimensions
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight) / scaleFactor);
|
||||
}
|
||||
});
|
||||
|
||||
const OsdWindowManager = new Lang.Class({
|
||||
Name: 'OsdWindowManager',
|
||||
|
||||
_init: function() {
|
||||
this._osdWindows = [];
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
this._monitorsChanged();
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
|
||||
if (this._osdWindows[i] == undefined)
|
||||
this._osdWindows[i] = new OsdWindow(i);
|
||||
}
|
||||
|
||||
for (let i = Main.layoutManager.monitors.length; i < this._osdWindows.length; i++) {
|
||||
this._osdWindows[i].actor.destroy();
|
||||
this._osdWindows[i] = null;
|
||||
}
|
||||
|
||||
this._osdWindows.length = Main.layoutManager.monitors.length;
|
||||
},
|
||||
|
||||
_showOsdWindow: function(monitorIndex, icon, label, level) {
|
||||
this._osdWindows[monitorIndex].setIcon(icon);
|
||||
this._osdWindows[monitorIndex].setLabel(label);
|
||||
this._osdWindows[monitorIndex].setLevel(level);
|
||||
this._osdWindows[monitorIndex].show();
|
||||
},
|
||||
|
||||
show: function(monitorIndex, icon, label, level) {
|
||||
if (monitorIndex != -1) {
|
||||
for (let i = 0; i < this._osdWindows.length; i++) {
|
||||
if (i == monitorIndex)
|
||||
this._showOsdWindow(i, icon, label, level);
|
||||
else
|
||||
this._osdWindows[i].cancel();
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._osdWindows.length; i++)
|
||||
this._showOsdWindow(i, icon, label, level);
|
||||
}
|
||||
},
|
||||
|
||||
hideAll: function() {
|
||||
for (let i = 0; i < this._osdWindows.length; i++)
|
||||
this._osdWindows[i].cancel();
|
||||
}
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Mainloop = imports.mainloop;
|
||||
@ -13,6 +14,7 @@ const Gdk = imports.gi.Gdk;
|
||||
const Background = imports.ui.background;
|
||||
const DND = imports.ui.dnd;
|
||||
const LayoutManager = imports.ui.layout;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const OverviewControls = imports.ui.overviewControls;
|
||||
@ -112,9 +114,6 @@ const Overview = new Lang.Class({
|
||||
// rendering options without duplicating the texture data.
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
|
||||
this._desktopFade = new St.Bin();
|
||||
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
|
||||
|
||||
let layout = new Clutter.BinLayout();
|
||||
this._stack = new Clutter.Actor({ layout_manager: layout });
|
||||
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
|
||||
@ -133,6 +132,9 @@ const Overview = new Lang.Class({
|
||||
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
|
||||
this._bgManagers = [];
|
||||
|
||||
this._desktopFade = new St.Widget();
|
||||
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
|
||||
|
||||
this._activationTime = 0;
|
||||
|
||||
this.visible = false; // animating to overview, in overview, animating out
|
||||
@ -147,7 +149,7 @@ const Overview = new Lang.Class({
|
||||
this._coverPane = new Clutter.Actor({ opacity: 0,
|
||||
reactive: true });
|
||||
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._stack.add_actor(this._overview);
|
||||
Main.layoutManager.overviewGroup.add_child(this._stack);
|
||||
@ -183,7 +185,7 @@ const Overview = new Lang.Class({
|
||||
for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
|
||||
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
|
||||
monitorIndex: i,
|
||||
effects: Meta.BackgroundEffects.VIGNETTE });
|
||||
vignette: true });
|
||||
this._bgManagers.push(bgManager);
|
||||
}
|
||||
},
|
||||
@ -191,15 +193,9 @@ const Overview = new Lang.Class({
|
||||
_unshadeBackgrounds: function() {
|
||||
let backgrounds = this._backgroundGroup.get_children();
|
||||
for (let i = 0; i < backgrounds.length; i++) {
|
||||
let background = backgrounds[i]._delegate;
|
||||
|
||||
Tweener.addTween(background,
|
||||
Tweener.addTween(backgrounds[i],
|
||||
{ brightness: 1.0,
|
||||
time: SHADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
Tweener.addTween(background,
|
||||
{ vignetteSharpness: 0.0,
|
||||
vignette_sharpness: 0.0,
|
||||
time: SHADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
@ -209,15 +205,9 @@ const Overview = new Lang.Class({
|
||||
_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,
|
||||
Tweener.addTween(backgrounds[i],
|
||||
{ brightness: Lightbox.VIGNETTE_BRIGHTNESS,
|
||||
vignette_sharpness: Lightbox.VIGNETTE_SHARPNESS,
|
||||
time: SHADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
@ -248,7 +238,7 @@ const Overview = new Lang.Class({
|
||||
opacity: 0 });
|
||||
this._overview.add_actor(this._panelGhost);
|
||||
|
||||
this._searchEntry = new St.Entry({ name: 'searchEntry',
|
||||
this._searchEntry = new St.Entry({ style_class: 'search-entry',
|
||||
/* Translators: this is the text displayed
|
||||
in the search entry when no search is
|
||||
active; it should not exceed ~30
|
||||
@ -363,12 +353,15 @@ const Overview = new Lang.Class({
|
||||
this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
|
||||
this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
|
||||
Lang.bind(this, function() {
|
||||
this._windowSwitchTimeoutId = 0;
|
||||
this._needsFakePointerEvent = true;
|
||||
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
|
||||
this._windowSwitchTimestamp);
|
||||
this.hide();
|
||||
this._lastHoveredWindow = null;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(this._windowSwitchTimeoutId, '[gnome-shell] Main.activateWindow');
|
||||
}
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
@ -376,6 +369,7 @@ const Overview = new Lang.Class({
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
this.emit('scroll-event', event);
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
addAction: function(action) {
|
||||
@ -427,8 +421,6 @@ const Overview = new Lang.Class({
|
||||
this.emit('windows-restacked', stackIndices);
|
||||
},
|
||||
|
||||
//// Public methods ////
|
||||
|
||||
beginItemDrag: function(source) {
|
||||
this.emit('item-drag-begin');
|
||||
this._inDrag = true;
|
||||
@ -443,37 +435,20 @@ const Overview = new Lang.Class({
|
||||
this._inDrag = false;
|
||||
},
|
||||
|
||||
beginWindowDrag: function(source) {
|
||||
this.emit('window-drag-begin');
|
||||
beginWindowDrag: function(window) {
|
||||
this.emit('window-drag-begin', window);
|
||||
this._inDrag = true;
|
||||
},
|
||||
|
||||
cancelledWindowDrag: function(source) {
|
||||
this.emit('window-drag-cancelled');
|
||||
cancelledWindowDrag: function(window) {
|
||||
this.emit('window-drag-cancelled', window);
|
||||
},
|
||||
|
||||
endWindowDrag: function(source) {
|
||||
this.emit('window-drag-end');
|
||||
endWindowDrag: function(window) {
|
||||
this.emit('window-drag-end', window);
|
||||
this._inDrag = false;
|
||||
},
|
||||
|
||||
// show:
|
||||
//
|
||||
// Animates the overview visible and grabs mouse and keyboard input
|
||||
show: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
if (this._shown)
|
||||
return;
|
||||
this._shown = true;
|
||||
|
||||
if (!this._syncGrab())
|
||||
return;
|
||||
|
||||
Main.layoutManager.showOverview();
|
||||
this._animateVisible();
|
||||
},
|
||||
|
||||
focusSearch: function() {
|
||||
this.show();
|
||||
this._searchEntry.grab_key_focus();
|
||||
@ -489,8 +464,13 @@ const Overview = new Lang.Class({
|
||||
},
|
||||
|
||||
fadeOutDesktop: function() {
|
||||
if (!this._desktopFade.child)
|
||||
this._desktopFade.child = this._getDesktopClone();
|
||||
if (!this._desktopFade.get_n_children()) {
|
||||
let clone = this._getDesktopClone();
|
||||
if (!clone)
|
||||
return;
|
||||
|
||||
this._desktopFade.add_child(clone);
|
||||
}
|
||||
|
||||
this._desktopFade.opacity = 255;
|
||||
this._desktopFade.show();
|
||||
@ -501,69 +481,6 @@ const Overview = new Lang.Class({
|
||||
});
|
||||
},
|
||||
|
||||
_animateVisible: function() {
|
||||
if (this.visible || this.animationInProgress)
|
||||
return;
|
||||
|
||||
this.visible = true;
|
||||
this.animationInProgress = true;
|
||||
this.visibleTarget = true;
|
||||
this._activationTime = Date.now() / 1000;
|
||||
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
this.viewSelector.show();
|
||||
|
||||
this._stack.opacity = 0;
|
||||
Tweener.addTween(this._stack,
|
||||
{ opacity: 255,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
onComplete: this._showDone,
|
||||
onCompleteScope: this
|
||||
});
|
||||
this._shadeBackgrounds();
|
||||
|
||||
this._coverPane.raise_top();
|
||||
this._coverPane.show();
|
||||
this.emit('showing');
|
||||
},
|
||||
|
||||
// hide:
|
||||
//
|
||||
// Reverses the effect of show()
|
||||
hide: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
if (!this._shown)
|
||||
return;
|
||||
|
||||
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._shown = false;
|
||||
this._syncGrab();
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
if (this.visible)
|
||||
this.hide();
|
||||
else
|
||||
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
|
||||
@ -580,8 +497,6 @@ const Overview = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
//// Private methods ////
|
||||
|
||||
_syncGrab: function() {
|
||||
// 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
|
||||
@ -611,28 +526,49 @@ const Overview = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
_animateNotVisible: function() {
|
||||
if (!this.visible || this.animationInProgress)
|
||||
// show:
|
||||
//
|
||||
// Animates the overview visible and grabs mouse and keyboard input
|
||||
show: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
if (this._shown)
|
||||
return;
|
||||
this._shown = true;
|
||||
|
||||
if (!this._syncGrab())
|
||||
return;
|
||||
|
||||
Main.layoutManager.showOverview();
|
||||
this._animateVisible();
|
||||
},
|
||||
|
||||
|
||||
_animateVisible: function() {
|
||||
if (this.visible || this.animationInProgress)
|
||||
return;
|
||||
|
||||
this.visible = true;
|
||||
this.animationInProgress = true;
|
||||
this.visibleTarget = false;
|
||||
this.visibleTarget = true;
|
||||
this._activationTime = Date.now() / 1000;
|
||||
|
||||
this.viewSelector.zoomFromOverview();
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
this.viewSelector.show();
|
||||
|
||||
// Make other elements fade out.
|
||||
this._stack.opacity = 0;
|
||||
Tweener.addTween(this._stack,
|
||||
{ opacity: 0,
|
||||
{ opacity: 255,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
onComplete: this._hideDone,
|
||||
onComplete: this._showDone,
|
||||
onCompleteScope: this
|
||||
});
|
||||
this._unshadeBackgrounds();
|
||||
this._shadeBackgrounds();
|
||||
|
||||
this._coverPane.raise_top();
|
||||
this._coverPane.show();
|
||||
this.emit('hiding');
|
||||
this.emit('showing');
|
||||
},
|
||||
|
||||
_showDone: function() {
|
||||
@ -649,6 +585,57 @@ const Overview = new Lang.Class({
|
||||
global.sync_pointer();
|
||||
},
|
||||
|
||||
// hide:
|
||||
//
|
||||
// Reverses the effect of show()
|
||||
hide: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
if (!this._shown)
|
||||
return;
|
||||
|
||||
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._shown = false;
|
||||
this._syncGrab();
|
||||
},
|
||||
|
||||
|
||||
_animateNotVisible: function() {
|
||||
if (!this.visible || this.animationInProgress)
|
||||
return;
|
||||
|
||||
this.animationInProgress = true;
|
||||
this.visibleTarget = false;
|
||||
|
||||
this.viewSelector.animateFromOverview();
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._stack,
|
||||
{ opacity: 0,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
onComplete: this._hideDone,
|
||||
onCompleteScope: this
|
||||
});
|
||||
this._unshadeBackgrounds();
|
||||
|
||||
this._coverPane.raise_top();
|
||||
this._coverPane.show();
|
||||
this.emit('hiding');
|
||||
},
|
||||
|
||||
_hideDone: function() {
|
||||
// Re-enable unredirection
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
@ -674,6 +661,20 @@ const Overview = new Lang.Class({
|
||||
this._fakePointerEvent();
|
||||
this._needsFakePointerEvent = false;
|
||||
}
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
if (this.visible)
|
||||
this.hide();
|
||||
else
|
||||
this.show();
|
||||
},
|
||||
|
||||
getShowAppsButton: function() {
|
||||
return this._dash.showAppsButton;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Overview.prototype);
|
||||
|
@ -36,6 +36,7 @@ const SlideLayout = new Lang.Class({
|
||||
|
||||
_init: function(params) {
|
||||
this._slideX = 1;
|
||||
this._translationX = undefined;
|
||||
this._direction = SlideDirection.LEFT;
|
||||
|
||||
this.parent(params);
|
||||
@ -63,12 +64,13 @@ const SlideLayout = new Lang.Class({
|
||||
// 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 alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth)
|
||||
: (availWidth - natWidth * this._slideX);
|
||||
|
||||
let actorBox = new Clutter.ActorBox();
|
||||
actorBox.x1 = alignX;
|
||||
actorBox.x2 = actorBox.x1 + child.x_expand ? availWidth : natWidth;
|
||||
actorBox.y1 = 0;
|
||||
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);
|
||||
@ -90,7 +92,16 @@ const SlideLayout = new Lang.Class({
|
||||
|
||||
get slideDirection() {
|
||||
return this._direction;
|
||||
}
|
||||
},
|
||||
|
||||
set translationX(value) {
|
||||
this._translationX = value;
|
||||
this.layout_changed();
|
||||
},
|
||||
|
||||
get translationX() {
|
||||
return this._translationX;
|
||||
},
|
||||
});
|
||||
|
||||
const SlidingControl = new Lang.Class({
|
||||
@ -99,8 +110,8 @@ const SlidingControl = new Lang.Class({
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
|
||||
|
||||
this.visible = true;
|
||||
this.inDrag = false;
|
||||
this._visible = true;
|
||||
this._inDrag = false;
|
||||
|
||||
this.layout = new SlideLayout();
|
||||
this.layout.slideDirection = params.slideDirection;
|
||||
@ -108,7 +119,7 @@ const SlidingControl = new Lang.Class({
|
||||
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));
|
||||
@ -119,12 +130,12 @@ const SlidingControl = new Lang.Class({
|
||||
Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
|
||||
},
|
||||
|
||||
getSlide: function() {
|
||||
_getSlide: function() {
|
||||
throw new Error('getSlide() must be overridden');
|
||||
},
|
||||
|
||||
updateSlide: function() {
|
||||
Tweener.addTween(this.layout, { slideX: this.getSlide(),
|
||||
_updateSlide: function() {
|
||||
Tweener.addTween(this.layout, { slideX: this._getSlide(),
|
||||
time: SIDE_CONTROLS_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
@ -151,28 +162,26 @@ const SlidingControl = new Lang.Class({
|
||||
let translationEnd = 0;
|
||||
let translation = this._getTranslation();
|
||||
|
||||
if (this.visible) {
|
||||
let shouldShow = (this._getSlide() > 0);
|
||||
if (shouldShow) {
|
||||
translationStart = translation;
|
||||
} else {
|
||||
translationEnd = translation;
|
||||
}
|
||||
|
||||
if (this.actor.translation_x == translationEnd)
|
||||
if (this.layout.translationX == translationEnd)
|
||||
return;
|
||||
|
||||
this.actor.translation_x = translationStart;
|
||||
Tweener.addTween(this.actor, { translation_x: translationEnd,
|
||||
time: SIDE_CONTROLS_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
this.layout.translationX = translationStart;
|
||||
Tweener.addTween(this.layout, { translationX: translationEnd,
|
||||
time: SIDE_CONTROLS_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
_onOverviewShowing: function() {
|
||||
// reset any translation and make sure the actor is visible when
|
||||
// entering the overview
|
||||
this.visible = true;
|
||||
this.layout.slideX = this.getSlide();
|
||||
this.actor.translation_x = 0;
|
||||
_onOverviewHiding: function() {
|
||||
// We need to explicitly slideOut since showing pages
|
||||
// doesn't imply sliding out, instead, hiding the overview does.
|
||||
this.slideOut();
|
||||
},
|
||||
|
||||
_onWindowDragBegin: function() {
|
||||
@ -184,14 +193,14 @@ const SlidingControl = new Lang.Class({
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
this.inDrag = true;
|
||||
this.actor.translation_x = 0;
|
||||
this.updateSlide();
|
||||
this._inDrag = true;
|
||||
this._updateTranslation();
|
||||
this._updateSlide();
|
||||
},
|
||||
|
||||
_onDragEnd: function() {
|
||||
this.inDrag = false;
|
||||
this.updateSlide();
|
||||
this._inDrag = false;
|
||||
this._updateSlide();
|
||||
},
|
||||
|
||||
fadeIn: function() {
|
||||
@ -209,13 +218,12 @@ const SlidingControl = new Lang.Class({
|
||||
},
|
||||
|
||||
slideIn: function() {
|
||||
this.visible = true;
|
||||
this._updateTranslation();
|
||||
this._visible = true;
|
||||
// we will update slideX and the translation from pageEmpty
|
||||
},
|
||||
|
||||
slideOut: function() {
|
||||
this.visible = false;
|
||||
this._visible = false;
|
||||
this._updateTranslation();
|
||||
// we will update slideX from pageEmpty
|
||||
},
|
||||
@ -225,7 +233,7 @@ const SlidingControl = new Lang.Class({
|
||||
// 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.layout.slideX = this._getSlide();
|
||||
this._updateTranslation();
|
||||
}
|
||||
});
|
||||
@ -244,16 +252,20 @@ const ThumbnailsSlider = new Lang.Class({
|
||||
this.actor.track_hover = true;
|
||||
this.actor.add_actor(this._thumbnailsBox.actor);
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
|
||||
Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateSlide));
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this._updateSlide));
|
||||
global.window_manager.connect('switch-workspace', 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;
|
||||
// actually used, e.g. there are windows on any non-active workspace
|
||||
let alwaysZoomOut = this.actor.hover ||
|
||||
this._inDrag ||
|
||||
!Meta.prefs_get_dynamic_workspaces() ||
|
||||
global.screen.n_workspaces > 2 ||
|
||||
global.screen.get_active_workspace_index() != 0;
|
||||
|
||||
if (!alwaysZoomOut) {
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
@ -273,20 +285,13 @@ const ThumbnailsSlider = new Lang.Class({
|
||||
return alwaysZoomOut;
|
||||
},
|
||||
|
||||
_onOverviewShowing: function() {
|
||||
this.visible = true;
|
||||
this.layout.slideX = this.getSlide();
|
||||
this.actor.translation_x = this._getTranslation();
|
||||
this.slideIn();
|
||||
},
|
||||
|
||||
getNonExpandedWidth: function() {
|
||||
let child = this.actor.get_first_child();
|
||||
return child.get_theme_node().get_length('visible-width');
|
||||
},
|
||||
|
||||
getSlide: function() {
|
||||
if (!this.visible)
|
||||
_getSlide: function() {
|
||||
if (!this._visible)
|
||||
return 0;
|
||||
|
||||
let alwaysZoomOut = this._getAlwaysZoomOut();
|
||||
@ -323,29 +328,22 @@ const DashSlider = new Lang.Class({
|
||||
// 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));
|
||||
Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
|
||||
this._dash.connect('icon-size-changed', Lang.bind(this, this._updateSlide));
|
||||
},
|
||||
|
||||
getSlide: function() {
|
||||
if (this.visible || this.inDrag)
|
||||
_getSlide: function() {
|
||||
if (this._visible || this._inDrag)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
},
|
||||
|
||||
_onOverviewShowing: function() {
|
||||
this.visible = true;
|
||||
this.layout.slideX = this.getSlide();
|
||||
this.actor.translation_x = this._getTranslation();
|
||||
this.slideIn();
|
||||
},
|
||||
|
||||
_onWindowDragBegin: function() {
|
||||
this.fadeHalf();
|
||||
},
|
||||
@ -462,9 +460,6 @@ const MessagesIndicator = new Lang.Class({
|
||||
if (source.trayIcon)
|
||||
return;
|
||||
|
||||
if (source.isTransient)
|
||||
return;
|
||||
|
||||
source.connect('count-updated', Lang.bind(this, this._updateCount));
|
||||
this._sources.push(source);
|
||||
this._updateCount();
|
||||
|
@ -14,7 +14,6 @@ const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
|
||||
const Animation = imports.ui.animation;
|
||||
const Config = imports.misc.config;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
@ -213,7 +212,7 @@ const AppMenuButton = new Lang.Class({
|
||||
this._label = new TextShadower();
|
||||
this._label.actor.y_align = Clutter.ActorAlign.CENTER;
|
||||
this._hbox.add_actor(this._label.actor);
|
||||
this._arrow = PopupMenu.unicodeArrow(St.Side.BOTTOM);
|
||||
this._arrow = PopupMenu.arrowIcon(St.Side.BOTTOM);
|
||||
this._hbox.add_actor(this._arrow);
|
||||
|
||||
this._iconBottomClip = 0;
|
||||
@ -274,7 +273,7 @@ const AppMenuButton = new Lang.Class({
|
||||
_onStyleChanged: function(actor) {
|
||||
let node = actor.get_theme_node();
|
||||
let [success, icon] = node.lookup_url('spinner-image', false);
|
||||
if (!success || this._spinnerIcon == icon)
|
||||
if (!success || (this._spinnerIcon && this._spinnerIcon.equal(icon)))
|
||||
return;
|
||||
this._spinnerIcon = icon;
|
||||
this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
|
||||
@ -572,7 +571,6 @@ const ActivitiesButton = new Lang.Class({
|
||||
this.actor.label_actor = this._label;
|
||||
|
||||
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('key-release-event', Lang.bind(this, this._onKeyRelease));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
@ -595,21 +593,28 @@ const ActivitiesButton = new Lang.Class({
|
||||
Mainloop.source_remove(this._xdndTimeOut);
|
||||
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
|
||||
Lang.bind(this, this._xdndToggleOverview, actor));
|
||||
GLib.Source.set_name_by_id(this._xdndTimeOut, '[gnome-shell] this._xdndToggleOverview');
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||
if (event.type() == Clutter.EventType.BUTTON_PRESS ||
|
||||
event.type() == Clutter.EventType.TOUCH_BEGIN) {
|
||||
if (!Main.overview.shouldToggleByCornerOrButton())
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onButtonRelease: function() {
|
||||
Main.overview.toggle();
|
||||
this.menu.close();
|
||||
_onEvent: function(actor, event) {
|
||||
this.parent(actor, event);
|
||||
|
||||
if (event.type() == Clutter.EventType.TOUCH_END ||
|
||||
event.type() == Clutter.EventType.BUTTON_RELEASE)
|
||||
Main.overview.toggle();
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onKeyRelease: function(actor, event) {
|
||||
@ -617,6 +622,7 @@ const ActivitiesButton = new Lang.Class({
|
||||
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
|
||||
Main.overview.toggle();
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_xdndToggleOverview: function(actor) {
|
||||
@ -628,6 +634,7 @@ const ActivitiesButton = new Lang.Class({
|
||||
|
||||
Mainloop.source_remove(this._xdndTimeOut);
|
||||
this._xdndTimeOut = 0;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
});
|
||||
|
||||
@ -809,7 +816,11 @@ const AggregateMenu = new Lang.Class({
|
||||
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_NETWORKMANAGER) {
|
||||
this._network = new imports.ui.status.network.NMApplet();
|
||||
} else {
|
||||
this._network = null;
|
||||
}
|
||||
if (Config.HAVE_BLUETOOTH) {
|
||||
this._bluetooth = new imports.ui.status.bluetooth.Indicator();
|
||||
} else {
|
||||
@ -822,24 +833,31 @@ const AggregateMenu = new Lang.Class({
|
||||
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._network.indicators);
|
||||
this._indicators.add_child(this._location.indicators);
|
||||
if (this._network) {
|
||||
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._indicators.add_child(PopupMenu.arrowIcon(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._network) {
|
||||
this.menu.addMenuItem(this._network.menu);
|
||||
}
|
||||
if (this._bluetooth) {
|
||||
this.menu.addMenuItem(this._bluetooth.menu);
|
||||
}
|
||||
this.menu.addMenuItem(this._location.menu);
|
||||
this.menu.addMenuItem(this._rfkill.menu);
|
||||
this.menu.addMenuItem(this._power.menu);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
@ -983,32 +1001,32 @@ const Panel = new Lang.Class({
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
if (Main.modalCount > 0)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (event.get_source() != actor)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let button = event.get_button();
|
||||
if (button != 1)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let focusWindow = global.display.focus_window;
|
||||
if (!focusWindow)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for()
|
||||
: focusWindow;
|
||||
if (!dragWindow)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let rect = dragWindow.get_outer_rect();
|
||||
let rect = dragWindow.get_frame_rect();
|
||||
let [stageX, stageY] = event.get_coords();
|
||||
|
||||
let allowDrag = dragWindow.maximized_vertically &&
|
||||
stageX > rect.x && stageX < rect.x + rect.width;
|
||||
|
||||
if (!allowDrag)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
global.display.begin_grab_op(global.screen,
|
||||
dragWindow,
|
||||
@ -1020,7 +1038,7 @@ const Panel = new Lang.Class({
|
||||
event.get_time(),
|
||||
stageX, stageY);
|
||||
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
toggleAppMenu: function() {
|
||||
|
@ -41,8 +41,7 @@ const ButtonBox = new Lang.Class({
|
||||
},
|
||||
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
let children = actor.get_children();
|
||||
let child = children.length > 0 ? children[0] : null;
|
||||
let child = actor.get_first_child();
|
||||
|
||||
if (child) {
|
||||
[alloc.min_size, alloc.natural_size] = child.get_preferred_width(-1);
|
||||
@ -55,8 +54,7 @@ const ButtonBox = new Lang.Class({
|
||||
},
|
||||
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
let children = actor.get_children();
|
||||
let child = children.length > 0 ? children[0] : null;
|
||||
let child = actor.get_first_child();
|
||||
|
||||
if (child) {
|
||||
[alloc.min_size, alloc.natural_size] = child.get_preferred_height(-1);
|
||||
@ -66,13 +64,11 @@ const ButtonBox = new Lang.Class({
|
||||
},
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
let children = actor.get_children();
|
||||
if (children.length == 0)
|
||||
let child = actor.get_first_child();
|
||||
if (!child)
|
||||
return;
|
||||
|
||||
let child = children[0];
|
||||
let [minWidth, natWidth] = child.get_preferred_width(-1);
|
||||
let [minHeight, natHeight] = child.get_preferred_height(-1);
|
||||
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
@ -104,8 +100,7 @@ const Button = new Lang.Class({
|
||||
accessible_name: nameText ? nameText : "",
|
||||
accessible_role: Atk.Role.MENU });
|
||||
|
||||
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('event', Lang.bind(this, this._onEvent));
|
||||
this.actor.connect('notify::visible', Lang.bind(this, this._onVisibilityChanged));
|
||||
|
||||
if (dontCreateMenu)
|
||||
@ -135,31 +130,13 @@ const Button = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
if (!this.menu)
|
||||
return;
|
||||
|
||||
this.menu.toggle();
|
||||
},
|
||||
|
||||
_onSourceKeyPress: function(actor, event) {
|
||||
if (!this.menu)
|
||||
return false;
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
|
||||
_onEvent: function(actor, event) {
|
||||
if (this.menu &&
|
||||
(event.type() == Clutter.EventType.TOUCH_BEGIN ||
|
||||
event.type() == Clutter.EventType.BUTTON_PRESS))
|
||||
this.menu.toggle();
|
||||
return true;
|
||||
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
|
||||
this.menu.close();
|
||||
return true;
|
||||
} else if (symbol == Clutter.KEY_Down) {
|
||||
if (!this.menu.isOpen)
|
||||
this.menu.toggle();
|
||||
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onVisibilityChanged: function() {
|
||||
@ -172,7 +149,7 @@ const Button = new Lang.Class({
|
||||
|
||||
_onMenuKeyPress: function(actor, event) {
|
||||
if (global.focus_manager.navigate_from_event(event))
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
|
||||
@ -180,10 +157,10 @@ const Button = new Lang.Class({
|
||||
if (group) {
|
||||
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
|
||||
group.navigate_focus(this.actor, direction, false);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onOpenStateChanged: function(menu, open) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -106,11 +107,12 @@ const PointerWatcher = new Lang.Class({
|
||||
|
||||
this._timeoutId = Mainloop.timeout_add(minInterval,
|
||||
Lang.bind(this, this._onTimeout));
|
||||
GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._onTimeout');
|
||||
},
|
||||
|
||||
_onTimeout: function() {
|
||||
this._updatePointer();
|
||||
return true;
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
},
|
||||
|
||||
_updatePointer: function() {
|
||||
|
@ -1,9 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
@ -15,7 +13,6 @@ const GrabHelper = imports.ui.grabHelper;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Separator = imports.ui.separator;
|
||||
const Slider = imports.ui.slider;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const Ornament = {
|
||||
@ -24,17 +21,6 @@ const Ornament = {
|
||||
CHECK: 2,
|
||||
};
|
||||
|
||||
function _ensureStyle(actor) {
|
||||
if (actor.get_children) {
|
||||
let children = actor.get_children();
|
||||
for (let i = 0; i < children.length; i++)
|
||||
_ensureStyle(children[i]);
|
||||
}
|
||||
|
||||
if (actor instanceof St.Widget)
|
||||
actor.ensure_style();
|
||||
}
|
||||
|
||||
function isPopupMenuItemVisible(child) {
|
||||
if (child._delegate instanceof PopupMenuSection)
|
||||
if (child._delegate.isEmpty())
|
||||
@ -45,28 +31,30 @@ function isPopupMenuItemVisible(child) {
|
||||
/**
|
||||
* @side Side to which the arrow points.
|
||||
*/
|
||||
function unicodeArrow(side) {
|
||||
let arrowChar;
|
||||
function arrowIcon(side) {
|
||||
let iconName;
|
||||
switch (side) {
|
||||
case St.Side.TOP:
|
||||
arrowChar = '\u25B4';
|
||||
iconName = 'pan-up-symbolic';
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
arrowChar = '\u25B8';
|
||||
iconName = 'pan-end-symbolic';
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
arrowChar = '\u25BE';
|
||||
iconName = 'pan-down-symbolic';
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
arrowChar = '\u25C2';
|
||||
iconName = 'pan-start-symbolic';
|
||||
break;
|
||||
}
|
||||
|
||||
return new St.Label({ text: arrowChar,
|
||||
style_class: 'unicode-arrow',
|
||||
accessible_role: Atk.Role.ARROW,
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
let arrow = new St.Icon({ style_class: 'popup-menu-arrow',
|
||||
icon_name: iconName,
|
||||
accessible_role: Atk.Role.ARROW,
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
|
||||
return arrow;
|
||||
}
|
||||
|
||||
const PopupBaseMenuItem = new Lang.Class({
|
||||
@ -104,6 +92,7 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
|
||||
if (this._activatable) {
|
||||
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonReleaseEvent));
|
||||
this.actor.connect('touch-event', Lang.bind(this, this._onTouchEvent));
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
}
|
||||
if (params.reactive && params.hover)
|
||||
@ -111,6 +100,7 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
|
||||
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
|
||||
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
},
|
||||
|
||||
_getTopMenu: function() {
|
||||
@ -126,7 +116,15 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
|
||||
_onButtonReleaseEvent: function (actor, event) {
|
||||
this.activate(event);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_onTouchEvent: function (actor, event) {
|
||||
if (event.type() == Clutter.EventType.TOUCH_END) {
|
||||
this.activate(event);
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function (actor, event) {
|
||||
@ -134,9 +132,9 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
|
||||
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
|
||||
this.activate(event);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onKeyFocusIn: function (actor) {
|
||||
@ -192,6 +190,9 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
|
||||
destroy: function() {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this.emit('destroy');
|
||||
},
|
||||
|
||||
@ -240,8 +241,16 @@ const PopupSeparatorMenuItem = new Lang.Class({
|
||||
this.actor.add(this.label);
|
||||
this.actor.label_actor = this.label;
|
||||
|
||||
this.label.connect('notify::text',
|
||||
Lang.bind(this, this._syncVisibility));
|
||||
this._syncVisibility();
|
||||
|
||||
this._separator = new Separator.HorizontalSeparator({ style_class: 'popup-separator-menu-item' });
|
||||
this.actor.add(this._separator.actor, { expand: true });
|
||||
},
|
||||
|
||||
_syncVisibility: function() {
|
||||
this.label.visible = this.label.text != '';
|
||||
}
|
||||
});
|
||||
|
||||
@ -364,9 +373,9 @@ const PopupImageMenuItem = new Lang.Class({
|
||||
this.parent(params);
|
||||
|
||||
this.label = new St.Label({ text: text });
|
||||
this.addActor(this.label);
|
||||
this.actor.add_child(this.label);
|
||||
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
||||
this.addActor(this._icon, { align: St.Align.END });
|
||||
this.actor.add_child(this._icon, { align: St.Align.END });
|
||||
|
||||
this.setIcon(iconName);
|
||||
},
|
||||
@ -722,6 +731,10 @@ const PopupMenu = new Lang.Class({
|
||||
global.focus_manager.add_group(this.actor);
|
||||
this.actor.reactive = true;
|
||||
|
||||
if (this.sourceActor)
|
||||
this._keyPressId = this.sourceActor.connect('key-press-event',
|
||||
Lang.bind(this, this._onKeyPress));
|
||||
|
||||
this._openedSubMenu = null;
|
||||
},
|
||||
|
||||
@ -732,6 +745,40 @@ const PopupMenu = new Lang.Class({
|
||||
this._openedSubMenu = submenu;
|
||||
},
|
||||
|
||||
_onKeyPress: function(actor, event) {
|
||||
let navKey;
|
||||
switch (this._boxPointer.arrowSide) {
|
||||
case St.Side.TOP:
|
||||
navKey = Clutter.KEY_Down;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
navKey = Clutter.KEY_Up;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
navKey = Clutter.KEY_Right;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
navKey = Clutter.KEY_Left;
|
||||
break;
|
||||
}
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
|
||||
this.toggle();
|
||||
return Clutter.EVENT_STOP;
|
||||
} else if (symbol == Clutter.KEY_Escape && this.isOpen) {
|
||||
this.close();
|
||||
return Clutter.EVENT_STOP;
|
||||
} else if (symbol == navKey) {
|
||||
if (!this.isOpen)
|
||||
this.toggle();
|
||||
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
return Clutter.EVENT_STOP;
|
||||
} else
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
|
||||
setArrowOrigin: function(origin) {
|
||||
this._boxPointer.setArrowOrigin(origin);
|
||||
},
|
||||
@ -772,6 +819,12 @@ const PopupMenu = new Lang.Class({
|
||||
|
||||
this.isOpen = false;
|
||||
this.emit('open-state-changed', false);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._keyPressId)
|
||||
this.sourceActor.disconnect(this._keyPressId);
|
||||
this.parent();
|
||||
}
|
||||
});
|
||||
|
||||
@ -865,17 +918,19 @@ const PopupSubMenu = new Lang.Class({
|
||||
if (animate && needsScrollbar)
|
||||
animate = false;
|
||||
|
||||
let targetAngle = this.actor.text_direction == Clutter.TextDirection.RTL ? -90 : 90;
|
||||
|
||||
if (animate) {
|
||||
let [minHeight, naturalHeight] = this.actor.get_preferred_height(-1);
|
||||
this.actor.height = 0;
|
||||
this.actor._arrow_rotation = this._arrow.rotation_angle_z;
|
||||
this.actor._arrowRotation = this._arrow.rotation_angle_z;
|
||||
Tweener.addTween(this.actor,
|
||||
{ _arrow_rotation: 90,
|
||||
{ _arrowRotation: targetAngle,
|
||||
height: naturalHeight,
|
||||
time: 0.25,
|
||||
onUpdateScope: this,
|
||||
onUpdate: function() {
|
||||
this._arrow.rotation_angle_z = this.actor._arrow_rotation;
|
||||
this._arrow.rotation_angle_z = this.actor._arrowRotation;
|
||||
},
|
||||
onCompleteScope: this,
|
||||
onComplete: function() {
|
||||
@ -883,7 +938,7 @@ const PopupSubMenu = new Lang.Class({
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._arrow.rotation_angle_z = 90;
|
||||
this._arrow.rotation_angle_z = targetAngle;
|
||||
}
|
||||
},
|
||||
|
||||
@ -901,14 +956,14 @@ const PopupSubMenu = new Lang.Class({
|
||||
animate = false;
|
||||
|
||||
if (animate) {
|
||||
this.actor._arrow_rotation = this._arrow.rotation_angle_z;
|
||||
this.actor._arrowRotation = this._arrow.rotation_angle_z;
|
||||
Tweener.addTween(this.actor,
|
||||
{ _arrow_rotation: 0,
|
||||
{ _arrowRotation: 0,
|
||||
height: 0,
|
||||
time: 0.25,
|
||||
onUpdateScope: this,
|
||||
onUpdate: function() {
|
||||
this._arrow.rotation_angle_z = this.actor._arrow_rotation;
|
||||
this._arrow.rotation_angle_z = this.actor._arrowRotation;
|
||||
},
|
||||
onCompleteScope: this,
|
||||
onComplete: function() {
|
||||
@ -928,10 +983,10 @@ const PopupSubMenu = new Lang.Class({
|
||||
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
|
||||
this.close(BoxPointer.PopupAnimation.FULL);
|
||||
this.sourceActor._delegate.setActive(true);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
});
|
||||
|
||||
@ -989,14 +1044,12 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this.actor.add_child(this.status);
|
||||
|
||||
this._triangle = unicodeArrow(St.Side.RIGHT);
|
||||
this._triangle = arrowIcon(St.Side.RIGHT);
|
||||
this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 });
|
||||
|
||||
this._triangleBin = new St.Widget({ y_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this._triangleBin.add_child(this._triangle);
|
||||
if (this._triangleBin.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
this._triangleBin.set_scale(-1.0, 1.0);
|
||||
|
||||
this.actor.add_child(this._triangleBin);
|
||||
this.actor.add_accessible_state (Atk.StateType.EXPANDABLE);
|
||||
@ -1056,10 +1109,10 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
if (symbol == Clutter.KEY_Right) {
|
||||
this._setOpenState(true);
|
||||
this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
} else if (symbol == Clutter.KEY_Left && this._getOpenState()) {
|
||||
this._setOpenState(false);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
return this.parent(actor, event);
|
||||
@ -1071,6 +1124,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
|
||||
|
||||
_onButtonReleaseEvent: function(actor) {
|
||||
this._setOpenState(!this._getOpenState());
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
});
|
||||
|
||||
@ -1102,7 +1156,7 @@ const PopupMenuManager = new Lang.Class({
|
||||
if (source) {
|
||||
if (!menu.blockSourceEvents)
|
||||
this._grabHelper.addActor(source);
|
||||
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
|
||||
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { return this._onMenuSourceEnter(menu); }));
|
||||
menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
|
||||
}
|
||||
|
||||
@ -1114,7 +1168,7 @@ const PopupMenuManager = new Lang.Class({
|
||||
|
||||
removeMenu: function(menu) {
|
||||
if (menu == this.activeMenu)
|
||||
this._closeMenu(menu);
|
||||
this._closeMenu(false, menu);
|
||||
|
||||
let position = this._findMenu(menu);
|
||||
if (position == -1) // not a menu we manage
|
||||
@ -1164,13 +1218,13 @@ const PopupMenuManager = new Lang.Class({
|
||||
|
||||
_onMenuSourceEnter: function(menu) {
|
||||
if (!this._grabHelper.grabbed)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (this._grabHelper.isActorGrabbed(menu.actor))
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
this._changeMenu(menu);
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onMenuDestroy: function(menu) {
|
||||
|
@ -7,6 +7,7 @@ const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Search = imports.ui.search;
|
||||
|
||||
const KEY_FILE_GROUP = 'Shell Search Provider';
|
||||
@ -62,7 +63,7 @@ const SearchProvider2Iface = '<node> \
|
||||
var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface);
|
||||
var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface);
|
||||
|
||||
function loadRemoteSearchProviders(addProviderCallback) {
|
||||
function loadRemoteSearchProviders(callback) {
|
||||
let objectPaths = {};
|
||||
let loadedProviders = [];
|
||||
|
||||
@ -109,6 +110,13 @@ function loadRemoteSearchProviders(addProviderCallback) {
|
||||
else
|
||||
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
|
||||
|
||||
remoteProvider.defaultEnabled = true;
|
||||
try {
|
||||
remoteProvider.defaultEnabled = !keyfile.get_boolean(group, 'DefaultDisabled');
|
||||
} catch(e) {
|
||||
// ignore error
|
||||
}
|
||||
|
||||
objectPaths[objectPath] = remoteProvider;
|
||||
loadedProviders.push(remoteProvider);
|
||||
} catch(e) {
|
||||
@ -116,30 +124,31 @@ function loadRemoteSearchProviders(addProviderCallback) {
|
||||
}
|
||||
}
|
||||
|
||||
let dataDirs = GLib.get_system_data_dirs();
|
||||
dataDirs.forEach(function(dataDir) {
|
||||
let path = GLib.build_filenamev([dataDir, 'gnome-shell', 'search-providers']);
|
||||
let dir = Gio.File.new_for_path(path);
|
||||
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)))
|
||||
loadRemoteSearchProvider(fileEnum.get_child(info));
|
||||
}
|
||||
});
|
||||
let searchSettings = new Gio.Settings({ schema_id: Search.SEARCH_PROVIDERS_SCHEMA });
|
||||
if (searchSettings.get_boolean('disable-external')) {
|
||||
callback([]);
|
||||
return;
|
||||
}
|
||||
|
||||
FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider);
|
||||
|
||||
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
|
||||
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();
|
||||
|
||||
if (provider.defaultEnabled) {
|
||||
let disabled = searchSettings.get_strv('disabled');
|
||||
return disabled.indexOf(appId) == -1;
|
||||
} else {
|
||||
let enabled = searchSettings.get_strv('enabled');
|
||||
return enabled.indexOf(appId) != -1;
|
||||
}
|
||||
});
|
||||
|
||||
loadedProviders.sort(function(providerA, providerB) {
|
||||
let idxA, idxB;
|
||||
let appIdA, appIdB;
|
||||
@ -170,7 +179,7 @@ function loadRemoteSearchProviders(addProviderCallback) {
|
||||
return (idxA - idxB);
|
||||
});
|
||||
|
||||
loadedProviders.forEach(addProviderCallback);
|
||||
callback(loadedProviders);
|
||||
}
|
||||
|
||||
const RemoteSearchProvider = new Lang.Class({
|
||||
@ -192,12 +201,12 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
this.appInfo = appInfo;
|
||||
this.id = appInfo.get_id();
|
||||
this.isRemoteProvider = true;
|
||||
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
},
|
||||
|
||||
createIcon: function(size, meta) {
|
||||
let gicon;
|
||||
let gicon = null;
|
||||
let icon = null;
|
||||
|
||||
if (meta['icon']) {
|
||||
gicon = Gio.icon_deserialize(meta['icon']);
|
||||
} else if (meta['gicon']) {
|
||||
@ -209,8 +218,10 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
bitsPerSample, width, height, rowStride);
|
||||
}
|
||||
|
||||
return new St.Icon({ gicon: gicon,
|
||||
icon_size: size });
|
||||
if (gicon)
|
||||
icon = new St.Icon({ gicon: gicon,
|
||||
icon_size: size });
|
||||
return icon;
|
||||
},
|
||||
|
||||
filterResults: function(results, maxNumber) {
|
||||
@ -223,29 +234,27 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
|
||||
},
|
||||
|
||||
_getResultsFinished: function(results, error) {
|
||||
_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)));
|
||||
} else {
|
||||
this.searchSystem.setResults(this, results[0]);
|
||||
callback([]);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(results[0]);
|
||||
},
|
||||
|
||||
getInitialResultSet: function(terms) {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
getInitialResultSet: function(terms, callback, cancellable) {
|
||||
this.proxy.GetInitialResultSetRemote(terms,
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
Lang.bind(this, this._getResultsFinished, callback),
|
||||
cancellable);
|
||||
},
|
||||
|
||||
getSubsearchResultSet: function(previousResults, newTerms) {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
getSubsearchResultSet: function(previousResults, newTerms, callback, cancellable) {
|
||||
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
Lang.bind(this, this._getResultsFinished, callback),
|
||||
cancellable);
|
||||
},
|
||||
|
||||
_getResultMetasFinished: function(results, error, callback) {
|
||||
@ -273,12 +282,10 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
callback(resultMetas);
|
||||
},
|
||||
|
||||
getResultMetas: function(ids, callback) {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
getResultMetas: function(ids, callback, cancellable) {
|
||||
this.proxy.GetResultMetasRemote(ids,
|
||||
Lang.bind(this, this._getResultMetasFinished, callback),
|
||||
this._cancellable);
|
||||
cancellable);
|
||||
},
|
||||
|
||||
activateResult: function(id) {
|
||||
@ -289,7 +296,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
// 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
|
||||
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));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -38,8 +38,8 @@ const RunDialog = new Lang.Class({
|
||||
this.parent({ styleClass: 'run-dialog',
|
||||
destroyOnClose: false });
|
||||
|
||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||
this._terminalSettings = new Gio.Settings({ schema: TERMINAL_SCHEMA });
|
||||
this._lockdownSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA });
|
||||
this._terminalSettings = new Gio.Settings({ schema_id: TERMINAL_SCHEMA });
|
||||
global.settings.connect('changed::development-tools', Lang.bind(this, function () {
|
||||
this._enableInternalCommands = global.settings.get_boolean('development-tools');
|
||||
}));
|
||||
@ -50,14 +50,10 @@ const RunDialog = new Lang.Class({
|
||||
Main.createLookingGlass().open();
|
||||
}),
|
||||
|
||||
'r': Lang.bind(this, function() {
|
||||
global.reexec_self();
|
||||
}),
|
||||
'r': Lang.bind(this, this._restart),
|
||||
|
||||
// Developer brain backwards compatibility
|
||||
'restart': Lang.bind(this, function() {
|
||||
global.reexec_self();
|
||||
}),
|
||||
'restart': Lang.bind(this, this._restart),
|
||||
|
||||
'debugexit': Lang.bind(this, function() {
|
||||
Meta.quit(Meta.ExitCode.ERROR);
|
||||
@ -73,7 +69,9 @@ const RunDialog = new Lang.Class({
|
||||
let label = new St.Label({ style_class: 'run-dialog-label',
|
||||
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',
|
||||
can_focus: true });
|
||||
@ -101,6 +99,8 @@ const RunDialog = new Lang.Class({
|
||||
this._errorMessage.clutter_text.line_wrap = true;
|
||||
|
||||
this._errorBox.add(this._errorMessage, { expand: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: false,
|
||||
y_align: St.Align.MIDDLE,
|
||||
y_fill: false });
|
||||
|
||||
@ -124,7 +124,7 @@ const RunDialog = new Lang.Class({
|
||||
!this.pushModal())
|
||||
this.close();
|
||||
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
if (symbol == Clutter.Tab) {
|
||||
let text = o.get_text();
|
||||
@ -138,9 +138,9 @@ const RunDialog = new Lang.Class({
|
||||
o.insert_text(postfix, -1);
|
||||
o.set_cursor_position(text.length + postfix.length);
|
||||
}
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
},
|
||||
|
||||
@ -182,6 +182,10 @@ const RunDialog = new Lang.Class({
|
||||
let results = someResults.reduce(function(a, b) {
|
||||
return a.concat(b);
|
||||
}, []);
|
||||
|
||||
if (!results.length)
|
||||
return null;
|
||||
|
||||
let common = results.reduce(_getCommon, null);
|
||||
return common.substr(text.length);
|
||||
},
|
||||
@ -229,7 +233,7 @@ const RunDialog = new Lang.Class({
|
||||
let file = Gio.file_new_for_path(path);
|
||||
try {
|
||||
Gio.app_info_launch_default_for_uri(file.get_uri(),
|
||||
global.create_app_launch_context());
|
||||
global.create_app_launch_context(0, -1));
|
||||
} catch (e) {
|
||||
// The exception from gjs contains an error string like:
|
||||
// Error invoking Gio.app_info_launch_default_for_uri: No application
|
||||
@ -267,6 +271,12 @@ const RunDialog = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_restart: function() {
|
||||
this._shouldFadeOut = false;
|
||||
this.close();
|
||||
Meta.restart(_("Restarting…"));
|
||||
},
|
||||
|
||||
open: function() {
|
||||
this._history.lastItem();
|
||||
this._errorBox.hide();
|
||||
|
@ -17,7 +17,6 @@ const TweenerEquations = imports.tweener.equations;
|
||||
|
||||
const Background = imports.ui.background;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const Hash = imports.misc.hash;
|
||||
const Layout = imports.ui.layout;
|
||||
const OVirt = imports.gdm.oVirt;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
@ -86,7 +85,8 @@ const Clock = new Lang.Class({
|
||||
let date = new Date();
|
||||
/* Translators: This is a time format for a date in
|
||||
long format */
|
||||
this._date.text = date.toLocaleFormat(_("%A, %B %d"));
|
||||
let dateFormat = Shell.util_translate_time_string("%A, %B %d");
|
||||
this._date.text = date.toLocaleFormat(dateFormat);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -115,7 +115,7 @@ const NotificationsBox = new Lang.Class({
|
||||
this.actor.add(this._musicBin);
|
||||
this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START });
|
||||
|
||||
this._sources = new Hash.Map();
|
||||
this._sources = new Map();
|
||||
Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
|
||||
this._sourceAdded(Main.messageTray, source, true);
|
||||
}));
|
||||
@ -130,9 +130,8 @@ const NotificationsBox = new Lang.Class({
|
||||
this._sourceAddedId = 0;
|
||||
}
|
||||
|
||||
let items = this._sources.items();
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let [source, obj] = items[i];
|
||||
let items = this._sources.entries();
|
||||
for (let [source, obj] of items) {
|
||||
this._removeSource(source, obj);
|
||||
}
|
||||
|
||||
@ -198,8 +197,8 @@ const NotificationsBox = new Lang.Class({
|
||||
|
||||
let body = '';
|
||||
if (n.bannerBodyText) {
|
||||
body = n.bannerBodyMarkup ? n.bannerBodyText :
|
||||
GLib.markup_escape_text(n.bannerBodyMarkup, -1);
|
||||
body = n.bannerBodyMarkup ? n.bannerBodyText
|
||||
: GLib.markup_escape_text(n.bannerBodyText, -1);
|
||||
}
|
||||
|
||||
let label = new St.Label({ style_class: 'screen-shield-notification-count-text' });
|
||||
@ -240,10 +239,6 @@ const NotificationsBox = new Lang.Class({
|
||||
},
|
||||
|
||||
_sourceAdded: function(tray, source, initial) {
|
||||
// Ignore transient sources
|
||||
if (source.isTransient)
|
||||
return;
|
||||
|
||||
let obj = {
|
||||
visible: source.policy.showInLockScreen,
|
||||
detailed: source.policy.detailsInLockScreen,
|
||||
@ -307,7 +302,7 @@ const NotificationsBox = new Lang.Class({
|
||||
});
|
||||
|
||||
this._updateVisibility();
|
||||
Shell.util_wake_up_screen();
|
||||
this.emit('wake-up-screen');
|
||||
}
|
||||
},
|
||||
|
||||
@ -333,7 +328,7 @@ const NotificationsBox = new Lang.Class({
|
||||
|
||||
this._updateVisibility();
|
||||
if (obj.sourceBox.visible)
|
||||
Shell.util_wake_up_screen();
|
||||
this.emit('wake-up-screen');
|
||||
},
|
||||
|
||||
_visibleChanged: function(source, obj) {
|
||||
@ -348,7 +343,7 @@ const NotificationsBox = new Lang.Class({
|
||||
|
||||
this._updateVisibility();
|
||||
if (obj.sourceBox.visible)
|
||||
Shell.util_wake_up_screen();
|
||||
this.emit('wake-up-screen');
|
||||
},
|
||||
|
||||
_detailedChanged: function(source, obj) {
|
||||
@ -386,6 +381,7 @@ const NotificationsBox = new Lang.Class({
|
||||
this._sources.delete(source);
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(NotificationsBox.prototype);
|
||||
|
||||
const Arrow = new Lang.Class({
|
||||
Name: 'Arrow',
|
||||
@ -419,6 +415,7 @@ const Arrow = new Lang.Class({
|
||||
cr.lineTo(w/2, thickness);
|
||||
cr.lineTo(w - thickness / 2, h - thickness / 2);
|
||||
cr.stroke();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
vfunc_style_changed: function() {
|
||||
@ -448,14 +445,12 @@ function clamp(value, min, max) {
|
||||
}
|
||||
|
||||
/**
|
||||
* To test screen shield, make sure to kill gnome-screensaver.
|
||||
*
|
||||
* If you are setting org.gnome.desktop.session.idle-delay directly in dconf,
|
||||
* rather than through System Settings, you also need to set
|
||||
* org.gnome.settings-daemon.plugins.power.sleep-display-ac and
|
||||
* org.gnome.settings-daemon.plugins.power.sleep-display-battery to the same value.
|
||||
* This will ensure that the screen blanks at the right time when it fades out.
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependance.
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=668703 explains the dependency.
|
||||
*/
|
||||
const ScreenShield = new Lang.Class({
|
||||
Name: 'ScreenShield',
|
||||
@ -541,7 +536,7 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this._smartcardManager = SmartcardManager.getSmartcardManager();
|
||||
this._smartcardManager.connect('smartcard-inserted',
|
||||
Lang.bind(this, function(token) {
|
||||
Lang.bind(this, function(manager, token) {
|
||||
if (this._isLocked && token.UsedToLogin)
|
||||
this._liftShield(true, 0);
|
||||
}));
|
||||
@ -567,7 +562,7 @@ const ScreenShield = new Lang.Class({
|
||||
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.deactivate(false); }));
|
||||
}));
|
||||
|
||||
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
||||
this._settings = new Gio.Settings({ schema_id: SCREENSAVER_SCHEMA });
|
||||
|
||||
this._isModal = false;
|
||||
this._hasLockScreen = false;
|
||||
@ -673,11 +668,11 @@ const ScreenShield = new Lang.Class({
|
||||
// down after cancel.
|
||||
|
||||
if (this._lockScreenState != MessageTray.State.SHOWN)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
|
||||
if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape))
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (this._isLocked &&
|
||||
this._ensureUnlockDialog(true, true) &&
|
||||
@ -685,18 +680,18 @@ const ScreenShield = new Lang.Class({
|
||||
this._dialog.addCharacter(unichar);
|
||||
|
||||
this._liftShield(true, 0);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_onLockScreenScroll: function(actor, event) {
|
||||
if (this._lockScreenState != MessageTray.State.SHOWN)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
let delta = 0;
|
||||
if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
|
||||
if (event.get_scroll_direction() == Clutter.ScrollDirection.SMOOTH)
|
||||
delta = Math.abs(event.get_scroll_delta()[0]);
|
||||
else
|
||||
delta = 5;
|
||||
else if (event.get_scroll_direction() == Clutter.ScrollDirection.SMOOTH)
|
||||
delta = Math.max(0, event.get_scroll_delta()[0]);
|
||||
|
||||
this._lockScreenScrollCounter += delta;
|
||||
|
||||
@ -705,7 +700,7 @@ const ScreenShield = new Lang.Class({
|
||||
this._liftShield(true, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_inhibitSuspend: function() {
|
||||
@ -733,7 +728,7 @@ const ScreenShield = new Lang.Class({
|
||||
} else {
|
||||
this._inhibitSuspend();
|
||||
|
||||
this._onUserBecameActive();
|
||||
this._wakeUpScreen();
|
||||
}
|
||||
},
|
||||
|
||||
@ -756,7 +751,7 @@ const ScreenShield = new Lang.Class({
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
@ -852,8 +847,9 @@ const ScreenShield = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this._lockTimeoutId = 0;
|
||||
this.lock(false);
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(this._lockTimeoutId, '[gnome-shell] this.lock');
|
||||
}
|
||||
|
||||
this._activateFade(this._longLightbox, STANDARD_FADE_TIME);
|
||||
@ -905,17 +901,11 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
showDialog: function() {
|
||||
// Ensure that the stage window is mapped, before taking a grab
|
||||
// otherwise X errors out
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||
if (!this._becomeModal()) {
|
||||
// In the login screen, this is a hard error. Fail-whale
|
||||
log('Could not acquire modal grab for the login screen. Aborting login process.');
|
||||
Meta.quit(Meta.ExitCode.ERROR);
|
||||
}
|
||||
|
||||
return false;
|
||||
}));
|
||||
if (!this._becomeModal()) {
|
||||
// In the login screen, this is a hard error. Fail-whale
|
||||
log('Could not acquire modal grab for the login screen. Aborting login process.');
|
||||
Meta.quit(Meta.ExitCode.ERROR);
|
||||
}
|
||||
|
||||
this.actor.show();
|
||||
this._isGreeter = Main.sessionMode.isGreeter;
|
||||
@ -930,6 +920,11 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
this._lockScreenState = MessageTray.State.HIDDEN;
|
||||
this._lockScreenGroup.hide();
|
||||
|
||||
if (this._dialog) {
|
||||
this._dialog.actor.grab_key_focus();
|
||||
this._dialog.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
}
|
||||
},
|
||||
|
||||
_hideLockScreen: function(animate, velocity) {
|
||||
@ -1045,6 +1040,7 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
if (!this._arrowAnimationId) {
|
||||
this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
|
||||
GLib.Source.set_name_by_id(this._arrowAnimationId, '[gnome-shell] this._animateArrows');
|
||||
this._animateArrows();
|
||||
}
|
||||
|
||||
@ -1101,7 +1097,7 @@ const ScreenShield = new Lang.Class({
|
||||
global.stage.disconnect(motionId);
|
||||
}
|
||||
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}));
|
||||
this._cursorTracker.set_pointer_visible(false);
|
||||
|
||||
@ -1112,9 +1108,11 @@ const ScreenShield = new Lang.Class({
|
||||
if (params.fadeToBlack && params.animateFade) {
|
||||
// Take a beat
|
||||
|
||||
Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() {
|
||||
let id = Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() {
|
||||
this._activateFade(this._shortLightbox, MANUAL_FADE_TIME);
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
GLib.Source.set_name_by_id(id, '[gnome-shell] this._activateFade');
|
||||
} else {
|
||||
if (params.fadeToBlack)
|
||||
this._activateFade(this._shortLightbox, 0);
|
||||
@ -1155,6 +1153,7 @@ const ScreenShield = new Lang.Class({
|
||||
this._lockScreenContents.add_actor(this._lockScreenContentsBox);
|
||||
|
||||
this._notificationsBox = new NotificationsBox();
|
||||
this._wakeUpScreenId = this._notificationsBox.connect('wake-up-screen', Lang.bind(this, this._wakeUpScreen));
|
||||
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
@ -1162,11 +1161,17 @@ const ScreenShield = new Lang.Class({
|
||||
this._hasLockScreen = true;
|
||||
},
|
||||
|
||||
_wakeUpScreen: function() {
|
||||
this._onUserBecameActive();
|
||||
this.emit('wake-up-screen');
|
||||
},
|
||||
|
||||
_clearLockScreen: function() {
|
||||
this._clock.destroy();
|
||||
this._clock = null;
|
||||
|
||||
if (this._notificationsBox) {
|
||||
this._notificationsBox.disconnect(this._wakeUpScreenId);
|
||||
this._notificationsBox.destroy();
|
||||
this._notificationsBox = null;
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Hash = imports.misc.hash;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
const ScreencastIface = '<node> \
|
||||
@ -42,13 +41,15 @@ const ScreencastService = new Lang.Class({
|
||||
|
||||
Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
|
||||
this._recorders = new Hash.Map();
|
||||
this._recorders = new Map();
|
||||
|
||||
this._lockdownSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' });
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
},
|
||||
|
||||
get isRecording() {
|
||||
return this._recorders.size() > 0;
|
||||
return this._recorders.size > 0;
|
||||
},
|
||||
|
||||
_ensureRecorderForSender: function(sender) {
|
||||
@ -69,9 +70,8 @@ const ScreencastService = new Lang.Class({
|
||||
if (Main.sessionMode.allowScreencast)
|
||||
return;
|
||||
|
||||
for (let sender in this._recorders.keys())
|
||||
this._recorders.delete(sender);
|
||||
this.emit('updated');
|
||||
for (let sender of this._recorders.keys())
|
||||
this._stopRecordingForSender(sender);
|
||||
},
|
||||
|
||||
_onNameVanished: function(connection, name) {
|
||||
@ -105,8 +105,11 @@ const ScreencastService = new Lang.Class({
|
||||
|
||||
ScreencastAsync: function(params, invocation) {
|
||||
let returnValue = [false, ''];
|
||||
if (!Main.sessionMode.allowScreencast)
|
||||
if (!Main.sessionMode.allowScreencast ||
|
||||
this._lockdownSettings.get_boolean('disable-save-to-disk')) {
|
||||
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||
return;
|
||||
}
|
||||
|
||||
let sender = invocation.get_sender();
|
||||
let recorder = this._ensureRecorderForSender(sender);
|
||||
@ -117,6 +120,8 @@ const ScreencastService = new Lang.Class({
|
||||
this._applyOptionalParameters(recorder, options);
|
||||
let [success, fileName] = recorder.record();
|
||||
returnValue = [success, fileName ? fileName : ''];
|
||||
if (!success)
|
||||
this._stopRecordingForSender(sender);
|
||||
}
|
||||
|
||||
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||
@ -124,8 +129,11 @@ const ScreencastService = new Lang.Class({
|
||||
|
||||
ScreencastAreaAsync: function(params, invocation) {
|
||||
let returnValue = [false, ''];
|
||||
if (!Main.sessionMode.allowScreencast)
|
||||
if (!Main.sessionMode.allowScreencast ||
|
||||
this._lockdownSettings.get_boolean('disable-save-to-disk')) {
|
||||
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||
return;
|
||||
}
|
||||
|
||||
let sender = invocation.get_sender();
|
||||
let recorder = this._ensureRecorderForSender(sender);
|
||||
@ -133,11 +141,23 @@ const ScreencastService = new Lang.Class({
|
||||
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 : ''];
|
||||
if (!success)
|
||||
this._stopRecordingForSender(sender);
|
||||
}
|
||||
|
||||
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
|
||||
|
@ -11,6 +11,7 @@ const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const GrabHelper = imports.ui.grabHelper;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
@ -64,27 +65,97 @@ const ScreenshotService = new Lang.Class({
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenshotIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
|
||||
|
||||
this._screenShooter = new Map();
|
||||
|
||||
this._lockdownSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' });
|
||||
|
||||
Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
_createScreenshot: function(invocation) {
|
||||
let sender = invocation.get_sender();
|
||||
if (this._screenShooter.has(sender) ||
|
||||
this._lockdownSettings.get_boolean('disable-save-to-disk')) {
|
||||
invocation.return_value(GLib.Variant.new('(bs)', [false, '']));
|
||||
return null;
|
||||
}
|
||||
|
||||
let shooter = new Shell.Screenshot();
|
||||
shooter._watchNameId =
|
||||
Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
|
||||
Lang.bind(this, this._onNameVanished));
|
||||
|
||||
this._screenShooter.set(sender, shooter);
|
||||
|
||||
return shooter;
|
||||
},
|
||||
|
||||
_onNameVanished: function(connection, name) {
|
||||
this._removeShooterForSender(name);
|
||||
},
|
||||
|
||||
_removeShooterForSender: function(sender) {
|
||||
let shooter = this._screenShooter.get(sender);
|
||||
if (!shooter)
|
||||
return;
|
||||
|
||||
Gio.bus_unwatch_name(shooter._watchNameId);
|
||||
this._screenShooter.delete(sender);
|
||||
},
|
||||
|
||||
_checkArea: function(x, y, width, height) {
|
||||
return x >= 0 && y >= 0 &&
|
||||
width > 0 && height > 0 &&
|
||||
x + width <= global.screen_width &&
|
||||
y + height <= global.screen_height;
|
||||
},
|
||||
|
||||
_onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
|
||||
if (flash && result) {
|
||||
let flashspot = new Flashspot(area);
|
||||
flashspot.fire();
|
||||
if (result) {
|
||||
if (flash) {
|
||||
let flashspot = new Flashspot(area);
|
||||
flashspot.fire(Lang.bind(this, function() {
|
||||
this._removeShooterForSender(invocation.get_sender());
|
||||
}));
|
||||
}
|
||||
else
|
||||
this._removeShooterForSender(invocation.get_sender());
|
||||
}
|
||||
|
||||
let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
|
||||
invocation.return_value(retval);
|
||||
},
|
||||
|
||||
_scaleArea: function(x, y, width, height) {
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
x *= scaleFactor;
|
||||
y *= scaleFactor;
|
||||
width *= scaleFactor;
|
||||
height *= scaleFactor;
|
||||
return [x, y, width, height];
|
||||
},
|
||||
|
||||
_unscaleArea: function(x, y, width, height) {
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
x /= scaleFactor;
|
||||
y /= scaleFactor;
|
||||
width /= scaleFactor;
|
||||
height /= scaleFactor;
|
||||
return [x, y, width, height];
|
||||
},
|
||||
|
||||
ScreenshotAreaAsync : function (params, invocation) {
|
||||
let [x, y, width, height, flash, filename, callback] = params;
|
||||
if (height <= 0 || width <= 0) {
|
||||
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
|
||||
"Invalid params");
|
||||
[x, y, width, height] = this._scaleArea(x, y, width, height);
|
||||
if (!this._checkArea(x, y, width, height)) {
|
||||
invocation.return_error_literal(Gio.IOErrorEnum,
|
||||
Gio.IOErrorEnum.CANCELLED,
|
||||
"Invalid params");
|
||||
return;
|
||||
}
|
||||
let screenshot = new Shell.Screenshot();
|
||||
let screenshot = this._createScreenshot(invocation);
|
||||
if (!screenshot)
|
||||
return;
|
||||
screenshot.screenshot_area (x, y, width, height, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
@ -92,7 +163,9 @@ const ScreenshotService = new Lang.Class({
|
||||
|
||||
ScreenshotWindowAsync : function (params, invocation) {
|
||||
let [include_frame, include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
let screenshot = this._createScreenshot(invocation);
|
||||
if (!screenshot)
|
||||
return;
|
||||
screenshot.screenshot_window (include_frame, include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
@ -100,7 +173,9 @@ const ScreenshotService = new Lang.Class({
|
||||
|
||||
ScreenshotAsync : function (params, invocation) {
|
||||
let [include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
let screenshot = this._createScreenshot(invocation);
|
||||
if (!screenshot)
|
||||
return;
|
||||
screenshot.screenshot(include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
@ -112,9 +187,9 @@ const ScreenshotService = new Lang.Class({
|
||||
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]);
|
||||
let retRectangle = this._unscaleArea(areaRectangle.x, areaRectangle.y,
|
||||
areaRectangle.width, areaRectangle.height);
|
||||
let retval = GLib.Variant.new('(iiii)', retRectangle);
|
||||
invocation.return_value(retval);
|
||||
} else {
|
||||
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
|
||||
@ -123,9 +198,18 @@ const ScreenshotService = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
FlashArea: function(x, y, width, height) {
|
||||
FlashAreaAsync: function(params, invocation) {
|
||||
let [x, y, width, height] = params;
|
||||
[x, y, width, height] = this._scaleArea(x, y, width, height);
|
||||
if (!this._checkArea(x, y, width, height)) {
|
||||
invocation.return_error_literal(Gio.IOErrorEnum,
|
||||
Gio.IOErrorEnum.CANCELLED,
|
||||
"Invalid params");
|
||||
return;
|
||||
}
|
||||
let flashspot = new Flashspot({ x : x, y : y, width: width, height: height});
|
||||
flashspot.fire();
|
||||
invocation.return_value(null);
|
||||
}
|
||||
});
|
||||
|
||||
@ -137,6 +221,7 @@ const SelectArea = new Lang.Class({
|
||||
this._startY = -1;
|
||||
this._lastX = 0;
|
||||
this._lastY = 0;
|
||||
this._result = null;
|
||||
|
||||
this._initRubberbandColors();
|
||||
|
||||
@ -146,12 +231,12 @@ const SelectArea = new Lang.Class({
|
||||
y: 0 });
|
||||
Main.uiGroup.add_actor(this._group);
|
||||
|
||||
this._grabHelper = new GrabHelper.GrabHelper(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));
|
||||
|
||||
@ -167,10 +252,12 @@ const SelectArea = new Lang.Class({
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (!Main.pushModal(this._group) || this._group.visible)
|
||||
if (!this._grabHelper.grab({ actor: this._group,
|
||||
onUngrab: Lang.bind(this, this._onUngrab) }))
|
||||
return;
|
||||
|
||||
global.screen.set_cursor(Meta.Cursor.CROSSHAIR);
|
||||
Main.uiGroup.set_child_above_sibling(this._group, null);
|
||||
this._group.visible = true;
|
||||
},
|
||||
|
||||
@ -200,16 +287,9 @@ const SelectArea = new Lang.Class({
|
||||
height: Math.abs(this._startY - this._lastY) };
|
||||
},
|
||||
|
||||
_onKeyPress: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape)
|
||||
this._destroy(null, false);
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
_onMotionEvent: function(actor, event) {
|
||||
if (this._startX == -1 || this._startY == -1)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
[this._lastX, this._lastY] = event.get_coords();
|
||||
let geometry = this._getGeometry();
|
||||
@ -217,35 +297,39 @@ const SelectArea = new Lang.Class({
|
||||
this._rubberband.set_position(geometry.x, geometry.y);
|
||||
this._rubberband.set_size(geometry.width, geometry.height);
|
||||
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
[this._startX, this._startY] = event.get_coords();
|
||||
this._rubberband.set_position(this._startX, this._startY);
|
||||
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onButtonRelease: function(actor, event) {
|
||||
this._destroy(this._getGeometry(), true);
|
||||
return false;
|
||||
},
|
||||
|
||||
_destroy: function(geometry, fade) {
|
||||
this._result = this._getGeometry();
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
time: fade ? 0.2 : 0,
|
||||
time: 0.2,
|
||||
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);
|
||||
this._grabHelper.ungrab();
|
||||
})
|
||||
});
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onUngrab: function() {
|
||||
global.screen.set_cursor(Meta.Cursor.DEFAULT);
|
||||
this.emit('finished', this._result);
|
||||
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this,
|
||||
function() {
|
||||
this._group.destroy();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}));
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SelectArea.prototype);
|
||||
@ -265,7 +349,7 @@ const Flashspot = new Lang.Class({
|
||||
this.actor.set_position(area.x, area.y);
|
||||
},
|
||||
|
||||
fire: function() {
|
||||
fire: function(doneCallback) {
|
||||
this.actor.show();
|
||||
this.actor.opacity = 255;
|
||||
Tweener.addTween(this.actor,
|
||||
@ -273,6 +357,8 @@ const Flashspot = new Lang.Class({
|
||||
time: FLASHSPOT_ANIMATION_OUT_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
if (doneCallback)
|
||||
doneCallback();
|
||||
this.destroy();
|
||||
})
|
||||
});
|
||||
|
@ -1,11 +1,13 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
// This module provides functionality for driving the shell user interface
|
||||
// in an automated fashion. The primary current use case for this is
|
||||
@ -38,11 +40,12 @@ const Main = imports.ui.main;
|
||||
function sleep(milliseconds) {
|
||||
let cb;
|
||||
|
||||
Mainloop.timeout_add(milliseconds, function() {
|
||||
let id = Mainloop.timeout_add(milliseconds, function() {
|
||||
if (cb)
|
||||
cb();
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
GLib.Source.set_name_by_id(id, '[gnome-shell] sleep');
|
||||
|
||||
return function(callback) {
|
||||
cb = callback;
|
||||
@ -76,6 +79,7 @@ const PerfHelperIface = '<node> \
|
||||
<arg type="i" direction="in" /> \
|
||||
<arg type="b" direction="in" /> \
|
||||
<arg type="b" direction="in" /> \
|
||||
<arg type="b" direction="in" /> \
|
||||
</method> \
|
||||
<method name="WaitWindows" /> \
|
||||
<method name="DestroyWindows" /> \
|
||||
@ -95,11 +99,36 @@ function _getPerfHelper() {
|
||||
return _perfHelper;
|
||||
}
|
||||
|
||||
function _callRemote(obj, method, ...args) {
|
||||
let cb;
|
||||
let errcb;
|
||||
|
||||
args.push(function(result, excp) {
|
||||
if (excp) {
|
||||
if (errcb)
|
||||
errcb(excp);
|
||||
} else {
|
||||
if (cb)
|
||||
cb();
|
||||
}
|
||||
});
|
||||
|
||||
method.apply(obj, args);
|
||||
|
||||
return function(callback, error_callback) {
|
||||
cb = callback;
|
||||
errcb = error_callback;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* createTestWindow:
|
||||
* @width: width of window, in pixels
|
||||
* @height: height of window, in pixels
|
||||
* @alpha: whether the window should be alpha transparent
|
||||
* @params: options for window creation.
|
||||
* width - width of window, in pixels (default 640)
|
||||
* height - height of window, in pixels (default 480)
|
||||
* alpha - whether the window should have an alpha channel (default false)
|
||||
* maximized - whether the window should be created maximized (default false)
|
||||
* redraws - whether the window should continually redraw itself (default false)
|
||||
* @maximized: whethe the window should be created maximized
|
||||
*
|
||||
* Creates a window using gnome-shell-perf-helper for testing purposes.
|
||||
@ -108,19 +137,17 @@ function _getPerfHelper() {
|
||||
* because of the normal X asynchronous mapping process, to actually wait
|
||||
* until the window has been mapped and exposed, use waitTestWindows().
|
||||
*/
|
||||
function createTestWindow(width, height, alpha, maximized) {
|
||||
let cb;
|
||||
function createTestWindow(width, height, params) {
|
||||
params = Params.parse(params, { width: 640,
|
||||
height: 480,
|
||||
alpha: false,
|
||||
maximized: false,
|
||||
redraws: false });
|
||||
|
||||
let perfHelper = _getPerfHelper();
|
||||
|
||||
perfHelper.CreateWindowRemote(width, height, alpha, maximized,
|
||||
function(result, excp) {
|
||||
if (cb)
|
||||
cb();
|
||||
});
|
||||
|
||||
return function(callback) {
|
||||
cb = callback;
|
||||
};
|
||||
return _callRemote(perfHelper, perfHelper.CreateWindowRemote,
|
||||
params.width, params.height,
|
||||
params.alpha, params.maximized, params.redraws);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,17 +157,8 @@ function createTestWindow(width, height, alpha, maximized) {
|
||||
* created with createTestWindow have been mapped and exposed.
|
||||
*/
|
||||
function waitTestWindows() {
|
||||
let cb;
|
||||
let perfHelper = _getPerfHelper();
|
||||
|
||||
perfHelper.WaitWindowsRemote(function(result, excp) {
|
||||
if (cb)
|
||||
cb();
|
||||
});
|
||||
|
||||
return function(callback) {
|
||||
cb = callback;
|
||||
};
|
||||
return _callRemote(perfHelper, perfHelper.WaitWindowsRemote);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,17 +171,8 @@ function waitTestWindows() {
|
||||
* process because of normal X asynchronicity.
|
||||
*/
|
||||
function destroyTestWindows() {
|
||||
let cb;
|
||||
let perfHelper = _getPerfHelper();
|
||||
|
||||
perfHelper.DestroyWindowsRemote(function(result, excp) {
|
||||
if (cb)
|
||||
cb();
|
||||
});
|
||||
|
||||
return function(callback) {
|
||||
cb = callback;
|
||||
};
|
||||
return _callRemote(perfHelper, perfHelper.DestroyWindowsRemote);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -206,6 +215,10 @@ function _step(g, finish, onError) {
|
||||
let waitFunction = g.next();
|
||||
waitFunction(function() {
|
||||
_step(g, finish, onError);
|
||||
},
|
||||
function(err) {
|
||||
if (onError)
|
||||
onError(err);
|
||||
});
|
||||
} catch (err if err instanceof StopIteration) {
|
||||
if (finish)
|
||||
@ -304,8 +317,8 @@ function _collect(scriptModule, outputFile) {
|
||||
print ('------------------------------------------------------------');
|
||||
for (let i = 0; i < metrics.length; i++) {
|
||||
let metric = metrics[i];
|
||||
print ('# ' + scriptModule.METRIC_DESCRIPTIONS[metric]);
|
||||
print (metric + ': ' + scriptModule.METRICS[metric]);
|
||||
print ('# ' + scriptModule.METRICS[metric].description);
|
||||
print (metric + ': ' + scriptModule.METRICS[metric].value + scriptModule.METRICS[metric].units);
|
||||
}
|
||||
print ('------------------------------------------------------------');
|
||||
}
|
||||
@ -358,7 +371,12 @@ function runPerfScript(scriptModule, outputFile) {
|
||||
|
||||
_step(g,
|
||||
function() {
|
||||
_collect(scriptModule, outputFile);
|
||||
try {
|
||||
_collect(scriptModule, outputFile);
|
||||
} catch (err) {
|
||||
log("Script failed: " + err + "\n" + err.stack);
|
||||
Meta.exit(Meta.ExitCode.ERROR);
|
||||
}
|
||||
Meta.exit(Meta.ExitCode.SUCCESS);
|
||||
},
|
||||
function(err) {
|
||||
|
553
js/ui/search.js
553
js/ui/search.js
@ -2,18 +2,22 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const Shell = imports.gi.Shell;
|
||||
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 Search = imports.ui.search;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
|
||||
@ -21,97 +25,6 @@ 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({
|
||||
Name: 'SearchSystem',
|
||||
|
||||
_init: function() {
|
||||
this._providers = [];
|
||||
this._remoteProviders = [];
|
||||
this.reset();
|
||||
},
|
||||
|
||||
registerProvider: function (provider) {
|
||||
provider.searchSystem = this;
|
||||
this._providers.push(provider);
|
||||
|
||||
if (provider.isRemoteProvider)
|
||||
this._remoteProviders.push(provider);
|
||||
},
|
||||
|
||||
unregisterProvider: function (provider) {
|
||||
let index = this._providers.indexOf(provider);
|
||||
if (index == -1)
|
||||
return;
|
||||
provider.searchSystem = null;
|
||||
this._providers.splice(index, 1);
|
||||
|
||||
let remoteIndex = this._remoteProviders.indexOf(provider);
|
||||
if (remoteIndex != -1)
|
||||
this._remoteProviders.splice(remoteIndex, 1);
|
||||
},
|
||||
|
||||
getProviders: function() {
|
||||
return this._providers;
|
||||
},
|
||||
|
||||
getRemoteProviders: function() {
|
||||
return this._remoteProviders;
|
||||
},
|
||||
|
||||
getTerms: function() {
|
||||
return this._previousTerms;
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._previousTerms = [];
|
||||
this._previousResults = [];
|
||||
},
|
||||
|
||||
setResults: function(provider, results) {
|
||||
let i = this._providers.indexOf(provider);
|
||||
if (i == -1)
|
||||
return;
|
||||
|
||||
this._previousResults[i] = [provider, results];
|
||||
this.emit('search-updated', this._previousResults[i]);
|
||||
},
|
||||
|
||||
updateSearchResults: function(terms) {
|
||||
if (!terms)
|
||||
return;
|
||||
|
||||
let searchString = terms.join(' ');
|
||||
let previousSearchString = this._previousTerms.join(' ');
|
||||
if (searchString == previousSearchString)
|
||||
return;
|
||||
|
||||
let isSubSearch = false;
|
||||
if (this._previousTerms.length > 0)
|
||||
isSubSearch = searchString.indexOf(previousSearchString) == 0;
|
||||
|
||||
let previousResultsArr = this._previousResults;
|
||||
|
||||
let results = [];
|
||||
this._previousTerms = terms;
|
||||
this._previousResults = results;
|
||||
|
||||
if (isSubSearch) {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let [provider, previousResults] = previousResultsArr[i];
|
||||
results.push([provider, []]);
|
||||
provider.getSubsearchResultSet(previousResults, terms);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
results.push([provider, []]);
|
||||
provider.getInitialResultSet(terms);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SearchSystem.prototype);
|
||||
|
||||
const MaxWidthBin = new Lang.Class({
|
||||
Name: 'MaxWidthBin',
|
||||
Extends: St.Bin,
|
||||
@ -135,10 +48,9 @@ const MaxWidthBin = new Lang.Class({
|
||||
const SearchResult = new Lang.Class({
|
||||
Name: 'SearchResult',
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
_init: function(provider, metaInfo) {
|
||||
this.provider = provider;
|
||||
this.metaInfo = metaInfo;
|
||||
this.terms = terms;
|
||||
|
||||
this.actor = new St.Button({ reactive: true,
|
||||
can_focus: true,
|
||||
@ -151,17 +63,10 @@ const SearchResult = new Lang.Class({
|
||||
},
|
||||
|
||||
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');
|
||||
this.emit('activate', this.metaInfo.id);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SearchResult.prototype);
|
||||
|
||||
const ListSearchResult = new Lang.Class({
|
||||
Name: 'ListSearchResult',
|
||||
@ -169,8 +74,8 @@ const ListSearchResult = new Lang.Class({
|
||||
|
||||
ICON_SIZE: 64,
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.parent(provider, metaInfo, terms);
|
||||
_init: function(provider, metaInfo) {
|
||||
this.parent(provider, metaInfo);
|
||||
|
||||
this.actor.style_class = 'list-search-result';
|
||||
this.actor.x_fill = true;
|
||||
@ -214,64 +119,16 @@ const GridSearchResult = new Lang.Class({
|
||||
Name: 'GridSearchResult',
|
||||
Extends: SearchResult,
|
||||
|
||||
_init: function(provider, metaInfo, terms) {
|
||||
this.parent(provider, metaInfo, terms);
|
||||
_init: function(provider, metaInfo) {
|
||||
this.parent(provider, metaInfo);
|
||||
|
||||
this.actor.style_class = 'grid-search-result';
|
||||
|
||||
let content = provider.createResultObject(metaInfo, terms);
|
||||
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);
|
||||
this.icon = new IconGrid.BaseIcon(this.metaInfo['name'],
|
||||
{ createIcon: this.metaInfo['createIcon'] });
|
||||
let content = new St.Bin({ child: this.icon.actor });
|
||||
this.actor.set_child(content);
|
||||
this.actor.label_actor = this.icon.label;
|
||||
}
|
||||
});
|
||||
|
||||
@ -292,6 +149,10 @@ const SearchResultsBase = new Lang.Class({
|
||||
|
||||
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
|
||||
this.actor.add(separator.actor);
|
||||
|
||||
this._resultDisplays = {};
|
||||
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -299,10 +160,17 @@ const SearchResultsBase = new Lang.Class({
|
||||
this._terms = [];
|
||||
},
|
||||
|
||||
_clearResultDisplay: function() {
|
||||
_createResultDisplay: function(meta) {
|
||||
if (this.provider.createResultObject)
|
||||
return this.provider.createResultObject(meta);
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
for (let resultId in this._resultDisplays)
|
||||
this._resultDisplays[resultId].actor.destroy();
|
||||
this._resultDisplays = {};
|
||||
this._clearResultDisplay();
|
||||
this.actor.hide();
|
||||
},
|
||||
@ -311,9 +179,48 @@ const SearchResultsBase = new Lang.Class({
|
||||
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(true);
|
||||
} else {
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
|
||||
this.provider.getResultMetas(metasNeeded, Lang.bind(this, function(metas) {
|
||||
if (metas.length == 0) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
if (metas.length != metasNeeded.length) {
|
||||
log('Wrong number of result metas returned by search provider');
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
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(true);
|
||||
}), this._cancellable);
|
||||
}
|
||||
},
|
||||
|
||||
updateSearch: function(providerResults, terms, callback) {
|
||||
this._terms = terms;
|
||||
|
||||
@ -326,15 +233,20 @@ const SearchResultsBase = new Lang.Class({
|
||||
let results = this.provider.filterResults(providerResults, maxResults);
|
||||
let hasMoreResults = results.length < providerResults.length;
|
||||
|
||||
this.provider.getResultMetas(results, Lang.bind(this, function(metas) {
|
||||
this.clear();
|
||||
this._ensureResultActors(results, Lang.bind(this, function(successful) {
|
||||
if (!successful) {
|
||||
this._clearResultDisplay();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
this._renderResults(metas);
|
||||
results.forEach(Lang.bind(this, function(resultId) {
|
||||
this._addItem(this._resultDisplays[resultId]);
|
||||
}));
|
||||
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
|
||||
this.actor.show();
|
||||
callback();
|
||||
@ -355,6 +267,7 @@ const ListSearchResults = new Lang.Class({
|
||||
this.providerIcon.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this.providerIcon.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
this.providerIcon.animateLaunch();
|
||||
provider.launchSearch(this._terms);
|
||||
Main.overview.toggle();
|
||||
}));
|
||||
@ -372,23 +285,23 @@ const ListSearchResults = new Lang.Class({
|
||||
},
|
||||
|
||||
_setMoreIconVisible: function(visible) {
|
||||
this.providerIcon.moreIcon.visible = true;
|
||||
this.providerIcon.moreIcon.visible = visible;
|
||||
},
|
||||
|
||||
_getMaxDisplayedResults: function() {
|
||||
return MAX_LIST_SEARCH_RESULTS_ROWS;
|
||||
},
|
||||
|
||||
_renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new ListSearchResult(this.provider, metas[i], this._terms);
|
||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._content.add_actor(display.actor);
|
||||
}
|
||||
_clearResultDisplay: function () {
|
||||
this._content.remove_all_children();
|
||||
},
|
||||
|
||||
_clearResultDisplay: function () {
|
||||
this._content.destroy_all_children();
|
||||
_createResultDisplay: function(meta) {
|
||||
return this.parent(meta) || new ListSearchResult(this.provider, meta);
|
||||
},
|
||||
|
||||
_addItem: function(display) {
|
||||
this._content.add_actor(display.actor);
|
||||
},
|
||||
|
||||
getFirstResult: function() {
|
||||
@ -404,8 +317,14 @@ const GridSearchResults = new Lang.Class({
|
||||
Name: 'GridSearchResults',
|
||||
Extends: SearchResultsBase,
|
||||
|
||||
_init: function(provider) {
|
||||
_init: function(provider, parentContainer) {
|
||||
this.parent(provider);
|
||||
// We need to use the parent container to know how much results we can show.
|
||||
// None of the actors in this class can be used for that, since the main actor
|
||||
// goes hidden when no results are displayed, and then it lost its allocation.
|
||||
// Then on the next use of _getMaxDisplayedResults allocation is 0, en therefore
|
||||
// it doesn't show any result although we have some.
|
||||
this._parentContainer = parentContainer;
|
||||
|
||||
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
|
||||
xAlign: St.Align.START });
|
||||
@ -416,21 +335,23 @@ const GridSearchResults = new Lang.Class({
|
||||
},
|
||||
|
||||
_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], this._terms);
|
||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._grid.addItem(display);
|
||||
}
|
||||
let parentThemeNode = this._parentContainer.get_theme_node();
|
||||
let availableWidth = parentThemeNode.adjust_for_width(this._parentContainer.width);
|
||||
return this._grid.columnsForWidth(availableWidth) * this._grid.getRowLimit();
|
||||
},
|
||||
|
||||
_clearResultDisplay: function () {
|
||||
this._grid.removeAll();
|
||||
},
|
||||
|
||||
_createResultDisplay: function(meta) {
|
||||
return this.parent(meta) || 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;
|
||||
@ -443,10 +364,7 @@ Signals.addSignalMethods(GridSearchResults.prototype);
|
||||
const SearchResults = new Lang.Class({
|
||||
Name: 'SearchResults',
|
||||
|
||||
_init: function(searchSystem) {
|
||||
this._searchSystem = searchSystem;
|
||||
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ name: 'searchResults',
|
||||
vertical: true });
|
||||
|
||||
@ -479,16 +397,136 @@ const SearchResults = new Lang.Class({
|
||||
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.actor.add(this._statusBin, { expand: true });
|
||||
this._statusBin.add_actor(this._statusText);
|
||||
this._providers = this._searchSystem.getProviders();
|
||||
this._providerDisplays = {};
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
this.createProviderDisplay(this._providers[i]);
|
||||
}
|
||||
|
||||
this._highlightDefault = false;
|
||||
this._defaultResult = null;
|
||||
this._startingSearch = false;
|
||||
|
||||
this._terms = [];
|
||||
this._results = {};
|
||||
|
||||
this._providers = [];
|
||||
|
||||
this._searchSettings = new Gio.Settings({ schema_id: 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._searchTimeoutId = 0;
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
|
||||
this._registerProvider(new AppDisplay.AppSearchProvider());
|
||||
this._reloadRemoteProviders();
|
||||
},
|
||||
|
||||
_reloadRemoteProviders: function() {
|
||||
let remoteProviders = this._providers.filter(function(provider) {
|
||||
return provider.isRemoteProvider;
|
||||
});
|
||||
remoteProviders.forEach(Lang.bind(this, function(provider) {
|
||||
this._unregisterProvider(provider);
|
||||
}));
|
||||
|
||||
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, function(providers) {
|
||||
providers.forEach(Lang.bind(this, this._registerProvider));
|
||||
}));
|
||||
},
|
||||
|
||||
_registerProvider: function (provider) {
|
||||
this._providers.push(provider);
|
||||
this._ensureProviderDisplay(provider);
|
||||
},
|
||||
|
||||
_unregisterProvider: function (provider) {
|
||||
let index = this._providers.indexOf(provider);
|
||||
this._providers.splice(index, 1);
|
||||
|
||||
if (provider.display)
|
||||
provider.display.destroy();
|
||||
},
|
||||
|
||||
_gotResults: function(results, provider) {
|
||||
this._results[provider.id] = results;
|
||||
this._updateResults(provider, results);
|
||||
},
|
||||
|
||||
_clearSearchTimeout: function() {
|
||||
if (this._searchTimeoutId > 0) {
|
||||
GLib.source_remove(this._searchTimeoutId);
|
||||
this._searchTimeoutId = 0;
|
||||
}
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this._terms = [];
|
||||
this._results = {};
|
||||
this._clearDisplay();
|
||||
this._clearSearchTimeout();
|
||||
this._defaultResult = null;
|
||||
this._startingSearch = false;
|
||||
|
||||
this._updateSearchProgress();
|
||||
},
|
||||
|
||||
_doSearch: function() {
|
||||
this._startingSearch = false;
|
||||
|
||||
let previousResults = this._results;
|
||||
this._results = {};
|
||||
|
||||
this._providers.forEach(Lang.bind(this, function(provider) {
|
||||
provider.searchInProgress = true;
|
||||
|
||||
let previousProviderResults = previousResults[provider.id];
|
||||
if (this._isSubSearch && previousProviderResults)
|
||||
provider.getSubsearchResultSet(previousProviderResults, this._terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
|
||||
else
|
||||
provider.getInitialResultSet(this._terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
|
||||
}));
|
||||
|
||||
this._updateSearchProgress();
|
||||
|
||||
this._clearSearchTimeout();
|
||||
},
|
||||
|
||||
_onSearchTimeout: function() {
|
||||
this._searchTimeoutId = 0;
|
||||
this._doSearch();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
setTerms: function(terms) {
|
||||
// Check for the case of making a duplicate previous search before
|
||||
// setting state of the current search or cancelling the search.
|
||||
// This will prevent incorrect state being as a result of a duplicate
|
||||
// search while the previous search is still active.
|
||||
let searchString = terms.join(' ');
|
||||
let previousSearchString = this._terms.join(' ');
|
||||
if (searchString == previousSearchString)
|
||||
return;
|
||||
|
||||
this._startingSearch = true;
|
||||
|
||||
this._cancellable.cancel();
|
||||
this._cancellable.reset();
|
||||
|
||||
if (terms.length == 0) {
|
||||
this._reset();
|
||||
return;
|
||||
}
|
||||
|
||||
let isSubSearch = false;
|
||||
if (this._terms.length > 0)
|
||||
isSubSearch = searchString.indexOf(previousSearchString) == 0;
|
||||
|
||||
this._terms = terms;
|
||||
this._isSubSearch = isSubSearch;
|
||||
this._updateSearchProgress();
|
||||
|
||||
if (this._searchTimeoutId == 0)
|
||||
this._searchTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 150, Lang.bind(this, this._onSearchTimeout));
|
||||
},
|
||||
|
||||
_onPan: function(action) {
|
||||
@ -502,52 +540,35 @@ const SearchResults = new Lang.Class({
|
||||
Util.ensureActorVisibleInScrollView(this._scrollView, actor);
|
||||
},
|
||||
|
||||
createProviderDisplay: function(provider) {
|
||||
let providerDisplay = null;
|
||||
_ensureProviderDisplay: function(provider) {
|
||||
if (provider.display)
|
||||
return;
|
||||
|
||||
if (provider.appInfo) {
|
||||
let providerDisplay;
|
||||
if (provider.appInfo)
|
||||
providerDisplay = new ListSearchResults(provider);
|
||||
} else {
|
||||
providerDisplay = new GridSearchResults(provider);
|
||||
}
|
||||
else
|
||||
providerDisplay = new GridSearchResults(provider, this.actor);
|
||||
|
||||
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._providerDisplays[provider.id] = providerDisplay;
|
||||
providerDisplay.actor.hide();
|
||||
this._content.add(providerDisplay.actor);
|
||||
},
|
||||
|
||||
destroyProviderDisplay: function(provider) {
|
||||
this._providerDisplays[provider.id].destroy();
|
||||
delete this._providerDisplays[provider.id];
|
||||
provider.display = providerDisplay;
|
||||
},
|
||||
|
||||
_clearDisplay: function() {
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
let providerDisplay = this._providerDisplays[provider.id];
|
||||
providerDisplay.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();
|
||||
this._providers.forEach(function(provider) {
|
||||
provider.display.clear();
|
||||
});
|
||||
},
|
||||
|
||||
_maybeSetInitialSelection: function() {
|
||||
let newDefaultResult = null;
|
||||
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
let display = this._providerDisplays[provider.id];
|
||||
let providers = this._providers;
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
let display = provider.display;
|
||||
|
||||
if (!display.actor.visible)
|
||||
continue;
|
||||
@ -560,55 +581,64 @@ const SearchResults = new Lang.Class({
|
||||
}
|
||||
|
||||
if (newDefaultResult != this._defaultResult) {
|
||||
if (this._defaultResult)
|
||||
this._defaultResult.setSelected(false);
|
||||
if (newDefaultResult)
|
||||
newDefaultResult.setSelected(this._highlightDefault);
|
||||
this._setSelected(this._defaultResult, false);
|
||||
this._setSelected(newDefaultResult, this._highlightDefault);
|
||||
|
||||
this._defaultResult = newDefaultResult;
|
||||
}
|
||||
},
|
||||
|
||||
_updateStatusText: function () {
|
||||
let haveResults = false;
|
||||
get searchInProgress() {
|
||||
if (this._startingSearch)
|
||||
return true;
|
||||
|
||||
for (let i = 0; i < this._providers.length; i++) {
|
||||
let provider = this._providers[i];
|
||||
let display = this._providerDisplays[provider.id];
|
||||
if (display.getFirstResult()) {
|
||||
haveResults = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this._providers.some(function(provider) {
|
||||
return provider.searchInProgress;
|
||||
});
|
||||
},
|
||||
|
||||
_updateSearchProgress: function () {
|
||||
let haveResults = this._providers.some(function(provider) {
|
||||
let display = provider.display;
|
||||
return (display.getFirstResult() != null);
|
||||
});
|
||||
|
||||
this._scrollView.visible = haveResults;
|
||||
this._statusBin.visible = !haveResults;
|
||||
|
||||
if (!haveResults) {
|
||||
this._statusText.set_text(_("No results."));
|
||||
this._statusBin.show();
|
||||
} else {
|
||||
this._statusBin.hide();
|
||||
if (this.searchInProgress) {
|
||||
this._statusText.set_text(_("Searching…"));
|
||||
} else {
|
||||
this._statusText.set_text(_("No results."));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_updateResults: function(searchSystem, results) {
|
||||
let terms = searchSystem.getTerms();
|
||||
let [provider, providerResults] = results;
|
||||
let display = this._providerDisplays[provider.id];
|
||||
_updateResults: function(provider, results) {
|
||||
let terms = this._terms;
|
||||
let display = provider.display;
|
||||
|
||||
display.updateSearch(results, terms, Lang.bind(this, function() {
|
||||
provider.searchInProgress = false;
|
||||
|
||||
display.updateSearch(providerResults, terms, Lang.bind(this, function() {
|
||||
this._maybeSetInitialSelection();
|
||||
this._updateStatusText();
|
||||
this._updateSearchProgress();
|
||||
}));
|
||||
},
|
||||
|
||||
activateDefault: function() {
|
||||
// If we have a search queued up, force the search now.
|
||||
if (this._searchTimeoutId > 0)
|
||||
this._doSearch();
|
||||
|
||||
if (this._defaultResult)
|
||||
this._defaultResult.activate();
|
||||
},
|
||||
|
||||
highlightDefault: function(highlight) {
|
||||
this._highlightDefault = highlight;
|
||||
if (this._defaultResult)
|
||||
this._defaultResult.setSelected(highlight);
|
||||
this._setSelected(this._defaultResult, highlight);
|
||||
},
|
||||
|
||||
navigateFocus: function(direction) {
|
||||
@ -623,6 +653,18 @@ const SearchResults = new Lang.Class({
|
||||
|
||||
let from = this._defaultResult ? this._defaultResult.actor : null;
|
||||
this.actor.navigate_focus(from, direction, false);
|
||||
},
|
||||
|
||||
_setSelected: function(result, selected) {
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
if (selected) {
|
||||
result.actor.add_style_pseudo_class('selected');
|
||||
Util.ensureActorVisibleInScrollView(this._scrollView, result.actor);
|
||||
} else {
|
||||
result.actor.remove_style_pseudo_class('selected');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -656,5 +698,12 @@ const ProviderIcon = new Lang.Class({
|
||||
gicon: provider.appInfo.get_icon() });
|
||||
this._content.add_actor(icon);
|
||||
this._content.add_actor(this.moreIcon);
|
||||
},
|
||||
|
||||
animateLaunch: function() {
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let app = appSys.lookup_app(this.provider.appInfo.get_id());
|
||||
if (app.state == Shell.AppState.STOPPED)
|
||||
IconGrid.zoomOutActor(this._content);
|
||||
}
|
||||
});
|
||||
|
@ -10,13 +10,14 @@ const FileUtils = imports.misc.fileUtils;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
|
||||
const DEFAULT_MODE = 'restrictive';
|
||||
|
||||
const _modes = {
|
||||
'restrictive': {
|
||||
parentMode: null,
|
||||
stylesheetName: 'gnome-shell.css',
|
||||
overridesSchema: 'org.gnome.shell.overrides',
|
||||
hasOverview: false,
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
@ -92,8 +93,12 @@ const _modes = {
|
||||
isLocked: false,
|
||||
isPrimary: true,
|
||||
unlockDialog: imports.ui.unlockDialog.UnlockDialog,
|
||||
components: ['networkAgent', 'polkitAgent', 'telepathyClient',
|
||||
components: Config.HAVE_NETWORKMANAGER ?
|
||||
['networkAgent', 'polkitAgent', 'telepathyClient',
|
||||
'keyring', 'autorunManager', 'automountManager'] :
|
||||
['polkitAgent', 'telepathyClient',
|
||||
'keyring', 'autorunManager', 'automountManager'],
|
||||
|
||||
panel: {
|
||||
left: ['activities', 'appMenu'],
|
||||
center: ['dateMenu'],
|
||||
@ -102,19 +107,12 @@ const _modes = {
|
||||
}
|
||||
};
|
||||
|
||||
function _getModes(modesLoadedCallback) {
|
||||
FileUtils.collectFromDatadirsAsync('modes',
|
||||
{ processFile: _loadMode,
|
||||
loadedCallback: modesLoadedCallback,
|
||||
data: _modes });
|
||||
}
|
||||
|
||||
function _loadMode(file, info, loadedData) {
|
||||
function _loadMode(file, info) {
|
||||
let name = info.get_name();
|
||||
let suffix = name.indexOf('.json');
|
||||
let modeName = suffix == -1 ? name : name.slice(name, suffix);
|
||||
|
||||
if (loadedData.hasOwnProperty(modeName))
|
||||
if (_modes.hasOwnProperty(modeName))
|
||||
return;
|
||||
|
||||
let fileContent, success, tag, newMode;
|
||||
@ -125,41 +123,43 @@ function _loadMode(file, info, loadedData) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadedData[modeName] = {};
|
||||
_modes[modeName] = {};
|
||||
let propBlacklist = ['unlockDialog'];
|
||||
for (let prop in loadedData[DEFAULT_MODE]) {
|
||||
for (let prop in _modes[DEFAULT_MODE]) {
|
||||
if (newMode[prop] !== undefined &&
|
||||
propBlacklist.indexOf(prop) == -1)
|
||||
loadedData[modeName][prop]= newMode[prop];
|
||||
_modes[modeName][prop] = newMode[prop];
|
||||
}
|
||||
loadedData[modeName]['isPrimary'] = true;
|
||||
_modes[modeName]['isPrimary'] = true;
|
||||
}
|
||||
|
||||
function _loadModes() {
|
||||
FileUtils.collectFromDatadirs('modes', false, _loadMode);
|
||||
}
|
||||
|
||||
function listModes() {
|
||||
_getModes(function(modes) {
|
||||
let names = Object.getOwnPropertyNames(modes);
|
||||
_loadModes();
|
||||
let id = Mainloop.idle_add(function() {
|
||||
let names = Object.getOwnPropertyNames(_modes);
|
||||
for (let i = 0; i < names.length; i++)
|
||||
if (_modes[names[i]].isPrimary)
|
||||
print(names[i]);
|
||||
Mainloop.quit('listModes');
|
||||
});
|
||||
GLib.Source.set_name_by_id(id, '[gnome-shell] listModes');
|
||||
Mainloop.run('listModes');
|
||||
}
|
||||
|
||||
const SessionMode = new Lang.Class({
|
||||
Name: 'SessionMode',
|
||||
|
||||
init: function() {
|
||||
_getModes(Lang.bind(this, function(modes) {
|
||||
this._modes = modes;
|
||||
let primary = modes[global.session_mode] &&
|
||||
modes[global.session_mode].isPrimary;
|
||||
let mode = primary ? global.session_mode : 'user';
|
||||
this._modeStack = [mode];
|
||||
this._sync();
|
||||
|
||||
this.emit('sessions-loaded');
|
||||
}));
|
||||
_init: function() {
|
||||
_loadModes();
|
||||
let isPrimary = (_modes[global.session_mode] &&
|
||||
_modes[global.session_mode].isPrimary);
|
||||
let mode = isPrimary ? global.session_mode : 'user';
|
||||
this._modeStack = [mode];
|
||||
this._sync();
|
||||
},
|
||||
|
||||
pushMode: function(mode) {
|
||||
@ -186,13 +186,13 @@ const SessionMode = new Lang.Class({
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let params = this._modes[this.currentMode];
|
||||
let params = _modes[this.currentMode];
|
||||
let defaults;
|
||||
if (params.parentMode)
|
||||
defaults = Params.parse(this._modes[params.parentMode],
|
||||
this._modes[DEFAULT_MODE]);
|
||||
defaults = Params.parse(_modes[params.parentMode],
|
||||
_modes[DEFAULT_MODE]);
|
||||
else
|
||||
defaults = this._modes[DEFAULT_MODE];
|
||||
defaults = _modes[DEFAULT_MODE];
|
||||
params = Params.parse(params, defaults);
|
||||
|
||||
// A simplified version of Lang.copyProperties, handles
|
||||
|
@ -10,7 +10,6 @@ const Config = imports.misc.config;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Hash = imports.misc.hash;
|
||||
const Main = imports.ui.main;
|
||||
const Screenshot = imports.ui.screenshot;
|
||||
const ViewSelector = imports.ui.viewSelector;
|
||||
@ -70,6 +69,7 @@ const ScreenSaverIface = '<node> \
|
||||
<signal name="ActiveChanged"> \
|
||||
<arg name="new_value" type="b" /> \
|
||||
</signal> \
|
||||
<signal name="WakeUpScreen" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
@ -83,8 +83,8 @@ const GnomeShell = new Lang.Class({
|
||||
this._extensionsService = new GnomeShellExtensions();
|
||||
this._screenshotService = new Screenshot.ScreenshotService();
|
||||
|
||||
this._grabbedAccelerators = new Hash.Map();
|
||||
this._grabbers = new Hash.Map();
|
||||
this._grabbedAccelerators = new Map();
|
||||
this._grabbers = new Map();
|
||||
|
||||
global.display.connect('accelerator-activated', Lang.bind(this,
|
||||
function(display, action, deviceid, timestamp) {
|
||||
@ -119,7 +119,7 @@ const GnomeShell = new Lang.Class({
|
||||
returnValue = '';
|
||||
success = true;
|
||||
} catch (e) {
|
||||
returnValue = JSON.stringify(e);
|
||||
returnValue = '' + e;
|
||||
success = false;
|
||||
}
|
||||
return [success, returnValue];
|
||||
@ -133,15 +133,15 @@ const GnomeShell = new Lang.Class({
|
||||
for (let param in params)
|
||||
params[param] = params[param].deep_unpack();
|
||||
|
||||
let monitorIndex = -1;
|
||||
if (params['monitor'])
|
||||
monitorIndex = params['monitor'];
|
||||
|
||||
let icon = null;
|
||||
if (params['icon'])
|
||||
icon = Gio.Icon.new_for_string(params['icon']);
|
||||
|
||||
Main.osdWindow.setIcon(icon);
|
||||
Main.osdWindow.setLabel(params['label']);
|
||||
Main.osdWindow.setLevel(params['level']);
|
||||
|
||||
Main.osdWindow.show();
|
||||
Main.osdWindowManager.show(monitorIndex, icon, params['label'], params['level']);
|
||||
},
|
||||
|
||||
FocusApp: function(id) {
|
||||
@ -223,9 +223,8 @@ const GnomeShell = new Lang.Class({
|
||||
},
|
||||
|
||||
_onGrabberBusNameVanished: function(connection, name) {
|
||||
let grabs = this._grabbedAccelerators.items();
|
||||
for (let i = 0; i < grabs.length; i++) {
|
||||
let [action, sender] = grabs[i];
|
||||
let grabs = this._grabbedAccelerators.entries();
|
||||
for (let [action, sender] of grabs) {
|
||||
if (sender == name)
|
||||
this._ungrabAccelerator(action);
|
||||
}
|
||||
@ -368,8 +367,10 @@ const GnomeShellExtensions = new Lang.Class({
|
||||
LaunchExtensionPrefs: function(uuid) {
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let app = appSys.lookup_app('gnome-shell-extension-prefs.desktop');
|
||||
app.launch(global.display.get_current_time_roundtrip(),
|
||||
['extension:///' + uuid], -1, null);
|
||||
let info = app.get_app_info();
|
||||
let timestamp = global.display.get_current_time_roundtrip();
|
||||
info.launch_uris(['extension:///' + uuid],
|
||||
global.create_app_launch_context(timestamp, -1));
|
||||
},
|
||||
|
||||
ReloadExtension: function(uuid) {
|
||||
@ -402,6 +403,9 @@ const ScreenSaverDBus = new Lang.Class({
|
||||
screenShield.connect('active-changed', Lang.bind(this, function(shield) {
|
||||
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
|
||||
}));
|
||||
screenShield.connect('wake-up-screen', Lang.bind(this, function(shield) {
|
||||
this._dbusImpl.emit_signal('WakeUpScreen', null);
|
||||
}));
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
|
||||
|
@ -17,8 +17,6 @@ const EntryMenu = new Lang.Class({
|
||||
_init: function(entry) {
|
||||
this.parent(entry, 0, St.Side.TOP);
|
||||
|
||||
this.actor.add_style_class_name('entry-context-menu');
|
||||
|
||||
this._entry = entry;
|
||||
this._clipboard = St.Clipboard.get_default();
|
||||
|
||||
@ -132,14 +130,14 @@ function _setMenuAlignment(entry, stageX) {
|
||||
function _onButtonPressEvent(actor, event, entry) {
|
||||
if (entry.menu.isOpen) {
|
||||
entry.menu.close(BoxPointer.PopupAnimation.FULL);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
} else if (event.get_button() == 3) {
|
||||
let [stageX, stageY] = event.get_coords();
|
||||
_setMenuAlignment(entry, stageX);
|
||||
entry.menu.open(BoxPointer.PopupAnimation.FULL);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
};
|
||||
|
||||
function _onPopup(actor, entry) {
|
||||
|
@ -1,11 +1,11 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Atk = imports.gi.Atk;
|
||||
const Cairo = imports.cairo;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
|
||||
|
||||
@ -24,6 +24,7 @@ const Slider = new Lang.Class({
|
||||
accessible_role: Atk.Role.SLIDER });
|
||||
this.actor.connect('repaint', Lang.bind(this, this._sliderRepaint));
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
|
||||
this.actor.connect('touch-event', Lang.bind(this, this._touchDragging));
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
this.actor.connect('key-press-event', Lang.bind(this, this.onKeyPressEvent));
|
||||
|
||||
@ -111,39 +112,74 @@ const Slider = new Lang.Class({
|
||||
},
|
||||
|
||||
_startDragging: function(actor, event) {
|
||||
this.startDragging(event);
|
||||
return this.startDragging(event);
|
||||
},
|
||||
|
||||
startDragging: function(event) {
|
||||
if (this._dragging)
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
this._dragging = true;
|
||||
|
||||
let device = event.get_device();
|
||||
device.grab(this.actor);
|
||||
this._grabbedDevice = device;
|
||||
let sequence = event.get_event_sequence();
|
||||
|
||||
if (sequence != null)
|
||||
device.sequence_grab(sequence, this.actor);
|
||||
else
|
||||
device.grab(this.actor);
|
||||
|
||||
this._grabbedDevice = device;
|
||||
this._grabbedSequence = sequence;
|
||||
|
||||
if (sequence == null) {
|
||||
this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging));
|
||||
this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent));
|
||||
}
|
||||
|
||||
this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging));
|
||||
this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent));
|
||||
let absX, absY;
|
||||
[absX, absY] = event.get_coords();
|
||||
this._moveHandle(absX, absY);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_endDragging: function() {
|
||||
if (this._dragging) {
|
||||
this.actor.disconnect(this._releaseId);
|
||||
this.actor.disconnect(this._motionId);
|
||||
if (this._releaseId)
|
||||
this.actor.disconnect(this._releaseId);
|
||||
if (this._motionId)
|
||||
this.actor.disconnect(this._motionId);
|
||||
|
||||
this._grabbedDevice.ungrab();
|
||||
if (this._grabbedSequence != null)
|
||||
this._grabbedDevice.sequence_ungrab(this._grabbedSequence);
|
||||
else
|
||||
this._grabbedDevice.ungrab();
|
||||
|
||||
this._grabbedSequence = null;
|
||||
this._grabbedDevice = null;
|
||||
this._dragging = false;
|
||||
|
||||
this.emit('drag-end');
|
||||
}
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_touchDragging: function(actor, event) {
|
||||
let device = event.get_device();
|
||||
let sequence = event.get_event_sequence();
|
||||
|
||||
if (!this._dragging &&
|
||||
event.type() == Clutter.EventType.TOUCH_BEGIN) {
|
||||
this.startDragging(event);
|
||||
return Clutter.EVENT_STOP;
|
||||
} else if (device.sequence_get_grabbed_actor(sequence) == actor) {
|
||||
if (event.type() == Clutter.EventType.TOUCH_UPDATE)
|
||||
return this._motionEvent(actor, event);
|
||||
else if (event.type() == Clutter.EventType.TOUCH_END)
|
||||
return this._endDragging();
|
||||
}
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
scroll: function(event) {
|
||||
@ -151,7 +187,7 @@ const Slider = new Lang.Class({
|
||||
let delta;
|
||||
|
||||
if (event.is_pointer_emulated())
|
||||
return;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
delta = -SLIDER_SCROLL_STEP;
|
||||
@ -168,17 +204,18 @@ const Slider = new Lang.Class({
|
||||
|
||||
this.actor.queue_repaint();
|
||||
this.emit('value-changed', this._value);
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
this.scroll(event);
|
||||
return this.scroll(event);
|
||||
},
|
||||
|
||||
_motionEvent: function(actor, event) {
|
||||
let absX, absY;
|
||||
[absX, absY] = event.get_coords();
|
||||
this._moveHandle(absX, absY);
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
},
|
||||
|
||||
onKeyPressEvent: function (actor, event) {
|
||||
@ -189,9 +226,9 @@ const Slider = new Lang.Class({
|
||||
this.actor.queue_repaint();
|
||||
this.emit('value-changed', this._value);
|
||||
this.emit('drag-end');
|
||||
return true;
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
return false;
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_moveHandle: function(absX, absY) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const St = imports.gi.St;
|
||||
@ -43,11 +44,11 @@ const ATIndicator = new Lang.Class({
|
||||
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
|
||||
this._hbox.add_child(new St.Icon({ style_class: 'system-status-icon',
|
||||
icon_name: 'preferences-desktop-accessibility-symbolic' }));
|
||||
this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
|
||||
this._hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
|
||||
|
||||
this.actor.add_child(this._hbox);
|
||||
|
||||
this._a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
|
||||
this._a11ySettings = new Gio.Settings({ schema_id: A11Y_SCHEMA });
|
||||
this._a11ySettings.connect('changed::' + KEY_ALWAYS_SHOW, Lang.bind(this, this._queueSyncMenuVisibility));
|
||||
|
||||
let highContrast = this._buildHCItem();
|
||||
@ -94,7 +95,7 @@ const ATIndicator = new Lang.Class({
|
||||
|
||||
this.actor.visible = alwaysShow || items.some(function(f) { return !!f.state; });
|
||||
|
||||
return false;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
},
|
||||
|
||||
_queueSyncMenuVisibility: function() {
|
||||
@ -102,6 +103,7 @@ const ATIndicator = new Lang.Class({
|
||||
return;
|
||||
|
||||
this._syncMenuVisbilityIdle = Mainloop.idle_add(Lang.bind(this, this._syncMenuVisibility));
|
||||
GLib.Source.set_name_by_id(this._syncMenuVisbilityIdle, '[gnome-shell] this._syncMenuVisibility');
|
||||
},
|
||||
|
||||
_buildItemExtended: function(string, initial_value, writable, on_set) {
|
||||
@ -116,7 +118,7 @@ const ATIndicator = new Lang.Class({
|
||||
},
|
||||
|
||||
_buildItem: function(string, schema, key) {
|
||||
let settings = new Gio.Settings({ schema: schema });
|
||||
let settings = new Gio.Settings({ schema_id: schema });
|
||||
let widget = this._buildItemExtended(string,
|
||||
settings.get_boolean(key),
|
||||
settings.is_writable(key),
|
||||
@ -132,8 +134,8 @@ const ATIndicator = new Lang.Class({
|
||||
},
|
||||
|
||||
_buildHCItem: function() {
|
||||
let interfaceSettings = new Gio.Settings({ schema: DESKTOP_INTERFACE_SCHEMA });
|
||||
let wmSettings = new Gio.Settings({ schema: WM_SCHEMA });
|
||||
let interfaceSettings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA });
|
||||
let wmSettings = new Gio.Settings({ schema_id: WM_SCHEMA });
|
||||
let gtkTheme = interfaceSettings.get_string(KEY_GTK_THEME);
|
||||
let iconTheme = interfaceSettings.get_string(KEY_ICON_THEME);
|
||||
let wmTheme = wmSettings.get_string(KEY_WM_THEME);
|
||||
@ -184,7 +186,7 @@ const ATIndicator = new Lang.Class({
|
||||
},
|
||||
|
||||
_buildFontItem: function() {
|
||||
let settings = new Gio.Settings({ schema: DESKTOP_INTERFACE_SCHEMA });
|
||||
let settings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA });
|
||||
|
||||
let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
|
||||
let initial_setting = (factor > 1.0);
|
||||
|
@ -2,16 +2,27 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GnomeBluetooth = imports.gi.GnomeBluetooth;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
|
||||
|
||||
const RfkillManagerInterface = '<node> \
|
||||
<interface name="org.gnome.SettingsDaemon.Rfkill"> \
|
||||
<property name="BluetoothAirplaneMode" type="b" access="readwrite" /> \
|
||||
<property name="BluetoothHasAirplaneMode" type="b" access="read" /> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'BTIndicator',
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
@ -22,238 +33,76 @@ const Indicator = new Lang.Class({
|
||||
this._indicator = this._addIndicator();
|
||||
this._indicator.icon_name = 'bluetooth-active-symbolic';
|
||||
|
||||
this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
this._sync();
|
||||
}));
|
||||
this._proxy.connect('g-properties-changed', Lang.bind(this, this._sync));
|
||||
|
||||
// The Bluetooth menu only appears when Bluetooth is in use,
|
||||
// so just statically build it with a "Turn Off" menu item.
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
|
||||
this._item.icon.icon_name = 'bluetooth-active-symbolic';
|
||||
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
|
||||
this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
|
||||
this._proxy.BluetoothAirplaneMode = true;
|
||||
}));
|
||||
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
|
||||
this.menu.addMenuItem(this._item);
|
||||
|
||||
this._applet = new GnomeBluetoothApplet.Applet();
|
||||
this._applet.connect('devices-changed', Lang.bind(this, this._sync));
|
||||
this._client = new GnomeBluetooth.Client();
|
||||
this._model = this._client.get_model();
|
||||
this._model.connect('row-changed', Lang.bind(this, this._sync));
|
||||
this._model.connect('row-deleted', Lang.bind(this, this._sync));
|
||||
this._model.connect('row-inserted', Lang.bind(this, this._sync));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sync));
|
||||
this._sync();
|
||||
},
|
||||
|
||||
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
|
||||
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
|
||||
this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
|
||||
this._applet.connect('auth-service-request', Lang.bind(this, this._authServiceRequest));
|
||||
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
_getDefaultAdapter: function() {
|
||||
let [ret, iter] = this._model.get_iter_first();
|
||||
while (ret) {
|
||||
let isDefault = this._model.get_value(iter,
|
||||
GnomeBluetooth.Column.DEFAULT);
|
||||
if (isDefault)
|
||||
return iter;
|
||||
ret = this._model.iter_next(iter);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_getNConnectedDevices: function() {
|
||||
let adapter = this._getDefaultAdapter();
|
||||
if (!adapter)
|
||||
return 0;
|
||||
|
||||
let nDevices = 0;
|
||||
let [ret, iter] = this._model.iter_children(adapter);
|
||||
while (ret) {
|
||||
let isConnected = this._model.get_value(iter,
|
||||
GnomeBluetooth.Column.CONNECTED);
|
||||
if (isConnected)
|
||||
nDevices++;
|
||||
ret = this._model.iter_next(iter);
|
||||
}
|
||||
return nDevices;
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
let connectedDevices = this._applet.get_devices().filter(function(device) {
|
||||
return device.connected;
|
||||
});
|
||||
let nDevices = connectedDevices.length;
|
||||
let nDevices = this._getNConnectedDevices();
|
||||
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||
|
||||
let on = nDevices > 0;
|
||||
this._indicator.visible = on;
|
||||
this._item.actor.visible = on;
|
||||
this.menu.setSensitive(sensitive);
|
||||
this._indicator.visible = nDevices > 0;
|
||||
this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode;
|
||||
|
||||
if (on)
|
||||
if (nDevices > 0)
|
||||
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).format(nDevices);
|
||||
},
|
||||
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
|
||||
this._source.policy = new MessageTray.NotificationApplicationPolicy('gnome-bluetooth-panel');
|
||||
Main.messageTray.add(this._source);
|
||||
}
|
||||
},
|
||||
|
||||
_authRequest: function(applet, device_path, name, long_name) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name));
|
||||
},
|
||||
|
||||
_authServiceRequest: function(applet, device_path, name, long_name, uuid) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new AuthServiceNotification(this._source, this._applet, device_path, name, long_name, uuid));
|
||||
},
|
||||
|
||||
_confirmRequest: function(applet, device_path, name, long_name, pin) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new ConfirmNotification(this._source, this._applet, device_path, name, long_name, pin));
|
||||
},
|
||||
|
||||
_pinRequest: function(applet, device_path, name, long_name, numeric) {
|
||||
this._ensureSource();
|
||||
this._source.notify(new PinNotification(this._source, this._applet, device_path, name, long_name, numeric));
|
||||
},
|
||||
|
||||
_cancelRequest: function() {
|
||||
this._source.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
const AuthNotification = new Lang.Class({
|
||||
Name: 'AuthNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source, applet, device_path, name, long_name) {
|
||||
this.parent(source,
|
||||
_("Bluetooth"),
|
||||
_("Authorization request from %s").format(name),
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this._applet = applet;
|
||||
this._devicePath = device_path;
|
||||
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
||||
|
||||
this.addAction('allow', _("Allow"));
|
||||
this.addAction('deny', _("Deny"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
if (action == 'allow')
|
||||
this._applet.agent_reply_confirm(this._devicePath, true);
|
||||
else
|
||||
this._applet.agent_reply_confirm(this._devicePath, false);
|
||||
this.destroy();
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
const AuthServiceNotification = new Lang.Class({
|
||||
Name: 'AuthServiceNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source, applet, device_path, name, long_name, uuid) {
|
||||
this.parent(source,
|
||||
_("Bluetooth"),
|
||||
_("Authorization request from %s").format(name),
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this._applet = applet;
|
||||
this._devicePath = device_path;
|
||||
this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid));
|
||||
|
||||
this.addAction('always-grant', _("Always grant access"));
|
||||
this.addAction('grant', _("Grant this time only"));
|
||||
this.addAction('reject', _("Reject"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'always-grant':
|
||||
this._applet.agent_reply_auth_service(this._devicePath, true, true);
|
||||
break;
|
||||
case 'grant':
|
||||
this._applet.agent_reply_auth_service(this._devicePath, true, false);
|
||||
break;
|
||||
case 'reject':
|
||||
default:
|
||||
this._applet.agent_reply_auth_service(this._devicePath, false, false);
|
||||
}
|
||||
this.destroy();
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
const ConfirmNotification = new Lang.Class({
|
||||
Name: 'ConfirmNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source, applet, device_path, name, long_name, pin) {
|
||||
this.parent(source,
|
||||
_("Bluetooth"),
|
||||
/* Translators: argument is the device short name */
|
||||
_("Pairing confirmation for %s").format(name),
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this._applet = applet;
|
||||
this._devicePath = device_path;
|
||||
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
||||
this.addBody(_("Please confirm whether the Passkey '%06d' matches the one on the device.").format(pin));
|
||||
|
||||
/* Translators: this is the verb, not the noun */
|
||||
this.addAction('matches', _("Matches"));
|
||||
this.addAction('does-not-match', _("Does not match"));
|
||||
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
if (action == 'matches')
|
||||
this._applet.agent_reply_confirm(this._devicePath, true);
|
||||
else
|
||||
this._applet.agent_reply_confirm(this._devicePath, false);
|
||||
this.destroy();
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
const PinNotification = new Lang.Class({
|
||||
Name: 'PinNotification',
|
||||
Extends: MessageTray.Notification,
|
||||
|
||||
_init: function(source, applet, device_path, name, long_name, numeric) {
|
||||
this.parent(source,
|
||||
_("Bluetooth"),
|
||||
_("Pairing request for %s").format(name),
|
||||
{ customContent: true });
|
||||
this.setResident(true);
|
||||
|
||||
this._applet = applet;
|
||||
this._devicePath = device_path;
|
||||
this._numeric = numeric;
|
||||
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
|
||||
this.addBody(_("Please enter the PIN mentioned on the device."));
|
||||
|
||||
this._entry = new St.Entry();
|
||||
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
|
||||
let key = event.get_key_symbol();
|
||||
if (key == Clutter.KEY_Return) {
|
||||
if (this._canActivateOkButton())
|
||||
this._ok();
|
||||
return true;
|
||||
} else if (key == Clutter.KEY_Escape) {
|
||||
this._cancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
this.addActor(this._entry);
|
||||
|
||||
let okButton = this.addAction(_("OK"), Lang.bind(this, this._ok));
|
||||
this.addAction(_("Cancel"), Lang.bind(this, this._cancel));
|
||||
|
||||
okButton.reactive = this._canActivateOkButton();
|
||||
this._entry.clutter_text.connect('text-changed', Lang.bind(this, function() {
|
||||
okButton.reactive = this._canActivateOkButton();
|
||||
}));
|
||||
},
|
||||
|
||||
_ok: function() {
|
||||
if (this._numeric) {
|
||||
let num = parseInt(this._entry.text);
|
||||
if (isNaN(num)) {
|
||||
// user reply was empty, or was invalid
|
||||
// cancel the operation
|
||||
num = -1;
|
||||
}
|
||||
this._applet.agent_reply_passkey(this._devicePath, num);
|
||||
} else {
|
||||
this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
|
||||
}
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
_cancel: function() {
|
||||
if (this._numeric)
|
||||
this._applet.agent_reply_passkey(this._devicePath, -1);
|
||||
else
|
||||
this._applet.agent_reply_pincode(this._devicePath, null);
|
||||
this.destroy();
|
||||
this._item.status.text = _("Not Connected");
|
||||
},
|
||||
|
||||
_canActivateOkButton: function() {
|
||||
// PINs have a fixed length of 6
|
||||
if (this._numeric)
|
||||
return this._entry.clutter_text.text.length == 6;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
@ -48,7 +48,7 @@ const Indicator = new Lang.Class({
|
||||
this._item.actor.add(icon);
|
||||
this._item.actor.add(this._slider.actor, { expand: true });
|
||||
this._item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
|
||||
this._slider.startDragging(event);
|
||||
return this._slider.startDragging(event);
|
||||
}));
|
||||
this._item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) {
|
||||
return this._slider.onKeyPressEvent(actor, event);
|
||||
|
File diff suppressed because it is too large
Load Diff
218
js/ui/status/location.js
Normal file
218
js/ui/status/location.js
Normal file
@ -0,0 +1,218 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const LOCATION_SCHEMA = 'org.gnome.system.location';
|
||||
const MAX_ACCURACY_LEVEL = 'max-accuracy-level';
|
||||
const ENABLED = 'enabled';
|
||||
|
||||
const GeoclueAccuracyLevel = {
|
||||
NONE: 0,
|
||||
COUNTRY: 1,
|
||||
CITY: 4,
|
||||
NEIGHBORHOOD: 5,
|
||||
STREET: 6,
|
||||
EXACT: 8
|
||||
};
|
||||
|
||||
var GeoclueIface = '<node> \
|
||||
<interface name="org.freedesktop.GeoClue2.Manager"> \
|
||||
<property name="InUse" type="b" access="read"/> \
|
||||
<property name="AvailableAccuracyLevel" type="u" access="read"/> \
|
||||
<method name="AddAgent"> \
|
||||
<arg name="id" type="s" direction="in"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const GeoclueManager = Gio.DBusProxy.makeProxyWrapper(GeoclueIface);
|
||||
|
||||
var AgentIface = '<node> \
|
||||
<interface name="org.freedesktop.GeoClue2.Agent"> \
|
||||
<property name="MaxAccuracyLevel" type="u" access="read"/> \
|
||||
<method name="AuthorizeApp"> \
|
||||
<arg name="desktop_id" type="s" direction="in"/> \
|
||||
<arg name="req_accuracy_level" type="u" direction="in"/> \
|
||||
<arg name="authorized" type="b" direction="out"/> \
|
||||
<arg name="allowed_accuracy_level" type="u" direction="out"/> \
|
||||
</method> \
|
||||
</interface> \
|
||||
</node>';
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'LocationIndicator',
|
||||
Extends: PanelMenu.SystemIndicator,
|
||||
|
||||
_init: function() {
|
||||
this.parent();
|
||||
|
||||
this._settings = new Gio.Settings({ schema_id: LOCATION_SCHEMA });
|
||||
this._settings.connect('changed::' + ENABLED,
|
||||
Lang.bind(this, this._onMaxAccuracyLevelChanged));
|
||||
this._settings.connect('changed::' + MAX_ACCURACY_LEVEL,
|
||||
Lang.bind(this, this._onMaxAccuracyLevelChanged));
|
||||
|
||||
this._indicator = this._addIndicator();
|
||||
this._indicator.icon_name = 'find-location-symbolic';
|
||||
|
||||
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Location"), true);
|
||||
this._item.icon.icon_name = 'find-location-symbolic';
|
||||
|
||||
this._agent = Gio.DBusExportedObject.wrapJSObject(AgentIface, this);
|
||||
this._agent.export(Gio.DBus.system, '/org/freedesktop/GeoClue2/Agent');
|
||||
|
||||
this._item.status.text = _("Enabled");
|
||||
this._onOffAction = this._item.menu.addAction(_("Disable"), Lang.bind(this, this._onOnOffAction));
|
||||
this._item.menu.addSettingsAction(_("Privacy Settings"), 'gnome-privacy-panel.desktop');
|
||||
|
||||
this.menu.addMenuItem(this._item);
|
||||
|
||||
this._watchId = Gio.bus_watch_name(Gio.BusType.SYSTEM,
|
||||
'org.freedesktop.GeoClue2',
|
||||
0,
|
||||
Lang.bind(this, this._connectToGeoclue),
|
||||
Lang.bind(this, this._onGeoclueVanished));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._onSessionUpdated));
|
||||
this._onSessionUpdated();
|
||||
this._onMaxAccuracyLevelChanged();
|
||||
this._connectToGeoclue();
|
||||
},
|
||||
|
||||
get MaxAccuracyLevel() {
|
||||
return this._getMaxAccuracyLevel();
|
||||
},
|
||||
|
||||
// We (and geoclue) have currently no way to reliably identifying apps so
|
||||
// for now, lets just authorize all apps as long as they provide a valid
|
||||
// desktop ID. We also ensure they don't get more accuracy than global max.
|
||||
AuthorizeApp: function(desktop_id, reqAccuracyLevel) {
|
||||
var appSystem = Shell.AppSystem.get_default();
|
||||
var app = appSystem.lookup_app(desktop_id + ".desktop");
|
||||
if (app == null) {
|
||||
return [false, 0];
|
||||
}
|
||||
|
||||
let allowedAccuracyLevel = clamp(reqAccuracyLevel, 0, this._getMaxAccuracyLevel());
|
||||
return [true, allowedAccuracyLevel];
|
||||
},
|
||||
|
||||
_syncIndicator: function() {
|
||||
if (this._proxy == null) {
|
||||
this._indicator.visible = false;
|
||||
this._item.actor.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this._indicator.visible = this._proxy.InUse;
|
||||
this._item.actor.visible = this._indicator.visible;
|
||||
this._updateMenuLabels();
|
||||
},
|
||||
|
||||
_connectToGeoclue: function() {
|
||||
if (this._proxy != null || this._connecting)
|
||||
return false;
|
||||
|
||||
this._connecting = true;
|
||||
new GeoclueManager(Gio.DBus.system,
|
||||
'org.freedesktop.GeoClue2',
|
||||
'/org/freedesktop/GeoClue2/Manager',
|
||||
Lang.bind(this, this._onProxyReady));
|
||||
return true;
|
||||
},
|
||||
|
||||
_onProxyReady: function(proxy, error) {
|
||||
if (error != null) {
|
||||
log(error.message);
|
||||
this._connecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this._proxy = proxy;
|
||||
this._propertiesChangedId = this._proxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._onGeocluePropsChanged));
|
||||
|
||||
this._syncIndicator();
|
||||
|
||||
this._proxy.AddAgentRemote('gnome-shell', Lang.bind(this, this._onAgentRegistered));
|
||||
},
|
||||
|
||||
_onAgentRegistered: function(result, error) {
|
||||
this._connecting = false;
|
||||
this._notifyMaxAccuracyLevel();
|
||||
|
||||
if (error != null)
|
||||
log(error.message);
|
||||
},
|
||||
|
||||
_onGeoclueVanished: function() {
|
||||
if (this._propertiesChangedId) {
|
||||
this._proxy.disconnect(this._propertiesChangedId);
|
||||
this._propertiesChangedId = 0;
|
||||
}
|
||||
this._proxy = null;
|
||||
|
||||
this._syncIndicator();
|
||||
},
|
||||
|
||||
_onOnOffAction: function() {
|
||||
let enabled = this._settings.get_boolean(ENABLED);
|
||||
this._settings.set_boolean(ENABLED, !enabled);
|
||||
},
|
||||
|
||||
_onSessionUpdated: function() {
|
||||
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
||||
this.menu.setSensitive(sensitive);
|
||||
},
|
||||
|
||||
_updateMenuLabels: function() {
|
||||
if (this._settings.get_boolean(ENABLED)) {
|
||||
this._item.status.text = this._indicator.visible ? _("In Use") : _("Enabled");
|
||||
this._onOffAction.label.text = _("Disable");
|
||||
} else {
|
||||
this._item.status.text = _("Disabled");
|
||||
this._onOffAction.label.text = _("Enable");
|
||||
}
|
||||
},
|
||||
|
||||
_onMaxAccuracyLevelChanged: function() {
|
||||
this._updateMenuLabels();
|
||||
|
||||
// Gotta ensure geoclue is up and we are registered as agent to it
|
||||
// before we emit the notify for this property change.
|
||||
if (!this._connectToGeoclue())
|
||||
this._notifyMaxAccuracyLevel();
|
||||
},
|
||||
|
||||
_getMaxAccuracyLevel: function() {
|
||||
if (this._settings.get_boolean(ENABLED)) {
|
||||
let level = this._settings.get_string(MAX_ACCURACY_LEVEL);
|
||||
|
||||
return GeoclueAccuracyLevel[level.toUpperCase()] ||
|
||||
GeoclueAccuracyLevel.NONE;
|
||||
} else {
|
||||
return GeoclueAccuracyLevel.NONE;
|
||||
}
|
||||
},
|
||||
|
||||
_notifyMaxAccuracyLevel: function() {
|
||||
let variant = new GLib.Variant('u', this._getMaxAccuracyLevel());
|
||||
this._agent.emit_property_changed('MaxAccuracyLevel', variant);
|
||||
},
|
||||
|
||||
_onGeocluePropsChanged: function(proxy, properties) {
|
||||
let unpacked = properties.deep_unpack();
|
||||
if ("InUse" in unpacked)
|
||||
this._syncIndicator();
|
||||
}
|
||||
});
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user